the test case is for "kvm/x86: fix a fetch fault emulation" . 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] v1 --> v2: simply some functions. Signed-off-by: Peng Hao <peng.hao2@xxxxxxxxxx> --- lib/vmalloc.c | 18 ++++++++++++++---- lib/vmalloc.h | 9 +++++++-- lib/x86/io.c | 2 +- lib/x86/vm.c | 5 +++++ x86/emulator.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 76 insertions(+), 10 deletions(-) diff --git a/lib/vmalloc.c b/lib/vmalloc.c index b583786..31c1fe0 100644 --- a/lib/vmalloc.c +++ b/lib/vmalloc.c @@ -36,18 +36,28 @@ void init_alloc_vpage(void *top) vfree_top = top; } -void *vmap(phys_addr_t phys, size_t size) +/* + * op: VMAP_MAP + * VMAP_UNMAP + * VMAP_REMAP + */ +void *vmap(phys_addr_t phys, void *vmem, size_t size, int op) { void *mem, *p; unsigned pages; size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); pages = size / PAGE_SIZE; - mem = p = alloc_vpages(pages); - + if (op == VMAP_MAP) + mem = p = alloc_vpages(pages); + else + mem = p = vmem; phys &= ~(unsigned long long)(PAGE_SIZE - 1); while (pages--) { - install_page(page_root, phys, p); + if (op == VMAP_UNMAP) + install_page(page_root, phys, p); + else + uninstall_page(page_root, phys, p); phys += PAGE_SIZE; p += PAGE_SIZE; } diff --git a/lib/vmalloc.h b/lib/vmalloc.h index 3658b80..579e1e8 100644 --- a/lib/vmalloc.h +++ b/lib/vmalloc.h @@ -3,6 +3,11 @@ #include <asm/page.h> +/* vmap op type */ +#define VMAP_MAP 0 +#define VMAP_UNMAP 1 +#define VMAP_REMAP 2 + extern void *alloc_vpages(ulong nr); extern void *alloc_vpage(void); extern void init_alloc_vpage(void *top); @@ -11,7 +16,7 @@ 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); - -void *vmap(phys_addr_t phys, size_t size); +extern pteval_t *uninstall_page(pgd_t *pgtable, phys_addr_t phys, void *virt); +void *vmap(phys_addr_t phys, void * vmem, size_t size, int op); #endif diff --git a/lib/x86/io.c b/lib/x86/io.c index 7e64877..266b132 100644 --- a/lib/x86/io.c +++ b/lib/x86/io.c @@ -116,5 +116,5 @@ void __iomem *ioremap(phys_addr_t phys_addr, size_t size) * mappings would be uncached - and may help us find bugs when we * properly map that way. */ - return vmap(phys_addr, size) + offset; + return vmap(phys_addr, NULL, size, VMAP_MAP) + offset; } diff --git a/lib/x86/vm.c b/lib/x86/vm.c index 73d9be4..cf77eb8 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..cfc9057 100644 --- a/x86/emulator.c +++ b/x86/emulator.c @@ -11,6 +11,8 @@ #define TESTDEV_IO_PORT 0xe0 static int exceptions; +static void *insn_page; +static void *insn_ram; /* Forced emulation prefix, used to invoke the emulator unconditionally. */ #define KVM_FEP "ud2; .byte 'k', 'v', 'm';" @@ -33,6 +35,49 @@ struct insn_desc { static char st1[] = "abcdefghijklmnop"; +void do_pf_tss(void); +void do_pf_tss(void) +{ + printf("PF running\n"); + vmap(virt_to_phys(insn_page), insn_ram, 4096, VMAP_REMAP); + 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_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; + + vmap(phys, insn_ram, 4096, VMAP_UNMAP); + + *mem = 0; + asm("callq *%1" : "+m"(*mem) : "r"(insn_ram)); + report("fetch ", *mem == 0x10000); +} + static void test_stringio(void) { unsigned char r = 0; @@ -1018,12 +1063,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); @@ -1033,7 +1077,7 @@ int main(void) // install the page twice to test cross-page mmio install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem + 4096); insn_page = alloc_page(); - insn_ram = vmap(virt_to_phys(insn_page), 4096); + insn_ram = vmap(virt_to_phys(insn_page), NULL, 4096, VMAP_MAP); // test mov reg, r/m and mov r/m, reg t1 = 0x123456789abcdef; @@ -1068,6 +1112,8 @@ int main(void) test_sse(mem); test_mmx(mem); test_rip_relative(mem, insn_ram); + 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