Signed-off-by: Maxim Levitsky <mlevitsk@xxxxxxxxxx> --- lib/x86/processor.h | 3 +- x86/svm.c | 11 +++++++ x86/svm.h | 5 +++- x86/svm_tests.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 7 ++++- 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index b3fe924..9a0dad6 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -190,10 +190,11 @@ static inline bool is_intel(void) #define X86_FEATURE_LBRV (CPUID(0x8000000A, 0, EDX, 1)) #define X86_FEATURE_NRIPS (CPUID(0x8000000A, 0, EDX, 3)) #define X86_FEATURE_TSCRATEMSR (CPUID(0x8000000A, 0, EDX, 4)) +#define X86_FEATURE_PAUSEFILTER (CPUID(0x8000000A, 0, EDX, 10)) +#define X86_FEATURE_PFTHRESHOLD (CPUID(0x8000000A, 0, EDX, 12)) #define X86_FEATURE_VGIF (CPUID(0x8000000A, 0, EDX, 16)) - static inline bool this_cpu_has(u64 feature) { u32 input_eax = feature >> 32; diff --git a/x86/svm.c b/x86/svm.c index 460fc59..f6896f0 100644 --- a/x86/svm.c +++ b/x86/svm.c @@ -80,6 +80,17 @@ bool tsc_scale_supported(void) return this_cpu_has(X86_FEATURE_TSCRATEMSR); } +bool pause_filter_supported(void) +{ + return this_cpu_has(X86_FEATURE_PAUSEFILTER); +} + +bool pause_threshold_supported(void) +{ + return this_cpu_has(X86_FEATURE_PFTHRESHOLD); +} + + void default_prepare(struct svm_test *test) { vmcb_ident(vmcb); diff --git a/x86/svm.h b/x86/svm.h index d92c4f2..e93822b 100644 --- a/x86/svm.h +++ b/x86/svm.h @@ -75,7 +75,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area { u16 intercept_dr_write; u32 intercept_exceptions; u64 intercept; - u8 reserved_1[42]; + u8 reserved_1[40]; + u16 pause_filter_thresh; u16 pause_filter_count; u64 iopm_base_pa; u64 msrpm_base_pa; @@ -411,6 +412,8 @@ bool default_supported(void); bool vgif_supported(void); bool lbrv_supported(void); bool tsc_scale_supported(void); +bool pause_filter_supported(void); +bool pause_threshold_supported(void); void default_prepare(struct svm_test *test); void default_prepare_gif_clear(struct svm_test *test); bool default_finished(struct svm_test *test); diff --git a/x86/svm_tests.c b/x86/svm_tests.c index e7bd788..6a9b03b 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -3030,6 +3030,75 @@ static bool vgif_check(struct svm_test *test) return get_test_stage(test) == 3; } + +static int pause_test_counter; +static int wait_counter; + +static void pause_filter_test_guest_main(struct svm_test *test) +{ + int i; + for (i = 0 ; i < pause_test_counter ; i++) + pause(); + + if (!wait_counter) + return; + + for (i = 0; i < wait_counter; i++) + ; + + for (i = 0 ; i < pause_test_counter ; i++) + pause(); + +} + +static void pause_filter_run_test(int pause_iterations, int filter_value, int wait_iterations, int threshold) +{ + test_set_guest(pause_filter_test_guest_main); + + pause_test_counter = pause_iterations; + wait_counter = wait_iterations; + + vmcb->control.pause_filter_count = filter_value; + vmcb->control.pause_filter_thresh = threshold; + svm_vmrun(); + + if (filter_value <= pause_iterations || wait_iterations < threshold) + report(vmcb->control.exit_code == SVM_EXIT_PAUSE, "expected PAUSE vmexit"); + else + report(vmcb->control.exit_code == SVM_EXIT_VMMCALL, "no expected PAUSE vmexit"); +} + +static void pause_filter_test(void) +{ + if (!pause_filter_supported()) { + report_skip("PAUSE filter not supported in the guest"); + return; + } + + vmcb->control.intercept |= (1 << INTERCEPT_PAUSE); + + // filter count more that pause count - no VMexit + pause_filter_run_test(10, 9, 0, 0); + + // filter count smaller pause count - no VMexit + pause_filter_run_test(20, 21, 0, 0); + + + if (pause_threshold_supported()) { + // filter count smaller pause count - no VMexit + large enough threshold + // so that filter counter resets + pause_filter_run_test(20, 21, 1000, 10); + + // filter count smaller pause count - no VMexit + small threshold + // so that filter doesn't reset + pause_filter_run_test(20, 21, 10, 1000); + } else { + report_skip("PAUSE threshold not supported in the guest"); + return; + } +} + + static int of_test_counter; static void guest_test_of_handler(struct ex_regs *r) @@ -3698,5 +3767,6 @@ struct svm_test svm_tests[] = { TEST(svm_intr_intercept_mix_nmi), TEST(svm_intr_intercept_mix_smi), TEST(svm_tsc_scale_test), + TEST(pause_filter_test), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 89ff949..c277088 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -238,7 +238,12 @@ arch = x86_64 [svm] file = svm.flat smp = 2 -extra_params = -cpu max,+svm -m 4g +extra_params = -cpu max,+svm -m 4g -append "-pause_filter_test" +arch = x86_64 + +[svm_pause_filter] +file = svm.flat +extra_params = -cpu max,+svm -overcommit cpu-pm=on -m 4g -append pause_filter_test arch = x86_64 [taskswitch] -- 2.26.3