aboutsummaryrefslogtreecommitdiff
path: root/sys/arm64/arm64/undefined.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm64/arm64/undefined.c')
-rw-r--r--sys/arm64/arm64/undefined.c208
1 files changed, 154 insertions, 54 deletions
diff --git a/sys/arm64/arm64/undefined.c b/sys/arm64/arm64/undefined.c
index c307281ea523..19f34fa91702 100644
--- a/sys/arm64/arm64/undefined.c
+++ b/sys/arm64/arm64/undefined.c
@@ -82,41 +82,23 @@ struct undef_handler {
undef_handler_t uh_handler;
};
-/*
- * Create two undefined instruction handler lists, one for userspace, one for
- * the kernel. This allows us to handle instructions that will trap
- */
-LIST_HEAD(, undef_handler) undef_handlers[2];
+/* System instruction handlers, e.g. msr, mrs, sys */
+struct sys_handler {
+ LIST_ENTRY(sys_handler) sys_link;
+ undef_sys_handler_t sys_handler;
+};
/*
- * 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.
+ * Create the undefined instruction handler lists.
+ * This allows us to handle instructions that will trap.
*/
-static int
-id_aa64mmfr2_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame,
- uint32_t esr)
-{
- int reg;
-
-#define MRS_ID_AA64MMFR2_EL0_MASK (MRS_MASK | 0x000fffe0)
-#define MRS_ID_AA64MMFR2_EL0_VALUE (MRS_VALUE | 0x00080740)
-
- /* mrs xn, id_aa64mfr2_el1 */
- if ((insn & MRS_ID_AA64MMFR2_EL0_MASK) == MRS_ID_AA64MMFR2_EL0_VALUE) {
- reg = MRS_REGISTER(insn);
-
- frame->tf_elr += INSN_SIZE;
- if (reg < nitems(frame->tf_x)) {
- frame->tf_x[reg] = 0;
- } else if (reg == 30) {
- frame->tf_lr = 0;
- }
- /* If reg is 32 then write to xzr, i.e. do nothing */
-
- return (1);
- }
- return (0);
-}
+LIST_HEAD(, sys_handler) sys_handlers = LIST_HEAD_INITIALIZER(sys_handler);
+LIST_HEAD(, undef_handler) undef_handlers =
+ LIST_HEAD_INITIALIZER(undef_handlers);
+#ifdef COMPAT_FREEBSD32
+LIST_HEAD(, undef_handler) undef32_handlers =
+ LIST_HEAD_INITIALIZER(undef32_handlers);
+#endif
static bool
arm_cond_match(uint32_t insn, struct trapframe *frame)
@@ -179,8 +161,7 @@ gdb_trapper(vm_offset_t va, uint32_t insn, struct trapframe *frame,
struct thread *td = curthread;
if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) {
- if (SV_PROC_FLAG(td->td_proc, SV_ILP32) &&
- va < VM_MAXUSER_ADDRESS) {
+ if (va < VM_MAXUSER_ADDRESS) {
ksiginfo_t ksi;
ksiginfo_init_trap(&ksi);
@@ -212,8 +193,7 @@ swp_emulate(vm_offset_t va, uint32_t insn, struct trapframe *frame,
* swp, swpb only; there are no Thumb swp/swpb instructions so we can
* safely bail out if we're in Thumb mode.
*/
- if (!compat32_emul_swp || !SV_PROC_FLAG(td->td_proc, SV_ILP32) ||
- (frame->tf_spsr & PSR_T) != 0)
+ if (!compat32_emul_swp || (frame->tf_spsr & PSR_T) != 0)
return (0);
else if ((insn & 0x0fb00ff0) != 0x01000090)
return (0);
@@ -278,29 +258,38 @@ fault:
void
undef_init(void)
{
-
- LIST_INIT(&undef_handlers[0]);
- LIST_INIT(&undef_handlers[1]);
-
- install_undef_handler(false, id_aa64mmfr2_handler);
#ifdef COMPAT_FREEBSD32
- install_undef_handler(true, gdb_trapper);
- install_undef_handler(true, swp_emulate);
+ install_undef32_handler(gdb_trapper);
+ install_undef32_handler(swp_emulate);
#endif
}
void *
-install_undef_handler(bool user, undef_handler_t func)
+install_undef_handler(undef_handler_t func)
{
struct undef_handler *uh;
uh = malloc(sizeof(*uh), M_UNDEF, M_WAITOK);
uh->uh_handler = func;
- LIST_INSERT_HEAD(&undef_handlers[user ? 0 : 1], uh, uh_link);
+ LIST_INSERT_HEAD(&undef_handlers, uh, uh_link);
return (uh);
}
+#ifdef COMPAT_FREEBSD32
+void *
+install_undef32_handler(undef_handler_t func)
+{
+ struct undef_handler *uh;
+
+ uh = malloc(sizeof(*uh), M_UNDEF, M_WAITOK);
+ uh->uh_handler = func;
+ LIST_INSERT_HEAD(&undef32_handlers, uh, uh_link);
+
+ return (uh);
+}
+#endif
+
void
remove_undef_handler(void *handle)
{
@@ -311,24 +300,135 @@ remove_undef_handler(void *handle)
free(handle, M_UNDEF);
}
+void
+install_sys_handler(undef_sys_handler_t func)
+{
+ struct sys_handler *sysh;
+
+ sysh = malloc(sizeof(*sysh), M_UNDEF, M_WAITOK);
+ sysh->sys_handler = func;
+ LIST_INSERT_HEAD(&sys_handlers, sysh, sys_link);
+}
+
+bool
+undef_sys(uint64_t esr, struct trapframe *frame)
+{
+ struct sys_handler *sysh;
+
+ LIST_FOREACH(sysh, &sys_handlers, sys_link) {
+ if (sysh->sys_handler(esr, frame))
+ return (true);
+ }
+
+ return (false);
+}
+
+static bool
+undef_sys_insn(struct trapframe *frame, uint32_t insn)
+{
+ uint64_t esr;
+ bool read;
+
+#define MRS_MASK 0xfff00000
+#define MRS_VALUE 0xd5300000
+#define MSR_REG_VALUE 0xd5100000
+#define MSR_IMM_VALUE 0xd5000000
+#define MRS_REGISTER(insn) ((insn) & 0x0000001f)
+#define MRS_Op0_SHIFT 19
+#define MRS_Op0_MASK 0x00180000
+#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
+
+ read = false;
+ switch (insn & MRS_MASK) {
+ case MRS_VALUE:
+ read = true;
+ break;
+ case MSR_REG_VALUE:
+ break;
+ case MSR_IMM_VALUE:
+ /*
+ * MSR (immediate) needs special handling. The
+ * source register is always 31 (xzr), CRn is 4,
+ * and op0 is hard coded as 0.
+ */
+ if (MRS_REGISTER(insn) != 31)
+ return (false);
+ if ((insn & MRS_CRn_MASK) >> MRS_CRn_SHIFT != 4)
+ return (false);
+ if ((insn & MRS_Op0_MASK) >> MRS_Op0_SHIFT != 0)
+ return (false);
+ break;
+ default:
+ return (false);
+ }
+
+ /* Create a fake EXCP_MSR esr value */
+ esr = EXCP_MSR << ESR_ELx_EC_SHIFT;
+ esr |= ESR_ELx_IL;
+ esr |= __ISS_MSR_REG(
+ (insn & MRS_Op0_MASK) >> MRS_Op0_SHIFT,
+ (insn & MRS_Op1_MASK) >> MRS_Op1_SHIFT,
+ (insn & MRS_CRn_MASK) >> MRS_CRn_SHIFT,
+ (insn & MRS_CRm_MASK) >> MRS_CRm_SHIFT,
+ (insn & MRS_Op2_MASK) >> MRS_Op2_SHIFT);
+ esr |= MRS_REGISTER(insn) << ISS_MSR_Rt_SHIFT;
+ if (read)
+ esr |= ISS_MSR_DIR;
+
+#undef MRS_MASK
+#undef MRS_VALUE
+#undef MSR_REG_VALUE
+#undef MSR_IMM_VALUE
+#undef MRS_REGISTER
+#undef MRS_Op0_SHIFT
+#undef MRS_Op0_MASK
+#undef MRS_Op1_SHIFT
+#undef MRS_Op1_MASK
+#undef MRS_CRn_SHIFT
+#undef MRS_CRn_MASK
+#undef MRS_CRm_SHIFT
+#undef MRS_CRm_MASK
+#undef MRS_Op2_SHIFT
+#undef MRS_Op2_MASK
+
+ return (undef_sys(esr, frame));
+}
+
int
-undef_insn(u_int el, struct trapframe *frame)
+undef_insn(struct trapframe *frame)
{
struct undef_handler *uh;
uint32_t insn;
int ret;
- KASSERT(el < 2, ("Invalid exception level %u", el));
+ ret = fueword32((uint32_t *)frame->tf_elr, &insn);
+ /* Raise a SIGILL if we are unable to read the instruction */
+ if (ret != 0)
+ return (0);
- if (el == 0) {
- ret = fueword32((uint32_t *)frame->tf_elr, &insn);
- if (ret != 0)
- panic("Unable to read userspace faulting instruction");
- } else {
- insn = *(uint32_t *)frame->tf_elr;
+#ifdef COMPAT_FREEBSD32
+ if (SV_PROC_FLAG(curthread->td_proc, SV_ILP32)) {
+ LIST_FOREACH(uh, &undef32_handlers, uh_link) {
+ ret = uh->uh_handler(frame->tf_elr, insn, frame,
+ frame->tf_esr);
+ if (ret)
+ return (1);
+ }
+ return (0);
}
+#endif
+
+ if (undef_sys_insn(frame, insn))
+ return (1);
- LIST_FOREACH(uh, &undef_handlers[el], uh_link) {
+ LIST_FOREACH(uh, &undef_handlers, uh_link) {
ret = uh->uh_handler(frame->tf_elr, insn, frame, frame->tf_esr);
if (ret)
return (1);