Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/aarch64 - On some systems with a different cache li...



details:   https://anonhg.NetBSD.org/src/rev/842292528795
branches:  trunk
changeset: 935379:842292528795
user:      ryo <ryo%NetBSD.org@localhost>
date:      Wed Jul 01 08:01:07 2020 +0000

description:
- On some systems with a different cache line size (and DIC,IDC) per CPU, trap "mrs Xt,ctr_el0" instruction
  to return the minimum cache line size of the system to userland.
- add CLIDR_EL1 and CTR_EL0 to struct aarch64_sysctl_cpu_id.

On most systems, cache line size is the same for all CPUs, so this mechanism won't be required.
Rather, this is primarily for errata support, which will be committed later.

diffstat:

 sys/arch/aarch64/aarch64/aarch64_machdep.c |    6 +-
 sys/arch/aarch64/aarch64/cpu.c             |    7 +-
 sys/arch/aarch64/aarch64/trap.c            |  169 +++++++++++++++++++++++++++-
 sys/arch/aarch64/include/armreg.h          |    6 +-
 sys/arch/aarch64/include/cpu.h             |    3 +-
 sys/arch/aarch64/include/machdep.h         |    3 +-
 6 files changed, 179 insertions(+), 15 deletions(-)

diffs (truncated from 336 to 300 lines):

diff -r 54ab0494e9c3 -r 842292528795 sys/arch/aarch64/aarch64/aarch64_machdep.c
--- a/sys/arch/aarch64/aarch64/aarch64_machdep.c        Wed Jul 01 07:59:16 2020 +0000
+++ b/sys/arch/aarch64/aarch64/aarch64_machdep.c        Wed Jul 01 08:01:07 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: aarch64_machdep.c,v 1.43 2020/06/21 17:25:03 jmcneill Exp $ */
+/* $NetBSD: aarch64_machdep.c,v 1.44 2020/07/01 08:01:07 ryo Exp $ */
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: aarch64_machdep.c,v 1.43 2020/06/21 17:25:03 jmcneill Exp $");
+__KERNEL_RCSID(1, "$NetBSD: aarch64_machdep.c,v 1.44 2020/07/01 08:01:07 ryo Exp $");
 
 #include "opt_arm_debug.h"
 #include "opt_cpuoptions.h"
@@ -523,6 +523,8 @@
 {
        /* clear cpu reset hook for early boot */
        cpu_reset_address0 = NULL;
+
+       configure_cpu_traps();
 }
 
 #ifdef MODULAR
diff -r 54ab0494e9c3 -r 842292528795 sys/arch/aarch64/aarch64/cpu.c
--- a/sys/arch/aarch64/aarch64/cpu.c    Wed Jul 01 07:59:16 2020 +0000
+++ b/sys/arch/aarch64/aarch64/cpu.c    Wed Jul 01 08:01:07 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: cpu.c,v 1.51 2020/07/01 07:59:16 ryo Exp $ */
+/* $NetBSD: cpu.c,v 1.52 2020/07/01 08:01:07 ryo Exp $ */
 
 /*
  * Copyright (c) 2017 Ryo Shimizu <ryo%nerv.org@localhost>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: cpu.c,v 1.51 2020/07/01 07:59:16 ryo Exp $");
+__KERNEL_RCSID(1, "$NetBSD: cpu.c,v 1.52 2020/07/01 08:01:07 ryo Exp $");
 
 #include "locators.h"
 #include "opt_arm_debug.h"
@@ -503,6 +503,9 @@
        id->ac_mvfr1     = reg_mvfr1_el1_read();
        id->ac_mvfr2     = reg_mvfr2_el1_read();
 
+       id->ac_clidr     = reg_clidr_el1_read();
+       id->ac_ctr       = reg_ctr_el0_read();
+
        /* Only in ARMv8.2. */
        id->ac_aa64zfr0  = 0 /* reg_id_aa64zfr0_el1_read() */;
 
diff -r 54ab0494e9c3 -r 842292528795 sys/arch/aarch64/aarch64/trap.c
--- a/sys/arch/aarch64/aarch64/trap.c   Wed Jul 01 07:59:16 2020 +0000
+++ b/sys/arch/aarch64/aarch64/trap.c   Wed Jul 01 08:01:07 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: trap.c,v 1.27 2020/04/13 05:40:25 maxv Exp $ */
+/* $NetBSD: trap.c,v 1.28 2020/07/01 08:01:07 ryo Exp $ */
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.27 2020/04/13 05:40:25 maxv Exp $");
+__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.28 2020/07/01 08:01:07 ryo Exp $");
 
 #include "opt_arm_intr_impl.h"
 #include "opt_compat_netbsd32.h"
@@ -42,6 +42,7 @@
 #include <sys/types.h>
 #include <sys/atomic.h>
 #include <sys/cpu.h>
