The nVMX tests already have coverage for TDP A/D bits. Add a similar test for nSVM to improve test parity between nSVM and nVMX. Signed-off-by: Kevin Cheng --- x86/svm.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++ x86/svm.h | 5 +++ x86/svm_npt.c | 46 +++++++++++++++++++++++++ 3 files changed, 144 insertions(+) diff --git a/x86/svm.c b/x86/svm.c index e715e270..53b78d16 100644 --- a/x86/svm.c +++ b/x86/svm.c @@ -14,6 +14,8 @@ #include "isr.h" #include "apic.h" +#include + /* for the nested page table*/ u64 *pml4e; @@ -43,6 +45,97 @@ u64 *npt_get_pml4e(void) return pml4e; } +void clear_npt_ad(unsigned long *pml4e, u64 guest_cr3, + unsigned long guest_addr) +{ + int l; + unsigned long *pt = (unsigned long *)guest_cr3, gpa; + u64 *npt_pte, pte, offset_in_page; + unsigned offset; + + for (l = PAGE_LEVEL; ; --l) { + offset = PGDIR_OFFSET(guest_addr, l); + npt_pte = npt_get_pte((u64) &pt[offset]); + + if(!npt_pte) { + printf("NPT - guest level %d page table is not mapped.\n", l); + return; + } + + *npt_pte &= ~(PT_AD_MASK); + + pte = pt[offset]; + if (l == 1 || (l < 4 && (pte & PT_PAGE_SIZE_MASK))) + break; + if (!(pte & PT_PRESENT_MASK)) + return; + pt = (unsigned long *)(pte & PT_ADDR_MASK); + } + + offset = PGDIR_OFFSET(guest_addr, l); + offset_in_page = guest_addr & ((1 << PGDIR_BITS(l)) - 1); + gpa = (pt[offset] & PT_ADDR_MASK) | (guest_addr & offset_in_page); + npt_pte = npt_get_pte(gpa); + *npt_pte &= ~(PT_AD_MASK); +} + +void check_npt_ad(unsigned long *pml4e, u64 guest_cr3, + unsigned long guest_addr, int expected_gpa_ad, + int expected_pt_ad) +{ + int l; + unsigned long *pt = (unsigned long *)guest_cr3, gpa; + u64 *npt_pte, pte, offset_in_page; + unsigned offset; + bool bad_pt_ad = false; + + for (l = PAGE_LEVEL; ; --l) { + offset = PGDIR_OFFSET(guest_addr, l); + npt_pte = npt_get_pte((u64) &pt[offset]); + + if(!npt_pte) { + printf("NPT - guest level %d page table is not mapped.\n", l); + return; + } + + if (!bad_pt_ad) { + bad_pt_ad |= (*npt_pte & PT_AD_MASK) != expected_pt_ad; + if(bad_pt_ad) + report_fail("NPT - received guest level %d page table A=%d/D=%d", + l, + !!(expected_pt_ad & PT_ACCESSED_MASK), + !!(expected_pt_ad & PT_DIRTY_MASK)); + } + + pte = pt[offset]; + if (l == 1 || (l < 4 && (pte & PT_PAGE_SIZE_MASK))) + break; + if (!(pte & PT_PRESENT_MASK)) + return; + pt = (unsigned long *)(pte & PT_ADDR_MASK); + } + + if (!bad_pt_ad) + report_pass("NPT - guest page table structures A=%d/D=%d", + !!(expected_pt_ad & PT_ACCESSED_MASK), + !!(expected_pt_ad & PT_DIRTY_MASK)); + + offset = PGDIR_OFFSET(guest_addr, l); + offset_in_page = guest_addr & ((1 << PGDIR_BITS(l)) - 1); + gpa = (pt[offset] & PT_ADDR_MASK) | (guest_addr & offset_in_page); + + npt_pte = npt_get_pte(gpa); + + if (!npt_pte) { + report_fail("NPT - guest physical address is not mapped"); + return; + } + report((*npt_pte & PT_AD_MASK) == expected_gpa_ad, + "NPT - guest physical address A=%d/D=%d", + !!(expected_gpa_ad & PT_ACCESSED_MASK), + !!(expected_gpa_ad & PT_DIRTY_MASK)); +} + bool smp_supported(void) { return cpu_count() > 1; diff --git a/x86/svm.h b/x86/svm.h index c1dd84af..1a83d778 100644 --- a/x86/svm.h +++ b/x86/svm.h @@ -415,6 +415,11 @@ u64 *npt_get_pte(u64 address); u64 *npt_get_pde(u64 address); u64 *npt_get_pdpe(u64 address); u64 *npt_get_pml4e(void); +void clear_npt_ad(unsigned long *pml4e, u64 guest_cr3, + unsigned long guest_addr); +void check_npt_ad(unsigned long *pml4e, u64 guest_cr3, + unsigned long guest_addr, int expected_gpa_ad, + int expected_pt_ad); bool smp_supported(void); bool default_supported(void); bool vgif_supported(void); diff --git a/x86/svm_npt.c b/x86/svm_npt.c index bd5e8f35..abf44eb0 100644 --- a/x86/svm_npt.c +++ b/x86/svm_npt.c @@ -380,6 +380,51 @@ skip_pte_test: vmcb->save.cr4 = sg_cr4; } +static void npt_ad_read_guest(struct svm_test *test) +{ + u64 *data = (void *)(0x80000); + (void)*(volatile u64 *)data; +} + +static void npt_ad_write_guest(struct svm_test *test) +{ + u64 *data = (void *)(0x80000); + *data = 0; +} + +static void npt_ad_test(void) +{ + u64 *data = (void *)(0x80000); + u64 guest_cr3 = vmcb->save.cr3; + + if (!npt_supported()) { + report_skip("NPT not supported"); + return; + } + + clear_npt_ad(npt_get_pml4e(), guest_cr3, (unsigned long)data); + + check_npt_ad(npt_get_pml4e(), guest_cr3, (unsigned long)data, 0, 0); + + test_set_guest(npt_ad_read_guest); + svm_vmrun(); + + check_npt_ad(npt_get_pml4e(), guest_cr3, + (unsigned long)data, + PT_ACCESSED_MASK, + PT_AD_MASK); + + test_set_guest(npt_ad_write_guest); + svm_vmrun(); + + check_npt_ad(npt_get_pml4e(), guest_cr3, + (unsigned long)data, + PT_AD_MASK, + PT_AD_MASK); + + clear_npt_ad(npt_get_pml4e(), guest_cr3, (unsigned long)data); +} + #define NPT_V1_TEST(name, prepare, guest_code, check) \ { #name, npt_supported, prepare, default_prepare_gif_clear, guest_code, \ default_finished, check } @@ -395,6 +440,7 @@ static struct svm_test npt_tests[] = { NPT_V1_TEST(npt_l1mmio, npt_l1mmio_prepare, npt_l1mmio_test, npt_l1mmio_check), NPT_V1_TEST(npt_rw_l1mmio, npt_rw_l1mmio_prepare, npt_rw_l1mmio_test, npt_rw_l1mmio_check), NPT_V2_TEST(svm_npt_rsvd_bits_test), + NPT_V2_TEST(npt_ad_test), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; -- 2.51.0.261.g7ce5a0a67e-goog