svn commit: r362777 - in head/sys/arm64: arm64 include
Andrew Turner
andrew at FreeBSD.org
Mon Jun 29 09:08:38 UTC 2020
Author: andrew
Date: Mon Jun 29 09:08:36 2020
New Revision: 362777
URL: https://svnweb.freebsd.org/changeset/base/362777
Log:
Create a kernel arm64 ID register view
In preparation for using ifuncs in the kernel is is useful to have a common
view of the arm64 ID registers across all CPUs. Add this and extract the
logic for finding the lower value of two fields to a new helper function.
Sponsored by: Innovate UK
Differential Revision: https://reviews.freebsd.org/D25463
Modified:
head/sys/arm64/arm64/identcpu.c
head/sys/arm64/include/undefined.h
Modified: head/sys/arm64/arm64/identcpu.c
==============================================================================
--- head/sys/arm64/arm64/identcpu.c Mon Jun 29 08:14:45 2020 (r362776)
+++ head/sys/arm64/arm64/identcpu.c Mon Jun 29 09:08:36 2020 (r362777)
@@ -134,6 +134,7 @@ struct cpu_desc {
};
static struct cpu_desc cpu_desc[MAXCPU];
+static struct cpu_desc kern_cpu_desc;
static struct cpu_desc user_cpu_desc;
static u_int cpu_print_regs;
#define PRINT_ID_AA64_AFR0 0x00000001
@@ -936,46 +937,109 @@ extract_user_id_field(u_int reg, u_int field_shift, ui
return (false);
}
+bool
+get_kernel_reg(u_int reg, uint64_t *val)
+{
+ int i;
+
+ for (i = 0; i < nitems(user_regs); i++) {
+ if (user_regs[i].reg == reg) {
+ *val = CPU_DESC_FIELD(kern_cpu_desc, i);
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+static uint64_t
+update_lower_register(uint64_t val, uint64_t new_val, u_int shift,
+ int width, bool sign)
+{
+ uint64_t mask;
+ uint64_t new_field, old_field;
+ bool update;
+
+ KASSERT(width > 0 && width < 64, ("%s: Invalid width %d", __func__,
+ width));
+
+ mask = (1ul << width) - 1;
+ new_field = (new_val >> shift) & mask;
+ old_field = (val >> shift) & mask;
+
+ update = false;
+ if (sign) {
+ /*
+ * The field is signed. Toggle the upper bit so the comparison
+ * works on unsigned values as this makes positive numbers,
+ * i.e. those with a 0 bit, larger than negative numbers,
+ * i.e. those with a 1 bit, in an unsigned comparison.
+ */
+ if ((new_field ^ (1ul << (width - 1))) <
+ (old_field ^ (1ul << (width - 1))))
+ update = true;
+ } else {
+ if (new_field < old_field)
+ update = true;
+ }
+
+ if (update) {
+ val &= ~(mask << shift);
+ val |= new_field << shift;
+ }
+
+ return (val);
+}
+
static void
-update_user_regs(u_int cpu)
+update_special_regs(u_int cpu)
{
struct mrs_field *fields;
- uint64_t cur, value;
- int i, j, cur_field, new_field;
+ uint64_t user_reg, kern_reg, value;
+ int i, j;
+ if (cpu == 0) {
+ /* Create a user visible cpu description with safe values */
+ memset(&user_cpu_desc, 0, sizeof(user_cpu_desc));
+ /* Safe values for these registers */
+ user_cpu_desc.id_aa64pfr0 = ID_AA64PFR0_AdvSIMD_NONE |
+ ID_AA64PFR0_FP_NONE | ID_AA64PFR0_EL1_64 |
+ ID_AA64PFR0_EL0_64;
+ user_cpu_desc.id_aa64dfr0 = ID_AA64DFR0_DebugVer_8;
+ }
+
for (i = 0; i < nitems(user_regs); i++) {
value = CPU_DESC_FIELD(cpu_desc[cpu], i);
- if (cpu == 0)
- cur = value;
- else
- cur = CPU_DESC_FIELD(user_cpu_desc, i);
+ if (cpu == 0) {
+ kern_reg = value;
+ user_reg = value;
+ } else {
+ kern_reg = CPU_DESC_FIELD(kern_cpu_desc, i);
+ user_reg = CPU_DESC_FIELD(user_cpu_desc, i);
+ }
fields = user_regs[i].fields;
for (j = 0; fields[j].type != 0; j++) {
switch (fields[j].type & MRS_TYPE_MASK) {
case MRS_EXACT:
- cur &= ~(0xfu << fields[j].shift);
- cur |=
+ user_reg &= ~(0xfu << fields[j].shift);
+ user_reg |=
(uint64_t)MRS_EXACT_FIELD(fields[j].type) <<
fields[j].shift;
break;
case MRS_LOWER:
- new_field = (value >> fields[j].shift) & 0xf;
- cur_field = (cur >> fields[j].shift) & 0xf;
- if ((fields[j].sign &&
- (int)new_field < (int)cur_field) ||
- (!fields[j].sign &&
- (u_int)new_field < (u_int)cur_field)) {
- cur &= ~(0xfu << fields[j].shift);
- cur |= new_field << fields[j].shift;
- }
+ user_reg = update_lower_register(user_reg,
+ value, fields[j].shift, 4, fields[j].sign);
break;
default:
panic("Invalid field type: %d", fields[j].type);
}
+ kern_reg = update_lower_register(kern_reg, value,
+ fields[j].shift, 4, fields[j].sign);
}
- CPU_DESC_FIELD(user_cpu_desc, i) = cur;
+ CPU_DESC_FIELD(kern_cpu_desc, i) = kern_reg;
+ CPU_DESC_FIELD(user_cpu_desc, i) = user_reg;
}
}
@@ -997,13 +1061,6 @@ identify_cpu_sysinit(void *dummy __unused)
u_long hwcap;
bool dic, idc;
- /* Create a user visible cpu description with safe values */
- memset(&user_cpu_desc, 0, sizeof(user_cpu_desc));
- /* Safe values for these registers */
- user_cpu_desc.id_aa64pfr0 = ID_AA64PFR0_AdvSIMD_NONE |
- ID_AA64PFR0_FP_NONE | ID_AA64PFR0_EL1_64 | ID_AA64PFR0_EL0_64;
- user_cpu_desc.id_aa64dfr0 = ID_AA64DFR0_DebugVer_8;
-
dic = (allow_dic != 0);
idc = (allow_idc != 0);
CPU_FOREACH(cpu) {
@@ -1013,7 +1070,7 @@ identify_cpu_sysinit(void *dummy __unused)
elf_hwcap = hwcap;
else
elf_hwcap &= hwcap;
- update_user_regs(cpu);
+ update_special_regs(cpu);
if (CTR_DIC_VAL(cpu_desc[cpu].ctr) == 0)
dic = false;
Modified: head/sys/arm64/include/undefined.h
==============================================================================
--- head/sys/arm64/include/undefined.h Mon Jun 29 08:14:45 2020 (r362776)
+++ head/sys/arm64/include/undefined.h Mon Jun 29 09:08:36 2020 (r362777)
@@ -62,7 +62,10 @@ void undef_init(void);
void *install_undef_handler(bool, undef_handler_t);
void remove_undef_handler(void *);
int undef_insn(u_int, struct trapframe *);
+
+/* Functions to read the sanitised view of the special registers */
bool extract_user_id_field(u_int, u_int, uint8_t *);
+bool get_kernel_reg(u_int, uint64_t *);
#endif /* _KERNEL */
More information about the svn-src-head
mailing list