@@ -7032,6 +7032,164 @@ static void test_load_host_pat(void)
test_pat(HOST_PAT, "HOST_PAT", EXI_CONTROLS, EXI_LOAD_PAT);
}
+union cpuidA_eax {
+ struct {
+ unsigned int version_id:8;
+ unsigned int num_counters_gp:8;
+ unsigned int bit_width:8;
+ unsigned int mask_length:8;
+ } split;
+ unsigned int full;
+};
+
+union cpuidA_edx {
+ struct {
+ unsigned int num_counters_fixed:5;
+ unsigned int bit_width_fixed:8;
+ unsigned int reserved:9;
+ } split;
+ unsigned int full;
+};
+
+static bool valid_pgc(u64 val)
+{
+ struct cpuid id;
+ union cpuidA_eax eax;
+ union cpuidA_edx edx;
+ u64 mask;
+
+ id = cpuid(0xA);
+ eax.full = id.a;
+ edx.full = id.d;
+ mask = ~(((1ull << eax.split.num_counters_gp) - 1) |
+ (((1ull << edx.split.num_counters_fixed) - 1) << 32));
+
+ return !(val & mask);
+}
+
+static void test_pgc_vmlaunch(u32 xerror, u32 xreason, bool xfail, bool host)
+{
+ u32 inst_err;
+ u64 obs;
+ bool success;
+ struct vmx_state_area_test_data *data = &vmx_state_area_test_data;
+
+ if (host) {
+ success = vmlaunch_succeeds();
+ obs = rdmsr(data->msr);
+ if (!success) {
+ inst_err = vmcs_read(VMX_INST_ERROR);
+ report("vmlaunch failed, VMX Inst Error is %d (expected %d)",
+ xerror == inst_err, inst_err, xerror);
+ } else {
+ report("Host state is 0x%lx (expected 0x%lx)",
+ !data->enabled || data->exp == obs, obs, data->exp);
+ report("vmlaunch succeeded", success != xfail);
+ }
+ } else {
+ if (xfail) {
+ enter_guest_with_invalid_guest_state();
+ } else {
+ enter_guest();
+ }
+ report_guest_state_test("load GUEST_PERF_GLOBAL_CTRL",
+ xreason, GUEST_PERF_GLOBAL_CTRL,
+ "GUEST_PERF_GLOBAL_CTRL");
+ }
+}
+
+/*
+ * test_load_perf_global_ctrl is a generic function for testing the
+ * "load IA32_PERF_GLOBAL_CTRL" VM-{Entry,Exit} controls. This test function
+ * tests the provided ctrl_val when disabled and enabled.
+ *
+ * @nr: VMCS field number corresponding to the host/guest state field
+ * @name: Name of the above VMCS field for printing in test report
+ * @ctrl_nr: VMCS field number corresponding to the VM-{Entry,Exit} control
+ * @ctrl_val: Bit to set on the ctrl_field
+ */
+static void test_perf_global_ctrl(u32 nr, const char *name, u32 ctrl_nr,
+ const char *ctrl_name, u64 ctrl_val)
+{
+ u64 ctrl_saved = vmcs_read(ctrl_nr);
+ u64 pgc_saved = vmcs_read(nr);
+ u64 i, val;
+ bool host = nr == HOST_PERF_GLOBAL_CTRL;
+ struct vmx_state_area_test_data *data = &vmx_state_area_test_data;
+
+ data->msr = MSR_CORE_PERF_GLOBAL_CTRL;
+ msr_bmp_init();
+ vmcs_write(ctrl_nr, ctrl_saved & ~ctrl_val);
+ data->enabled = false;
+ report_prefix_pushf("\"load IA32_PERF_GLOBAL_CTRL\"=0 on %s",
+ ctrl_name);
+
+ for (i = 0; i < 64; i++) {
+ val = 1ull << i;
+ vmcs_write(nr, val);
+ report_prefix_pushf("%s = 0x%lx", name, val);
+ test_pgc_vmlaunch(0, VMX_VMCALL, false, host);
+ report_prefix_pop();
+ }
+ report_prefix_pop();
+
+ vmcs_write(ctrl_nr, ctrl_saved | ctrl_val);
+ data->enabled = true;
+ report_prefix_pushf("\"load IA32_PERF_GLOBAL_CTRL\"=1 on %s",
+ ctrl_name);
+ for (i = 0; i < 64; i++) {
+ val = 1ull << i;
+ data->exp = val;
+ vmcs_write(nr, val);
+ report_prefix_pushf("%s = 0x%lx", name, val);
+ if (valid_pgc(val)) {
+ test_pgc_vmlaunch(0, VMX_VMCALL, false, host);
+ } else {
+ if (host)
+ test_pgc_vmlaunch(
+ VMXERR_ENTRY_INVALID_HOST_STATE_FIELD,
+ 0,
+ true,
+ host);
+ else
+ test_pgc_vmlaunch(
+ 0,
+ VMX_ENTRY_FAILURE | VMX_FAIL_STATE,
+ true,
+ host);
+ }
+ report_prefix_pop();
+ }
+
+ report_prefix_pop();
+ vmcs_write(ctrl_nr, ctrl_saved);
+ vmcs_write(nr, pgc_saved);
+}
+
+static void test_load_host_perf_global_ctrl(void)
+{
+ if (!(ctrl_exit_rev.clr & EXI_LOAD_PERF)) {
+ printf("\"load IA32_PERF_GLOBAL_CTRL\" exit control not supported\n");
+ return;
+ }
+
+ test_perf_global_ctrl(HOST_PERF_GLOBAL_CTRL, "HOST_PERF_GLOBAL_CTRL",
+ EXI_CONTROLS, "EXI_CONTROLS", EXI_LOAD_PERF);
+}
+
+
+static void test_load_guest_perf_global_ctrl(void)
+{
+ if (!(ctrl_enter_rev.clr & ENT_LOAD_PERF)) {
+ printf("\"load IA32_PERF_GLOBAL_CTRL\" entry control not supported\n");
+ return;
+ }
+
+ test_perf_global_ctrl(GUEST_PERF_GLOBAL_CTRL, "GUEST_PERF_GLOBAL_CTRL",
+ ENT_CONTROLS, "ENT_CONTROLS", ENT_LOAD_PERF);
+}
+
+
/*
* test_vmcs_field - test a value for the given VMCS field
* @field: VMCS field
@@ -7261,6 +7419,7 @@ static void vmx_host_state_area_test(void)
test_host_segment_regs();
test_host_desc_tables();
test_host_addr_size();
+ test_load_host_perf_global_ctrl();
}
/*
@@ -7296,6 +7455,7 @@ static void vmx_guest_state_area_test(void)
test_load_guest_pat();
test_guest_efer();
+ test_load_guest_perf_global_ctrl();
/*
* Let the guest finish execution