From 8fbb14ef50b418bcb11e3258d5a8e912a48be417 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Thu, 13 Aug 2020 19:18:52 -0700 Subject: [PATCH] coredump: add support for x86 and x86_64 This adds the necessary bits to enable coredump for x86 and x86_64. Signed-off-by: Daniel Leung --- arch/Kconfig | 1 + arch/x86/core/fatal.c | 4 + arch/x86/core/ia32.cmake | 2 + arch/x86/core/ia32/coredump.c | 82 +++++++++ arch/x86/core/ia32/fatal.c | 8 + arch/x86/core/intel64.cmake | 2 + arch/x86/core/intel64/coredump.c | 108 ++++++++++++ arch/x86/core/intel64/locore.S | 4 +- include/arch/x86/ia32/arch.h | 1 + include/arch/x86/intel64/arch.h | 2 +- include/debug/coredump.h | 2 + scripts/coredump/gdbstubs/__init__.py | 10 ++ scripts/coredump/gdbstubs/arch/x86.py | 157 ++++++++++++++++++ scripts/coredump/gdbstubs/arch/x86_64.py | 201 +++++++++++++++++++++++ 14 files changed, 581 insertions(+), 3 deletions(-) create mode 100644 arch/x86/core/ia32/coredump.c create mode 100644 arch/x86/core/intel64/coredump.c create mode 100644 scripts/coredump/gdbstubs/arch/x86.py create mode 100644 scripts/coredump/gdbstubs/arch/x86_64.py diff --git a/arch/Kconfig b/arch/Kconfig index 8ce3b975366..57bdd213a60 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -40,6 +40,7 @@ config X86 select ATOMIC_OPERATIONS_BUILTIN select HAS_DTS select ARCH_HAS_CUSTOM_SWAP_TO_MAIN if !X86_64 + select ARCH_SUPPORTS_COREDUMP select CPU_HAS_MMU help x86 architecture diff --git a/arch/x86/core/fatal.c b/arch/x86/core/fatal.c index 1952cf885fe..7eb7c5801c7 100644 --- a/arch/x86/core/fatal.c +++ b/arch/x86/core/fatal.c @@ -357,6 +357,10 @@ static const struct z_exc_handle exceptions[] = { void z_x86_page_fault_handler(z_arch_esf_t *esf) { +#if !defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_COREDUMP) + z_x86_exception_vector = IV_PAGE_FAULT; +#endif + #ifdef CONFIG_USERSPACE int i; diff --git a/arch/x86/core/ia32.cmake b/arch/x86/core/ia32.cmake index cc89fcd67ef..9739555e8db 100644 --- a/arch/x86/core/ia32.cmake +++ b/arch/x86/core/ia32.cmake @@ -23,5 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD ia32/irq_offload.c) zephyr_library_sources_ifdef(CONFIG_X86_USERSPACE ia32/userspace.S) zephyr_library_sources_ifdef(CONFIG_LAZY_FPU_SHARING ia32/float.c) +zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP ia32/coredump.c) + # Last since we declare default exception handlers here zephyr_library_sources(ia32/fatal.c) diff --git a/arch/x86/core/ia32/coredump.c b/arch/x86/core/ia32/coredump.c new file mode 100644 index 00000000000..bc60ce020f4 --- /dev/null +++ b/arch/x86/core/ia32/coredump.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define ARCH_HDR_VER 1 + +struct x86_arch_block { + uint32_t vector; + uint32_t code; + + struct { + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t eip; + uint32_t eflags; + uint32_t cs; + } r; +} __packed; + +/* + * This might be too large for stack space if defined + * inside function. So do it here. + */ +static struct x86_arch_block arch_blk; + +void arch_coredump_info_dump(const z_arch_esf_t *esf) +{ + struct z_coredump_arch_hdr_t hdr = { + .id = Z_COREDUMP_ARCH_HDR_ID, + .hdr_version = ARCH_HDR_VER, + .num_bytes = sizeof(arch_blk), + }; + + /* Nothing to process */ + if (esf == NULL) { + return; + } + + (void)memset(&arch_blk, 0, sizeof(arch_blk)); + + arch_blk.vector = z_x86_exception_vector; + arch_blk.code = esf->errorCode; + + /* + * 16 registers expected by GDB. + * Not all are in ESF but the GDB stub + * will need to send all 16 as one packet. + * The stub will need to send undefined + * for registers not presented in coredump. + */ + arch_blk.r.eax = esf->eax; + arch_blk.r.ebx = esf->ebx; + arch_blk.r.ecx = esf->ecx; + arch_blk.r.edx = esf->edx; + arch_blk.r.esp = esf->esp; + arch_blk.r.ebp = esf->ebp; + arch_blk.r.esi = esf->esi; + arch_blk.r.edi = esf->edi; + arch_blk.r.eip = esf->eip; + arch_blk.r.eflags = esf->eflags; + arch_blk.r.cs = esf->cs & 0xFFFFU; + + /* Send for output */ + z_coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr)); + z_coredump_buffer_output((uint8_t *)&arch_blk, sizeof(arch_blk)); +} + +uint16_t arch_coredump_tgt_code_get(void) +{ + return COREDUMP_TGT_X86; +} diff --git a/arch/x86/core/ia32/fatal.c b/arch/x86/core/ia32/fatal.c index db0d0ac126c..298ec9b562f 100644 --- a/arch/x86/core/ia32/fatal.c +++ b/arch/x86/core/ia32/fatal.c @@ -20,6 +20,10 @@ #include LOG_MODULE_DECLARE(os); +#ifdef CONFIG_DEBUG_COREDUMP +unsigned int z_x86_exception_vector; +#endif + __weak void z_debug_fatal_hook(const z_arch_esf_t *esf) { ARG_UNUSED(esf); } void z_x86_spurious_irq(const z_arch_esf_t *esf) @@ -58,6 +62,10 @@ NANO_CPU_INT_REGISTER(_kernel_oops_handler, NANO_SOFT_IRQ, FUNC_NORETURN static void generic_exc_handle(unsigned int vector, const z_arch_esf_t *pEsf) { +#ifdef CONFIG_DEBUG_COREDUMP + z_x86_exception_vector = vector; +#endif + z_x86_unhandled_cpu_exception(vector, pEsf); } diff --git a/arch/x86/core/intel64.cmake b/arch/x86/core/intel64.cmake index 5fc04a49699..a50a1732681 100644 --- a/arch/x86/core/intel64.cmake +++ b/arch/x86/core/intel64.cmake @@ -17,3 +17,5 @@ zephyr_library_sources( ) zephyr_library_sources_ifdef(CONFIG_USERSPACE intel64/userspace.S) + +zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP intel64/coredump.c) diff --git a/arch/x86/core/intel64/coredump.c b/arch/x86/core/intel64/coredump.c new file mode 100644 index 00000000000..42837792a80 --- /dev/null +++ b/arch/x86/core/intel64/coredump.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define ARCH_HDR_VER 1 + +struct x86_64_arch_block { + uint64_t vector; + uint64_t code; + + struct { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rsi; + uint64_t rdi; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t rip; + uint64_t eflags; + uint64_t cs; + uint64_t ss; + uint64_t rbp; + +#ifdef CONFIG_EXCEPTION_DEBUG + uint64_t rbx; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; +#endif + } r; +} __packed; + +/* + * Register block takes up too much stack space + * if defined within function. So define it here. + */ +static struct x86_64_arch_block arch_blk; + +void arch_coredump_info_dump(const z_arch_esf_t *esf) +{ + struct z_coredump_arch_hdr_t hdr = { + .id = Z_COREDUMP_ARCH_HDR_ID, + .hdr_version = ARCH_HDR_VER, + .num_bytes = sizeof(arch_blk), + }; + + /* Nothing to process */ + if (esf == NULL) { + return; + } + + (void)memset(&arch_blk, 0, sizeof(arch_blk)); + + arch_blk.vector = esf->vector; + arch_blk.code = esf->code; + + /* + * 34 registers expected by GDB. + * Not all are in ESF but the GDB stub + * will need to send all 34 as one packet. + * The stub will need to send undefined + * for registers not presented in coredump. + */ + arch_blk.r.rax = esf->rax; + arch_blk.r.rcx = esf->rcx; + arch_blk.r.rdx = esf->rdx; + arch_blk.r.rsi = esf->rsi; + arch_blk.r.rdi = esf->rdi; + arch_blk.r.rsp = esf->rsp; + arch_blk.r.rip = esf->rip; + arch_blk.r.r8 = esf->r8; + arch_blk.r.r9 = esf->r9; + arch_blk.r.r10 = esf->r10; + arch_blk.r.r11 = esf->r11; + + arch_blk.r.eflags = esf->rflags; + arch_blk.r.cs = esf->cs & 0xFFFFU; + arch_blk.r.ss = esf->ss; + + arch_blk.r.rbp = esf->rbp; + +#ifdef CONFIG_EXCEPTION_DEBUG + arch_blk.r.rbx = esf->rbx; + arch_blk.r.r12 = esf->r12; + arch_blk.r.r13 = esf->r13; + arch_blk.r.r14 = esf->r14; + arch_blk.r.r15 = esf->r15; +#endif + + /* Send for output */ + z_coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr)); + z_coredump_buffer_output((uint8_t *)&arch_blk, sizeof(arch_blk)); +} + +uint16_t arch_coredump_tgt_code_get(void) +{ + return COREDUMP_TGT_X86_64; +} diff --git a/arch/x86/core/intel64/locore.S b/arch/x86/core/intel64/locore.S index afb75362e17..9f28a62ddfd 100644 --- a/arch/x86/core/intel64/locore.S +++ b/arch/x86/core/intel64/locore.S @@ -480,13 +480,13 @@ except: /* pushq %rdx pushq %rcx pushq %rax + pushq %rbp #ifdef CONFIG_EXCEPTION_DEBUG /* Callee saved regs */ pushq %r15 pushq %r14 pushq %r13 pushq %r12 - pushq %rbp pushq %rbx #endif /* CONFIG_EXCEPTION_DEBUG */ movq %rsp, %rdi @@ -498,12 +498,12 @@ except: /* */ #ifdef CONFIG_EXCEPTION_DEBUG popq %rbx - popq %rbp popq %r12 popq %r13 popq %r14 popq %r15 #endif /* CONFIG_EXCEPTION_DEBUG */ + popq %rbp popq %rax popq %rcx popq %rdx diff --git a/include/arch/x86/ia32/arch.h b/include/arch/x86/ia32/arch.h index 75d3a655bcb..f5e07f3538a 100644 --- a/include/arch/x86/ia32/arch.h +++ b/include/arch/x86/ia32/arch.h @@ -343,6 +343,7 @@ typedef struct nanoEsf { unsigned int eflags; } z_arch_esf_t; +extern unsigned int z_x86_exception_vector; struct _x86_syscall_stack_frame { uint32_t eip; diff --git a/include/arch/x86/intel64/arch.h b/include/arch/x86/intel64/arch.h index 1abb3ed34de..9a61de7da2d 100644 --- a/include/arch/x86/intel64/arch.h +++ b/include/arch/x86/intel64/arch.h @@ -35,12 +35,12 @@ struct x86_esf { #ifdef CONFIG_EXCEPTION_DEBUG /* callee-saved */ unsigned long rbx; - unsigned long rbp; unsigned long r12; unsigned long r13; unsigned long r14; unsigned long r15; #endif /* CONFIG_EXCEPTION_DEBUG */ + unsigned long rbp; /* Caller-saved regs */ unsigned long rax; diff --git a/include/debug/coredump.h b/include/debug/coredump.h index ada94ce4228..03a608c1f70 100644 --- a/include/debug/coredump.h +++ b/include/debug/coredump.h @@ -23,6 +23,8 @@ /* Target code */ enum z_coredump_tgt_code { COREDUMP_TGT_UNKNOWN = 0, + COREDUMP_TGT_X86, + COREDUMP_TGT_X86_64, }; /* Coredump header */ diff --git a/scripts/coredump/gdbstubs/__init__.py b/scripts/coredump/gdbstubs/__init__.py index 76c04d0bfdd..d71c8f23d2d 100644 --- a/scripts/coredump/gdbstubs/__init__.py +++ b/scripts/coredump/gdbstubs/__init__.py @@ -4,12 +4,22 @@ # # SPDX-License-Identifier: Apache-2.0 +from gdbstubs.arch.x86 import GdbStub_x86 +from gdbstubs.arch.x86_64 import GdbStub_x86_64 + class TgtCode: UNKNOWN = 0 + X86 = 1 + X86_64 = 2 def get_gdbstub(logfile, elffile): stub = None tgt_code = logfile.log_hdr['tgt_code'] + if tgt_code == TgtCode.X86: + stub = GdbStub_x86(logfile=logfile, elffile=elffile) + elif tgt_code == TgtCode.X86_64: + stub = GdbStub_x86_64(logfile=logfile, elffile=elffile) + return stub diff --git a/scripts/coredump/gdbstubs/arch/x86.py b/scripts/coredump/gdbstubs/arch/x86.py new file mode 100644 index 00000000000..64c1956db1b --- /dev/null +++ b/scripts/coredump/gdbstubs/arch/x86.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +import binascii +import logging +import struct + +from gdbstubs.gdbstub import GdbStub + + +logger = logging.getLogger("gdbstub") + + +class RegNum(): + # Matches the enum i386_regnum in GDB + EAX = 0 + ECX = 1 + EDX = 2 + EBX = 3 + ESP = 4 + EBP = 5 + ESI = 6 + EDI = 7 + EIP = 8 + EFLAGS = 9 + CS = 10 + SS = 11 + DS = 12 + ES = 13 + FS = 14 + GS = 15 + + +class ExceptionVectors(): + # Matches arch/x86/include/kernel_arch_data.h + IV_DIVIDE_ERROR = 0 + IV_DEBUG = 1 + IV_NON_MASKABLE_INTERRUPT = 2 + IV_BREAKPOINT = 3 + IV_OVERFLOW = 4 + IV_BOUND_RANGE = 5 + IV_INVALID_OPCODE = 6 + IV_DEVICE_NOT_AVAILABLE = 7 + IV_DOUBLE_FAULT = 8 + IV_COPROC_SEGMENT_OVERRUN = 9 + IV_INVALID_TSS = 10 + IV_SEGMENT_NOT_PRESENT = 11 + IV_STACK_FAULT = 12 + IV_GENERAL_PROTECTION = 13 + IV_PAGE_FAULT = 14 + IV_RESERVED = 15 + IV_X87_FPU_FP_ERROR = 16 + IV_ALIGNMENT_CHECK = 17 + IV_MACHINE_CHECK = 18 + IV_SIMD_FP = 19 + IV_VIRT_EXCEPTION = 20 + IV_SECURITY_EXCEPTION = 30 + + +class GdbStub_x86(GdbStub): + ARCH_DATA_BLK_STRUCT = " unknown value + # Send in "xxxxxxxx" + pkt += b'x' * 8 + + idx += 1 + + self.put_gdb_packet(pkt) + + def handle_register_single_read_packet(self, pkt): + # Mark registers as "". + # 'p' packets are usually used for registers + # other than the general ones (e.g. eax, ebx) + # so we can safely reply "xxxxxxxx" here. + self.put_gdb_packet(b'x' * 8) diff --git a/scripts/coredump/gdbstubs/arch/x86_64.py b/scripts/coredump/gdbstubs/arch/x86_64.py new file mode 100644 index 00000000000..58faebf2133 --- /dev/null +++ b/scripts/coredump/gdbstubs/arch/x86_64.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +import binascii +import logging +import struct + +from gdbstubs.gdbstub import GdbStub + + +logger = logging.getLogger("gdbstub") + + +class RegNum(): + # Matches the enum amd64_regnum in GDB + RAX = 0 + RBX = 1 + RCX = 2 + RDX = 3 + RSI = 4 + RDI = 5 + RBP = 6 + RSP = 7 + R8 = 8 + R9 = 9 + R10 = 10 + R11 = 11 + R12 = 12 + R13 = 13 + R14 = 14 + R15 = 15 + RIP = 16 + EFLAGS = 17 + CS = 18 + SS = 19 + DS = 20 + ES = 21 + FS = 22 + GS = 23 + FS_BASE = 24 + GS_BASE = 25 + K_GS_BASE = 26 + + +class ExceptionVectors(): + # Matches arch/x86/include/kernel_arch_data.h + IV_DIVIDE_ERROR = 0 + IV_DEBUG = 1 + IV_NON_MASKABLE_INTERRUPT = 2 + IV_BREAKPOINT = 3 + IV_OVERFLOW = 4 + IV_BOUND_RANGE = 5 + IV_INVALID_OPCODE = 6 + IV_DEVICE_NOT_AVAILABLE = 7 + IV_DOUBLE_FAULT = 8 + IV_COPROC_SEGMENT_OVERRUN = 9 + IV_INVALID_TSS = 10 + IV_SEGMENT_NOT_PRESENT = 11 + IV_STACK_FAULT = 12 + IV_GENERAL_PROTECTION = 13 + IV_PAGE_FAULT = 14 + IV_RESERVED = 15 + IV_X87_FPU_FP_ERROR = 16 + IV_ALIGNMENT_CHECK = 17 + IV_MACHINE_CHECK = 18 + IV_SIMD_FP = 19 + IV_VIRT_EXCEPTION = 20 + IV_SECURITY_EXCEPTION = 30 + + +class GdbStub_x86_64(GdbStub): + GDB_SIGNAL_DEFAULT = 7 + + # Mapping is from GDB's gdb/i386-stubs.c + GDB_SIGNAL_MAPPING = { + ExceptionVectors.IV_DIVIDE_ERROR: 8, + ExceptionVectors.IV_DEBUG: 5, + ExceptionVectors.IV_BREAKPOINT: 5, + ExceptionVectors.IV_OVERFLOW: 16, + ExceptionVectors.IV_BOUND_RANGE: 16, + ExceptionVectors.IV_INVALID_OPCODE: 4, + ExceptionVectors.IV_DEVICE_NOT_AVAILABLE: 8, + ExceptionVectors.IV_DOUBLE_FAULT: 7, + ExceptionVectors.IV_COPROC_SEGMENT_OVERRUN: 11, + ExceptionVectors.IV_INVALID_TSS: 11, + ExceptionVectors.IV_SEGMENT_NOT_PRESENT: 11, + ExceptionVectors.IV_STACK_FAULT: 11, + ExceptionVectors.IV_GENERAL_PROTECTION: 11, + ExceptionVectors.IV_PAGE_FAULT: 11, + ExceptionVectors.IV_X87_FPU_FP_ERROR: 7, + } + + GDB_G_PKT_NUM_REGS = 34 + + GDB_32BIT_REGS = { + RegNum.EFLAGS, + RegNum.CS, + RegNum.SS, + RegNum.DS, + RegNum.ES, + RegNum.FS, + RegNum.GS, + } + + def __init__(self, logfile, elffile): + super().__init__(logfile=logfile, elffile=elffile) + self.registers = None + self.exception_vector = None + self.exception_code = None + self.gdb_signal = self.GDB_SIGNAL_DEFAULT + + self.parse_arch_data_block() + self.compute_signal() + + def parse_arch_data_block(self): + arch_data_blk = self.logfile.get_arch_data()['data'] + + arch_data_blk_struct = " unknown value + # Send in "xxxxxxxx" + pkt += b'x' * (reg_bytes * 2) + + idx += 1 + + self.put_gdb_packet(pkt) + + def handle_register_single_read_packet(self, pkt): + # Mark registers as "". + # 'p' packets are usually used for registers + # other than the general ones (e.g. eax, ebx) + # so we can safely reply "xxxxxxxx" here. + self.put_gdb_packet(b'x' * 16)