Reviewed-by: Dapeng Mi <[email protected]>
On 3/5/2026 2:07 AM, Zide Chen wrote:
> From: Dapeng Mi <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Dapeng Mi <[email protected]>
> Co-developed-by: Zide Chen <[email protected]>
> Signed-off-by: Zide Chen <[email protected]>
> ---
> 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)