> On 14 Sep 2018, at 22:22, Jim Mattson <jmattson@xxxxxxxxxx> wrote: > > When L1 intercepts #NM exceptions encountered in L2, the #NM exception > should always be reflected from L0 to L1. I would also reference the relevant KVM commit which fixes this issue. > > Signed-off-by: Jim Mattson <jmattson@xxxxxxxxxx> > Reviewed-by: Peter Shier <pshier@xxxxxxxxxx> > --- > lib/x86/processor.h | 1 + > x86/unittests.cfg | 6 +++++ > x86/vmx_tests.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 72 insertions(+) > > diff --git a/lib/x86/processor.h b/lib/x86/processor.h > index b8e884f..15237a5 100644 > --- a/lib/x86/processor.h > +++ b/lib/x86/processor.h > @@ -25,6 +25,7 @@ > > #define X86_CR0_PE 0x00000001 > #define X86_CR0_MP 0x00000002 > +#define X86_CR0_EM 0x00000004 > #define X86_CR0_TS 0x00000008 > #define X86_CR0_WP 0x00010000 > #define X86_CR0_AM 0x00040000 > diff --git a/x86/unittests.cfg b/x86/unittests.cfg > index 9d39319..4a9702d 100644 > --- a/x86/unittests.cfg > +++ b/x86/unittests.cfg > @@ -554,6 +554,12 @@ extra_params = -cpu host,+vmx -m 2560 -append vmx_cr_load_test > arch = x86_64 > groups = vmx > > +[vmx_nm_test] > +file = vmx.flat > +extra_params = -cpu host,+vmx -m 2560 -append vmx_nm_test > +arch = x86_64 > +groups = vmx > + > [vmx_eoi_bitmap_ioapic_scan] > file = vmx.flat > smp = 2 > diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c > index 0e9d900..5d4f39a 100644 > --- a/x86/vmx_tests.c > +++ b/x86/vmx_tests.c > @@ -4585,6 +4585,70 @@ static void vmx_cr_load_test(void) > TEST_ASSERT(!write_cr4_checking(cr4 & ~X86_CR4_PCIDE)); > } > > +static void vmx_nm_test_guest(void) > +{ > + write_cr0(read_cr0() | X86_CR0_TS); > + asm volatile("fnop"); > +} > + > +static void check_nm_exit(const char *test) > +{ > + u32 reason = vmcs_read(EXI_REASON); > + u32 intr_info = vmcs_read(EXI_INTR_INFO); > + const u32 expected = INTR_INFO_VALID_MASK | INTR_TYPE_HARD_EXCEPTION | > + NM_VECTOR; > + > + report("%s", reason == VMX_EXC_NMI && intr_info == expected, test); > +} > + > +/* > + * This test checks that: > + * > + * (a) If L2 launches with CR0.TS clear, but later sets CR0.TS, then > + * a subsequent #NM VM-exit is reflected to L1. > + * > + * (b) If L2 launches with CR0.TS clear and CR0.EM set, then a > + * subsequent #NM VM-exit is reflected to L1. > + */ > +static void vmx_nm_test(void) > +{ > + unsigned long cr0 = read_cr0(); > + > + test_set_guest(vmx_nm_test_guest); > + > + /* > + * L1 wants to intercept #NM exceptions encountered in L2. > + */ > + vmcs_write(EXC_BITMAP, 1 << NM_VECTOR); > + > + /* > + * Launch L2 with CR0.TS clear, but don't claim host ownership of > + * any CR0 bits. L2 will set CR0.TS and then try to execute fnop, > + * which will raise #NM. L0 should reflect the #NM VM-exit to L1. > + */ > + vmcs_write(CR0_MASK, 0); > + vmcs_write(GUEST_CR0, cr0 & ~X86_CR0_TS); > + enter_guest(); > + check_nm_exit("fnop with CR0.TS set in L2 triggers #NM VM-exit to L1"); > + > + /* > + * Re-enter L2 at the fnop instruction, with CR0.TS clear but > + * CR0.EM set. The fnop will still raise #NM, and L0 should > + * reflect the #NM VM-exit to L1. > + */ > + vmcs_write(GUEST_CR0, (cr0 & ~X86_CR0_TS) | X86_CR0_EM); > + enter_guest(); > + check_nm_exit("fnop with CR0.EM set in L2 triggers #NM VM-exit to L1"); > + > + /* > + * Re-enter L2 at the fnop instruction, with both CR0.TS and > + * CR0.EM clear. There will be no #NM, and the L2 guest should > + * exit normally. > + */ > + vmcs_write(GUEST_CR0, cr0 & ~(X86_CR0_TS | X86_CR0_EM)); > + enter_guest(); > +} > + > static bool cpu_has_apicv(void) > { > return ((ctrl_cpu_rev[1].clr & CPU_APIC_REG_VIRT) && > @@ -5144,6 +5208,7 @@ struct vmx_test vmx_tests[] = { > TEST(vmx_vmcs_shadow_test), > /* Regression tests */ > TEST(vmx_cr_load_test), > + TEST(vmx_nm_test), > /* EPT access tests. */ > TEST(ept_access_test_not_present), > TEST(ept_access_test_read_only), > -- > 2.19.0.397.gdd90340f6a-goog > Looks good to me. Reviewed-by: Liran Alon <liran.alon@xxxxxxxxxx>