Module Name: src
Committed By: ryo
Date: Sun Jul 26 07:25:38 UTC 2020
Modified Files:
src/sys/arch/aarch64/aarch64: trap.c
Log Message:
- add support conditionally execution for A32 instruction emulation
- separated the processing of ARM and THUMB emul clearly. do not confuse the
Thumb-32bit instruction with the ARM instruction.
- use far_el1 instead of tf_pc to return correct fault address when instruction
emulation
To generate a diff of this commit:
cvs rdiff -u -r1.31 -r1.32 src/sys/arch/aarch64/aarch64/trap.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/aarch64/aarch64/trap.c
diff -u src/sys/arch/aarch64/aarch64/trap.c:1.31 src/sys/arch/aarch64/aarch64/trap.c:1.32
--- src/sys/arch/aarch64/aarch64/trap.c:1.31 Wed Jul 8 03:45:13 2020
+++ src/sys/arch/aarch64/aarch64/trap.c Sun Jul 26 07:25:38 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: trap.c,v 1.31 2020/07/08 03:45:13 ryo Exp $ */
+/* $NetBSD: trap.c,v 1.32 2020/07/26 07:25:38 ryo Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.31 2020/07/08 03:45:13 ryo Exp $");
+__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.32 2020/07/26 07:25:38 ryo Exp $");
#include "opt_arm_intr_impl.h"
#include "opt_compat_netbsd32.h"
@@ -374,8 +374,10 @@ emul_aarch64_insn(struct trapframe *tf)
{
uint32_t insn;
- if (ufetch_32((uint32_t *)tf->tf_pc, &insn))
+ if (ufetch_32((uint32_t *)tf->tf_pc, &insn)) {
+ tf->tf_far = reg_far_el1_read();
return EMUL_ARM_FAULT;
+ }
if ((insn & 0xffffffe0) == 0xd53b0020) {
/* mrs x?,ctr_el0 */
@@ -457,7 +459,7 @@ trap_el0_sync(struct trapframe *tf)
goto unknown;
case EMUL_ARM_FAULT:
do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
- (void *)tf->tf_pc, esr);
+ (void *)tf->tf_far, esr);
break;
}
userret(l);
@@ -550,74 +552,126 @@ fetch_arm_insn(uint64_t pc, uint64_t sps
return 4;
}
+static bool
+arm_cond_match(uint32_t insn, uint64_t spsr)
+{
+ bool invert = (insn >> 28) & 1;
+ bool match;
+
+ switch (insn >> 29) {
+ case 0: /* EQ or NE */
+ match = spsr & SPSR_Z;
+ break;
+ case 1: /* CS/HI or CC/LO */
+ match = spsr & SPSR_C;
+ break;
+ case 2: /* MI or PL */
+ match = spsr & SPSR_N;
+ break;
+ case 3: /* VS or VC */
+ match = spsr & SPSR_V;
+ break;
+ case 4: /* HI or LS */
+ match = ((spsr & (SPSR_C | SPSR_Z)) == SPSR_C);
+ break;
+ case 5: /* GE or LT */
+ match = (!(spsr & SPSR_N) == !(spsr & SPSR_V));
+ break;
+ case 6: /* GT or LE */
+ match = !(spsr & SPSR_Z) &&
+ (!(spsr & SPSR_N) == !(spsr & SPSR_V));
+ break;
+ case 7: /* AL */
+ match = true;
+ break;
+ }
+ return (!match != !invert);
+}
+
+static enum emul_arm_result
+emul_thumb_insn(struct trapframe *tf, uint32_t insn, int insn_size)
+{
+ /* T32-16bit or 32bit instructions */
+ switch (insn_size) {
+ case 2:
+ /* Breakpoint used by GDB */
+ if (insn == 0xdefe) {
+ do_trapsignal(curlwp, SIGTRAP, TRAP_BRKPT,
+ (void *)tf->tf_pc, 0);
+ return EMUL_ARM_SUCCESS;
+ }
+ /* XXX: some T32 IT instruction deprecated should be emulated */
+ break;
+ case 4:
+ break;
+ default:
+ return EMUL_ARM_FAULT;
+ }
+ return EMUL_ARM_UNKNOWN;
+}
+
static enum emul_arm_result
emul_arm_insn(struct trapframe *tf)
{
- struct lwp * const l = curlwp;
uint32_t insn;
int insn_size;
insn_size = fetch_arm_insn(tf->tf_pc, tf->tf_spsr, &insn);
+ tf->tf_far = reg_far_el1_read();
- switch (insn_size) {
- case 2:
- /* T32-16bit instruction */
+ if (tf->tf_spsr & SPSR_A32_T)
+ return emul_thumb_insn(tf, insn, insn_size);
+ if (insn_size != 4)
+ return EMUL_ARM_FAULT;
- /*
- * Breakpoint used by GDB.
- */
- if (insn == 0xdefe)
- goto trap;
+ /* Breakpoint used by GDB */
+ if (insn == 0xe6000011 || insn == 0xe7ffdefe) {
+ do_trapsignal(curlwp, SIGTRAP, TRAP_BRKPT,
+ (void *)tf->tf_pc, 0);
+ return EMUL_ARM_SUCCESS;
+ }
- /* XXX: some T32 IT instruction deprecated should be emulated */
- break;
- case 4:
- /* T32-32bit instruction, or A32 instruction */
+ /* Unconditional instruction extension space? */
+ if ((insn & 0xf0000000) == 0xf0000000)
+ goto unknown_insn;
- /*
- * Breakpoint used by GDB.
- */
- if (insn == 0xe6000011 || insn == 0xe7ffdefe) {
- trap:
- do_trapsignal(l, SIGTRAP, TRAP_BRKPT,
- (void *)tf->tf_pc, 0);
- return 0;
- }
-
- /*
- * Emulate ARMv6 instructions with cache operations
- * register (c7), that can be used in user mode.
- */
- switch (insn & 0x0fff0fff) {
- case 0x0e070f95:
+ /*
+ * Emulate ARMv6 instructions with cache operations
+ * register (c7), that can be used in user mode.
+ */
+ switch (insn & 0x0fff0fff) {
+ case 0x0e070f95:
+ if (arm_cond_match(insn, tf->tf_spsr)) {
/*
* mcr p15, 0, <Rd>, c7, c5, 4
* (flush prefetch buffer)
*/
__asm __volatile("isb sy" ::: "memory");
- goto emulated;
- case 0x0e070f9a:
+ }
+ goto emulated;
+ case 0x0e070f9a:
+ if (arm_cond_match(insn, tf->tf_spsr)) {
/*
* mcr p15, 0, <Rd>, c7, c10, 4
* (data synchronization barrier)
*/
__asm __volatile("dsb sy" ::: "memory");
- goto emulated;
- case 0x0e070fba:
+ }
+ goto emulated;
+ case 0x0e070fba:
+ if (arm_cond_match(insn, tf->tf_spsr)) {
/*
* mcr p15, 0, <Rd>, c7, c10, 5
* (data memory barrier)
*/
__asm __volatile("dmb sy" ::: "memory");
- goto emulated;
- default:
- break;
}
- break;
+ goto emulated;
default:
- return EMUL_ARM_FAULT;
+ break;
}
+ unknown_insn:
/* unknown, or unsupported instruction */
return EMUL_ARM_UNKNOWN;
@@ -685,7 +739,7 @@ trap_el0_32sync(struct trapframe *tf)
goto unknown;
case EMUL_ARM_FAULT:
do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
- (void *)tf->tf_pc, esr);
+ (void *)tf->tf_far, esr);
break;
}
userret(l);