diff options
-rw-r--r-- | arch/arm/lib/interrupts_64.c | 47 | ||||
-rw-r--r-- | include/semihosting.h | 11 |
2 files changed, 58 insertions, 0 deletions
diff --git a/arch/arm/lib/interrupts_64.c b/arch/arm/lib/interrupts_64.c index 049beeca7e..2e091415a4 100644 --- a/arch/arm/lib/interrupts_64.c +++ b/arch/arm/lib/interrupts_64.c @@ -5,11 +5,13 @@ */ #include <common.h> +#include <asm/esr.h> #include <asm/global_data.h> #include <asm/ptrace.h> #include <irq_func.h> #include <linux/compiler.h> #include <efi_loader.h> +#include <semihosting.h> DECLARE_GLOBAL_DATA_PTR; @@ -64,6 +66,48 @@ void show_regs(struct pt_regs *regs) } /* + * Try to "emulate" a semihosting call in the event that we don't have a + * debugger attached. + */ +static bool smh_emulate_trap(struct pt_regs *regs) +{ + int size; + + if (ESR_ELx_EC(regs->esr) != ESR_ELx_EC_UNKNOWN) + return false; + + if (regs->spsr & PSR_MODE32_BIT) { + if (regs->spsr & PSR_AA32_T_BIT) { + u16 *insn = (u16 *)ALIGN_DOWN(regs->elr, 2); + + if (*insn != SMH_T32_SVC && *insn != SMH_T32_HLT) + return false; + size = 2; + } else { + u32 *insn = (u32 *)ALIGN_DOWN(regs->elr, 4); + + if (*insn != SMH_A32_SVC && *insn != SMH_A32_HLT) + return false; + size = 4; + } + } else { + u32 *insn = (u32 *)ALIGN_DOWN(regs->elr, 4); + + if (*insn != SMH_A64_HLT) + return false; + size = 4; + } + + /* Avoid future semihosting calls */ + disable_semihosting(); + + /* Just pretend the call failed */ + regs->regs[0] = -1; + regs->elr += size; + return true; +} + +/* * do_bad_sync handles the impossible case in the Synchronous Abort vector. */ void do_bad_sync(struct pt_regs *pt_regs) @@ -117,6 +161,9 @@ void do_bad_error(struct pt_regs *pt_regs) */ void do_sync(struct pt_regs *pt_regs) { + if (CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) && + smh_emulate_trap(pt_regs)) + return; efi_restore_gd(); printf("\"Synchronous Abort\" handler, esr 0x%08lx\n", pt_regs->esr); show_regs(pt_regs); diff --git a/include/semihosting.h b/include/semihosting.h index 9816233c50..f1f73464e4 100644 --- a/include/semihosting.h +++ b/include/semihosting.h @@ -6,6 +6,17 @@ #ifndef _SEMIHOSTING_H #define _SEMIHOSTING_H +/* + * These are the encoded instructions used to indicate a semihosting trap. They + * are named like SMH_ISA_INSN, where ISA is the instruction set (e.g. + * AArch64), and INSN is the mneumonic for the instruction. + */ +#define SMH_A64_HLT 0xD45E0000 +#define SMH_A32_SVC 0xEF123456 +#define SMH_A32_HLT 0xE10F0070 +#define SMH_T32_SVC 0xDFAB +#define SMH_T32_HLT 0xBABC + #if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) /** * semihosting_enabled() - Determine whether semihosting is supported |