git: fbec1f91951f - main - arm64: Add a masked get_kernel_reg()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 28 Jun 2023 20:47:35 UTC
The branch main has been updated by markj:

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

commit fbec1f91951fe01c1c590bab5e6c16a30884f352
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-06-28 20:12:47 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2023-06-28 20:29:49 +0000

    arm64: Add a masked get_kernel_reg()
    
    This lets consumers fetch the value of a system register and apply a
    mask over individual fields.  That is, each field in the returned value
    will be the "smaller" of the two provided by "mask" and the value saved
    in kern_cpu_desc.  This will be used by vmm to sanitize host system
    register fields.
    
    Reviewed by:    andrew
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Sponsored by:   Klara, Inc. (hardware)
    Differential Revision:  https://reviews.freebsd.org/D40500
---
 sys/arm64/arm64/identcpu.c | 90 +++++++++++++++++++++++++++++-----------------
 sys/arm64/include/cpu.h    |  1 +
 2 files changed, 59 insertions(+), 32 deletions(-)

diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c
index e96f0a089784..01e663ff47c2 100644
--- a/sys/arm64/arm64/identcpu.c
+++ b/sys/arm64/arm64/identcpu.c
@@ -1721,38 +1721,6 @@ user_mrs_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
 	return (1);
 }
 
-bool
-extract_user_id_field(u_int reg, u_int field_shift, uint8_t *val)
-{
-	uint64_t value;
-	int i;
-
-	for (i = 0; i < nitems(user_regs); i++) {
-		if (user_regs[i].reg == reg) {
-			value = CPU_DESC_FIELD(user_cpu_desc, i);
-			*val = value >> field_shift;
-			return (true);
-		}
-	}
-
-	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);
-}
-
 /*
  * Compares two field values that may be signed or unsigned.
  * Returns:
@@ -1808,6 +1776,64 @@ update_lower_register(uint64_t val, uint64_t new_val, u_int shift,
 	return (val);
 }
 
+bool
+extract_user_id_field(u_int reg, u_int field_shift, uint8_t *val)
+{
+	uint64_t value;
+	int i;
+
+	for (i = 0; i < nitems(user_regs); i++) {
+		if (user_regs[i].reg == reg) {
+			value = CPU_DESC_FIELD(user_cpu_desc, i);
+			*val = value >> field_shift;
+			return (true);
+		}
+	}
+
+	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);
+}
+
+/*
+ * Fetch the specified register's value, ensuring that individual field values
+ * do not exceed those in the mask.
+ */
+bool
+get_kernel_reg_masked(u_int reg, uint64_t *valp, uint64_t mask)
+{
+	struct mrs_field *fields;
+	uint64_t val;
+
+	for (int i = 0; i < nitems(user_regs); i++) {
+		if (user_regs[i].reg == reg) {
+			val = CPU_DESC_FIELD(kern_cpu_desc, i);
+			*valp = 0;
+			fields = user_regs[i].fields;
+			for (int j = 0; fields[j].type != 0; j++) {
+				*valp |= update_lower_register(mask, val,
+				    fields[j].shift, 4, fields[j].sign);
+			}
+			return (true);
+		}
+	}
+
+	return (false);
+}
+
 void
 update_special_regs(u_int cpu)
 {
diff --git a/sys/arm64/include/cpu.h b/sys/arm64/include/cpu.h
index 1ea497756698..0bb320f2e0a0 100644
--- a/sys/arm64/include/cpu.h
+++ b/sys/arm64/include/cpu.h
@@ -213,6 +213,7 @@ void	ptrauth_mp_start(uint64_t);
 void	update_special_regs(u_int);
 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);
 
 void	cpu_desc_init(void);