8 #include "static_init.h"
9 #include "timer_tick.h"
12 Jdb_kern_info_bench::show_time(Unsigned64 time, Unsigned32 rounds,
15 Unsigned64 cycs = div32(time, rounds);
16 printf(" %-24s %6lld.%lld cycles\n",
17 descr, cycs, div32(time-cycs*rounds, rounds/10));
22 Jdb_kern_info_bench::get_time_now()
23 { return Cpu::rdtsc(); }
26 asm volatile ("wbinvd")
29 asm volatile ("invlpg %0" \
30 : : "m" (*(char*)Mem_layout::Kmem_tmp_page_1))
32 #define inst_read_cr3 \
33 asm volatile ("mov %%cr3,%0" \
36 #define inst_reload_cr3 \
37 asm volatile ("mov %%cr3,%0; mov %0,%%cr3" \
43 #define inst_cli_sti \
44 asm volatile ("cli; sti")
46 #define inst_set_cr0_ts \
47 asm volatile ("mov %%cr0,%0; or %1,%0; mov %0,%%cr0" \
48 : "=r" (dummy) : "i" (CR0_TS))
50 #define inst_push_pop \
51 asm volatile ("push %eax; pop %eax")
53 #define inst_pushf_pop \
54 asm volatile ("pushf; pop %%eax" : : : "eax")
56 #define inst_in8_pic \
57 asm volatile ("inb $0x21, %%al" : "=a" (dummy))
60 asm volatile ("inb $0x80, %%al" : "=a" (dummy))
62 #define inst_out8_pic \
63 asm volatile ("outb %%al, $0x21" : : "a" (0xff))
65 #define inst_apic_timer_read \
66 (volatile Unsigned32)Apic::timer_reg_read()
68 #define BENCH(name, instruction, rounds) \
71 time = Cpu::rdtsc(); \
72 for (i=rounds; i; i--) \
74 time = Cpu::rdtsc() - time; \
75 show_time (time, rounds, name); \
84 Jdb_kern_info_bench::show_arch()
89 Unsigned32 time_reload_cr3, time_invlpg;
91 Gdt *gdt = Cpu::boot_cpu()->get_gdt();
92 Unsigned32 flags = Proc::cli_save();
94 if (!Cpu::boot_cpu()->tsc())
97 // we need a cached, non-global mapping for measuring the time to load
99 Address phys = Kmem::virt_to_phys((void*)Mem_layout::Tbuf_status_page);
100 Kmem::map_phys_page(phys, Mem_layout::Jdb_bench_page, true, false);
104 asm volatile ("mov $10000000,%%ecx; .align 4;\n\t"
105 "1:dec %%ecx; jnz 1b" : : : "ecx");
106 time = Cpu::rdtsc() - time;
107 show_time(time, 10000000, "1:dec ECX, jnz 1b");
109 BENCH("wbinvd", inst_wbinvd, 5000);
110 BENCH("invlpg", inst_invlpg, 200000);
112 BENCH("read CR3", inst_read_cr3, 200000);
113 BENCH("reload CR3", inst_reload_cr3, 200000);
114 time_reload_cr3 = time;
115 cr0 = Cpu::get_cr0();
116 BENCH("clts", inst_clts, 200000);
117 BENCH("cli + sti", inst_cli_sti, 200000);
119 BENCH("set CR0.ts", inst_set_cr0_ts, 200000);
122 BENCH("in8(PIC)", inst_in8_pic, 200000);
123 BENCH("in8(iodelay)", inst_in8_80, 200000);
124 BENCH("out8(PIC)", inst_out8_pic, 200000);
127 // read ES segment descriptor
129 asm volatile ("mov $200000,%%ebx\n\t"
130 ".align 8; 1: mov %%es, %%eax; dec %%ebx; jnz 1b"
132 time = Cpu::rdtsc() - time;
133 show_time(time, 200000, "get ES");
136 // set ES segment descriptor and access memory through the ES segment
138 asm volatile ("mov $200000,%%ebx \n\t"
141 "mov %%eax, %%es \n\t"
143 "mov %%es:(%c1),%%edi \n\t"
146 : "a"(Gdt::gdt_data_kernel | Gdt::Selector_kernel),
147 "i"(Mem_layout::Kernel_image)
149 time = Cpu::rdtsc() - time;
150 show_time(time, 200000, "set ES + load ES:mem");
153 // write a GDT entry, set ES segment descriptor to this gdt entry
154 // and access memory through the ES segment
155 Unsigned32 *gdt_e = (Unsigned32*)&(*gdt)[Gdt::gdt_data_kernel/8];
157 asm volatile ("mov $200000,%%ebx \n\t"
160 "mov %%eax, (%%esi) \n\t"
161 "mov %%ecx, 4(%%esi) \n\t"
162 "mov %%edx, %%es \n\t"
164 "mov %%es:(%c4),%%edi \n\t"
167 : "a"(gdt_e[0]), "c"(gdt_e[1]),
168 "d"(Gdt::gdt_data_kernel | Gdt::Selector_kernel),
169 "S"(gdt->entries() + Gdt::gdt_data_kernel/8),
170 "i"(Mem_layout::Kernel_image)
172 time = Cpu::rdtsc() - time;
173 show_time(time, 200000, "modify ES + load ES:mem");
176 // Write "1: int 0x1f ; jmp 1b" to the last 4 bytes of the Tbuf status
177 // page (which is accessible by user mode), set IDT entry #0x1f, set
178 // TSS.esp0 and do "iret ; int 0x1f". Take care that the user code
179 // segment is loaded with limit = 0xffffffff if small spaces are active
180 Idt::set_writable(true);
181 Gdt_entry orig = (*gdt)[Gdt::gdt_code_user/8];
182 Unsigned32 *gdt_e = (Unsigned32*)&(*gdt)[Gdt::gdt_code_user/8];
183 gdt_e[0] = 0x0000FFFF;
184 gdt_e[1] = 0x00CFFB00;
185 asm volatile ("pushf \n\t"
186 "push %c3 \n\t" // save IDT entry low
187 "push %c3+4 \n\t" // save IDT entry high
192 "mov %%esp, %%edi \n\t" // save esp
193 "andl $0xffffffc0, %%esp \n\t" // cache line align esp
194 "movl $(%c4*256+0xfceb00cd), %c2 \n\t"
195 "mov $1f, %%eax \n\t" // '1: int 0x1f; jmp 1b'
196 "and $0x0000ffff, %%eax \n\t"
197 "or $0x00080000, %%eax \n\t"
198 "mov %%eax, %c3 \n\t" // change IDT entry low
199 "mov $1f, %%eax \n\t"
200 "and $0xffff0000, %%eax \n\t"
201 "or $0x0000ee00, %%eax \n\t"
202 "mov %%eax, %c3+4 \n\t" // change IDT entry high
203 "mov (%%ecx), %%ebx \n\t" // save kernel esp
204 "push $%c5 \n\t" // user ss
205 "push $0 \n\t" // user esp
206 "push $0x3082 \n\t" // user eflags (IOPL3)
207 "push $%c6 \n\t" // user cs
208 "push $%c2 \n\t" // user eip
209 "mov %%esp, (%%ecx) \n\t" // change kernel esp
210 "mov $%c5, %%eax \n\t"
211 "mov %%eax, %%ds \n\t" // ensure dpl3 at ds
212 "mov %%eax, %%es \n\t" // ensure dpl3 at es
213 "mov %%eax, %%fs \n\t" // ensure dpl3 at fs
214 "mov %%eax, %%gs \n\t" // enusre dpl3 at gs
217 "mov %%eax, %%esi \n\t"
218 "movl $200001, %%eax \n\t"
228 "mov %%edi, %%esp \n\t" // restore esp
229 "sub %%esi, %%eax \n\t"
234 "sub %%edx, %%edx \n\t"
235 "mov %%ebx, (%%ecx) \n\t" // restore kernel esp
236 "pop %c3+4 \n\t" // restore IDT entry high
237 "pop %c3 \n\t" // restore IDT entry low
240 : "=A"(time),"=c"(dummy)
241 : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-4),
242 "i"(Mem_layout::Idt + (0x1f<<3)),
244 "i"(Gdt::gdt_data_user | Gdt::Selector_user),
245 "i"(Gdt::gdt_code_user | Gdt::Selector_user),
246 "c"((Address)Cpu::boot_cpu()->kernel_sp())
247 : "ebx", "esi", "edi", "memory");
248 Idt::set_writable(false);
249 (*gdt)[Gdt::gdt_code_user/8] = orig;
250 show_time(time, 200000, "int + iret");
252 if (Cpu::have_sysenter())
254 // Set Sysenter MSR, write "sysenter" to the last two bytes of the
255 // Tbuf status page (which is accessible from usermode) and do "sysexit ;
256 // sysenter". Gdt doesn't care us since sysexit loads a flat segment.
257 // The sysenter esp also doesn't care us since we don't access esp
258 // inside the measurement loop.
259 asm volatile ("pushf \n\t"
260 "mov $0x176, %%ecx \n\t"
262 "mov %%eax, %%ebx \n\t" // save sysenter eip
263 "mov $1f, %%eax \n\t"
264 "wrmsr \n\t" // change sysenter eip
265 "mov %%esp, %%edi \n\t" // save esp
266 "movw $0x340f, %c1 \n\t"
268 "mov %%eax, %%esi \n\t"
269 "movl $200001, %%eax \n\t"
270 "mov $%c1, %%edx \n\t"
280 "sub %%eax, %%esi \n\t"
281 "mov $0x176, %%ecx \n\t"
282 "mov %%ebx, %%eax \n\t"
283 "sub %%edx, %%edx \n\t"
286 "mov %%esi, %%eax \n\t"
288 "mov %%edi, %%esp \n\t" // restore esp
291 : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-2)
292 : "ebx", "ecx", "esi", "edi", "memory");
293 show_time(time, 200000, "sysenter + sysexit");
295 if (Cpu::have_sysenter())
297 // Set Sysenter MSR, write "sysenter" to the last two bytes of the
298 // Tbuf status page (which is accessible from usermode) and do "iret ;
300 asm volatile ("pushf \n\t"
306 "mov $0x176, %%ecx \n\t"
308 "mov %%eax, %%ebx \n\t" // save sysenter eip
309 "mov $1f, %%eax \n\t"
310 "wrmsr \n\t" // change sysenter eip
311 "mov %%esp, %%edi \n\t" // save esp
312 "andl $0xffffffc0, %%esp \n\t" // cache line align esp
313 "movw $0x340f, %c1 \n\t" // 'sysenter'
314 "push $%c3 \n\t" // user ss
315 "push $0 \n\t" // user esp
316 "push $0x3082 \n\t" // user flags
317 "push $%c2 \n\t" // user cs
318 "push $%c1 \n\t" // user eip
319 "mov $0x175, %%ecx \n\t"
321 "mov %%eax, %%ebp \n\t" // save sysenter esp
322 "mov %%esp, %%eax \n\t"
323 "wrmsr \n\t" // change sysenter esp
324 "mov $%c3, %%eax \n\t"
325 "mov %%eax, %%ds \n\t" // ensure dpl3 at ds
326 "mov %%eax, %%es \n\t" // ensure dpl3 at es
327 "mov %%eax, %%fs \n\t" // ensure dpl3 at fs
328 "mov %%eax, %%gs \n\t" // ensure dpl3 at gs
331 "mov %%eax, %%esi \n\t"
332 "movl $200001, %%eax \n\t"
342 "sub %%eax, %%esi \n\t"
343 "sub %%edx, %%edx \n\t"
344 "mov $0x176, %%ecx \n\t"
345 "mov %%ebx, %%eax \n\t"
346 "wrmsr \n\t" // restore sysenter eip
347 "mov $0x175, %%ecx \n\t"
348 "mov %%ebp, %%eax \n\t"
349 "wrmsr \n\t" // restore sysenter esp
351 "mov %%edi, %%esp \n\t" // restore esp
356 "mov %%esi, %%eax \n\t"
361 : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-2),
362 "i"(Gdt::gdt_code_user | Gdt::Selector_user),
363 "i"(Gdt::gdt_data_user | Gdt::Selector_user)
364 : "ebx", "ecx", "esi", "edi", "memory");
365 show_time(time, 200000, "sysenter + iret");
367 if (Cpu::have_syscall())
369 // Enable syscall/sysret, set Syscall MSR, write "syscall" to the last
370 // two bytes of the Tbuf status page (which is accessible from usermode)
371 // and do "sysret ; syscall". Gdt doesn't care us since sysret loads a
372 // flat segment. Sysret enables the interrupts again so make sure that
373 // we don't receive a timer interrupt.
374 Timer_tick::disable(Cpu_number::boot_cpu());
377 asm volatile ("pushf \n\t"
379 "mov $0xC0000080, %%ecx \n\t"
380 "sub %%edx, %%edx \n\t"
381 "movl $1, %%eax \n\t" // enable syscall
383 "mov $0xC0000081, %%ecx \n\t"
385 "mov %%eax, %%ebx \n\t" // save IA32_STAR
386 "mov %%edx, %%ebp \n\t"
387 "mov $1f, %%eax \n\t" // syscall eip
388 "mov $0x00180008, %%edx \n\t" // syscall cs+ss
390 "mov %%esp, %%edi \n\t"
391 "movw $0x050f, %c1 \n\t"
393 "mov %%eax, %%esi \n\t"
394 "movl $200001, %%eax \n\t"
395 "mov $(%c1+2), %%ecx \n\t"
406 "sub %%eax, %%esi \n\t"
407 "mov $0xC0000081, %%ecx \n\t"
408 "mov %%ebx, %%eax \n\t"
409 "mov %%ebp, %%edx \n\t"
410 "wrmsr \n\t" // restore IA32_STAR
412 "mov %%esi, %%eax \n\t"
414 "mov %%edi, %%esp \n\t"
416 "sub %%edx, %%edx \n\t"
419 : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-2)
420 : "ebx", "ecx", "esi", "edi", "memory");
422 Timer_tick::enable(Cpu_number::boot_cpu());
423 show_time(time, 200000, "syscall + sysret");
425 BENCH("push EAX + pop EAX", inst_push_pop, 200000);
426 BENCH("push flags + pop EAX", inst_pushf_pop, 200000);
429 asm volatile ("mov $200000,%%ebx\n\t"
430 ".align 4; 1: rdtsc; dec %%ebx; jnz 1b"
431 : : : "eax", "ebx", "edx");
432 time = Cpu::rdtsc() - time;
433 show_time(time, 200000, "rdtsc");
435 if (Cpu::boot_cpu()->local_features() & Cpu::Lf_rdpmc)
438 asm volatile ("mov $200000,%%ebx; movl $0x00000000,%%ecx\n\t"
439 ".align 4; 1: rdpmc; dec %%ebx; jnz 1b"
440 : : : "eax", "ebx", "ecx", "edx");
441 time = Cpu::rdtsc() - time;
442 show_time(time, 200000, "rdpmc(0)");
444 if (Cpu::boot_cpu()->local_features() & Cpu::Lf_rdpmc32)
447 asm volatile ("mov $200000,%%ebx; movl $0x80000000,%%ecx\n\t"
448 ".align 4; 1: rdpmc; dec %%ebx; jnz 1b"
449 : : : "eax", "ebx", "ecx", "edx");
450 time = Cpu::rdtsc() - time;
451 show_time(time, 200000, "rdpmc32(0)");
455 BENCH("APIC timer read", (void)inst_apic_timer_read, 200000);
459 for (i=200000; i; i--)
460 asm volatile ("invlpg %c2 \n\t"
462 : "=r" (dummy), "=r" (dummy)
463 : "i"(Mem_layout::Jdb_bench_page));
464 time = Cpu::rdtsc() - time - time_invlpg;
465 show_time (time, 200000, "load data TLB (4k)");
468 // asm ("1: mov %%cr3,%%edx; mov %%edx, %%cr3; dec %%eax; jnz 1b; ret")
469 *(Unsigned32*)(Mem_layout::Jdb_bench_page + 0xff0) = 0x0fda200f;
470 *(Unsigned32*)(Mem_layout::Jdb_bench_page + 0xff4) = 0x7548da22;
471 *(Unsigned32*)(Mem_layout::Jdb_bench_page + 0xff8) = 0xc3f7;
475 asm volatile ("call *%%ecx"
477 : "c"(Mem_layout::Jdb_bench_page + 0xff0), "a"(200000)
480 time = Cpu::rdtsc() - time - time_reload_cr3;
481 show_time (time, 200000, "load code TLB (4k)");
484 Proc::sti_restore(flags);