xen/arm: Hide PMU registers from the guest, when the vPMU feature is disabled

On ARMv8.4-A and newer platforms, booting Dom0 Linux with ACPI enabled
causes the domain to probe advanced PMU feature based on system ID
register ID_AA64DFR0_EL1. During this probe, Linux accesses PMMIR_EL1,
which causes unhandled register traps and crashes the domain.

To address this issue, implement the following:

- Add emulation for ID_AA64DFR{0,1}_EL1, ID_DFR{0,1}_EL1 and
  ID_DFR{0,1} register accesses for guest domains.
- Hide PMU registers from a guest domain when its vPMU feature is
  disabled.
- Proactively make SPE, TRBE, BRBE, Trace Extensions, SPMU, ITE and
  EBEP inaccessible to arm64 guest domains and hide TraceFilt,
  MMapTrc and CopTrc to arm32 guest domains, as they could
  potentially cause similar issues.

Fixes: dbb948110a ("xen: Expose the PMU to the guests")
Fixes: 07b9acea11 ("xen/arm: Add handler for ID registers on arm64")
Fixes: 3669a1cb95 ("xen/arm: create a cpuinfo structure for guest")
Signed-off-by: Hirokazu Takahashi <taka@valinux.co.jp>
Reviewed-by: Michal Orzel <michal.orzel@amd.com>
(cherry picked from commit b836ef686e)
This commit is contained in:
Hirokazu Takahashi authored and Michal Orzel committed 2026-09-21 13:56:50 +02:00
1 parent 6a3824e4d6
commit 84cc114ffd
8 files changed
+129 -18

No files matched your search

