]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/ia32/32/jdb_kern_info-bench-ia32-32.cpp
update
[l4.git] / kernel / fiasco / src / jdb / ia32 / 32 / jdb_kern_info-bench-ia32-32.cpp
1 IMPLEMENTATION:
2
3 #include <cstdio>
4 #include "cpu.h"
5 #include "div32.h"
6 #include "gdt.h"
7 #include "simpleio.h"
8 #include "static_init.h"
9 #include "timer.h"
10
11 static void
12 Jdb_kern_info_bench::show_time(Unsigned64 time, Unsigned32 rounds,
13                                const char *descr)
14 {
15   Unsigned64 cycs = div32(time, rounds);
16   printf("  %-24s %6lld.%lld cycles\n",
17       descr, cycs, div32(time-cycs*rounds, rounds/10));
18 }
19
20 IMPLEMENT inline
21 Unsigned64
22 Jdb_kern_info_bench::get_time_now()
23 { return Cpu::rdtsc(); }
24
25 #define inst_wbinvd                                                     \
26   asm volatile ("wbinvd")
27
28 #define inst_invlpg                                                     \
29   asm volatile ("invlpg %0"                                             \
30                 : : "m" (*(char*)Mem_layout::Kmem_tmp_page_1))
31
32 #define inst_read_cr3                                                   \
33   asm volatile ("mov %%cr3,%0"                                          \
34                 : "=r"(dummy))
35
36 #define inst_reload_cr3                                                 \
37   asm volatile ("mov %%cr3,%0; mov %0,%%cr3"                            \
38                 : "=r"(dummy))
39
40 #define inst_clts                                                       \
41   asm volatile ("clts")
42
43 #define inst_cli_sti                                                    \
44   asm volatile ("cli; sti")
45
46 #define inst_set_cr0_ts                                                 \
47   asm volatile ("mov %%cr0,%0; or %1,%0; mov %0,%%cr0"          \
48                 : "=r" (dummy) : "i" (CR0_TS))
49
50 #define inst_push_pop                                                   \
51   asm volatile ("push %eax; pop %eax")
52
53 #define inst_pushf_pop                                                  \
54   asm volatile ("pushf; pop %%eax" : : : "eax")
55
56 #define inst_in8_pic                                                    \
57   asm volatile ("inb $0x21, %%al" : "=a" (dummy))
58
59 #define inst_in8_80                                                     \
60   asm volatile ("inb $0x80, %%al" : "=a" (dummy))
61
62 #define inst_out8_pic                                                   \
63   asm volatile ("outb %%al, $0x21" : : "a" (0xff))
64
65 #define inst_apic_timer_read                                            \
66   (volatile Unsigned32)Apic::timer_reg_read()
67
68 #define BENCH(name, instruction, rounds)                                \
69   do                                                                    \
70     {                                                                   \
71       time = Cpu::rdtsc();                                              \
72       for (i=rounds; i; i--)                                            \
73         instruction;                                                    \
74       time = Cpu::rdtsc() - time;                                       \
75       show_time (time, rounds, name);                                   \
76     } while (0)
77
78
79 #include "apic.h"
80 #include "cpu.h"
81
82 IMPLEMENT
83 void
84 Jdb_kern_info_bench::show_arch()
85 {
86   Unsigned64 time;
87   Mword dummy;
88   Mword cr0, pic;
89   Unsigned32 time_reload_cr3, time_invlpg;
90   register int i;
91   Gdt *gdt = Cpu::boot_cpu()->get_gdt();
92   Unsigned32 flags = Proc::cli_save();
93
94   if (!Cpu::boot_cpu()->tsc())
95     return;
96
97   // we need a cached, non-global mapping for measuring the time to load
98   // TLB entries
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);
101
102     {
103       time = Cpu::rdtsc();
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");
108     }
109   BENCH("wbinvd",               inst_wbinvd,         5000);
110   BENCH("invlpg",               inst_invlpg,       200000);
111   time_invlpg = time;
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);
118   Proc::cli();
119   BENCH("set CR0.ts",           inst_set_cr0_ts,   200000);
120   Cpu::set_cr0(cr0);
121   pic = Io::in8(0x21);
122   BENCH("in8(PIC)",             inst_in8_pic,      200000);
123   BENCH("in8(iodelay)",         inst_in8_80,       200000);
124   BENCH("out8(PIC)",            inst_out8_pic,     200000);
125   Io::out8(pic, 0x21);
126     {
127       // read ES segment descriptor
128       time = Cpu::rdtsc();
129       asm volatile ("mov $200000,%%ebx\n\t"
130                     ".align 8; 1: mov %%es, %%eax; dec %%ebx; jnz 1b"
131                     : : : "eax", "ebx");
132       time = Cpu::rdtsc() - time;
133       show_time(time, 200000, "get ES");
134     }
135     {
136       // set ES segment descriptor and access memory through the ES segment
137       time = Cpu::rdtsc();
138       asm volatile ("mov  $200000,%%ebx         \n\t"
139                     ".align 8                   \n\t"
140                     "1:                         \n\t"
141                     "mov  %%eax, %%es           \n\t"
142                     "dec  %%ebx                 \n\t"
143                     "mov  %%es:(%c1),%%edi      \n\t"
144                     "jnz  1b                    \n\t"
145                     :
146                     : "a"(Gdt::gdt_data_kernel | Gdt::Selector_kernel),
147                       "i"(Mem_layout::Kernel_image)
148                     : "ebx", "edi");
149       time = Cpu::rdtsc() - time;
150       show_time(time, 200000, "set ES + load ES:mem");
151     }
152     {
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];
156       time = Cpu::rdtsc();
157       asm volatile ("mov  $200000,%%ebx         \n\t"
158                     ".align 16                  \n\t"
159                     "1:                         \n\t"
160                     "mov  %%eax, (%%esi)        \n\t"
161                     "mov  %%ecx, 4(%%esi)       \n\t"
162                     "mov  %%edx, %%es           \n\t"
163                     "dec  %%ebx                 \n\t"
164                     "mov  %%es:(%c4),%%edi      \n\t"
165                     "jnz  1b                    \n\t"
166                     :
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)
171                     : "ebx", "edi");
172       time = Cpu::rdtsc() - time;
173       show_time(time, 200000, "modify ES + load ES:mem");
174     }
175     {
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
188                     "push %%ds                  \n\t"
189                     "push %%es                  \n\t" 
190                     "push %%fs                  \n\t" 
191                     "push %%gs                  \n\t" 
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
215
216                     "rdtsc                      \n\t"
217                     "mov  %%eax, %%esi          \n\t"
218                     "movl $200001, %%eax        \n\t"
219
220                     ".align 4                   \n\t"
221                     "1:                         \n\t"
222                     "dec  %%eax                 \n\t"
223                     "jz   2f                    \n\t"
224                     "iret                       \n\t"
225
226                     "2:                         \n\t"
227                     "rdtsc                      \n\t"
228                     "mov  %%edi, %%esp          \n\t" // restore esp
229                     "sub  %%esi, %%eax          \n\t"
230                     "pop  %%gs                  \n\t"
231                     "pop  %%fs                  \n\t"
232                     "pop  %%es                  \n\t"
233                     "pop  %%ds                  \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
238                     "popf                       \n\t"
239
240                     : "=A"(time),"=c"(dummy)
241                     : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-4),
242                       "i"(Mem_layout::Idt + (0x1f<<3)),
243                       "i"(0x1f),
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");
251     }
252   if (Cpu::have_sysenter())
253     {
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"
261                     "rdmsr                      \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"
267                     "rdtsc                      \n\t"
268                     "mov  %%eax, %%esi          \n\t"
269                     "movl $200001, %%eax        \n\t"
270                     "mov  $%c1, %%edx           \n\t"
271
272                     ".align 8                   \n\t"
273                     "1:                         \n\t"
274                     "dec  %%eax                 \n\t"
275                     "jz   2f                    \n\t"
276                     "sysexit                    \n\t"
277
278                     "2:                         \n\t"
279                     "rdtsc                      \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"
284                     "wrmsr                      \n\t"
285
286                     "mov  %%esi, %%eax          \n\t"
287                     "neg  %%eax                 \n\t"
288                     "mov  %%edi, %%esp          \n\t" // restore esp
289                     "popf                       \n\t"
290                     : "=A"(time)
291                     : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-2)
292                     : "ebx", "ecx", "esi", "edi", "memory");
293       show_time(time, 200000, "sysenter + sysexit");
294     }
295   if (Cpu::have_sysenter())
296     {
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 ;
299       // sysenter".
300       asm volatile ("pushf                      \n\t"
301                     "push %%ebp                 \n\t"
302                     "push %%ds                  \n\t"
303                     "push %%es                  \n\t"
304                     "push %%fs                  \n\t"
305                     "push %%gs                  \n\t"
306                     "mov  $0x176, %%ecx         \n\t"
307                     "rdmsr                      \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"
320                     "rdmsr                      \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
329
330                     "rdtsc                      \n\t"
331                     "mov  %%eax, %%esi          \n\t"
332                     "movl $200001, %%eax        \n\t"
333
334                     ".align 8                   \n\t"
335                     "1:                         \n\t"
336                     "dec  %%eax                 \n\t"
337                     "jz   2f                    \n\t"
338                     "iret                       \n\t"
339
340                     "2:                         \n\t"
341                     "rdtsc                      \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
350
351                     "mov  %%edi, %%esp          \n\t" // restore esp
352                     "pop  %%gs                  \n\t"
353                     "pop  %%fs                  \n\t"
354                     "pop  %%es                  \n\t"
355                     "pop  %%ds                  \n\t"
356                     "mov  %%esi, %%eax          \n\t"
357                     "neg  %%eax                 \n\t"
358                     "pop  %%ebp                 \n\t"
359                     "popf                       \n\t"
360                     : "=A"(time)
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");
366     }
367   if (Cpu::have_syscall())
368     {
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::disable();
375       Proc::sti();
376       Proc::irq_chance();
377       asm volatile ("pushf                      \n\t"
378                     "push %%ebp                 \n\t"
379                     "mov  $0xC0000080, %%ecx    \n\t"
380                     "sub  %%edx, %%edx          \n\t"
381                     "movl $1, %%eax             \n\t" // enable syscall
382                     "wrmsr                      \n\t"
383                     "mov  $0xC0000081, %%ecx    \n\t"
384                     "rdmsr                      \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
389                     "wrmsr                      \n\t"
390                     "mov  %%esp, %%edi          \n\t"
391                     "movw $0x050f, %c1          \n\t"
392                     "rdtsc                      \n\t"
393                     "mov  %%eax, %%esi          \n\t"
394                     "movl $200001, %%eax        \n\t"
395                     "mov  $(%c1+2), %%ecx       \n\t"
396
397                     ".align 8                   \n\t"
398                     "1:                         \n\t"
399                     "dec  %%eax                 \n\t"
400                     "jz   2f                    \n\t"
401                     "sub  $2, %%ecx             \n\t"
402                     "sysret                     \n\t"
403
404                     "2:                         \n\t"
405                     "rdtsc                      \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
411
412                     "mov  %%esi, %%eax          \n\t"
413                     "neg  %%eax                 \n\t"
414                     "mov  %%edi, %%esp          \n\t"
415                     "pop  %%ebp                 \n\t"
416                     "sub  %%edx, %%edx          \n\t"
417                     "popf                       \n\t"
418                     : "=A"(time)
419                     : "i"(Mem_layout::Tbuf_status_page + Config::PAGE_SIZE-2)
420                     : "ebx", "ecx", "esi", "edi", "memory");
421       Proc::cli();
422       Timer::enable();
423       show_time(time, 200000, "syscall + sysret");
424     }
425   BENCH("push EAX + pop EAX",   inst_push_pop,     200000);
426   BENCH("push flags + pop EAX", inst_pushf_pop,    200000);
427     {
428       time = Cpu::rdtsc();
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");
434     }
435   if (Cpu::boot_cpu()->local_features() & Cpu::Lf_rdpmc)
436     {
437       time = Cpu::rdtsc();
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)");
443     }
444   if (Cpu::boot_cpu()->local_features() & Cpu::Lf_rdpmc32)
445     {
446       time = Cpu::rdtsc();
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)");
452     }
453   if (Config::apic)
454     {
455       BENCH("APIC timer read", inst_apic_timer_read, 200000);
456     }
457     {
458       time = Cpu::rdtsc();
459       for (i=200000; i; i--)
460         asm volatile ("invlpg %c2       \n\t"
461                       "mov %c2, %1      \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)");
466     }
467     {
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;
472
473       Mem::barrier();
474       time = Cpu::rdtsc();
475       asm volatile ("call  *%%ecx"
476                     : "=a"(dummy)
477                     : "c"(Mem_layout::Jdb_bench_page + 0xff0), "a"(200000)
478                     : "edx");
479
480       time = Cpu::rdtsc() - time - time_reload_cr3;
481       show_time (time, 200000, "load code TLB (4k)");
482     }
483
484   Proc::sti_restore(flags);
485 }