Add a simple test that runs a 32-bit code segment with two-level page tables. Signed-off-by: Paolo Bonzini --- x86/svm_tests.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 95d18359..3ba3b09c 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -769,6 +769,117 @@ static bool check_mode_switch(struct svm_test *test) return test->scratch == 2; } +/* + * 32-bit guest tests. These tests run a tiny guest in legacy 32-bit + * protected mode, with either 2-level (non-PAE) or 3-level (PAE) paging. + * The guest code is in the low 4 GB and is identity-mapped using large + * pages. The guest issues a few VMMCALLs and then spins; since no + * interrupts are taken, the rest of the address space need not be mapped. + * + * Unlike VMX (where the four PDPTEs are loaded from the VMCS when EPT is + * enabled), SVM always loads the PAE PDPTEs from memory at guest CR3, so + * a real PDPT is required even when NPT is enabled. + */ +extern char svm_32bit_guest_begin[]; + +asm(".code32\n\t" + "svm_32bit_guest_begin:\n\t" + " mov $1, %eax\n\t" + " vmmcall\n\t" + " mov $2, %eax\n\t" + " vmmcall\n\t" + "1: jmp 1b\n\t" + ".code64\n\t"); + +static u64 svm_32bit_build_page_tables(void) +{ + const u64 leaf_flags = PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_PAGE_SIZE_MASK; + u32 *pd = memalign_pages_flags(PAGE_SIZE, PAGE_SIZE, AREA_DMA32); + int i; + + for (i = 0; i < 1024; i++) + pd[i] = ((u32)i << 22) | leaf_flags; + return virt_to_phys(pd); +} + +static void svm_32bit_guest_prepare_common(struct svm_test *test, u64 cr3) +{ + struct vmcb_save_area *save = &vmcb->save; + + vmcb_ident(vmcb); + test->scratch = 0; + + /* + * 32-bit code segment attribute: + * type=B (execute/read/accessed), S=1, DPL=0, P=1, + * L=0 (32-bit), D/B=1 (32-bit operand), G=1 (4K granularity). + */ + save->cs.attrib = 0xc9b; + + save->efer &= ~(EFER_LMA | EFER_LME); + save->cr0 |= X86_CR0_PG | X86_CR0_PE; + save->cr4 &= ~X86_CR4_PSE; + save->cr4 |= X86_CR4_PAE; + save->cr3 = cr3; + regs.rax = 0x123457678; +} + +static void svm_32bit_guest_prepare(struct svm_test *test) +{ + u64 cr3 = svm_32bit_build_page_tables(); + + svm_32bit_guest_prepare_common(test, cr3); +} + +static void svm_32bit_guest_prepare_gif_clear(struct svm_test *test) +{ + if (test->scratch == 0) + vmcb->save.rip = (ulong)svm_32bit_guest_begin; +} + +static void svm_32bit_guest_test(struct svm_test *test) +{ + /* Unused: RIP is overridden in prepare_gif_clear. */ +} + +static bool svm_32bit_guest_finished(struct svm_test *test) +{ + if (vmcb->control.exit_code != SVM_EXIT_VMMCALL) { + report_fail("32-bit guest: unexpected exit code 0x%x", + vmcb->control.exit_code); + return true; + } + + vmcb->save.rip += 3; + + switch (test->scratch) { + case 0: + report(vmcb->save.rax == 1, "32-bit guest first VMMCALL"); + test->scratch = 1; + return false; + case 1: + report(vmcb->save.rax == 2, "32-bit guest second VMMCALL"); + test->scratch = 2; + return true; + default: + report_fail("32-bit guest: unexpected stage %ld", test->scratch); + return true; + } +} + +static void svm_32bit_free_page_tables(void) +{ + u64 cr3 = vmcb->save.cr3; + + free_page(phys_to_virt(cr3)); +} + +static bool svm_32bit_guest_check(struct svm_test *test) +{ + svm_32bit_free_page_tables(); + return test->scratch == 2; +} + extern u8 *io_bitmap; static void prepare_ioio(struct svm_test *test) @@ -3692,6 +3803,9 @@ struct svm_test svm_tests[] = { { "vgif", vgif_supported, prepare_vgif_enabled, default_prepare_gif_clear, test_vgif, vgif_finished, vgif_check }, + { "32bit_guest", default_supported, svm_32bit_guest_prepare, + svm_32bit_guest_prepare_gif_clear, svm_32bit_guest_test, svm_32bit_guest_finished, + svm_32bit_guest_check }, TEST(svm_cr4_osxsave_test), TEST(svm_guest_state_test), TEST(svm_vmrun_errata_test), -- 2.54.0