From e8331b74e5c20302d4bd948c9db722af688ccfc1 Mon Sep 17 00:00:00 2001 From: kettenis Date: Wed, 24 Jul 2024 21:24:18 +0000 Subject: [PATCH] If the CPU cores implement FEAT_IDST, emulate access to the CPU ID registers from userland and set HWCAP_CPUID. This will allow detection of features to be introduced into the architecture in the future without allocating new HWCAP_xxx or HWCAP2_xxx bits. We provide the same sanitized view of the CPU ID registers as is currently available through sysctl(2). Note that this introduces an unconditional read of ID_AA64MMFR2_EL1. This is known to cause problems on older versions of QEMU. If this turns out to be a problem in cases where updating QEMU is not an option, we'll have to implement a workaround. Also note that since we don't emulate the CPU ID registers on older core, this means that microarchitectural optimizations keyed of reads of MIDR_EL1 are not possible on OpenBSD. I don't think that is a real problem. ok jca@ --- sys/arch/arm64/arm64/cpu.c | 47 +++++++++++++++- sys/arch/arm64/arm64/machdep.c | 5 +- sys/arch/arm64/arm64/trap.c | 99 ++++++++++++++++++++++++++++++++- sys/arch/arm64/include/armreg.h | 26 ++++++++- sys/arch/arm64/include/cpu.h | 5 +- 5 files changed, 176 insertions(+), 6 deletions(-) diff --git a/sys/arch/arm64/arm64/cpu.c b/sys/arch/arm64/arm64/cpu.c index 6f828742e52..d244c7702e9 100644 --- a/sys/arch/arm64/arm64/cpu.c +++ b/sys/arch/arm64/arm64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.129 2024/07/21 18:57:31 kettenis Exp $ */ +/* $OpenBSD: cpu.c,v 1.130 2024/07/24 21:24:18 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn @@ -242,6 +242,9 @@ int cpu_node; uint64_t cpu_id_aa64isar0; uint64_t cpu_id_aa64isar1; uint64_t cpu_id_aa64isar2; +uint64_t cpu_id_aa64mmfr0; +uint64_t cpu_id_aa64mmfr1; +uint64_t cpu_id_aa64mmfr2; uint64_t cpu_id_aa64pfr0; uint64_t cpu_id_aa64pfr1; @@ -487,6 +490,7 @@ cpu_identify(struct cpu_info *ci) static uint64_t prev_id_aa64isar2; static uint64_t prev_id_aa64mmfr0; static uint64_t prev_id_aa64mmfr1; + static uint64_t prev_id_aa64mmfr2; static uint64_t prev_id_aa64pfr0; static uint64_t prev_id_aa64pfr1; uint64_t midr, impl, part; @@ -642,6 +646,7 @@ cpu_identify(struct cpu_info *ci) READ_SPECIALREG(id_aa64isar2_el1) == prev_id_aa64isar2 && READ_SPECIALREG(id_aa64mmfr0_el1) == prev_id_aa64mmfr0 && READ_SPECIALREG(id_aa64mmfr1_el1) == prev_id_aa64mmfr1 && + READ_SPECIALREG(id_aa64mmfr2_el1) == prev_id_aa64mmfr2 && READ_SPECIALREG(id_aa64pfr0_el1) == prev_id_aa64pfr0 && READ_SPECIALREG(id_aa64pfr1_el1) == prev_id_aa64pfr1) return; @@ -662,6 +667,18 @@ cpu_identify(struct cpu_info *ci) printf("\n%s: mismatched ID_AA64ISAR2_EL1", ci->ci_dev->dv_xname); } + if (READ_SPECIALREG(id_aa64mmfr0_el1) != cpu_id_aa64mmfr0) { + printf("\n%s: mismatched ID_AA64MMFR0_EL1", + ci->ci_dev->dv_xname); + } + if (READ_SPECIALREG(id_aa64mmfr1_el1) != cpu_id_aa64mmfr1) { + printf("\n%s: mismatched ID_AA64MMFR1_EL1", + ci->ci_dev->dv_xname); + } + if (READ_SPECIALREG(id_aa64mmfr2_el1) != cpu_id_aa64mmfr2) { + printf("\n%s: mismatched ID_AA64MMFR2_EL1", + ci->ci_dev->dv_xname); + } id = READ_SPECIALREG(id_aa64pfr0_el1); /* Allow CSV2/CVS3 to be different. */ id &= ~ID_AA64PFR0_CSV2_MASK; @@ -938,6 +955,16 @@ cpu_identify(struct cpu_info *ci) sep = ","; } + /* + * ID_AA64MMFR2 + */ + id = READ_SPECIALREG(id_aa64mmfr2_el1); + + if (ID_AA64MMFR2_IDS(id) >= ID_AA64MMFR2_IDS_IMPL) { + printf("%sIDS", sep); + sep = ","; + } + /* * ID_AA64PFR0 */ @@ -989,6 +1016,7 @@ cpu_identify(struct cpu_info *ci) prev_id_aa64isar2 = READ_SPECIALREG(id_aa64isar2_el1); prev_id_aa64mmfr0 = READ_SPECIALREG(id_aa64mmfr0_el1); prev_id_aa64mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1); + prev_id_aa64mmfr2 = READ_SPECIALREG(id_aa64mmfr2_el1); prev_id_aa64pfr0 = READ_SPECIALREG(id_aa64pfr0_el1); prev_id_aa64pfr1 = READ_SPECIALREG(id_aa64pfr1_el1); @@ -1023,6 +1051,7 @@ cpu_identify(struct cpu_info *ci) void cpu_identify_cleanup(void) { + uint64_t id_aa64mmfr2; uint64_t value; /* ID_AA64ISAR0_EL1 */ @@ -1040,6 +1069,15 @@ cpu_identify_cleanup(void) value &= ~ID_AA64ISAR2_CLRBHB_MASK; cpu_id_aa64isar2 = value; + /* ID_AA64MMFR0_EL1 */ + cpu_id_aa64mmfr0 = 0; + + /* ID_AA64MMFR1_EL1 */ + cpu_id_aa64mmfr1 = 0; + + /* ID_AA64MMFR2_EL1 */ + cpu_id_aa64mmfr2 = 0; + /* ID_AA64PFR0_EL1 */ value = 0; value |= cpu_id_aa64pfr0 & ID_AA64PFR0_FP_MASK; @@ -1071,7 +1109,9 @@ cpu_identify_cleanup(void) hwcap |= HWCAP_ATOMICS; /* HWCAP_FPHP */ /* HWCAP_ASIMDHP */ - /* HWCAP_CPUID */ + id_aa64mmfr2 = READ_SPECIALREG(id_aa64mmfr2_el1); + if (ID_AA64MMFR2_IDS(id_aa64mmfr2) >= ID_AA64MMFR2_IDS_IMPL) + hwcap |= HWCAP_CPUID; if (ID_AA64ISAR0_RDM(cpu_id_aa64isar0) >= ID_AA64ISAR0_RDM_IMPL) hwcap |= HWCAP_ASIMDRDM; if (ID_AA64ISAR1_JSCVT(cpu_id_aa64isar1) >= ID_AA64ISAR1_JSCVT_IMPL) @@ -1271,6 +1311,9 @@ cpu_attach(struct device *parent, struct device *dev, void *aux) cpu_id_aa64isar0 = READ_SPECIALREG(id_aa64isar0_el1); cpu_id_aa64isar1 = READ_SPECIALREG(id_aa64isar1_el1); cpu_id_aa64isar2 = READ_SPECIALREG(id_aa64isar2_el1); + cpu_id_aa64mmfr0 = READ_SPECIALREG(id_aa64mmfr0_el1); + cpu_id_aa64mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1); + cpu_id_aa64mmfr2 = READ_SPECIALREG(id_aa64mmfr2_el1); cpu_id_aa64pfr0 = READ_SPECIALREG(id_aa64pfr0_el1); cpu_id_aa64pfr1 = READ_SPECIALREG(id_aa64pfr1_el1); diff --git a/sys/arch/arm64/arm64/machdep.c b/sys/arch/arm64/arm64/machdep.c index c3605a01606..154bf2d4ebe 100644 --- a/sys/arch/arm64/arm64/machdep.c +++ b/sys/arch/arm64/arm64/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.91 2024/07/17 15:21:59 kettenis Exp $ */ +/* $OpenBSD: machdep.c,v 1.92 2024/07/24 21:24:18 kettenis Exp $ */ /* * Copyright (c) 2014 Patrick Wildt * Copyright (c) 2021 Mark Kettenis @@ -360,8 +360,11 @@ cpu_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, case CPU_ID_AA64PFR1: return sysctl_rdquad(oldp, oldlenp, newp, cpu_id_aa64pfr1); case CPU_ID_AA64MMFR0: + return sysctl_rdquad(oldp, oldlenp, newp, cpu_id_aa64mmfr0); case CPU_ID_AA64MMFR1: + return sysctl_rdquad(oldp, oldlenp, newp, cpu_id_aa64mmfr1); case CPU_ID_AA64MMFR2: + return sysctl_rdquad(oldp, oldlenp, newp, cpu_id_aa64mmfr2); case CPU_ID_AA64SMFR0: case CPU_ID_AA64ZFR0: return sysctl_rdquad(oldp, oldlenp, newp, 0); diff --git a/sys/arch/arm64/arm64/trap.c b/sys/arch/arm64/arm64/trap.c index c420b747768..672b62ec054 100644 --- a/sys/arch/arm64/arm64/trap.c +++ b/sys/arch/arm64/arm64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.48 2024/02/21 15:53:07 deraadt Exp $ */ +/* $OpenBSD: trap.c,v 1.49 2024/07/24 21:24:18 kettenis Exp $ */ /*- * Copyright (c) 2014 Andrew Turner * All rights reserved. @@ -187,6 +187,99 @@ kdata_abort(struct trapframe *frame, uint64_t esr, uint64_t far, int exe) } } +static int +emulate_msr(struct trapframe *frame, uint64_t esr) +{ + u_int rt = ISS_MSR_Rt(esr); + uint64_t val; + + /* Only emulate reads. */ + if ((esr & ISS_MSR_DIR) == 0) + return 0; + + /* Only emulate non-debug System register access. */ + if (ISS_MSR_OP0(esr) != 3 || ISS_MSR_OP1(esr) != 0 || + ISS_MSR_CRn(esr) != 0) + return 0; + + switch (ISS_MSR_CRm(esr)) { + case 0: + switch (ISS_MSR_OP2(esr)) { + case 0: /* MIDR_EL1 */ + val = READ_SPECIALREG(midr_el1); + break; + case 5: /* MPIDR_EL1 */ + /* + * Don't reveal the topology to userland. But + * return a valid value; Bit 31 is RES1. + */ + val = 0x80000000; + break; + case 6: /* REVIDR_EL1 */ + val = 0; + break; + default: + return 0; + } + break; + case 4: + switch (ISS_MSR_OP2(esr)) { + case 0: /* ID_AA64PFR0_EL1 */ + val = cpu_id_aa64pfr0; + break; + case 1: /* ID_AA64PFR1_EL1 */ + val = cpu_id_aa64pfr1; + break; + case 2: /* ID_AA64PFR2_EL1 */ + case 4: /* ID_AA64ZFR0_EL1 */ + case 5: /* ID_AA64SMFR0_EL1 */ + val = 0; + break; + default: + return 0; + } + break; + case 6: + switch (ISS_MSR_OP2(esr)) { + case 0: /* ID_AA64ISAR0_EL1 */ + val = cpu_id_aa64isar0; + break; + case 1: /* ID_AA64ISAR1_EL1 */ + val = cpu_id_aa64isar1; + break; + case 2: /* ID_AA64ISAR2_EL2 */ + val = cpu_id_aa64isar2; + break; + default: + return 0; + } + break; + case 7: + switch (ISS_MSR_OP2(esr)) { + case 0: /* ID_AA64MMFR0_EL1 */ + case 1: /* ID_AA64MMFR1_EL1 */ + case 2: /* ID_AA64MMFR2_EL1 */ + case 3: /* ID_AA64MMFR3_EL1 */ + case 4: /* ID_AA64MMFR4_EL1 */ + val = 0; + break; + default: + return 0; + } + break; + default: + return 0; + } + + if (rt < 30) + frame->tf_x[rt] = val; + else if (rt == 30) + frame->tf_lr = val; + frame->tf_elr += 4; + + return 1; +} + void do_el1h_sync(struct trapframe *frame) { @@ -288,6 +381,10 @@ do_el0_sync(struct trapframe *frame) sv.sival_ptr = (void *)frame->tf_elr; trapsignal(p, SIGILL, esr, ILL_BTCFI, sv); break; + case EXCP_MSR: + if (emulate_msr(frame, esr)) + break; + /* FALLTHROUGH */ case EXCP_FPAC: curcpu()->ci_flush_bp(); sv.sival_ptr = (void *)frame->tf_elr; diff --git a/sys/arch/arm64/include/armreg.h b/sys/arch/arm64/include/armreg.h index ed7b5301795..41d273b4b40 100644 --- a/sys/arch/arm64/include/armreg.h +++ b/sys/arch/arm64/include/armreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: armreg.h,v 1.35 2024/06/23 10:17:16 kettenis Exp $ */ +/* $OpenBSD: armreg.h,v 1.36 2024/07/24 21:24:18 kettenis Exp $ */ /*- * Copyright (c) 2013, 2014 Andrew Turner * Copyright (c) 2015 The FreeBSD Foundation @@ -171,6 +171,26 @@ #define ISS_DATA_DFSC_ECC_L3 (0x1f << 0) #define ISS_DATA_DFSC_ALIGN (0x21 << 0) #define ISS_DATA_DFSC_TLB_CONFLICT (0x30 << 0) +#define ISS_MSR_DIR_SHIFT 0 +#define ISS_MSR_DIR (0x01 << ISS_MSR_DIR_SHIFT) +#define ISS_MSR_Rt_SHIFT 5 +#define ISS_MSR_Rt_MASK (0x1f << ISS_MSR_Rt_SHIFT) +#define ISS_MSR_Rt(x) (((x) & ISS_MSR_Rt_MASK) >> ISS_MSR_Rt_SHIFT) +#define ISS_MSR_CRm_SHIFT 1 +#define ISS_MSR_CRm_MASK (0xf << ISS_MSR_CRm_SHIFT) +#define ISS_MSR_CRm(x) (((x) & ISS_MSR_CRm_MASK) >> ISS_MSR_CRm_SHIFT) +#define ISS_MSR_CRn_SHIFT 10 +#define ISS_MSR_CRn_MASK (0xf << ISS_MSR_CRn_SHIFT) +#define ISS_MSR_CRn(x) (((x) & ISS_MSR_CRn_MASK) >> ISS_MSR_CRn_SHIFT) +#define ISS_MSR_OP1_SHIFT 14 +#define ISS_MSR_OP1_MASK (0x7 << ISS_MSR_OP1_SHIFT) +#define ISS_MSR_OP1(x) (((x) & ISS_MSR_OP1_MASK) >> ISS_MSR_OP1_SHIFT) +#define ISS_MSR_OP2_SHIFT 17 +#define ISS_MSR_OP2_MASK (0x7 << ISS_MSR_OP2_SHIFT) +#define ISS_MSR_OP2(x) (((x) & ISS_MSR_OP2_MASK) >> ISS_MSR_OP2_SHIFT) +#define ISS_MSR_OP0_SHIFT 20 +#define ISS_MSR_OP0_MASK (0x3 << ISS_MSR_OP0_SHIFT) +#define ISS_MSR_OP0(x) (((x) & ISS_MSR_OP0_MASK) >> ISS_MSR_OP0_SHIFT) #define ESR_ELx_IL (0x01 << 25) #define ESR_ELx_EC_SHIFT 26 #define ESR_ELx_EC_MASK (0x3f << 26) @@ -537,6 +557,10 @@ #define ID_AA64MMFR2_CCIDX_MASK (0xfULL << ID_AA64MMFR2_CCIDX_SHIFT) #define ID_AA64MMFR2_CCIDX(x) ((x) & ID_AA64MMFR2_CCIDX_MASK) #define ID_AA64MMFR2_CCIDX_IMPL (0x1ULL << ID_AA64MMFR2_CCIDX_SHIFT) +#define ID_AA64MMFR2_IDS_SHIFT 36 +#define ID_AA64MMFR2_IDS_MASK (0xfULL << ID_AA64MMFR2_IDS_SHIFT) +#define ID_AA64MMFR2_IDS(x) ((x) & ID_AA64MMFR2_IDS_MASK) +#define ID_AA64MMFR2_IDS_IMPL (0x1ULL << ID_AA64MMFR2_IDS_SHIFT) /* ID_AA64PFR0_EL1 */ #define ID_AA64PFR0_MASK 0xff0fffffffffffffULL diff --git a/sys/arch/arm64/include/cpu.h b/sys/arch/arm64/include/cpu.h index 33084f67c99..569fe5c6b2b 100644 --- a/sys/arch/arm64/include/cpu.h +++ b/sys/arch/arm64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.49 2024/07/17 15:21:59 kettenis Exp $ */ +/* $OpenBSD: cpu.h,v 1.50 2024/07/24 21:24:18 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn * @@ -64,6 +64,9 @@ extern uint64_t cpu_id_aa64isar0; extern uint64_t cpu_id_aa64isar1; extern uint64_t cpu_id_aa64isar2; +extern uint64_t cpu_id_aa64mmfr0; +extern uint64_t cpu_id_aa64mmfr1; +extern uint64_t cpu_id_aa64mmfr2; extern uint64_t cpu_id_aa64pfr0; extern uint64_t cpu_id_aa64pfr1; -- 2.20.1