Skip to content

Commit

Permalink
If the CPU cores implement FEAT_IDST, emulate access to the CPU ID
Browse files Browse the repository at this point in the history
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@
  • Loading branch information
kettenis committed Jul 24, 2024
1 parent e06d8e0 commit e8331b7
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 6 deletions.
47 changes: 45 additions & 2 deletions sys/arch/arm64/arm64/cpu.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 */
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down
5 changes: 4 additions & 1 deletion sys/arch/arm64/arm64/machdep.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
* Copyright (c) 2021 Mark Kettenis <[email protected]>
Expand Down Expand Up @@ -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);
Expand Down
99 changes: 98 additions & 1 deletion sys/arch/arm64/arm64/trap.c
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
26 changes: 25 additions & 1 deletion sys/arch/arm64/include/armreg.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion sys/arch/arm64/include/cpu.h
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit e8331b7

Please sign in to comment.