BUG error infomation: KVM internal error. Suberror: 1 emulation failure RAX=0000000000463fe8 RBX=ffffffffffffd000 RCX=000000000000000c RDX=0000000000464006 RSI=0000000000000001 RDI=0000000000463000 RBP=0000000000452ce0 RSP=0000000000452c78 R8 =0000000000000000 R9 =0000000000410d7f R10=0000000000000000 R11=0000000000000000 R12=ffffffffffffe000 R13=1111111111111111 R14=3333333333333333 R15=00000000a06d39e8 RIP=ffffffffffffd000 RFL=00010002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0 ES =0010 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA] CS =0008 0000000000000000 ffffffff 00a09b00 DPL=0 CS64 [-RA] SS =0010 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA] new test case emulates the situation.If patch "kvm/x86: propagate fetch fault into guest" has merged, the test case can gracefully handle #PF in guest and continue execute. --- lib/vmalloc.c | 32 ++++++++++++++++++++++++++++++++ lib/vmalloc.h | 3 +++ lib/x86/vm.c | 5 +++++ x86/emulator.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/lib/vmalloc.c b/lib/vmalloc.c index b583786..1318163 100644 --- a/lib/vmalloc.c +++ b/lib/vmalloc.c @@ -54,6 +54,38 @@ void *vmap(phys_addr_t phys, size_t size) return mem; } +void unvmap(phys_addr_t phys,void * mem, size_t size) +{ + unsigned pages; + void * p = mem; + size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + pages = size / PAGE_SIZE; + + phys &= ~(unsigned long long)(PAGE_SIZE - 1); + while (pages--) { + uninstall_page(page_root, phys, p); + phys += PAGE_SIZE; + p += PAGE_SIZE; + } + return; +} + +void *revmap(phys_addr_t phys, void * mem, size_t size) +{ + void *p = mem; + unsigned pages; + + size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + pages = size / PAGE_SIZE; + + phys &= ~(unsigned long long)(PAGE_SIZE - 1); + while (pages--) { + install_page(page_root, phys, p); + phys += PAGE_SIZE; + p += PAGE_SIZE; + } + return mem; +} static void *vm_memalign(size_t alignment, size_t size) { void *mem, *p; diff --git a/lib/vmalloc.h b/lib/vmalloc.h index 3658b80..9eca9ec 100644 --- a/lib/vmalloc.h +++ b/lib/vmalloc.h @@ -11,7 +11,10 @@ extern void setup_vm(void); extern void *setup_mmu(phys_addr_t top); extern phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt); extern pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt); +extern pteval_t *uninstall_page(pgd_t *pgtable, phys_addr_t phys, void *virt); void *vmap(phys_addr_t phys, size_t size); +void *revmap(phys_addr_t phys, void * mem, size_t size); +void unvmap(phys_addr_t phys,void * mem, size_t size); #endif diff --git a/lib/x86/vm.c b/lib/x86/vm.c index 73d9be4..6472da5 100644 --- a/lib/x86/vm.c +++ b/lib/x86/vm.c @@ -100,6 +100,11 @@ pteval_t *install_page(pgd_t *cr3, phys_addr_t phys, void *virt) return install_pte(cr3, 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK, 0); } +pteval_t *uninstall_page(pgd_t *cr3, phys_addr_t phys, void *virt) +{ + return install_pte(cr3, 1, virt, phys | PT_WRITABLE_MASK | PT_USER_MASK, 0); +} + void install_pages(pgd_t *cr3, phys_addr_t phys, size_t len, void *virt) { phys_addr_t max = (u64)len + (u64)phys; diff --git a/x86/emulator.c b/x86/emulator.c index c856db4..9accb08 100644 --- a/x86/emulator.c +++ b/x86/emulator.c @@ -16,6 +16,8 @@ static int exceptions; #define KVM_FEP "ud2; .byte 'k', 'v', 'm';" #define KVM_FEP_LENGTH 5 static int fep_available = 1; +static void *insn_page; +static void *insn_ram; struct regs { u64 rax, rbx, rcx, rdx; @@ -33,6 +35,31 @@ struct insn_desc { static char st1[] = "abcdefghijklmnop"; +void do_pf_tss(void) +{ + printf("PF running\n"); + revmap(virt_to_phys(insn_page), insn_ram, 4096); + invlpg(insn_ram); +} + +extern void pf_tss(void); + +asm ("pf_tss: \n\t" +#ifdef __x86_64__ + // no task on x86_64, save/restore caller-save regs + "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n" + "push %r8; push %r9; push %r10; push %r11\n" +#endif + "call do_pf_tss \n\t" +#ifdef __x86_64__ + "pop %r11; pop %r10; pop %r9; pop %r8\n" + "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n" +#endif + "add $"S", %"R "sp\n\t" // discard error code + "iret"W" \n\t" + "jmp pf_tss\n\t" + ); + static void test_stringio(void) { unsigned char r = 0; @@ -714,6 +741,22 @@ static void test_rip_relative(unsigned *mem, char *insn_ram) report("movb $imm, 0(%%rip)", *mem == 0x10000); } +static void test_fetch_insn(unsigned *mem, unsigned long phys, char *insn_ram) +{ + /* movb $1, mem+2(%rip) */ + insn_ram[0] = 0xc6; + insn_ram[1] = 0x05; + *(unsigned *)&insn_ram[2] = 2 + (char *)mem - (insn_ram + 7); + insn_ram[6] = 0x01; + /* ret */ + insn_ram[7] = 0xc3; + unvmap(phys, insn_ram, 4096); + + *mem = 0; + asm("callq *%1" : "+m"(*mem) : "r"(insn_ram)); + report("fetch insn", *mem == 0x10000); +} + static void test_shld_shrd(u32 *mem) { *mem = 0x12345678; @@ -1018,12 +1061,11 @@ static void record_no_fep(struct ex_regs *regs) int main(void) { void *mem; - void *insn_page; - void *insn_ram; unsigned long t1, t2; setup_vm(); setup_idt(); + setup_alt_stack(); handle_exception(UD_VECTOR, record_no_fep); asm(KVM_FEP "nop"); handle_exception(UD_VECTOR, 0); @@ -1068,6 +1110,10 @@ int main(void) test_sse(mem); test_mmx(mem); test_rip_relative(mem, insn_ram); + /* fetch insn test need handle #PF in guest */ + set_intr_alt_stack(14, pf_tss); + test_fetch_insn(mem, virt_to_phys(insn_page), insn_ram); + test_shld_shrd(mem); //test_lgdt_lidt(mem); test_sreg(mem); -- 1.8.3.1