@@ -57,5 +57,16 @@ typedef unsigned long pgd_t;
#define PGDIR_BITS(lvl) (((lvl) - 1) * PGDIR_WIDTH + PAGE_SHIFT)
#define PGDIR_OFFSET(va, lvl) (((va) >> PGDIR_BITS(lvl)) & PGDIR_MASK)
+#ifdef __x86_64__
+enum {
+ PAT_UC = 0, /* uncached */
+ PAT_WC = 1, /* Write combining */
+ PAT_WT = 4, /* Write Through */
+ PAT_WP = 5, /* Write Protected */
+ PAT_WB = 6, /* Write Back (default) */
+ PAT_UC_MINUS = 7, /* UC, but can be overridden by MTRR */
+};
+#endif
+
#endif /* !__ASSEMBLY__ */
#endif
@@ -94,6 +94,14 @@ bool pat_supported(void)
return this_cpu_has(X86_FEATURE_PAT);
}
+bool pat_valid(u64 data)
+{
+ if (data & 0xF8F8F8F8F8F8F8F8ull)
+ return false;
+ /* 0, 1, 4, 5, 6, 7 are valid values. */
+ return (data | ((data & 0x0202020202020202ull) << 1)) == data;
+}
+
int get_test_stage(struct svm_test *test)
{
barrier();
@@ -410,6 +410,7 @@ void default_prepare_gif_clear(struct svm_test *test);
bool default_finished(struct svm_test *test);
bool npt_supported(void);
bool pat_supported(void);
+bool pat_valid(u64 data);
int get_test_stage(struct svm_test *test);
void set_test_stage(struct svm_test *test, int s);
void inc_test_stage(struct svm_test *test);
@@ -2547,6 +2547,71 @@ static void guest_rflags_test_db_handler(struct ex_regs *r)
r->rflags &= ~X86_EFLAGS_TF;
}
+#define G_PAT_VMRUN(nested_ctl, val, i, field_val) \
+{ \
+ u32 ret, xret; \
+ \
+ if (nested_ctl) { \
+ if (pat_valid(val)) \
+ xret = SVM_EXIT_VMMCALL; \
+ else \
+ xret = SVM_EXIT_ERR; \
+ } else { \
+ xret = SVM_EXIT_VMMCALL; \
+ } \
+ vmcb->save.g_pat = val; \
+ ret = svm_vmrun(); \
+ report (ret == xret, "Test G_PAT[%d]: %lx, wanted " \
+ "exit 0x%x, got 0x%x", i, field_val, xret, ret); \
+}
+
+#define TEST_G_PAT(g_pat_saved, nested_ctl) \
+{ \
+ int i, field_shift; \
+ u64 g_pat_mask, field_val, val, j; \
+ \
+ for (i = 0; i < 8; i++) { \
+ /* \
+ * Test each PAT field's encodings and \
+ * reserved values \
+ */ \
+ field_shift = i * 8; \
+ g_pat_mask = ~(0x7ul << field_shift) & \
+ g_pat_saved; \
+ for (j = PAT_UC; j <= PAT_UC_MINUS; j++) { \
+ val = g_pat_mask | j << field_shift; \
+ G_PAT_VMRUN(nested_ctl, val, i, j); \
+ } \
+ field_shift = i * 8 + 3; \
+ g_pat_mask = ~(0x1ful << field_shift) & \
+ g_pat_saved; \
+ for (j = 0; j < 5; j++) { \
+ field_val = 1ul << j; \
+ val = g_pat_mask | \
+ field_val << field_shift; \
+ G_PAT_VMRUN(nested_ctl, val, i, \
+ field_val); \
+ } \
+ } \
+}
+
+static void test_g_pat(void)
+{
+ u64 g_pat_saved = vmcb->save.g_pat;
+ u64 nested_ctl_saved = vmcb->control.nested_ctl;
+
+ if (!npt_supported() || !pat_supported()) {
+ report_skip("NPT or PAT or both not supported");
+ return;
+ }
+
+ TEST_G_PAT(g_pat_saved, (vmcb->control.nested_ctl = 0));
+ TEST_G_PAT(g_pat_saved, (vmcb->control.nested_ctl = 1));
+
+ vmcb->control.nested_ctl = nested_ctl_saved;
+ vmcb->save.g_pat = g_pat_saved;
+}
+
static void svm_guest_state_test(void)
{
test_set_guest(basic_guest_main);
@@ -2557,6 +2622,7 @@ static void svm_guest_state_test(void)
test_dr();
test_msrpm_iopm_bitmap_addrs();
test_canonicalization();
+ test_g_pat();
}
extern void guest_rflags_test_guest(struct svm_test *test);
According to section "Nested Paging and VMRUN/#VMEXIT" in APM vol 2, the following guest state is illegal: "Any G_PAT.PA field has an unsupported type encoding or any reserved field in G_PAT has a nonzero value." Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oracle.com> --- lib/x86/asm/page.h | 11 ++++++++ x86/svm.c | 8 ++++++ x86/svm.h | 1 + x86/svm_tests.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+)