Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 14 Aug 2018 11:00:55 +0000 (UTC)
From:      Andrew Turner <andrew@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r337739 - head/sys/arm64/arm64
Message-ID:  <201808141100.w7EB0taI010491@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: andrew
Date: Tue Aug 14 11:00:54 2018
New Revision: 337739
URL: https://svnweb.freebsd.org/changeset/base/337739

Log:
  Support reading from the arm64 ID registers from userspace.
  
  Trap reads to the arm64 ID registers and write a safe value into them. This
  will allow us to put more useful values in these later and have userland
  check them to find what features the hardware supports.
  
  These are currently safe defaults, but will later be populated with better
  values from the hardware.
  
  Sponsored by:	DARPA, AFRL
  Differential Revision:	https://reviews.freebsd.org/D16533

Modified:
  head/sys/arm64/arm64/undefined.c

Modified: head/sys/arm64/arm64/undefined.c
==============================================================================
--- head/sys/arm64/arm64/undefined.c	Tue Aug 14 08:33:47 2018	(r337738)
+++ head/sys/arm64/arm64/undefined.c	Tue Aug 14 11:00:54 2018	(r337739)
@@ -53,6 +53,135 @@ struct undef_handler {
  */
 LIST_HEAD(, undef_handler) undef_handlers[2];
 
+#define	MRS_MASK			0xfff00000
+#define	MRS_VALUE			0xd5300000
+#define	MRS_SPECIAL(insn)		((insn) & 0x000fffe0)
+#define	MRS_REGISTER(insn)		((insn) & 0x0000001f)
+#define	 MRS_Op0_SHIFT			19
+#define	 MRS_Op0_MASK			0x00080000
+#define	 MRS_Op1_SHIFT			16
+#define	 MRS_Op1_MASK			0x00070000
+#define	 MRS_CRn_SHIFT			12
+#define	 MRS_CRn_MASK			0x0000f000
+#define	 MRS_CRm_SHIFT			8
+#define	 MRS_CRm_MASK			0x00000f00
+#define	 MRS_Op2_SHIFT			5
+#define	 MRS_Op2_MASK			0x000000e0
+#define	 MRS_Rt_SHIFT			0
+#define	 MRS_Rt_MASK			0x0000001f
+
+static inline int
+mrs_Op0(uint32_t insn)
+{
+
+	/* op0 is encoded without the top bit in a mrs instruction */
+	return (2 | ((insn & MRS_Op0_MASK) >> MRS_Op0_SHIFT));
+}
+
+#define	MRS_GET(op)						\
+static inline int						\
+mrs_##op(uint32_t insn)						\
+{								\
+								\
+	return ((insn & MRS_##op##_MASK) >> MRS_##op##_SHIFT);	\
+}
+MRS_GET(Op1)
+MRS_GET(CRn)
+MRS_GET(CRm)
+MRS_GET(Op2)
+
+struct mrs_safe_value {
+	u_int		CRm;
+	u_int		Op2;
+	uint64_t	value;
+};
+
+static struct mrs_safe_value safe_values[] = {
+	{	/* id_aa64pfr0_el1 */
+		.CRm = 4,
+		.Op2 = 0,
+		.value = ID_AA64PFR0_ADV_SIMD_NONE | ID_AA64PFR0_FP_NONE |
+		    ID_AA64PFR0_EL1_64 | ID_AA64PFR0_EL0_64,
+	},
+	{	/* id_aa64dfr0_el1 */
+		.CRm = 5,
+		.Op2 = 0,
+		.value = ID_AA64DFR0_DEBUG_VER_8,
+	},
+};
+
+static int
+user_mrs_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
+    uint32_t esr)
+{
+	uint64_t value;
+	int CRm, Op2, i, reg;
+
+	if ((insn & MRS_MASK) != MRS_VALUE)
+		return (0);
+
+	/*
+	 * We only emulate Op0 == 3, Op1 == 0, CRn == 0, CRm == {0, 4-7}.
+	 * These are in the EL1 CPU identification space.
+	 * CRm == 0 holds MIDR_EL1, MPIDR_EL1, and REVID_EL1.
+	 * CRm == {4-7} holds the ID_AA64 registers.
+	 *
+	 * For full details see the ARMv8 ARM (ARM DDI 0487C.a)
+	 * Table D9-2 System instruction encodings for non-Debug System
+	 * register accesses.
+	 */
+	if (mrs_Op0(insn) != 3 || mrs_Op1(insn) != 0 || mrs_CRn(insn) != 0)
+		return (0);
+
+	CRm = mrs_CRm(insn);
+	if (CRm > 7 || (CRm < 4 && CRm != 0))
+		return (0);
+
+	Op2 = mrs_Op2(insn);
+	value = 0;
+
+	for (i = 0; i < nitems(safe_values); i++) {
+		if (safe_values[i].CRm == CRm && safe_values[i].Op2 == Op2) {
+			value = safe_values[i].value;
+			break;
+		}
+	}
+
+	if (CRm == 0) {
+		switch (Op2) {
+		case 0:
+			value = READ_SPECIALREG(midr_el1);
+			break;
+		case 5:
+			value = READ_SPECIALREG(mpidr_el1);
+			break;
+		case 6:
+			value = READ_SPECIALREG(revidr_el1);
+			break;
+		default:
+			return (0);
+		}
+	}
+
+	/*
+	 * We will handle this instruction, move to the next so we
+	 * don't trap here again.
+	 */
+	frame->tf_elr += INSN_SIZE;
+
+	reg = MRS_REGISTER(insn);
+	/* If reg is 31 then write to xzr, i.e. do nothing */
+	if (reg == 31)
+		return (1);
+
+	if (reg < nitems(frame->tf_x))
+		frame->tf_x[reg] = value;
+	else if (reg == 30)
+		frame->tf_lr = value;
+
+	return (1);
+}
+
 /*
  * Work around a bug in QEMU prior to 2.5.1 where reading unknown ID
  * registers would raise an exception when they should return 0.
@@ -63,9 +192,6 @@ id_aa64mmfr2_handler(vm_offset_t va, uint32_t insn, st
 {
 	int reg;
 
-#define	MRS_MASK			0xfff00000
-#define	MRS_VALUE			0xd5300000
-#define	MRS_REGISTER(insn)		((insn) & 0x1f)
 #define	 MRS_ID_AA64MMFR2_EL0_MASK	(MRS_MASK | 0x000fffe0)
 #define	 MRS_ID_AA64MMFR2_EL0_VALUE	(MRS_VALUE | 0x00080740)
 
@@ -93,6 +219,7 @@ undef_init(void)
 	LIST_INIT(&undef_handlers[0]);
 	LIST_INIT(&undef_handlers[1]);
 
+	install_undef_handler(true, user_mrs_handler);
 	install_undef_handler(false, id_aa64mmfr2_handler);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201808141100.w7EB0taI010491>