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@xxxxxxxxxx> --- lib/x86/asm/page.h | 11 ++++++++ x86/svm.c | 8 ++++++ x86/svm.h | 1 + x86/svm_tests.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/lib/x86/asm/page.h b/lib/x86/asm/page.h index fc14160..9ff9329 100644 --- a/lib/x86/asm/page.h +++ b/lib/x86/asm/page.h @@ -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 diff --git a/x86/svm.c b/x86/svm.c index d03f011..c949003 100644 --- a/x86/svm.c +++ b/x86/svm.c @@ -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(); diff --git a/x86/svm.h b/x86/svm.h index d4db4c1..d4c6e1c 100644 --- a/x86/svm.h +++ b/x86/svm.h @@ -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); diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 8ad6122..4536362 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -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); -- 2.27.0