From: Dapeng Mi <dapeng1.mi@linux.intel.com>
The legacy DS-based PEBS relies on IA32_DS_AREA and IA32_PEBS_ENABLE
to take snapshots of a subset of the machine registers into the Intel
Debug-Store.
Adaptive PEBS introduces MSR_PEBS_DATA_CFG to be able to capture only
the data of interest, which is enumerated via bit 14 (PEBS_BASELINE)
of IA32_PERF_CAPABILITIES.
QEMU must save, restore and migrate these MSRs when legacy PEBS is
enabled. Though the availability of these MSRs may not be the same,
it's still valid to put them in the same vmstate subsection for
implementation simplicity.
Originally-by: Luwei Kang <luwei.kang@intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Co-developed-by: Zide Chen <zide.chen@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
---
V3:
- Add the missing Originally-by tag to credit Luwei.
- Fix the vmstate name of msr_ds_pebs.
- Fix the criteria for determining availability of IA32_PEBS_ENABLE
and MSR_PEBS_DATA_CFG.
- Change title to cover all aspects of what this patch does.
- Re-work the commit messages.
---
target/i386/cpu.h | 10 ++++++++++
target/i386/kvm/kvm.c | 29 +++++++++++++++++++++++++++++
target/i386/machine.c | 27 ++++++++++++++++++++++++++-
3 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 7c241a20420c..3a10f3242329 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -422,6 +422,7 @@ typedef enum X86Seg {
#define MSR_IA32_PERF_CAPABILITIES 0x345
#define PERF_CAP_LBR_FMT 0x3f
#define PERF_CAP_FULL_WRITE (1U << 13)
+#define PERF_CAP_PEBS_BASELINE (1U << 14)
#define MSR_IA32_TSX_CTRL 0x122
#define MSR_IA32_TSCDEADLINE 0x6e0
@@ -479,6 +480,7 @@ typedef enum X86Seg {
/* Indicates good rep/movs microcode on some processors: */
#define MSR_IA32_MISC_ENABLE_FASTSTRING (1ULL << 0)
#define MSR_IA32_MISC_ENABLE_BTS_UNAVAIL (1ULL << 11)
+#define MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL (1ULL << 12)
#define MSR_IA32_MISC_ENABLE_MWAIT (1ULL << 18)
#define MSR_IA32_MISC_ENABLE_DEFAULT (MSR_IA32_MISC_ENABLE_FASTSTRING | \
MSR_IA32_MISC_ENABLE_BTS_UNAVAIL)
@@ -514,6 +516,11 @@ typedef enum X86Seg {
#define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS 0xc0000300
#define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301
+/* Legacy DS based PEBS MSRs */
+#define MSR_IA32_PEBS_ENABLE 0x3f1
+#define MSR_PEBS_DATA_CFG 0x3f2
+#define MSR_IA32_DS_AREA 0x600
+
#define MSR_K7_EVNTSEL0 0xc0010000
#define MSR_K7_PERFCTR0 0xc0010004
#define MSR_F15H_PERF_CTL0 0xc0010200
@@ -2099,6 +2106,9 @@ typedef struct CPUArchState {
uint64_t msr_fixed_ctr_ctrl;
uint64_t msr_global_ctrl;
uint64_t msr_global_status;
+ uint64_t msr_ds_area;
+ uint64_t msr_pebs_data_cfg;
+ uint64_t msr_pebs_enable;
uint64_t msr_fixed_counters[MAX_FIXED_COUNTERS];
uint64_t msr_gp_counters[MAX_GP_COUNTERS];
uint64_t msr_gp_evtsel[MAX_GP_COUNTERS];
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 4ba54151320f..8c4564bcbb9e 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -4280,6 +4280,16 @@ static int kvm_put_msrs(X86CPU *cpu, KvmPutState level)
kvm_msr_entry_add(cpu, MSR_CORE_PERF_GLOBAL_CTRL, 0);
}
+ if (env->features[FEAT_1_EDX] & CPUID_DTS) {
+ kvm_msr_entry_add(cpu, MSR_IA32_DS_AREA, env->msr_ds_area);
+ }
+ if (!(env->msr_ia32_misc_enable & MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL)) {
+ kvm_msr_entry_add(cpu, MSR_IA32_PEBS_ENABLE, env->msr_pebs_enable);
+ }
+ if (env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_PEBS_BASELINE) {
+ kvm_msr_entry_add(cpu, MSR_PEBS_DATA_CFG, env->msr_pebs_data_cfg);
+ }
+
/* Set the counter values. */
for (i = 0; i < num_pmu_fixed_counters; i++) {
kvm_msr_entry_add(cpu, MSR_CORE_PERF_FIXED_CTR0 + i,
@@ -4900,6 +4910,16 @@ static int kvm_get_msrs(X86CPU *cpu)
kvm_msr_entry_add(cpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0);
kvm_msr_entry_add(cpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, 0);
}
+
+ if (env->features[FEAT_1_EDX] & CPUID_DTS) {
+ kvm_msr_entry_add(cpu, MSR_IA32_DS_AREA, 0);
+ }
+ if (!(env->msr_ia32_misc_enable & MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL)) {
+ kvm_msr_entry_add(cpu, MSR_IA32_PEBS_ENABLE, 0);
+ }
+ if (env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_PEBS_BASELINE) {
+ kvm_msr_entry_add(cpu, MSR_PEBS_DATA_CFG, 0);
+ }
}
if (env->mcg_cap) {
@@ -5241,6 +5261,15 @@ static int kvm_get_msrs(X86CPU *cpu)
env->msr_gp_evtsel[index] = msrs[i].data;
}
break;
+ case MSR_IA32_DS_AREA:
+ env->msr_ds_area = msrs[i].data;
+ break;
+ case MSR_PEBS_DATA_CFG:
+ env->msr_pebs_data_cfg = msrs[i].data;
+ break;
+ case MSR_IA32_PEBS_ENABLE:
+ env->msr_pebs_enable = msrs[i].data;
+ break;
case HV_X64_MSR_HYPERCALL:
env->msr_hv_hypercall = msrs[i].data;
break;
diff --git a/target/i386/machine.c b/target/i386/machine.c
index 7d08a05835fc..5cff5d5a9db5 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -659,6 +659,27 @@ static const VMStateDescription vmstate_msr_ia32_feature_control = {
}
};
+static bool ds_pebs_enabled(void *opaque)
+{
+ X86CPU *cpu = opaque;
+ CPUX86State *env = &cpu->env;
+
+ return (env->msr_ds_area || env->msr_pebs_enable ||
+ env->msr_pebs_data_cfg);
+}
+
+static const VMStateDescription vmstate_msr_ds_pebs = {
+ .name = "cpu/msr_architectural_pmu/msr_ds_pebs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = ds_pebs_enabled,
+ .fields = (const VMStateField[]){
+ VMSTATE_UINT64(env.msr_ds_area, X86CPU),
+ VMSTATE_UINT64(env.msr_pebs_data_cfg, X86CPU),
+ VMSTATE_UINT64(env.msr_pebs_enable, X86CPU),
+ VMSTATE_END_OF_LIST()}
+};
+
static bool pmu_enable_needed(void *opaque)
{
X86CPU *cpu = opaque;
@@ -697,7 +718,11 @@ static const VMStateDescription vmstate_msr_architectural_pmu = {
VMSTATE_UINT64_ARRAY(env.msr_gp_counters, X86CPU, MAX_GP_COUNTERS),
VMSTATE_UINT64_ARRAY(env.msr_gp_evtsel, X86CPU, MAX_GP_COUNTERS),
VMSTATE_END_OF_LIST()
- }
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_msr_ds_pebs,
+ NULL,
+ },
};
static bool mpx_needed(void *opaque)
--
2.53.0
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
On 3/5/2026 2:07 AM, Zide Chen wrote:
> From: Dapeng Mi <dapeng1.mi@linux.intel.com>
>
> The legacy DS-based PEBS relies on IA32_DS_AREA and IA32_PEBS_ENABLE
> to take snapshots of a subset of the machine registers into the Intel
> Debug-Store.
>
> Adaptive PEBS introduces MSR_PEBS_DATA_CFG to be able to capture only
> the data of interest, which is enumerated via bit 14 (PEBS_BASELINE)
> of IA32_PERF_CAPABILITIES.
>
> QEMU must save, restore and migrate these MSRs when legacy PEBS is
> enabled. Though the availability of these MSRs may not be the same,
> it's still valid to put them in the same vmstate subsection for
> implementation simplicity.
>
> Originally-by: Luwei Kang <luwei.kang@intel.com>
> Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
> Co-developed-by: Zide Chen <zide.chen@intel.com>
> Signed-off-by: Zide Chen <zide.chen@intel.com>
> ---
> V3:
> - Add the missing Originally-by tag to credit Luwei.
> - Fix the vmstate name of msr_ds_pebs.
> - Fix the criteria for determining availability of IA32_PEBS_ENABLE
> and MSR_PEBS_DATA_CFG.
> - Change title to cover all aspects of what this patch does.
> - Re-work the commit messages.
> ---
> target/i386/cpu.h | 10 ++++++++++
> target/i386/kvm/kvm.c | 29 +++++++++++++++++++++++++++++
> target/i386/machine.c | 27 ++++++++++++++++++++++++++-
> 3 files changed, 65 insertions(+), 1 deletion(-)
>
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 7c241a20420c..3a10f3242329 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -422,6 +422,7 @@ typedef enum X86Seg {
> #define MSR_IA32_PERF_CAPABILITIES 0x345
> #define PERF_CAP_LBR_FMT 0x3f
> #define PERF_CAP_FULL_WRITE (1U << 13)
> +#define PERF_CAP_PEBS_BASELINE (1U << 14)
>
> #define MSR_IA32_TSX_CTRL 0x122
> #define MSR_IA32_TSCDEADLINE 0x6e0
> @@ -479,6 +480,7 @@ typedef enum X86Seg {
> /* Indicates good rep/movs microcode on some processors: */
> #define MSR_IA32_MISC_ENABLE_FASTSTRING (1ULL << 0)
> #define MSR_IA32_MISC_ENABLE_BTS_UNAVAIL (1ULL << 11)
> +#define MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL (1ULL << 12)
> #define MSR_IA32_MISC_ENABLE_MWAIT (1ULL << 18)
> #define MSR_IA32_MISC_ENABLE_DEFAULT (MSR_IA32_MISC_ENABLE_FASTSTRING | \
> MSR_IA32_MISC_ENABLE_BTS_UNAVAIL)
> @@ -514,6 +516,11 @@ typedef enum X86Seg {
> #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS 0xc0000300
> #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301
>
> +/* Legacy DS based PEBS MSRs */
> +#define MSR_IA32_PEBS_ENABLE 0x3f1
> +#define MSR_PEBS_DATA_CFG 0x3f2
> +#define MSR_IA32_DS_AREA 0x600
> +
> #define MSR_K7_EVNTSEL0 0xc0010000
> #define MSR_K7_PERFCTR0 0xc0010004
> #define MSR_F15H_PERF_CTL0 0xc0010200
> @@ -2099,6 +2106,9 @@ typedef struct CPUArchState {
> uint64_t msr_fixed_ctr_ctrl;
> uint64_t msr_global_ctrl;
> uint64_t msr_global_status;
> + uint64_t msr_ds_area;
> + uint64_t msr_pebs_data_cfg;
> + uint64_t msr_pebs_enable;
> uint64_t msr_fixed_counters[MAX_FIXED_COUNTERS];
> uint64_t msr_gp_counters[MAX_GP_COUNTERS];
> uint64_t msr_gp_evtsel[MAX_GP_COUNTERS];
> diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
> index 4ba54151320f..8c4564bcbb9e 100644
> --- a/target/i386/kvm/kvm.c
> +++ b/target/i386/kvm/kvm.c
> @@ -4280,6 +4280,16 @@ static int kvm_put_msrs(X86CPU *cpu, KvmPutState level)
> kvm_msr_entry_add(cpu, MSR_CORE_PERF_GLOBAL_CTRL, 0);
> }
>
> + if (env->features[FEAT_1_EDX] & CPUID_DTS) {
> + kvm_msr_entry_add(cpu, MSR_IA32_DS_AREA, env->msr_ds_area);
> + }
> + if (!(env->msr_ia32_misc_enable & MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL)) {
> + kvm_msr_entry_add(cpu, MSR_IA32_PEBS_ENABLE, env->msr_pebs_enable);
> + }
> + if (env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_PEBS_BASELINE) {
> + kvm_msr_entry_add(cpu, MSR_PEBS_DATA_CFG, env->msr_pebs_data_cfg);
> + }
> +
> /* Set the counter values. */
> for (i = 0; i < num_pmu_fixed_counters; i++) {
> kvm_msr_entry_add(cpu, MSR_CORE_PERF_FIXED_CTR0 + i,
> @@ -4900,6 +4910,16 @@ static int kvm_get_msrs(X86CPU *cpu)
> kvm_msr_entry_add(cpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0);
> kvm_msr_entry_add(cpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, 0);
> }
> +
> + if (env->features[FEAT_1_EDX] & CPUID_DTS) {
> + kvm_msr_entry_add(cpu, MSR_IA32_DS_AREA, 0);
> + }
> + if (!(env->msr_ia32_misc_enable & MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL)) {
> + kvm_msr_entry_add(cpu, MSR_IA32_PEBS_ENABLE, 0);
> + }
> + if (env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_PEBS_BASELINE) {
> + kvm_msr_entry_add(cpu, MSR_PEBS_DATA_CFG, 0);
> + }
> }
>
> if (env->mcg_cap) {
> @@ -5241,6 +5261,15 @@ static int kvm_get_msrs(X86CPU *cpu)
> env->msr_gp_evtsel[index] = msrs[i].data;
> }
> break;
> + case MSR_IA32_DS_AREA:
> + env->msr_ds_area = msrs[i].data;
> + break;
> + case MSR_PEBS_DATA_CFG:
> + env->msr_pebs_data_cfg = msrs[i].data;
> + break;
> + case MSR_IA32_PEBS_ENABLE:
> + env->msr_pebs_enable = msrs[i].data;
> + break;
> case HV_X64_MSR_HYPERCALL:
> env->msr_hv_hypercall = msrs[i].data;
> break;
> diff --git a/target/i386/machine.c b/target/i386/machine.c
> index 7d08a05835fc..5cff5d5a9db5 100644
> --- a/target/i386/machine.c
> +++ b/target/i386/machine.c
> @@ -659,6 +659,27 @@ static const VMStateDescription vmstate_msr_ia32_feature_control = {
> }
> };
>
> +static bool ds_pebs_enabled(void *opaque)
> +{
> + X86CPU *cpu = opaque;
> + CPUX86State *env = &cpu->env;
> +
> + return (env->msr_ds_area || env->msr_pebs_enable ||
> + env->msr_pebs_data_cfg);
> +}
> +
> +static const VMStateDescription vmstate_msr_ds_pebs = {
> + .name = "cpu/msr_architectural_pmu/msr_ds_pebs",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .needed = ds_pebs_enabled,
> + .fields = (const VMStateField[]){
> + VMSTATE_UINT64(env.msr_ds_area, X86CPU),
> + VMSTATE_UINT64(env.msr_pebs_data_cfg, X86CPU),
> + VMSTATE_UINT64(env.msr_pebs_enable, X86CPU),
> + VMSTATE_END_OF_LIST()}
> +};
> +
> static bool pmu_enable_needed(void *opaque)
> {
> X86CPU *cpu = opaque;
> @@ -697,7 +718,11 @@ static const VMStateDescription vmstate_msr_architectural_pmu = {
> VMSTATE_UINT64_ARRAY(env.msr_gp_counters, X86CPU, MAX_GP_COUNTERS),
> VMSTATE_UINT64_ARRAY(env.msr_gp_evtsel, X86CPU, MAX_GP_COUNTERS),
> VMSTATE_END_OF_LIST()
> - }
> + },
> + .subsections = (const VMStateDescription * const []) {
> + &vmstate_msr_ds_pebs,
> + NULL,
> + },
> };
>
> static bool mpx_needed(void *opaque)
© 2016 - 2026 Red Hat, Inc.