diff options
author | Julius Werner <jwerner@chromium.org> | 2018-10-05 13:26:00 -0700 |
---|---|---|
committer | Julius Werner <jwerner@chromium.org> | 2018-10-12 20:17:48 +0000 |
commit | f3aa6e9319dc6eaabbb16eb3d09956711a121d30 (patch) | |
tree | 85ab80c860e4c5b24af705a20d91a52b481af1a1 | |
parent | 1bfda7293ac0f06681097a8cd6964d86af0b24ba (diff) |
libpayload: arm64: Add GDB support
This patch adds remote GDB support for the arm64 architecture.
Change-Id: I2fa4dbca6c39f822f489a5e81bd052f53fda98a5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/29020
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
-rw-r--r-- | payloads/libpayload/arch/arm64/gdb.c | 96 |
1 files changed, 88 insertions, 8 deletions
diff --git a/payloads/libpayload/arch/arm64/gdb.c b/payloads/libpayload/arch/arm64/gdb.c index 29a037095f..10ae675fe4 100644 --- a/payloads/libpayload/arch/arm64/gdb.c +++ b/payloads/libpayload/arch/arm64/gdb.c @@ -18,40 +18,120 @@ struct gdb_regs { - u64 x[32]; + u64 x[31]; + u64 sp; /* shares encoding 0b11111 with XZR in insns */ + u64 pc; /* no longer encoded as a GPR in AArch64! */ + u32 spsr; struct fp_reg { - u64 quad[2]; - } __packed f[32]; - u32 fpcr; + u64 low; + u64 high; + } __packed v[32]; u32 fpsr; - u32 spsr; + u32 fpcr; } __packed; +/* Scratch value to write reentrant exception states to. We never read it. */ +static struct exception_state sentinel_exception_state; + static int gdb_exception_hook(u32 type) { - return -1; + if (!gdb_handle_reentrant_exception()) { + u8 signal; + + if (type >= EXC_SYNC_SPX) { + printf("Impossible exception type: %d!\n", type); + return 0; + } + + if (type == EXC_IRQ_SP0 || type == EXC_FIQ_SP0) + signal = GDB_SIGINT; + else switch (exception_state.ec) { + case ESR_EC_UNKNOWN: + signal = GDB_SIGILL; + break; + case ESR_EC_SVC_64: /* gdb_arch_enter() uses SVC */ + case ESR_EC_SS_SAME: /* single-step causes this one */ + case ESR_EC_BKPT_64: /* GDB itself likes to insert BRKs */ + signal = GDB_SIGTRAP; + break; + default: + /* We mostly expect INSN_ABT, DATA_ABT and SERROR here, + but it makes for a good catchall signal anyway. */ + signal = GDB_SIGSEGV; + /* GDB itself doesn't read out the ESR, so print it to + help people understand unexpected exceptions. But we + can't print anything if GDB is not connected yet. */ + if (gdb_state.connected) + printf("Remote-GDB Exception %d, ESR: %#08x\n", + type, (uint32_t)exception_state.esr); + } + + exception_set_state_ptr(&sentinel_exception_state); + gdb_command_loop(signal); + } + + exception_set_state_ptr(&exception_state); + + return 1; } void gdb_arch_init(void) { exception_install_hook(&gdb_exception_hook); + raw_write_oslar_el1(0); /* Disable OS lock (whatever that is) */ + raw_write_mdcr_el2(MDCR_TDE); /* Route debug exceptions to EL2 */ + raw_write_mdscr_el1(MDSCR_KDE); /* Enable debugging of current EL */ } void gdb_arch_enter(void) { + u64 *sp; + + asm volatile ("mov %0, sp" : "=r"(sp) ); + + /* Avoid reentrant exceptions, just call the hook if in one already. + This is mostly important when gdb_enter() is called as result of an + exception (as part of the halt() at the end). */ + if (sp >= exception_stack && sp <= exception_stack_end) + gdb_exception_hook(EXC_SYNC_SP0); + else /* BRK doesn't adjust ELR, so using SVC makes things easier. */ + asm volatile ("svc #0"); } int gdb_arch_set_single_step(int on) { - /* GDB seems to only need this on x86, ARM works fine without it. */ - return -1; + raw_write_mdscr_el1(MDSCR_KDE | (on ? MDSCR_SS : 0)); + exception_state.pstate.ss = !!on; + return 0; } void gdb_arch_encode_regs(struct gdb_message *message) { + gdb_message_encode_bytes(message, &exception_state.regs, + sizeof(exception_state.regs)); + gdb_message_encode_bytes(message, &exception_state.sp, + sizeof(exception_state.sp)); + gdb_message_encode_bytes(message, &exception_state.elr, + sizeof(exception_state.elr)); + gdb_message_encode_bytes(message, &exception_state.spsr, + sizeof(exception_state.spsr)); + gdb_message_encode_zero_bytes(message, + sizeof(struct gdb_regs) - offsetof(struct gdb_regs, v)); } void gdb_arch_decode_regs(int offset, struct gdb_message *message) { + gdb_message_decode_bytes(message, offset, + &exception_state.regs, sizeof(exception_state.regs)); + offset += sizeof(exception_state.regs) * 2; + gdb_message_decode_bytes(message, offset, + &exception_state.sp, sizeof(exception_state.sp)); + offset += sizeof(exception_state.sp) * 2; + gdb_message_decode_bytes(message, offset, + &exception_state.elr, sizeof(exception_state.elr)); + offset += sizeof(exception_state.elr) * 2; + gdb_message_decode_bytes(message, offset, + &exception_state.spsr, sizeof(exception_state.spsr)); + offset += sizeof(exception_state.spsr) * 2; } |