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-all mailing list