+55 -4
View File
@@ -229,6 +229,7 @@ void do_sysreg(struct cpu_user_regs *regs,
*/
case HSR_SYSREG_PMINTENSET_EL1:
case HSR_SYSREG_PMINTENCLR_EL1:
case HSR_SYSREG_PMMIR_EL1:
return handle_raz_wi(regs, regidx, hsr.sysreg.read, hsr, 1);
case HSR_SYSREG_PMUSERENR_EL0:
/* RO at EL0. RAZ/WI at EL1 */
@@ -302,8 +303,32 @@ void do_sysreg(struct cpu_user_regs *regs,
GENERATE_TID3_INFO(ID_PFR0_EL1, pfr32, 0)
GENERATE_TID3_INFO(ID_PFR1_EL1, pfr32, 1)
GENERATE_TID3_INFO(ID_PFR2_EL1, pfr32, 2)
GENERATE_TID3_INFO(ID_DFR0_EL1, dbg32, 0)
GENERATE_TID3_INFO(ID_DFR1_EL1, dbg32, 1)
case HSR_SYSREG_ID_DFR0_EL1:
{
union cpuinfo_dbg32 info_dbg32 = domain_cpuinfo.dbg32;
if ( !is_vpmu_domain(v->domain) )
info_dbg32.perfmon = 0;
return handle_ro_read_val(regs, regidx, hsr.sysreg.read, hsr, 1,
info_dbg32.bits[0]);
}
case HSR_SYSREG_ID_DFR1_EL1:
{
union cpuinfo_dbg32 info_dbg32 = domain_cpuinfo.dbg32;
if ( !is_vpmu_domain(v->domain) )
{
info_dbg32.mtpmu = 0;
info_dbg32.hpmn0 = 0;
}
return handle_ro_read_val(regs, regidx, hsr.sysreg.read, hsr, 1,
info_dbg32.bits[1]);
}
GENERATE_TID3_INFO(ID_AFR0_EL1, aux32, 0)
GENERATE_TID3_INFO(ID_MMFR0_EL1, mm32, 0)
GENERATE_TID3_INFO(ID_MMFR1_EL1, mm32, 1)
@@ -344,8 +369,34 @@ void do_sysreg(struct cpu_user_regs *regs,
}
GENERATE_TID3_INFO(ID_AA64PFR1_EL1, pfr64, 1)
GENERATE_TID3_INFO(ID_AA64DFR0_EL1, dbg64, 0)
GENERATE_TID3_INFO(ID_AA64DFR1_EL1, dbg64, 1)
case HSR_SYSREG_ID_AA64DFR0_EL1:
{
union cpuinfo_dbg64 info_dbg64 = domain_cpuinfo.dbg64;
if ( !is_vpmu_domain(v->domain) )
{
info_dbg64.pmu_ver = 0;
info_dbg64.pmss = 0;
info_dbg64.mtpmu = 0;
info_dbg64.hpmn0 = 0;
}
return handle_ro_read_val(regs, regidx, hsr.sysreg.read, hsr, 1,
info_dbg64.bits[0]);
}
case HSR_SYSREG_ID_AA64DFR1_EL1:
{
union cpuinfo_dbg64 info_dbg64 = domain_cpuinfo.dbg64;
if ( !is_vpmu_domain(v->domain) )
info_dbg64.pmicntr = 0;
return handle_ro_read_val(regs, regidx, hsr.sysreg.read, hsr, 1,
info_dbg64.bits[1]);
}
GENERATE_TID3_INFO(ID_AA64ISAR0_EL1, isa64, 0)
GENERATE_TID3_INFO(ID_AA64ISAR1_EL1, isa64, 1)
GENERATE_TID3_INFO(ID_AA64MMFR0_EL1, mm64, 0)
+18
View File
@@ -219,8 +219,26 @@ static int __init create_domain_cpuinfo(void)
domain_cpuinfo.isa64.api = 0;
domain_cpuinfo.isa64.gpa = 0;
domain_cpuinfo.isa64.gpi = 0;
/* Hide SPE, DPFZS, TRBE, BRBE, Trace Extensions, SPMU, ITE and EBEP */
domain_cpuinfo.dbg64.trace_ver = 0;
domain_cpuinfo.dbg64.pms_ver = 0;
domain_cpuinfo.dbg64.dpfzs = 0;
domain_cpuinfo.dbg64.trace_filt = 0;
domain_cpuinfo.dbg64.trace_buffer = 0;
domain_cpuinfo.dbg64.ext_trc_buff = 0;
domain_cpuinfo.dbg64.brbe = 0;
domain_cpuinfo.dbg64.syspmuid = 0;
domain_cpuinfo.dbg64.spmu = 0;
domain_cpuinfo.dbg64.ite = 0;
domain_cpuinfo.dbg64.ebep = 0;
#endif
/* Hide Trace Extensions for AArch32 domain */
domain_cpuinfo.dbg32.coptrc = 0;
domain_cpuinfo.dbg32.mmaptrc = 0;
domain_cpuinfo.dbg32.tracefilt = 0;
/* Hide AMU support */
#ifdef CONFIG_ARM_64
domain_cpuinfo.pfr64.amu = 0;
+1 -1
View File
@@ -501,7 +501,7 @@ int arch_vcpu_create(struct vcpu *v)
v->arch.hcr_el2 = get_default_hcr_flags();
v->arch.mdcr_el2 = HDCR_TDRA | HDCR_TDOSA | HDCR_TDA;
if ( !(v->domain->options & XEN_DOMCTL_CDF_vpmu) )
if ( !is_vpmu_domain(v->domain) )
v->arch.mdcr_el2 |= HDCR_TPM | HDCR_TPMCR;
if ( (rc = vcpu_vgic_init(v)) != 0 )
+1
View File
@@ -84,6 +84,7 @@
#define HSR_SYSREG_FAR_EL1 HSR_SYSREG(3,0,c6, c0,0)
#define HSR_SYSREG_PMINTENSET_EL1 HSR_SYSREG(3,0,c9,c14,1)
#define HSR_SYSREG_PMINTENCLR_EL1 HSR_SYSREG(3,0,c9,c14,2)
#define HSR_SYSREG_PMMIR_EL1 HSR_SYSREG(3,0,c9,c14,6)
#define HSR_SYSREG_MAIR_EL1 HSR_SYSREG(3,0,c10,c2,0)
#define HSR_SYSREG_AMAIR_EL1 HSR_SYSREG(3,0,c10,c3,0)
#define HSR_SYSREG_ICC_SGI1R_EL1 HSR_SYSREG(3,0,c12,c11,5)
+1
View File
@@ -246,6 +246,7 @@
#define PMINTENSET p15,0,c9,c14,1 /* Perf. Mon. Interrupt Enable Set Register */
#define PMINTENCLR p15,0,c9,c14,2 /* Perf. Mon. Interrupt Enable Clear Register */
#define PMOVSSET p15,0,c9,c14,3 /* Perf. Mon. Overflow Flag Status Set register */
#define PMMIR p15,0,c9,c14,6 /* Perf. Mon. Machine Identification Register */
/* CP15 CR10: */
#define MAIR0 p15,0,c10,c2,0 /* Memory Attribute Indirection Register 0 AKA PRRR */
+20 -7
View File
@@ -208,7 +208,7 @@ struct cpuinfo_arm {
};
} pfr64;
union {
union cpuinfo_dbg64 {
register_t bits[2];
struct {
/* DFR0 */
@@ -216,19 +216,31 @@ struct cpuinfo_arm {
unsigned long trace_ver:4;
unsigned long pmu_ver:4;
unsigned long brps:4;
unsigned long __res0:4;
unsigned long pmss:4;
unsigned long wrps:4;
unsigned long __res1:4;
unsigned long ctx_cmps:4;
unsigned long pms_ver:4;
unsigned long double_lock:4;
unsigned long trace_filt:4;
unsigned long __res2:4;
unsigned long trace_buffer:4;
unsigned long mtpmu:4;
unsigned long __res3:12;
unsigned long brbe:4;
unsigned long ext_trc_buff:4;
unsigned long hpmn0:4;
/* DFR1 */
unsigned long __res4:64;
unsigned long syspmuid:8;
unsigned long brps1:8;
unsigned long wrps1:8;
unsigned long ctx_cmps1:8;
unsigned long spmu:4;
unsigned long pmicntr:4;
unsigned long able:4;
unsigned long ite:4;
unsigned long ebep:4;
unsigned long dpfzs:4;
unsigned long abl_cmps:8;
};
} dbg64;
@@ -408,7 +420,7 @@ struct cpuinfo_arm {
};
} pfr32;
union {
union cpuinfo_dbg32 {
register_t bits[2];
struct {
/* DFR0 */
@@ -426,7 +438,8 @@ struct cpuinfo_arm {
/* DFR1 */
unsigned long mtpmu:4;
unsigned long __res1:28;
unsigned long hpmn0:4;
unsigned long __res1:24;
#ifdef CONFIG_ARM_64
unsigned long __res2:32;
#endif
+28 -6
View File
@@ -278,10 +278,7 @@ void do_cp15_32(struct cpu_user_regs *regs, const union hsr hsr)
*
* NB: Both MDCR_EL2.TPM and MDCR_EL2.TPMCR cause trapping of PMCR.
*/
/* We could trap ID_DFR0 and tell the guest we don't support
* performance monitoring, but Linux doesn't check the ID_DFR0.
* Therefore it will read PMCR.
*
/*
* We tell the guest we have 0 counters. Unfortunately we must
* always support PMCCNTR (the cyle counter): we just RAZ/WI for all
* PM register, which doesn't crash the kernel at least
@@ -294,6 +291,7 @@ void do_cp15_32(struct cpu_user_regs *regs, const union hsr hsr)
return handle_raz_wi(regs, regidx, cp32.read, hsr, 1);
case HSR_CPREG32(PMINTENSET):
case HSR_CPREG32(PMINTENCLR):
case HSR_CPREG32(PMMIR):
return handle_raz_wi(regs, regidx, cp32.read, hsr, 1);
case HSR_CPREG32(PMCR):
case HSR_CPREG32(PMCNTENSET):
@@ -322,8 +320,32 @@ void do_cp15_32(struct cpu_user_regs *regs, const union hsr hsr)
GENERATE_TID3_INFO(ID_PFR0, pfr32, 0)
GENERATE_TID3_INFO(ID_PFR1, pfr32, 1)
GENERATE_TID3_INFO(ID_PFR2, pfr32, 2)
GENERATE_TID3_INFO(ID_DFR0, dbg32, 0)
GENERATE_TID3_INFO(ID_DFR1, dbg32, 1)
case HSR_CPREG32(ID_DFR0):
{
union cpuinfo_dbg32 info_dbg32 = domain_cpuinfo.dbg32;
if ( !is_vpmu_domain(v->domain) )
info_dbg32.perfmon = 0;
return handle_ro_read_val(regs, regidx, cp32.read, hsr, 1,
info_dbg32.bits[0]);
}
case HSR_CPREG32(ID_DFR1):
{
union cpuinfo_dbg32 info_dbg32 = domain_cpuinfo.dbg32;
if ( !is_vpmu_domain(v->domain) )
{
info_dbg32.mtpmu = 0;
info_dbg32.hpmn0 = 0;
}
return handle_ro_read_val(regs, regidx, cp32.read, hsr, 1,
info_dbg32.bits[1]);
}
GENERATE_TID3_INFO(ID_AFR0, aux32, 0)
GENERATE_TID3_INFO(ID_MMFR0, mm32, 0)
GENERATE_TID3_INFO(ID_MMFR1, mm32, 1)
+5
View File
@@ -1252,6 +1252,11 @@ static always_inline bool is_iommu_enabled(const struct domain *d)
return evaluate_nospec(d->options & XEN_DOMCTL_CDF_iommu);
}
static inline bool is_vpmu_domain(const struct domain *d)
{
return d->options & XEN_DOMCTL_CDF_vpmu;
}
#ifdef CONFIG_MEM_PAGING
# define mem_paging_enabled(d) vm_event_check_ring((d)->vm_event_paging)
#else
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.