The branch main has been updated by andrew:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=2aeac25bcb72723d436721ab97bb2fd6df3dfb9b

commit 2aeac25bcb72723d436721ab97bb2fd6df3dfb9b
Author:     Andrew Turner <and...@freebsd.org>
AuthorDate: 2025-06-02 09:56:59 +0000
Commit:     Andrew Turner <and...@freebsd.org>
CommitDate: 2025-06-02 09:56:59 +0000

    arm64: Add a function to restrict the ID registers
    
    This will be used when we need to restrict the register values, e.g.
    when an erratum is present that means we need to disable a feature we
    will need to remove some fields from these registers.
    
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D50572
---
 sys/arm64/arm64/identcpu.c | 131 ++++++++++++++++++++++++++++++++-------------
 sys/arm64/include/cpu.h    |   1 +
 2 files changed, 94 insertions(+), 38 deletions(-)

diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c
index fe10e0eb551a..6dc9c447ee29 100644
--- a/sys/arm64/arm64/identcpu.c
+++ b/sys/arm64/arm64/identcpu.c
@@ -51,6 +51,9 @@
 static MALLOC_DEFINE(M_IDENTCPU, "CPU ID", "arm64 CPU identification memory");
 
 struct cpu_desc;
+#ifdef INVARIANTS
+static bool hwcaps_set = false;
+#endif
 
 static void print_cpu_midr(struct sbuf *sb, u_int cpu);
 static void print_cpu_features(u_int cpu, struct cpu_desc *desc,
@@ -2594,13 +2597,62 @@ update_special_reg_field(uint64_t user_reg, u_int type, 
uint64_t value,
        return (user_reg);
 }
 
+static void
+clear_set_special_reg_idx(int idx, uint64_t clear, uint64_t set)
+{
+       const struct mrs_field *fields;
+       uint64_t k_old, k_new;
+       uint64_t f_old, f_new;
+       uint64_t l_old, l_new;
+
+       MPASS(idx < nitems(user_regs));
+
+       k_old = CPU_DESC_FIELD(kern_cpu_desc, idx);
+       k_new = (k_old & ~clear) | set;
+
+       f_old = CPU_DESC_FIELD(user_cpu_desc, idx);
+       f_new = (f_old & ~clear) | set;
+
+       l_old = CPU_DESC_FIELD(l_user_cpu_desc, idx);
+       l_new = (l_old & ~clear) | set;
+
+       fields = user_regs[idx].fields;
+       for (int j = 0; fields[j].type != 0; j++) {
+               u_int type;
+
+               /* Update the FreeBSD userspace ID register view */
+               type = ((fields[j].type & MRS_FREEBSD) != 0) ?
+                   fields[j].type :
+                   (MRS_EXACT | (fields[j].type & MRS_SAFE_MASK));
+               f_new = update_special_reg_field(f_new,
+                   type, f_old, fields[j].width, fields[j].shift,
+                   fields[j].sign);
+
+               /* Update the Linux userspace ID register view */
+               type = ((fields[j].type & MRS_LINUX) != 0) ?
+                   fields[j].type :
+                   (MRS_EXACT | (fields[j].type & MRS_SAFE_MASK));
+               l_new = update_special_reg_field(l_new,
+                   type, l_old, fields[j].width, fields[j].shift,
+                   fields[j].sign);
+
+               /* Update the kernel ID register view */
+               k_new = update_special_reg_field(k_new,
+                   fields[j].type, k_old, fields[j].width,
+                   fields[j].shift, fields[j].sign);
+       }
+
+       CPU_DESC_FIELD(kern_cpu_desc, idx) = k_new;
+       CPU_DESC_FIELD(user_cpu_desc, idx) = f_new;
+       CPU_DESC_FIELD(l_user_cpu_desc, idx) = l_new;
+}
+
 void
 update_special_regs(u_int cpu)
 {
        struct cpu_desc *desc;
-       const struct mrs_field *fields;
-       uint64_t l_user_reg, user_reg, kern_reg, value;
-       int i, j;
+       uint64_t value;
+       int i;
 
        if (cpu == 0) {
                /* Create a user visible cpu description with safe values */
@@ -2618,44 +2670,42 @@ update_special_regs(u_int cpu)
        for (i = 0; i < nitems(user_regs); i++) {
                value = CPU_DESC_FIELD(*desc, i);
                if (cpu == 0) {
-                       kern_reg = value;
-                       user_reg = value;
-                       l_user_reg = value;
-               } else {
-                       kern_reg = CPU_DESC_FIELD(kern_cpu_desc, i);
-                       user_reg = CPU_DESC_FIELD(user_cpu_desc, i);
-                       l_user_reg = CPU_DESC_FIELD(l_user_cpu_desc, i);
+                       CPU_DESC_FIELD(kern_cpu_desc, i) = value;
+                       CPU_DESC_FIELD(user_cpu_desc, i) = value;
+                       CPU_DESC_FIELD(l_user_cpu_desc, i) = value;
                }
 
-               fields = user_regs[i].fields;
-               for (j = 0; fields[j].type != 0; j++) {
-                       u_int type;
-
-                       /* Update the FreeBSD userspace ID register view */
-                       type = ((fields[j].type & MRS_FREEBSD) != 0) ?
-                           fields[j].type :
-                           (MRS_EXACT | (fields[j].type & MRS_SAFE_MASK));
-                       user_reg = update_special_reg_field(user_reg,
-                           type, value, fields[j].width, fields[j].shift,
-                           fields[j].sign);
-
-                       /* Update the Linux userspace ID register view */
-                       type = ((fields[j].type & MRS_LINUX) != 0) ?
-                           fields[j].type :
-                           (MRS_EXACT | (fields[j].type & MRS_SAFE_MASK));
-                       l_user_reg = update_special_reg_field(l_user_reg,
-                           type, value, fields[j].width, fields[j].shift,
-                           fields[j].sign);
-
-                       /* Update the kernel ID register view */
-                       kern_reg = update_special_reg_field(kern_reg,
-                           fields[j].type, value, fields[j].width,
-                           fields[j].shift, fields[j].sign);
-               }
+               clear_set_special_reg_idx(i, UINT64_MAX, value);
+       }
+}
+
+/*
+ * Updates a special register in all views. This creates a copy of the
+ * register then clears it and sets new bits. It will then compare this
+ * with the old version as if it was the ID register for a new CPU.
+ *
+ * It is intended to let code that disables features, e.g. due to errata,
+ * to clear the user visible field.
+ *
+ * This needs to be called before the HWCAPs are set. If called from a CPU
+ * feature handler this safe to call from CPU_FEAT_EARLY_BOOT. It also needs
+ * to be before link_elf_late_ireloc is called. As this is called after the
+ * HWCAPs are set the check for these is enough.
+ */
+void
+update_special_reg(u_int reg, uint64_t clear, uint64_t set)
+{
+       MPASS(hwcaps_set == false);
+       /* There is no locking here, so we only support changing this on CPU0 */
+       /* TODO: Add said locking */
+       MPASS(PCPU_GET(cpuid) == 0);
+
+       for (int i = 0; i < nitems(user_regs); i++) {
+               if (user_regs[i].reg != reg)
+                       continue;
 
-               CPU_DESC_FIELD(kern_cpu_desc, i) = kern_reg;
-               CPU_DESC_FIELD(user_cpu_desc, i) = user_reg;
-               CPU_DESC_FIELD(l_user_cpu_desc, i) = l_user_reg;
+               clear_set_special_reg_idx(i, clear, set);
+               return;
        }
 }
 
@@ -2757,6 +2807,11 @@ identify_cpu_sysinit(void *dummy __unused)
                prev_desc = desc;
        }
 
+#ifdef INVARIANTS
+       /* Check we dont update the special registers after this point */
+       hwcaps_set = true;
+#endif
+
        /* Find the values to export to userspace as AT_HWCAP and AT_HWCAP2 */
        parse_cpu_features(true, &user_cpu_desc, &elf_hwcap, &elf_hwcap2);
        parse_cpu_features(true, &l_user_cpu_desc, &linux_elf_hwcap,
diff --git a/sys/arm64/include/cpu.h b/sys/arm64/include/cpu.h
index 91ed3634c6d8..df38c69fdb30 100644
--- a/sys/arm64/include/cpu.h
+++ b/sys/arm64/include/cpu.h
@@ -232,6 +232,7 @@ void        ptrauth_mp_start(uint64_t);
 
 /* Functions to read the sanitised view of the special registers */
 void   update_special_regs(u_int);
+void   update_special_reg(u_int reg, uint64_t, uint64_t);
 bool   extract_user_id_field(u_int, u_int, uint8_t *);
 bool   get_kernel_reg(u_int, uint64_t *);
 bool   get_kernel_reg_masked(u_int, uint64_t *, uint64_t);

Reply via email to