+#include <sys/evcnt.h>
 #ifdef KDB
 #include <sys/kdb.h>
 #endif
@@ -50,6 +51,7 @@
 #include <sys/signal.h>
 #include <sys/signalvar.h>
 #include <sys/siginfo.h>
+#include <sys/xcall.h>
 
 #ifdef ARM_INTR_IMPL
 #include ARM_INTR_IMPL
@@ -88,6 +90,12 @@
 int (*dtrace_invop_jump_addr)(struct trapframe *);
 #endif
 
+enum emul_arm_result {
+       EMUL_ARM_SUCCESS = 0,
+       EMUL_ARM_UNKNOWN,
+       EMUL_ARM_FAULT,
+};
+
 const char * const trap_names[] = {
        [ESR_EC_UNKNOWN]        = "Unknown Reason (Illegal Instruction)",
        [ESR_EC_SERROR]         = "SError Interrupt",
@@ -247,6 +255,142 @@
        }
 }
 
+/*
+ * There are some systems with different cache line sizes for each cpu.
+ * Userland programs can be preempted between CPUs at any time, so in such
+ * a system, the minimum cache line size must be visible to userland.
+ */
+#define CTR_EL0_USR_MASK       \
+       (CTR_EL0_DIC | CTR_EL0_IDC | CTR_EL0_DMIN_LINE | CTR_EL0_IMIN_LINE)
+uint64_t ctr_el0_usr __read_mostly;
+
+static xcfunc_t
+configure_cpu_traps0(void *arg1, void *arg2)
+{
+       struct cpu_info * const ci = curcpu();
+       uint64_t sctlr;
+       uint64_t ctr_el0_raw = reg_ctr_el0_read();
+
+#ifdef DEBUG_FORCE_TRAP_CTR_EL0
+       goto need_ctr_trap;
+#endif
+
+       if ((__SHIFTOUT(ctr_el0_raw, CTR_EL0_DMIN_LINE) >
+            __SHIFTOUT(ctr_el0_usr, CTR_EL0_DMIN_LINE)) ||
+           (__SHIFTOUT(ctr_el0_raw, CTR_EL0_IMIN_LINE) >
+            __SHIFTOUT(ctr_el0_usr, CTR_EL0_IMIN_LINE)))
+               goto need_ctr_trap;
+
+       if ((__SHIFTOUT(ctr_el0_raw, CTR_EL0_DIC) == 1 &&
+            __SHIFTOUT(ctr_el0_usr, CTR_EL0_DIC) == 0) ||
+           (__SHIFTOUT(ctr_el0_raw, CTR_EL0_IDC) == 1 &&
+            __SHIFTOUT(ctr_el0_usr, CTR_EL0_IDC) == 0))
+               goto need_ctr_trap;
+
+#if 0 /* XXX: To do or not to do */
+       /*
+        * IDC==0, but (LoC==0 || LoUIS==LoUU==0)?
+        * Would it be better to show IDC=1 to userland?
+        */
+       if (__SHIFTOUT(ctr_el0_raw, CTR_EL0_IDC) == 0 &&
+           __SHIFTOUT(ctr_el0_usr, CTR_EL0_IDC) == 1)
+               goto need_ctr_trap;
+#endif
+
+       return 0;
+
+ need_ctr_trap:
+       evcnt_attach_dynamic(&ci->ci_uct_trap, EVCNT_TYPE_MISC, NULL,
+           ci->ci_cpuname, "ctr_el0 trap");
+
+       /* trap CTR_EL0 access from EL0 on this cpu */
+       sctlr = reg_sctlr_el1_read();
+       sctlr &= ~SCTLR_UCT;
+       reg_sctlr_el1_write(sctlr);
+
+       return 0;
+}
+
+void
+configure_cpu_traps(void)
+{
+       CPU_INFO_ITERATOR cii;
+       struct cpu_info *ci;
+       uint64_t where;
+
+       /* remember minimum cache line size out of all CPUs */
+       for (CPU_INFO_FOREACH(cii, ci)) {
+               uint64_t ctr_el0_cpu = ci->ci_id.ac_ctr;
+               uint64_t clidr = ci->ci_id.ac_clidr;
+
+               if (__SHIFTOUT(clidr, CLIDR_LOC) == 0 ||
+                   (__SHIFTOUT(clidr, CLIDR_LOUIS) == 0 &&
+                    __SHIFTOUT(clidr, CLIDR_LOUU) == 0)) {
+                       /* this means the same as IDC=1 */
+                       ctr_el0_cpu |= CTR_EL0_IDC;
+               }
+
+               /*
+                * if DIC==1, there is no need to icache sync. however,
+                * to calculate the minimum cacheline, in this case
+                * ICacheLine is treated as the maximum.
+                */
+               if (__SHIFTOUT(ctr_el0_cpu, CTR_EL0_DIC) == 1)
+                       ctr_el0_cpu |= CTR_EL0_IMIN_LINE;
+
+               if (cii == 0) {
+                       ctr_el0_usr = ctr_el0_cpu;
+                       continue;
+               }
+
+               /* keep minimum cache line size, and worst DIC/IDC */
+               ctr_el0_usr &= (ctr_el0_cpu & CTR_EL0_DIC) | ~CTR_EL0_DIC;
+               ctr_el0_usr &= (ctr_el0_cpu & CTR_EL0_IDC) | ~CTR_EL0_IDC;
+               if (__SHIFTOUT(ctr_el0_cpu, CTR_EL0_DMIN_LINE) <
+                   __SHIFTOUT(ctr_el0_usr, CTR_EL0_DMIN_LINE)) {
+                       ctr_el0_usr &= ~CTR_EL0_DMIN_LINE;
+                       ctr_el0_usr |= ctr_el0_cpu & CTR_EL0_DMIN_LINE;
+               }
+               if ((ctr_el0_cpu & CTR_EL0_DIC) == 0 &&
+                   (__SHIFTOUT(ctr_el0_cpu, CTR_EL0_IMIN_LINE) <
+                   __SHIFTOUT(ctr_el0_usr, CTR_EL0_IMIN_LINE))) {
+                       ctr_el0_usr &= ~CTR_EL0_IMIN_LINE;
+                       ctr_el0_usr |= ctr_el0_cpu & CTR_EL0_IMIN_LINE;
+               }
+       }
+
+       where = xc_broadcast(0,
+           (xcfunc_t)configure_cpu_traps0, NULL, NULL);
+       xc_wait(where);
+}
+
+static enum emul_arm_result
+emul_aarch64_insn(struct trapframe *tf)
+{
+       uint32_t insn;
+
+       if (ufetch_32((uint32_t *)tf->tf_pc, &insn))
+               return EMUL_ARM_FAULT;
+
+       if ((insn & 0xffffffe0) == 0xd53b0020) {
+               /* mrs x?,ctr_el0 */
+               unsigned int Xt = insn & 31;
+               if (Xt != 31) { /* !xzr */
+                       uint64_t ctr_el0 = reg_ctr_el0_read();
+                       ctr_el0 &= ~CTR_EL0_USR_MASK;
+                       ctr_el0 |= (ctr_el0_usr & CTR_EL0_USR_MASK);
+                       tf->tf_reg[Xt] = ctr_el0;
+               }
+               curcpu()->ci_uct_trap.ev_count++;
+
+       } else {
+               return EMUL_ARM_UNKNOWN;
+       }
+
+       tf->tf_pc += 4;
+       return EMUL_ARM_SUCCESS;
+}
+
 void
 trap_el0_sync(struct trapframe *tf)
 {
@@ -300,8 +444,23 @@
                userret(l);
                break;
 
+       case ESR_EC_SYS_REG:
+               switch (emul_aarch64_insn(tf)) {
+               case EMUL_ARM_SUCCESS:
+                       break;
+               case EMUL_ARM_UNKNOWN:
+                       goto unknown;
+               case EMUL_ARM_FAULT:
+                       do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
+                           (void *)tf->tf_pc, esr);
+                       break;
+               }
+               userret(l);
+               break;
+
        default:
        case ESR_EC_UNKNOWN:
