Message ID | 20210114122159.1147290-1-mlevitsk@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add a reproducer for the AMD nested virtualization errata | expand |
On 14/01/21 13:21, Maxim Levitsky wrote: > While this test doesn't test every case of this errata, it should > reproduce it on all systems where the errata is known to exist. > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> > --- > x86/svm_tests.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ > x86/unittests.cfg | 2 +- > 2 files changed, 69 insertions(+), 1 deletion(-) > > diff --git a/x86/svm_tests.c b/x86/svm_tests.c > index dc86efd..0c75400 100644 > --- a/x86/svm_tests.c > +++ b/x86/svm_tests.c > @@ -2315,6 +2315,73 @@ static void svm_guest_state_test(void) > test_dr(); > } > > + > +static bool volatile svm_errata_reproduced = false; > +static unsigned long volatile physical = 0; > + > + > +/* > + * > + * Test the following errata: > + * If the VMRUN/VMSAVE/VMLOAD are attempted by the nested guest, > + * the CPU would first check the EAX against host reserved memory > + * regions (so far only SMM_ADDR/SMM_MASK are known to cause it), > + * and only then signal #VMexit > + * > + * Try to reproduce this by trying vmsave on each possible 4K aligned memory > + * address in the low 4G where the SMM area has to reside. > + */ > + > +static void gp_isr(struct ex_regs *r) > +{ > + svm_errata_reproduced = true; > + /* skip over the vmsave instruction*/ > + r->rip += 3; > +} > + > +static void svm_vmrun_errata_test(void) > +{ > + unsigned long *last_page = NULL; > + > + handle_exception(GP_VECTOR, gp_isr); > + > + while (!svm_errata_reproduced) { > + > + unsigned long *page = alloc_pages(1); > + > + if (!page) { > + report(true, "All guest memory tested, no bug found");; > + break; > + } > + > + physical = virt_to_phys(page); > + > + asm volatile ( > + "mov %[_physical], %%rax\n\t" > + "vmsave\n\t" > + > + : [_physical] "=m" (physical) > + : /* no inputs*/ > + : "rax" /*clobbers*/ > + ); > + > + if (svm_errata_reproduced) { > + report(false, "Got #GP exception - svm errata reproduced at 0x%lx", > + physical); > + break; > + } > + > + *page = (unsigned long)last_page; > + last_page = page; > + } > + > + while (last_page) { > + unsigned long *page = last_page; > + last_page = (unsigned long *)*last_page; > + free_pages_by_order(page, 1); > + } > +} > + > struct svm_test svm_tests[] = { > { "null", default_supported, default_prepare, > default_prepare_gif_clear, null_test, > @@ -2427,5 +2494,6 @@ struct svm_test svm_tests[] = { > init_intercept_finished, init_intercept_check, .on_vcpu = 2 }, > TEST(svm_cr4_osxsave_test), > TEST(svm_guest_state_test), > + TEST(svm_vmrun_errata_test), > { NULL, NULL, NULL, NULL, NULL, NULL, NULL } > }; > diff --git a/x86/unittests.cfg b/x86/unittests.cfg > index b48c98b..f4ea370 100644 > --- a/x86/unittests.cfg > +++ b/x86/unittests.cfg > @@ -213,7 +213,7 @@ arch = x86_64 > [svm] > file = svm.flat > smp = 2 > -extra_params = -cpu host,+svm > +extra_params = -cpu host,+svm -m 4g > arch = x86_64 > > [taskswitch] > Queued, thanks. Paolo
diff --git a/x86/svm_tests.c b/x86/svm_tests.c index dc86efd..0c75400 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -2315,6 +2315,73 @@ static void svm_guest_state_test(void) test_dr(); } + +static bool volatile svm_errata_reproduced = false; +static unsigned long volatile physical = 0; + + +/* + * + * Test the following errata: + * If the VMRUN/VMSAVE/VMLOAD are attempted by the nested guest, + * the CPU would first check the EAX against host reserved memory + * regions (so far only SMM_ADDR/SMM_MASK are known to cause it), + * and only then signal #VMexit + * + * Try to reproduce this by trying vmsave on each possible 4K aligned memory + * address in the low 4G where the SMM area has to reside. + */ + +static void gp_isr(struct ex_regs *r) +{ + svm_errata_reproduced = true; + /* skip over the vmsave instruction*/ + r->rip += 3; +} + +static void svm_vmrun_errata_test(void) +{ + unsigned long *last_page = NULL; + + handle_exception(GP_VECTOR, gp_isr); + + while (!svm_errata_reproduced) { + + unsigned long *page = alloc_pages(1); + + if (!page) { + report(true, "All guest memory tested, no bug found");; + break; + } + + physical = virt_to_phys(page); + + asm volatile ( + "mov %[_physical], %%rax\n\t" + "vmsave\n\t" + + : [_physical] "=m" (physical) + : /* no inputs*/ + : "rax" /*clobbers*/ + ); + + if (svm_errata_reproduced) { + report(false, "Got #GP exception - svm errata reproduced at 0x%lx", + physical); + break; + } + + *page = (unsigned long)last_page; + last_page = page; + } + + while (last_page) { + unsigned long *page = last_page; + last_page = (unsigned long *)*last_page; + free_pages_by_order(page, 1); + } +} + struct svm_test svm_tests[] = { { "null", default_supported, default_prepare, default_prepare_gif_clear, null_test, @@ -2427,5 +2494,6 @@ struct svm_test svm_tests[] = { init_intercept_finished, init_intercept_check, .on_vcpu = 2 }, TEST(svm_cr4_osxsave_test), TEST(svm_guest_state_test), + TEST(svm_vmrun_errata_test), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; diff --git a/x86/unittests.cfg b/x86/unittests.cfg index b48c98b..f4ea370 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -213,7 +213,7 @@ arch = x86_64 [svm] file = svm.flat smp = 2 -extra_params = -cpu host,+svm +extra_params = -cpu host,+svm -m 4g arch = x86_64 [taskswitch]
While this test doesn't test every case of this errata, it should reproduce it on all systems where the errata is known to exist. Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> --- x86/svm_tests.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 2 +- 2 files changed, 69 insertions(+), 1 deletion(-)