Module Name: src Committed By: martin Date: Fri Jan 1 13:14:29 UTC 2021
Modified Files: src/sys/arch/aarch64/aarch64 [netbsd-9]: trap.c Log Message: Pull up following revision(s) (requested by rin in ticket #1175): sys/arch/aarch64/aarch64/trap.c: revision 1.28,1.31,1.32 (patch) - 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.17.4.4 -r1.17.4.5 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.17.4.4 src/sys/arch/aarch64/aarch64/trap.c:1.17.4.5 --- src/sys/arch/aarch64/aarch64/trap.c:1.17.4.4 Fri Jan 1 12:58:35 2021 +++ src/sys/arch/aarch64/aarch64/trap.c Fri Jan 1 13:14:29 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: trap.c,v 1.17.4.4 2021/01/01 12:58:35 martin Exp $ */ +/* $NetBSD: trap.c,v 1.17.4.5 2021/01/01 13:14:29 martin Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.17.4.4 2021/01/01 12:58:35 martin Exp $"); +__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.17.4.5 2021/01/01 13:14:29 martin Exp $"); #include "opt_arm_intr_impl.h" #include "opt_compat_netbsd32.h" @@ -88,6 +88,12 @@ dtrace_trap_func_t dtrace_trap_func = N int (*dtrace_invop_jump_addr)(struct trapframe *); #endif +enum emul_arm_result { + EMUL_ARM_SUCCESS = 0, + EMUL_ARM_UNKNOWN, + EMUL_ARM_FAULT, +}; + const char * const trap_names[] = { [ESR_EC_UNKNOWN] = "Unknown Reason (Illegal Instruction)", [ESR_EC_SERROR] = "SError Interrupt", @@ -356,15 +362,15 @@ interrupt(struct trapframe *tf) #define THUMB_32BIT(hi) (((hi) & 0xe000) == 0xe000 && ((hi) & 0x1800)) static int -fetch_arm_insn(struct trapframe *tf, uint32_t *insn) +fetch_arm_insn(uint64_t pc, uint64_t spsr, uint32_t *insn) { /* THUMB? */ - if (tf->tf_spsr & SPSR_A32_T) { - uint16_t *pc = (uint16_t *)(tf->tf_pc & ~1UL); /* XXX */ + if (spsr & SPSR_A32_T) { + uint16_t *p = (uint16_t *)(pc & ~1UL); /* XXX */ uint16_t hi, lo; - if (ufetch_16(pc, &hi)) + if (ufetch_16(p, &hi)) return -1; if (!THUMB_32BIT(hi)) { @@ -374,93 +380,139 @@ fetch_arm_insn(struct trapframe *tf, uin } /* 32-bit Thumb instruction */ - if (ufetch_16(pc + 1, &lo)) + if (ufetch_16(p + 1, &lo)) return -1; *insn = ((uint32_t)hi << 16) | lo; return 4; } - if (ufetch_32((uint32_t *)tf->tf_pc, insn)) + if (ufetch_32((uint32_t *)pc, insn)) return -1; return 4; } -enum emul_arm_result { - EMUL_ARM_SUCCESS = 0, - EMUL_ARM_UNKNOWN, - EMUL_ARM_FAULT, -}; +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, &insn); + 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 */ - - /* - * Breakpoint used by GDB. - */ - if (insn == 0xdefe) - goto trap; + if (tf->tf_spsr & SPSR_A32_T) + return emul_thumb_insn(tf, insn, insn_size); + if (insn_size != 4) + return EMUL_ARM_FAULT; - /* XXX: some T32 IT instruction deprecated should be emulated */ - break; - case 4: - /* T32-32bit instruction, or A32 instruction */ + /* Breakpoint used by GDB */ + if (insn == 0xe6000011 || insn == 0xe7ffdefe) { + do_trapsignal(curlwp, SIGTRAP, TRAP_BRKPT, + (void *)tf->tf_pc, 0); + return EMUL_ARM_SUCCESS; + } - /* - * Breakpoint used by GDB. - */ - if (insn == 0xe6000011 || insn == 0xe7ffdefe) { - trap: - do_trapsignal(l, SIGTRAP, TRAP_BRKPT, - (void *)tf->tf_pc, 0); - return 0; - } + /* Unconditional instruction extension space? */ + if ((insn & 0xf0000000) == 0xf0000000) + goto unknown_insn; - /* - * 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; @@ -525,7 +577,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);