+ unknown:
 #ifdef DDB
                if (sigill_debug) {
                        /* show illegal instruction */
@@ -386,12 +545,6 @@
        return 4;
 }
 
-enum emul_arm_result {
-       EMUL_ARM_SUCCESS = 0,
-       EMUL_ARM_UNKNOWN,
-       EMUL_ARM_FAULT,
-};
-
 static enum emul_arm_result
 emul_arm_insn(struct trapframe *tf)
 {
diff -r 54ab0494e9c3 -r 842292528795 sys/arch/aarch64/include/armreg.h
--- a/sys/arch/aarch64/include/armreg.h Wed Jul 01 07:59:16 2020 +0000
+++ b/sys/arch/aarch64/include/armreg.h Wed Jul 01 08:01:07 2020 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: armreg.h,v 1.49 2020/06/14 16:10:18 riastradh Exp $ */
+/* $NetBSD: armreg.h,v 1.50 2020/07/01 08:01:07 ryo Exp $ */
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -1614,6 +1614,10 @@
        uint32_t ac_mvfr0;      /* Media and VFP Feature Register 0 */
        uint32_t ac_mvfr1;      /* Media and VFP Feature Register 1 */
        uint32_t ac_mvfr2;      /* Media and VFP Feature Register 2 */
+       uint32_t ac_pad;
+
+       uint64_t ac_clidr;      /* Cacle Level ID Register */
+       uint64_t ac_ctr;        /* Cache Type Register */
 };



Home | Main Index | Thread Index | Old Index