On Wed, Aug 20, 2025 at 12:29 PM Kevin Cheng <chengkev@xxxxxxxxxx> wrote: > > 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 <chengkev@xxxxxxxxxx> > --- > 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 <asm/page.h> > + > /* 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 > Just checking in as it's been a couple weeks :) If there is anyone else who would be better suited to take a look at these please let me know and I can cc them as well!