If the CPU cores implement FEAT_IDST, emulate access to the CPU ID
authorkettenis <kettenis@openbsd.org>
Wed, 24 Jul 2024 21:24:18 +0000 (21:24 +0000)
committerkettenis <kettenis@openbsd.org>
Wed, 24 Jul 2024 21:24:18 +0000 (21:24 +0000)
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
sys/arch/arm64/arm64/machdep.c
sys/arch/arm64/arm64/trap.c
sys/arch/arm64/include/armreg.h
sys/arch/arm64/include/cpu.h

index 6f82874..d244c77 100644 (file)
@@ -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 <drahn@dalerahn.com>
@@ -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);
 
index c3605a0..154bf2d 100644 (file)
@@ -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 <patrick@blueri.se>
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
@@ -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);
index c420b74..672b62e 100644 (file)
@@ -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;
index ed7b530..41d273b 100644 (file)
@@ -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
 #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)
 #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
index 33084f6..569fe5c 100644 (file)
@@ -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 <drahn@dalerahn.com>
  *
@@ -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;