[kvm-unit-tests PATCH] x86: nSVM: Add test for EPT A/D bits

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux