kvm-unit-tests: VMX: Test suite for preemption timer
PATCH v2 1/1 kvm-unit-tests: VMX: Test suite for preemption timer
Test cases for preemption timer in nested VMX. Two aspects are tested:
1. Save preemption timer on VMEXIT if relevant bit set in EXIT_CONTROL
2. Test a relevant bug of KVM. The bug will not save preemption timer value if exit L2->L0 for some reason and enter L0->L2. Thus preemption timer will never trigger if the value is large enough.
3. Some other aspects are tested, e.g. preempt without save, preempt when value is 0.
---
ChangeLog to v1:
1. Add test of EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable
2. Add test of PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable
3. Add test of preemption value is 0
x86/vmx.h | 3 + x86/vmx_tests.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+)
x86/vmx.h +3 -0
@@ -210,6 +210,7 @@ enum Encoding {GUEST_ACTV_STATE =0x4826ul ,GUEST_SMBASE =0x4828ul ,GUEST_SYSENTER_CS =0x482aul , +PREEMPT_TIMER_VALUE =0x482eul ,/* 32-Bit Host State Fields */ HOST_SYSENTER_CS = 0x4c00ul,
@@ -331,6 +332,7 @@ enum Ctrl_exi {EXI_LOAD_PERF =1UL <<12 ,EXI_INTA =1UL <<15 ,EXI_LOAD_EFER =1UL <<21 , + EXI_SAVE_PREEMPT = 1UL << 22, }; enum Ctrl_ent {
@@ -342,6 +344,7 @@ enum Ctrl_pin {PIN_EXTINT =1ul <<0 ,PIN_NMI =1ul <<3 ,PIN_VIRT_NMI =1ul <<5 , + PIN_PREEMPT = 1ul << 6, }; enum Ctrl0 {
x86/vmx_tests.c +175 -0
190 lines not on this page · raw message
Signature · 1 line
1.7.9.5
Paolo Bonzini
maintainerIl 06/09/2013 04:11, Arthur Chunqi Li ha scritto:
Quoted 8 lines
Test cases for preemption timer in nested VMX. Two aspects are tested:
1. Save preemption timer on VMEXIT if relevant bit set inEXIT_CONTROL
2. Test a relevant bug of KVM. The bug will not save preemption timer value if exitL2->L0for some reason and enterL0->L2. Thus preemption timer will never trigger if the value is large enough.
3. Some other aspects are tested, e.g. preempt without save, preempt when value is 0.
Quoted 6 lines
Signed-off-by Arthur Chunqi Li <[email protected]>---
ChangeLog to v1:
1. Add test ofEXI_SAVE_PREEMPTenable andPIN_PREEMPTdisable
2. Add test ofPIN_PREEMPTenable andEXI_SAVE_PREEMPTenable/disable
3. Add test of preemption value is 0
The patch looks good, however we have now 3 series that are conflicting (basic tests, EPT, preemption timer).
I ordered them and pushed them to "vmx" branch of kvm-unit-tests.git. Can you send them as a single series from now on?
Thanks,
Paolo
Quoted 230 lines
x86/vmx.h | 3 + x86/vmx_tests.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+)x86/vmx.h +3
-0@@ -210,6 +210,7 @@ enum Encoding {GUEST_ACTV_STATE =0x4826ul ,GUEST_SMBASE =0x4828ul ,GUEST_SYSENTER_CS =0x482aul , +PREEMPT_TIMER_VALUE =0x482eul ,/* 32-Bit Host State Fields */ HOST_SYSENTER_CS = 0x4c00ul,@@ -331,6 +332,7 @@ enum Ctrl_exi {EXI_LOAD_PERF =1UL <<12 ,EXI_INTA =1UL <<15 ,EXI_LOAD_EFER =1UL <<21 , + EXI_SAVE_PREEMPT = 1UL << 22, }; enum Ctrl_ent {@@ -342,6 +344,7 @@ enum Ctrl_pin {PIN_EXTINT =1ul <<0 ,PIN_NMI =1ul <<3 ,PIN_VIRT_NMI =1ul <<5 , + PIN_PREEMPT = 1ul << 6, }; enum Ctrl0 {x86/vmx_tests.c +175
-0@@ -1,4 +1,30 @@#include "vmx.h" +#include "msr.h" +#include "processor.h" + +volatile u32 stage; + +static inline void vmcall () +{ + asmvolatile ("vmcall" ); +} + +static inline void set_stage (u32 s) +{ +barrier (); + stage = s; +barrier (); +} + +static inline u32 get_stage () +{ +u32 s; + +barrier (); + s = stage; +barrier (); +return s; +}void basic_init () {@@ -76,6 +102,153 @@ int vmenter_exit_handler()return VMX_TEST_VMEXIT ; } +u32 preempt_scale; +volatile unsigned long long tsc_val; +volatile u32 preempt_val; + +void preemption_timer_init () +{ +u32 ctrl_exit; + +// Enable EXI_SAVE_PREEMPT with PIN_PREEMPT dieabled + ctrl_exit = (vmcs_read (EXI_CONTROLS ) | +EXI_SAVE_PREEMPT ) & ctrl_exit_rev.clr; +vmcs_write (EXI_CONTROLS , ctrl_exit); + preempt_val =10000000 ; +vmcs_write (PREEMPT_TIMER_VALUE , preempt_val); +set_stage (0 ); + preempt_scale =rdmsr (MSR_IA32_VMX_MISC ) &0x1F ; +} + +void preemption_timer_main () +{ +int i, j; + +if (!(ctrl_pin_rev.clr &PIN_PREEMPT )) { +printf ("\tPreemption timer is not supported\n" ); +return ; + } +if (!(ctrl_exit_rev.clr &EXI_SAVE_PREEMPT )) +printf ("\tSave preemption value is not supported\n" ); +else { +// Test EXI_SAVE_PREEMPT enable and PIN_PREEMPT disable +set_stage (0 ); +// Consume enough time to let L2->L0->L2 occurs +for (i =0 ; i <100000 ; i++) +for (j =0 ; j <10000 ; j++); +vmcall (); +// Test PIN_PREEMPT enable and EXI_SAVE_PREEMPT enable/disable +set_stage (1 ); +vmcall (); +// Test both enable +if (get_stage () ==2 ) +vmcall (); + } +// Test the bug of reseting preempt value when L2->L0->L2 +set_stage (3 ); +vmcall (); + tsc_val =rdtsc (); +while (1 ) { +if (((rdtsc () - tsc_val) >> preempt_scale) + >10 * preempt_val) { +report ("Preemption timer timeout" ,0 ); +break ; + } +if (get_stage () ==4 ) +break ; + } +// Test preempt val is 0 +set_stage (4 ); +report ("Preemption timer, val=0" ,0 ); +} + +int preemption_timer_exit_handler () +{ +u64 guest_rip; +ulong reason; +u32 insn_len; +u32 ctrl_exit; +u32 ctrl_pin; + + guest_rip =vmcs_read (GUEST_RIP ); + reason =vmcs_read (EXI_REASON ) &0xff ; + insn_len =vmcs_read (EXI_INST_LEN ); +switch (reason) { +case VMX_PREEMPT : +switch (get_stage ()) { +case 3 : +if (((rdtsc () - tsc_val) >> preempt_scale) < preempt_val) +report ("Preemption timer timeout" ,0 ); +else +report ("Preemption timer timeout" ,1 ); +set_stage (get_stage () +1 ); +break ; +case 4 : +if (vmcs_read (PREEMPT_TIMER_VALUE ) ==0 ) +report ("Preemption timer, val=0" ,1 ); +else +report ("Preemption timer, val=0" ,0 ); +return VMX_TEST_VMEXIT ; + } +return VMX_TEST_RESUME ; +case VMX_VMCALL : +switch (get_stage ()) { +case 0 : +if (vmcs_read (PREEMPT_TIMER_VALUE ) != preempt_val) +report ("Preemption timer off" ,0 ); +else +report ("Preemption timer off" ,1 ); +// Enable PIN_PREEMPT and disable EXI_SAVE_PREEMPT + ctrl_pin = (vmcs_read (PIN_CONTROLS ) |PIN_PREEMPT ) & + ctrl_pin_rev.clr; +vmcs_write (PIN_CONTROLS , ctrl_pin); + ctrl_exit = (vmcs_read (EXI_CONTROLS ) & + ~EXI_SAVE_PREEMPT ) & ctrl_exit_rev.clr; +vmcs_write (EXI_CONTROLS , ctrl_exit); +vmcs_write (PREEMPT_TIMER_VALUE , preempt_val); +break ; +case 1 : +if (vmcs_read (PREEMPT_TIMER_VALUE ) != preempt_val) +report ("Save preemption value" ,0 ); +else { +set_stage (get_stage () +1 ); +// Enable EXI_SAVE_PREEMPT with PIN_PREEMPT +// already enabled + ctrl_exit = (vmcs_read (EXI_CONTROLS ) | +EXI_SAVE_PREEMPT ) & ctrl_exit_rev.clr; +vmcs_write (EXI_CONTROLS , ctrl_exit); + } +vmcs_write (PREEMPT_TIMER_VALUE , preempt_val); +break ; +case 2 : +if (vmcs_read (PREEMPT_TIMER_VALUE ) >= preempt_val) +report ("Save preemption value" ,0 ); +else +report ("Save preemption value" ,1 ); +break ; +case 3 : +vmcs_write (PREEMPT_TIMER_VALUE , preempt_val); + ctrl_pin = (vmcs_read (PIN_CONTROLS ) |PIN_PREEMPT ) & + ctrl_pin_rev.clr; +vmcs_write (PIN_CONTROLS , ctrl_pin); + ctrl_exit = (vmcs_read (EXI_CONTROLS ) | +EXI_SAVE_PREEMPT ) & ctrl_exit_rev.clr; +vmcs_write (EXI_CONTROLS , ctrl_exit); +break ; +default : +printf ("Invalid stage.\n" ); +print_vmexit_info (); +return VMX_TEST_VMEXIT ; + } +vmcs_write (GUEST_RIP , guest_rip + insn_len); +return VMX_TEST_RESUME ; +default : +printf ("Unknown exit reason, %d\n" , reason); +print_vmexit_info (); + } +return VMX_TEST_VMEXIT ; +} +/* name/init/guest_main/exit_handler/syscall_handler/guest_regs basic_* just implement some basic functions */ struct vmx_test vmx_tests[] = {@@ -83,5 +256,7 @@ struct vmx_test vmx_tests[] = { basic_syscall_handler, {0} }, {"vmenter" , basic_init, vmenter_main, vmenter_exit_handler, basic_syscall_handler, {0} }, + {"preemption timer" , preemption_timer_init, preemption_timer_main, + preemption_timer_exit_handler, basic_syscall_handler, {0} }, { NULL, NULL, NULL, NULL, NULL, {0} }, };