x86: consolidate some fatal error code

Some code for unwinding stacks and z_x86_fatal_error()
now in a common C file, suitable for both modes.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2019-11-05 13:42:12 -08:00 committed by Andrew Boie
commit 9fb89ccf32
5 changed files with 172 additions and 135 deletions

View file

@ -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)

147
arch/x86/core/fatal.c Normal file
View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2019 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <ksched.h>
#include <kernel_structs.h>
#include <kernel_internal.h>
#include <logging/log.h>
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;
}

View file

@ -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

View file

@ -10,28 +10,6 @@
#include <logging/log.h>
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) **",

View file

@ -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_ */