On 13/10/20 07:28, yadong.qi@xxxxxxxxx wrote: > From: Yadong Qi <yadong.qi@xxxxxxxxx> > > The test verifies the following functionality: > A SIPI signal received when CPU is in VMX non-root mode: > if ACTIVITY_STATE == WAIT_SIPI > VMExit with (reason == 4) > else > SIPI signal is ignored > > The test cases depends on IA32_VMX_MISC:bit(8), if this bit is 1 > then the test cases would be executed, otherwise the test cases > would be skiped. > > Signed-off-by: Yadong Qi <yadong.qi@xxxxxxxxx> > --- > v1->v2: > * update CR3 on AP > > --- > lib/x86/msr.h | 1 + > lib/x86/processor.h | 5 ++ > x86/apic.c | 5 -- > x86/ioapic.c | 5 -- > x86/unittests.cfg | 8 +++ > x86/vmx.c | 2 +- > x86/vmx.h | 3 + > x86/vmx_tests.c | 138 ++++++++++++++++++++++++++++++++++++++++++++ > 8 files changed, 156 insertions(+), 11 deletions(-) > > diff --git a/lib/x86/msr.h b/lib/x86/msr.h > index 6ef5502..29e3947 100644 > --- a/lib/x86/msr.h > +++ b/lib/x86/msr.h > @@ -421,6 +421,7 @@ > #define MSR_IA32_VMX_TRUE_ENTRY 0x00000490 > > /* MSR_IA32_VMX_MISC bits */ > +#define MSR_IA32_VMX_MISC_ACTIVITY_WAIT_SIPI (1ULL << 8) > #define MSR_IA32_VMX_MISC_VMWRITE_SHADOW_RO_FIELDS (1ULL << 29) > > #define MSR_IA32_TSCDEADLINE 0x000006e0 > diff --git a/lib/x86/processor.h b/lib/x86/processor.h > index c2c487c..849554f 100644 > --- a/lib/x86/processor.h > +++ b/lib/x86/processor.h > @@ -359,6 +359,11 @@ static inline ulong read_cr3(void) > return val; > } > > +static inline void update_cr3(void *cr3) > +{ > + write_cr3((ulong)cr3); > +} > + > static inline void write_cr4(ulong val) > { > asm volatile ("mov %0, %%cr4" : : "r"(val) : "memory"); > diff --git a/x86/apic.c b/x86/apic.c > index a7681fe..b57345b 100644 > --- a/x86/apic.c > +++ b/x86/apic.c > @@ -318,11 +318,6 @@ static void nmi_handler(isr_regs_t *regs) > nmi_hlt_counter += regs->rip == (ulong)post_sti; > } > > -static void update_cr3(void *cr3) > -{ > - write_cr3((ulong)cr3); > -} > - > static void test_sti_nmi(void) > { > unsigned old_counter; > diff --git a/x86/ioapic.c b/x86/ioapic.c > index b9f6dd2..7cbccd7 100644 > --- a/x86/ioapic.c > +++ b/x86/ioapic.c > @@ -463,11 +463,6 @@ static void test_ioapic_logical_destination_mode(void) > report(g_isr_86 == nr_vcpus, "ioapic logical destination mode"); > } > > -static void update_cr3(void *cr3) > -{ > - write_cr3((ulong)cr3); > -} > - > int main(void) > { > setup_vm(); > diff --git a/x86/unittests.cfg b/x86/unittests.cfg > index 872d679..c035c79 100644 > --- a/x86/unittests.cfg > +++ b/x86/unittests.cfg > @@ -305,6 +305,14 @@ arch = x86_64 > groups = vmx > timeout = 10 > > +[vmx_sipi_signal_test] > +file = vmx.flat > +smp = 2 > +extra_params = -cpu host,+vmx -m 2048 -append vmx_sipi_signal_test > +arch = x86_64 > +groups = vmx > +timeout = 10 > + > [vmx_apic_passthrough_tpr_threshold_test] > file = vmx.flat > extra_params = -cpu host,+vmx -m 2048 -append vmx_apic_passthrough_tpr_threshold_test > diff --git a/x86/vmx.c b/x86/vmx.c > index 1a84a74..f0b853a 100644 > --- a/x86/vmx.c > +++ b/x86/vmx.c > @@ -1369,7 +1369,7 @@ static void init_vmcs_guest(void) > vmcs_write(GUEST_INTR_STATE, 0); > } > > -static int init_vmcs(struct vmcs **vmcs) > +int init_vmcs(struct vmcs **vmcs) > { > *vmcs = alloc_page(); > (*vmcs)->hdr.revision_id = basic.revision; > diff --git a/x86/vmx.h b/x86/vmx.h > index e29301e..7e39b84 100644 > --- a/x86/vmx.h > +++ b/x86/vmx.h > @@ -697,6 +697,8 @@ enum vm_entry_failure_code { > > #define ACTV_ACTIVE 0 > #define ACTV_HLT 1 > +#define ACTV_SHUTDOWN 2 > +#define ACTV_WAIT_SIPI 3 > > /* > * VMCS field encoding: > @@ -856,6 +858,7 @@ static inline bool invvpid(unsigned long type, u64 vpid, u64 gla) > > void enable_vmx(void); > void init_vmx(u64 *vmxon_region); > +int init_vmcs(struct vmcs **vmcs); > > const char *exit_reason_description(u64 reason); > void print_vmexit_info(union exit_reason exit_reason); > diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c > index d2084ae..4e8215e 100644 > --- a/x86/vmx_tests.c > +++ b/x86/vmx_tests.c > @@ -9855,6 +9855,143 @@ static void vmx_init_signal_test(void) > */ > } > > +#define SIPI_SIGNAL_TEST_DELAY 100000000ULL > + > +static void vmx_sipi_test_guest(void) > +{ > + if (apic_id() == 0) { > + /* wait AP enter guest with activity=WAIT_SIPI */ > + while (vmx_get_test_stage() != 1) > + ; > + delay(SIPI_SIGNAL_TEST_DELAY); > + > + /* First SIPI signal */ > + apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_STARTUP | APIC_INT_ASSERT, id_map[1]); > + report(1, "BSP(L2): Send first SIPI to cpu[%d]", id_map[1]); > + > + /* wait AP enter guest */ > + while (vmx_get_test_stage() != 2) > + ; > + delay(SIPI_SIGNAL_TEST_DELAY); > + > + /* Second SIPI signal should be ignored since AP is not in WAIT_SIPI state */ > + apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_STARTUP | APIC_INT_ASSERT, id_map[1]); > + report(1, "BSP(L2): Send second SIPI to cpu[%d]", id_map[1]); > + > + /* Delay a while to check whether second SIPI would cause VMExit */ > + delay(SIPI_SIGNAL_TEST_DELAY); > + > + /* Test is done, notify AP to exit test */ > + vmx_set_test_stage(3); > + > + /* wait AP exit non-root mode */ > + while (vmx_get_test_stage() != 5) > + ; > + } else { > + /* wait BSP notify test is done */ > + while (vmx_get_test_stage() != 3) > + ; > + > + /* AP exit guest */ > + vmx_set_test_stage(4); > + } > +} > + > +static void sipi_test_ap_thread(void *data) > +{ > + struct vmcs *ap_vmcs; > + u64 *ap_vmxon_region; > + void *ap_stack, *ap_syscall_stack; > + u64 cpu_ctrl_0 = CPU_SECONDARY; > + u64 cpu_ctrl_1 = 0; > + > + /* Enter VMX operation (i.e. exec VMXON) */ > + ap_vmxon_region = alloc_page(); > + enable_vmx(); > + init_vmx(ap_vmxon_region); > + _vmx_on(ap_vmxon_region); > + init_vmcs(&ap_vmcs); > + make_vmcs_current(ap_vmcs); > + > + /* Set stack for AP */ > + ap_stack = alloc_page(); > + ap_syscall_stack = alloc_page(); > + vmcs_write(GUEST_RSP, (u64)(ap_stack + PAGE_SIZE - 1)); > + vmcs_write(GUEST_SYSENTER_ESP, (u64)(ap_syscall_stack + PAGE_SIZE - 1)); > + > + /* passthrough lapic to L2 */ > + disable_intercept_for_x2apic_msrs(); > + vmcs_write(PIN_CONTROLS, vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT); > + vmcs_write(CPU_EXEC_CTRL0, vmcs_read(CPU_EXEC_CTRL0) | cpu_ctrl_0); > + vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) | cpu_ctrl_1); > + > + /* Set guest activity state to wait-for-SIPI state */ > + vmcs_write(GUEST_ACTV_STATE, ACTV_WAIT_SIPI); > + > + vmx_set_test_stage(1); > + > + /* AP enter guest */ > + enter_guest(); > + > + if (vmcs_read(EXI_REASON) == VMX_SIPI) { > + report(1, "AP: Handle SIPI VMExit"); > + vmcs_write(GUEST_ACTV_STATE, ACTV_ACTIVE); > + vmx_set_test_stage(2); > + } else { > + report(0, "AP: Unexpected VMExit, reason=%ld", vmcs_read(EXI_REASON)); > + vmx_off(); > + return; > + } > + > + /* AP enter guest */ > + enter_guest(); > + > + report(vmcs_read(EXI_REASON) != VMX_SIPI, > + "AP: should no SIPI VMExit since activity is not in WAIT_SIPI state"); > + > + /* notify BSP that AP is already exit from non-root mode */ > + vmx_set_test_stage(5); > + > + /* Leave VMX operation */ > + vmx_off(); > +} > + > +static void vmx_sipi_signal_test(void) > +{ > + if (!(rdmsr(MSR_IA32_VMX_MISC) & MSR_IA32_VMX_MISC_ACTIVITY_WAIT_SIPI)) { > + printf("\tACTIVITY_WAIT_SIPI state is not supported.\n"); > + return; > + } > + > + if (cpu_count() < 2) { > + report_skip(__func__); > + return; > + } > + > + u64 cpu_ctrl_0 = CPU_SECONDARY; > + u64 cpu_ctrl_1 = 0; > + > + /* passthrough lapic to L2 */ > + disable_intercept_for_x2apic_msrs(); > + vmcs_write(PIN_CONTROLS, vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT); > + vmcs_write(CPU_EXEC_CTRL0, vmcs_read(CPU_EXEC_CTRL0) | cpu_ctrl_0); > + vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) | cpu_ctrl_1); > + > + test_set_guest(vmx_sipi_test_guest); > + > + /* update CR3 on AP */ > + on_cpu(1, update_cr3, (void *)read_cr3()); > + > + /* start AP */ > + on_cpu_async(1, sipi_test_ap_thread, NULL); > + > + vmx_set_test_stage(0); > + > + /* BSP enter guest */ > + enter_guest(); > +} > + > + > enum vmcs_access { > ACCESS_VMREAD, > ACCESS_VMWRITE, > @@ -10492,6 +10629,7 @@ struct vmx_test vmx_tests[] = { > TEST(vmx_apic_passthrough_thread_test), > TEST(vmx_apic_passthrough_tpr_threshold_test), > TEST(vmx_init_signal_test), > + TEST(vmx_sipi_signal_test), > /* VMCS Shadowing tests */ > TEST(vmx_vmcs_shadow_test), > /* Regression tests */ > Queued, thanks. Paolo