diff --git a/arch/x86/core/CMakeLists.txt b/arch/x86/core/CMakeLists.txt index 36b6bcbc433..06873a05212 100644 --- a/arch/x86/core/CMakeLists.txt +++ b/arch/x86/core/CMakeLists.txt @@ -10,6 +10,7 @@ endif () zephyr_library_sources(cpuhalt.c) zephyr_library_sources(memmap.c) zephyr_library_sources(prep_c.c) +zephyr_library_sources(fatal.c) zephyr_library_sources_if_kconfig(pcie.c) zephyr_library_sources_if_kconfig(reboot_rst_cnt.c) diff --git a/arch/x86/core/fatal.c b/arch/x86/core/fatal.c new file mode 100644 index 00000000000..39e3e0d7720 --- /dev/null +++ b/arch/x86/core/fatal.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +LOG_MODULE_DECLARE(os); + +#ifdef CONFIG_THREAD_STACK_INFO +bool z_x86_check_stack_bounds(uintptr_t addr, size_t size, u16_t cs) +{ + uintptr_t start, end; + + if (z_arch_is_in_isr()) { + /* We were servicing an interrupt */ + start = (uintptr_t)Z_ARCH_THREAD_STACK_BUFFER(_interrupt_stack); + end = start + CONFIG_ISR_STACK_SIZE; + } else if ((cs & 0x3U) != 0U || + (_current->base.user_options & K_USER) == 0) { + /* Thread was in user mode, or is not a user mode thread. + * The normal stack buffer is what we will check. + */ + start = _current->stack_info.start; + end = STACK_ROUND_DOWN(_current->stack_info.start + + _current->stack_info.size); + } else { + /* User thread was doing a syscall, check kernel stack bounds */ + start = _current->stack_info.start - MMU_PAGE_SIZE; + end = _current->stack_info.start; + } + + return (addr <= start) || (addr + size > end); +} +#endif + +#if defined(CONFIG_EXCEPTION_STACK_TRACE) +struct stack_frame { + uintptr_t next; + uintptr_t ret_addr; +#ifndef CONFIG_X86_64 + uintptr_t args; +#endif +}; + +#define MAX_STACK_FRAMES 8 + +static void unwind_stack(uintptr_t base_ptr, u16_t cs) +{ + struct stack_frame *frame; + int i; + + if (base_ptr == 0U) { + LOG_ERR("NULL base ptr"); + return; + } + + for (i = 0; i < MAX_STACK_FRAMES; i++) { + if (base_ptr % sizeof(base_ptr) != 0U) { + LOG_ERR("unaligned frame ptr"); + return; + } + + frame = (struct stack_frame *)base_ptr; + if (frame == NULL) { + break; + } + +#ifdef CONFIG_THREAD_STACK_INFO + /* Ensure the stack frame is within the faulting context's + * stack buffer + */ + if (z_x86_check_stack_bounds((uintptr_t)frame, + sizeof(*frame), cs)) { + LOG_ERR(" corrupted? (bp=%p)", frame); + break; + } +#endif + + if (frame->ret_addr == 0U) { + break; + } +#ifdef CONFIG_X86_64 + LOG_ERR(" 0x%016lx", frame->ret_addr); +#else + LOG_ERR(" 0x%08lx (0x%lx)", frame->ret_addr, frame->args); +#endif + base_ptr = frame->next; + } +} +#endif /* CONFIG_EXCEPTION_STACK_TRACE */ + +#ifdef CONFIG_X86_64 +static void dump_regs(const z_arch_esf_t *esf) +{ + LOG_ERR("RAX: 0x%016lx RBX: 0x%016lx RCX: 0x%016lx RDX: 0x%016lx", + esf->rax, esf->rbx, esf->rcx, esf->rdx); + LOG_ERR("RSI: 0x%016lx RDI: 0x%016lx RBP: 0x%016lx RSP: 0x%016lx", + esf->rsi, esf->rdi, esf->rbp, esf->rsp); + LOG_ERR(" R8: 0x%016lx R9: 0x%016lx R10: 0x%016lx R11: 0x%016lx", + esf->r8, esf->r9, esf->r10, esf->r11); + LOG_ERR("R12: 0x%016lx R13: 0x%016lx R14: 0x%016lx R15: 0x%016lx", + esf->r12, esf->r13, esf->r14, esf->r15); + LOG_ERR("RSP: 0x%016lx RFLAGS: 0x%016lx CS: 0x%04lx CR3: %p", esf->rsp, + esf->rflags, esf->cs & 0xFFFFU, z_x86_page_tables_get()); + +#ifdef CONFIG_EXCEPTION_STACK_TRACE + LOG_ERR("call trace:"); +#endif + LOG_ERR("RIP: 0x%016lx", esf->rip); +#ifdef CONFIG_EXCEPTION_STACK_TRACE + unwind_stack(esf->rbp, esf->cs); +#endif +} +#else /* 32-bit */ +static void dump_regs(const z_arch_esf_t *esf) +{ + LOG_ERR("EAX: 0x%08x, EBX: 0x%08x, ECX: 0x%08x, EDX: 0x%08x", + esf->eax, esf->ebx, esf->ecx, esf->edx); + LOG_ERR("ESI: 0x%08x, EDI: 0x%08x, EBP: 0x%08x, ESP: 0x%08x", + esf->esi, esf->edi, esf->ebp, esf->esp); + LOG_ERR("EFLAGS: 0x%08x CS: 0x%04x CR3: %p", esf->eflags, + esf->cs & 0xFFFFU, z_x86_page_tables_get()); + +#ifdef CONFIG_EXCEPTION_STACK_TRACE + LOG_ERR("call trace:"); +#endif + LOG_ERR("EIP: 0x%08x", esf->eip); +#ifdef CONFIG_EXCEPTION_STACK_TRACE + unwind_stack(esf->ebp, esf->cs); +#endif +} +#endif /* CONFIG_X86_64 */ + +FUNC_NORETURN void z_x86_fatal_error(unsigned int reason, + const z_arch_esf_t *esf) +{ + if (esf != NULL) { + dump_regs(esf); + } + + z_fatal_error(reason, esf); + CODE_UNREACHABLE; +} diff --git a/arch/x86/core/ia32/fatal.c b/arch/x86/core/ia32/fatal.c index e5ec4c3f1c7..671a7f763c5 100644 --- a/arch/x86/core/ia32/fatal.c +++ b/arch/x86/core/ia32/fatal.c @@ -21,93 +21,6 @@ LOG_MODULE_DECLARE(os); __weak void z_debug_fatal_hook(const z_arch_esf_t *esf) { ARG_UNUSED(esf); } -#ifdef CONFIG_THREAD_STACK_INFO -/** - * @brief Check if a memory address range falls within the stack - * - * Given a memory address range, ensure that it falls within the bounds - * of the faulting context's stack. - * - * @param addr Starting address - * @param size Size of the region, or 0 if we just want to see if addr is - * in bounds - * @param cs Code segment of faulting context - * @return true if addr/size region is not within the thread stack - */ -static bool check_stack_bounds(u32_t addr, size_t size, u16_t cs) -{ - u32_t start, end; - - if (z_arch_is_in_isr()) { - /* We were servicing an interrupt */ - start = (u32_t)Z_ARCH_THREAD_STACK_BUFFER(_interrupt_stack); - end = start + CONFIG_ISR_STACK_SIZE; - } else if ((cs & 0x3U) != 0U || - (_current->base.user_options & K_USER) == 0) { - /* Thread was in user mode, or is not a user mode thread. - * The normal stack buffer is what we will check. - */ - start = _current->stack_info.start; - end = STACK_ROUND_DOWN(_current->stack_info.start + - _current->stack_info.size); - } else { - /* User thread was doing a syscall, check kernel stack bounds */ - start = _current->stack_info.start - MMU_PAGE_SIZE; - end = _current->stack_info.start; - } - - return (addr <= start) || (addr + size > end); -} -#endif - -#if defined(CONFIG_EXCEPTION_STACK_TRACE) -struct stack_frame { - u32_t next; - u32_t ret_addr; - u32_t args; -}; - -#define MAX_STACK_FRAMES 8 - -static void unwind_stack(u32_t base_ptr, u16_t cs) -{ - struct stack_frame *frame; - int i; - - if (base_ptr == 0U) { - LOG_ERR("NULL base ptr"); - return; - } - - for (i = 0; i < MAX_STACK_FRAMES; i++) { - if (base_ptr % sizeof(base_ptr) != 0U) { - LOG_ERR("unaligned frame ptr"); - return; - } - - frame = (struct stack_frame *)base_ptr; - if (frame == NULL) { - break; - } - -#ifdef CONFIG_THREAD_STACK_INFO - /* Ensure the stack frame is within the faulting context's - * stack buffer - */ - if (check_stack_bounds((u32_t)frame, sizeof(*frame), cs)) { - LOG_ERR(" corrupted? (bp=%p)", frame); - break; - } -#endif - - if (frame->ret_addr == 0U) { - break; - } - LOG_ERR(" 0x%08x (0x%x)", frame->ret_addr, frame->args); - base_ptr = frame->next; - } -} -#endif /* CONFIG_EXCEPTION_STACK_TRACE */ #ifdef CONFIG_BOARD_QEMU_X86 FUNC_NORETURN void z_arch_system_halt(unsigned int reason) @@ -122,29 +35,6 @@ FUNC_NORETURN void z_arch_system_halt(unsigned int reason) } #endif -FUNC_NORETURN void z_x86_fatal_error(unsigned int reason, const z_arch_esf_t *esf) -{ - if (esf != NULL) { - LOG_ERR("eax: 0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x", - esf->eax, esf->ebx, esf->ecx, esf->edx); - LOG_ERR("esi: 0x%08x, edi: 0x%08x, ebp: 0x%08x, esp: 0x%08x", - esf->esi, esf->edi, esf->ebp, esf->esp); - LOG_ERR("eflags: 0x%08x cs: 0x%04x cr3: %p", esf->eflags, - esf->cs & 0xFFFFU, z_x86_page_tables_get()); - -#ifdef CONFIG_EXCEPTION_STACK_TRACE - LOG_ERR("call trace:"); -#endif - LOG_ERR("eip: 0x%08x", esf->eip); -#ifdef CONFIG_EXCEPTION_STACK_TRACE - unwind_stack(esf->ebp, esf->cs); -#endif - } - - z_fatal_error(reason, esf); - CODE_UNREACHABLE; -} - void z_x86_spurious_irq(const z_arch_esf_t *esf) { int vector = z_irq_controller_isr_vector_get(); @@ -348,7 +238,7 @@ void page_fault_handler(z_arch_esf_t *esf) dump_page_fault(esf); #endif #ifdef CONFIG_THREAD_STACK_INFO - if (check_stack_bounds(esf->esp, 0, esf->cs)) { + if (z_x86_check_stack_bounds(esf->esp, 0, esf->cs)) { z_x86_fatal_error(K_ERR_STACK_CHK_FAIL, esf); } #endif @@ -407,7 +297,7 @@ static __used void df_handler_bottom(void) LOG_ERR("Double Fault"); #ifdef CONFIG_THREAD_STACK_INFO - if (check_stack_bounds(_df_esf.esp, 0, _df_esf.cs)) { + if (z_x86_check_stack_bounds(_df_esf.esp, 0, _df_esf.cs)) { reason = K_ERR_STACK_CHK_FAIL; } #endif diff --git a/arch/x86/core/intel64/fatal.c b/arch/x86/core/intel64/fatal.c index ada49723c94..9a084bd22b2 100644 --- a/arch/x86/core/intel64/fatal.c +++ b/arch/x86/core/intel64/fatal.c @@ -10,28 +10,6 @@ #include LOG_MODULE_DECLARE(os); -void z_x86_fatal_error(unsigned int reason, const z_arch_esf_t *esf) -{ - if (esf != NULL) { - LOG_ERR("RIP: 0x%016lx RSP: 0x%016lx RFLAGS: 0x%016lx", - esf->rip, esf->rsp, esf->rflags); - - LOG_ERR("RAX: 0x%016lx RBX: 0x%016lx RCX: 0x%016lx RDX: 0x%016lx", - esf->rax, esf->rbx, esf->rcx, esf->rdx); - - LOG_ERR("RSI: 0x%016lx RDI: 0x%016lx RBP: 0x%016lx RSP: 0x%016lx", - esf->rsi, esf->rdi, esf->rbp, esf->rsp); - - LOG_ERR(" R8: 0x%016lx R9: 0x%016lx R10: 0x%016lx R11: 0x%016lx", - esf->r8, esf->r9, esf->r10, esf->r11); - - LOG_ERR("R12: 0x%016lx R13: 0x%016lx R14: 0x%016lx R15: 0x%016lx", - esf->r12, esf->r13, esf->r14, esf->r15); - } - - z_fatal_error(reason, esf); -} - void z_x86_exception(const z_arch_esf_t *esf) { LOG_ERR("** CPU Exception %ld (code %ld/0x%lx) **", diff --git a/arch/x86/include/kernel_arch_func.h b/arch/x86/include/kernel_arch_func.h index a885b788452..363a727777a 100644 --- a/arch/x86/include/kernel_arch_func.h +++ b/arch/x86/include/kernel_arch_func.h @@ -47,6 +47,27 @@ void z_x86_early_serial_init(void); void z_x86_paging_init(void); #endif /* CONFIG_X86_MMU */ -#endif +/* Called upon unrecoverable error; dump registers and transfer control to + * kernel via z_fatal_error() + */ +FUNC_NORETURN void z_x86_fatal_error(unsigned int reason, + const z_arch_esf_t *esf); + +#ifdef CONFIG_THREAD_STACK_INFO +/** + * @brief Check if a memory address range falls within the stack + * + * Given a memory address range, ensure that it falls within the bounds + * of the faulting context's stack. + * + * @param addr Starting address + * @param size Size of the region, or 0 if we just want to see if addr is + * in bounds + * @param cs Code segment of faulting context + * @return true if addr/size region is not within the thread stack + */ +bool z_x86_check_stack_bounds(uintptr_t addr, size_t size, u16_t cs); +#endif /* CONFIG_THREAD_STACK_INFO */ +#endif /* !_ASMLANGUAGE */ #endif /* ZEPHYR_ARCH_X86_INCLUDE_KERNEL_ARCH_FUNC_H_ */