kvm-unit-tests: Add a func to run instruction in emulator
PATCH 1/2 kvm-unit-tests: Add a func to run instruction in emulator
Add a function trap_emulator to run an instruction in emulator. Set inregs first (%rax is invalid because it is used as return address), put instruction codec in alt_insn and call func with alt_insn_length. Get results in outregs.
Signed-off-by Arthur Chunqi Li <[email protected]>
--- x86/emulator.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+)
x86/emulator.c +132 -0
@@ -11,6 +11,16 @@ int fails, tests;static int exceptions; +struct regs { +u64 rax, rbx, rcx, rdx; +u64 rsi, rdi, rsp, rbp; +u64 r8, r9, r10, r11; +u64 r12, r13, r14, r15; +u64 rip, rflags; +}; +static struct regs inregs, outregs; +extern struct regs save; +void report (const char *name,int result) { ++tests;
@@ -685,6 +695,128 @@ static void test_shld_shrd(u32 *mem)report ("shrd (cl)" , *mem == ((0x12345678 >>3 ) | (5u <<29 ))); } +extern u8 insn_start[], insn_end[]; +extern u8 insn_emulate_start[], insn_emulate_end[]; + +static void mk_insn_page (uint8_t *insn_page,uint8_t *alt_insn_page, +uint8_t *alt_insn,int alt_insn_length) +{ +int i, emul_offset; +for (i=1 ; i<insn_emulate_end - insn_emulate_start; i++) + insn_emulate_start[i] =0x90 ;// nop +for (i=0 ; i<insn_end - insn_start; i++) + insn_page[i] = insn_start[i]; + emul_offset = insn_emulate_start - insn_start; +for (i=0 ; i<alt_insn_length; i++) + alt_insn_page[i+emul_offset] = alt_insn[i]; + + asmvolatile ( +".pushsection .text.insn, \"ax\" \n\t" +"insn_start:\n\t" +"ret\n\t" + +"push %%rax; push %%rbx\n\t" +"push %%rcx; push %%rdx\n\t" +"push %%rsi; push %%rdi\n\t" +"push %%rbp\n\t" +"push %%r8; push %%r9\n\t" +"push %%r10; push %%r11\n\t" +"push %%r12; push %%r13\n\t" +"push %%r14; push %%r15\n\t" +"pushf\n\t" + +"push 136+%[save] \n\t" +"popf \n\t" +"mov 0+%[save], %%rax \n\t" +"mov 8+%[save], %%rbx \n\t" +"mov 16+%[save], %%rcx \n\t" +"mov 24+%[save], %%rdx \n\t" +"mov 32+%[save], %%rsi \n\t" +"mov 40+%[save], %%rdi \n\t" +"mov 56+%[save], %%rbp \n\t" +"mov 64+%[save], %%r8 \n\t" +"mov 72+%[save], %%r9 \n\t" +"mov 80+%[save], %%r10 \n\t" +"mov 88+%[save], %%r11 \n\t" +"mov 96+%[save], %%r12 \n\t" +"mov 104+%[save], %%r13 \n\t" +"mov 112+%[save], %%r14 \n\t" +"mov 120+%[save], %%r15 \n\t" + +"insn_emulate_start:\n\t" +"in (%%dx),%%al\n\t" +". = . + 31\n\t" +"insn_emulate_end:\n\t" + +"pushf \n\t" +"pop 136+%[save] \n\t" +"mov %%rax, 0+%[save] \n\t" +"mov %%rbx, 8+%[save] \n\t" +"mov %%rcx, 16+%[save] \n\t" +"mov %%rdx, 24+%[save] \n\t" +"mov %%rsi, 32+%[save] \n\t" +"mov %%rdi, 40+%[save] \n\t" +"mov %%rbp, 56+%[save] \n\t" +"mov %%r8, 64+%[save]\n\t" +"mov %%r9, 72+%[save]\n\t" +"mov %%r10, 80+%[save]\n\t" +"mov %%r11, 88+%[save]\n\t" +"mov %%r12, 96+%[save]\n\t" +"mov %%r13, 104+%[save]\n\t" +"mov %%r14, 112+%[save]\n\t" +"mov %%r15, 120+%[save]\n\t" + +"popf\n\t" +"pop %%r15; pop %%r14 \n\t" +"pop %%r13; pop %%r12 \n\t" +"pop %%r11; pop %%r10 \n\t" +"pop %%r9; pop %%r8 \n\t" +"pop %%rbp \n\t" +"pop %%rdi; pop %%rsi \n\t" +"pop %%rdx; pop %%rcx \n\t" +"pop %%rbx; pop %%rax \n\t" + +"ret\n\t" + +"save:\n\t" +". = . + 256\n\t" +"insn_end:\n\t" +".popsection\n\t" + : [save]"=m" (save) + : :"memory" ,"cc" + ); +} + +static void trap_emulator (uint64_t *mem,uint8_t *insn_page, +uint8_t *alt_insn_page,void *insn_ram, +uint8_t * alt_insn,int alt_insn_length,int reserve_stack) +{ +ulong *cr3 = (ulong *)read_cr3 (); +extern u8 insn_start[]; +int save_offset = (u8 *)(&save) - insn_start; + +memset (insn_page,0x90 ,4096 ); +memset (alt_insn_page,0x90 ,4096 ); + + save = inregs; +mk_insn_page (insn_page, alt_insn_page, + alt_insn, alt_insn_length); + +// Load the code TLB with insn_page, but point the page tables at +// alt_insn_page (and keep the data TLB clear, for AMD decode assist). +// This will make the CPU trap on the insn_page instruction but the +// hypervisor will see alt_insn_page. +install_page (cr3,virt_to_phys (insn_page), insn_ram); +invlpg (insn_ram); +// Load code TLB + asmvolatile ("call *%0" : :"r" (insn_ram)); +install_page (cr3,virt_to_phys (alt_insn_page), insn_ram); +// Trap, let hypervisor emulate at alt_insn_page + asmvolatile ("call *%0" : :"r" (insn_ram+1 )); + + outregs = *((struct regs *)(&alt_insn_page[save_offset])); +} +static void advance_rip_by_3_and_note_exception (struct ex_regs *regs) { ++exceptions;
Signature · 1 line
1.7.9.5
PATCH 2/2 kvm-unit-tests: Change two cases to use trap_emulator
Change two functions (test_mmx_movq_mf and test_movabs) using unified trap_emulator.
Signed-off-by Arthur Chunqi Li <[email protected]>
--- x86/emulator.c | 85 +++++++++++++++----------------------------------------- 1 file changed, 23 insertions(+), 62 deletions(-)
x86/emulator.c +23 -62
@@ -826,73 +826,34 @@ static void advance_rip_by_3_and_note_exception(struct ex_regs *regs)static void test_mmx_movq_mf (uint64_t *mem,uint8_t *insn_page,uint8_t *alt_insn_page,void *insn_ram) { -uint16_t fcw =0 ;// all exceptions unmasked -ulong *cr3 = (ulong *)read_cr3 (); - -write_cr0 (read_cr0 () & ~6 );// TS, EM -// Place a trapping instruction in the page to trigger a VMEXIT - insn_page[0 ] =0x89 ;// mov %eax, (%rax) - insn_page[1 ] =0x00 ; - insn_page[2 ] =0x90 ;// nop - insn_page[3 ] =0xc3 ;// ret -// Place the instruction we want the hypervisor to see in the alternate page - alt_insn_page[0 ] =0x0f ;// movq %mm0, (%rax) - alt_insn_page[1 ] =0x7f ; - alt_insn_page[2 ] =0x00 ; - alt_insn_page[3 ] =0xc3 ;// ret - - exceptions =0 ; -handle_exception (MF_VECTOR , advance_rip_by_3_and_note_exception); - -// Load the code TLB with insn_page, but point the page tables at -// alt_insn_page (and keep the data TLB clear, for AMD decode assist). -// This will make the CPU trap on the insn_page instruction but the -// hypervisor will see alt_insn_page. -install_page (cr3,virt_to_phys (insn_page), insn_ram); - asmvolatile ("fninit; fldcw %0" : :"m" (fcw)); - asmvolatile ("fldz; fldz; fdivp" );// generate exception -invlpg (insn_ram); -// Load code TLB - asmvolatile ("call *%0" : :"r" (insn_ram +3 )); -install_page (cr3,virt_to_phys (alt_insn_page), insn_ram); -// Trap, let hypervisor emulate at alt_insn_page - asmvolatile ("call *%0" : :"r" (insn_ram),"a" (mem)); -// exit MMX mode - asmvolatile ("fnclex; emms" ); -report ("movq mmx generates #MF" , exceptions ==1 ); -handle_exception (MF_VECTOR ,0 ); +uint16_t fcw =0 ;// all exceptions unmasked +uint8_t alt_insn[] = {0x0f ,0x7f ,0x00 };// movq %mm0, (%rax) + +write_cr0 (read_cr0 () & ~6 );// TS, EM + exceptions =0 ; +handle_exception (MF_VECTOR , advance_rip_by_3_and_note_exception); + asmvolatile ("fninit; fldcw %0" : :"m" (fcw)); + asmvolatile ("fldz; fldz; fdivp" );// generate exception + + inregs = (struct regs ){0 }; +trap_emulator (mem, insn_page, alt_insn_page, insn_ram, + alt_insn,3 ,1 ); +// exit MMX mode + asmvolatile ("fnclex; emms" ); +report ("movq mmx generates #MF" , exceptions ==1 ); +handle_exception (MF_VECTOR ,0 ); }static void test_movabs (uint64_t *mem,uint8_t *insn_page,uint8_t *alt_insn_page,void *insn_ram) { -uint64_t val =0 ; -ulong *cr3 = (ulong *)read_cr3 (); - -// Pad with RET instructions -memset (insn_page,0xc3 ,4096 ); -memset (alt_insn_page,0xc3 ,4096 ); -// Place a trapping instruction in the page to trigger a VMEXIT - insn_page[0 ] =0x89 ;// mov %eax, (%rax) - insn_page[1 ] =0x00 ; -// Place the instruction we want the hypervisor to see in the alternate -// page. A buggy hypervisor will fetch a 32-bit immediate and return -// 0xffffffffc3c3c3c3. - alt_insn_page[0 ] =0x48 ;// mov $0xc3c3c3c3c3c3c3c3, %rcx - alt_insn_page[1 ] =0xb9 ; - -// Load the code TLB with insn_page, but point the page tables at -// alt_insn_page (and keep the data TLB clear, for AMD decode assist). -// This will make the CPU trap on the insn_page instruction but the -// hypervisor will see alt_insn_page. -install_page (cr3,virt_to_phys (insn_page), insn_ram); -// Load code TLB -invlpg (insn_ram); - asmvolatile ("call *%0" : :"r" (insn_ram +3 )); -// Trap, let hypervisor emulate at alt_insn_page -install_page (cr3,virt_to_phys (alt_insn_page), insn_ram); - asmvolatile ("call *%1" :"=c" (val) :"r" (insn_ram),"a" (mem),"c" (0 )); -report ("64-bit mov imm" , val ==0xc3c3c3c3c3c3c3c3 ); +// mov $0xc3c3c3c3c3c3c3c3, %rcx +uint8_t alt_insn[] = {0x48 ,0xb9 ,0xc3 ,0xc3 ,0xc3 , +0xc3 ,0xc3 ,0xc3 ,0xc3 ,0xc3 }; + inregs = (struct regs ){ .rbx =0x5678 , .rcx =0x1234 }; +trap_emulator (mem, insn_page, alt_insn_page, insn_ram, + alt_insn,10 ,1 ); +report ("64-bit mov imm2" , outregs.rcx ==0xc3c3c3c3c3c3c3c3 ); }static void test_crosspage_mmio (volatile uint8_t *mem)
Signature · 1 line
1.7.9.5