From 2b67ca8ac9f5213f667c9299c8165c11875207ec Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Mon, 18 Nov 2019 12:07:58 -0800 Subject: [PATCH] x86: improve exception debugging We now dump more information for less common cases, and this is now centralized code for 32-bit/64-bit. All of this code is now correctly wrapped around CONFIG_EXCEPTION_DEBUG. Some cruft and unused defines removed. Signed-off-by: Andrew Boie --- arch/x86/core/fatal.c | 148 +++++++++++++++++++++------ arch/x86/core/ia32/fatal.c | 16 +-- arch/x86/core/intel64/fatal.c | 4 +- arch/x86/include/kernel_arch_data.h | 2 + arch/x86/include/kernel_arch_func.h | 6 ++ include/arch/x86/ia32/segmentation.h | 17 --- 6 files changed, 125 insertions(+), 68 deletions(-) diff --git a/arch/x86/core/fatal.c b/arch/x86/core/fatal.c index 2769af002de..f04f68b001c 100644 --- a/arch/x86/core/fatal.c +++ b/arch/x86/core/fatal.c @@ -11,12 +11,6 @@ #include LOG_MODULE_DECLARE(os); -#ifdef CONFIG_X86_64 -#define PR_UPTR "0x%016lx" -#else -#define PR_UPTR "0x%08lx" -#endif - static inline uintptr_t esf_get_sp(const z_arch_esf_t *esf) { #ifdef CONFIG_X86_64 @@ -62,6 +56,7 @@ bool z_x86_check_stack_bounds(uintptr_t addr, size_t size, u16_t cs) } #endif +#ifdef CONFIG_EXCEPTION_DEBUG #if defined(CONFIG_EXCEPTION_STACK_TRACE) struct stack_frame { uintptr_t next; @@ -160,31 +155,75 @@ static void dump_regs(const z_arch_esf_t *esf) } #endif /* CONFIG_X86_64 */ -FUNC_NORETURN void z_x86_fatal_error(unsigned int reason, - const z_arch_esf_t *esf) +static void log_exception(uintptr_t vector, uintptr_t code) { - if (esf != NULL) { - dump_regs(esf); + switch (vector) { + case IV_DIVIDE_ERROR: + LOG_ERR("Divide by zero"); + break; + case IV_DEBUG: + LOG_ERR("Debug"); + break; + case IV_NON_MASKABLE_INTERRUPT: + LOG_ERR("Non-maskable interrupt"); + break; + case IV_BREAKPOINT: + LOG_ERR("Breakpoint"); + break; + case IV_OVERFLOW: + LOG_ERR("Overflow"); + break; + case IV_BOUND_RANGE: + LOG_ERR("Bound range exceeded"); + break; + case IV_INVALID_OPCODE: + LOG_ERR("Invalid opcode"); + break; + case IV_DEVICE_NOT_AVAILABLE: + LOG_ERR("Floating point unit device not available"); + break; + case IV_DOUBLE_FAULT: + LOG_ERR("Double fault (code 0x%lx)", code); + break; + case IV_COPROC_SEGMENT_OVERRUN: + LOG_ERR("Co-processor segment overrun"); + break; + case IV_INVALID_TSS: + LOG_ERR("Invalid TSS (code 0x%lx)", code); + break; + case IV_SEGMENT_NOT_PRESENT: + LOG_ERR("Segment not present (code 0x%lx)", code); + break; + case IV_STACK_FAULT: + LOG_ERR("Stack segment fault"); + break; + case IV_GENERAL_PROTECTION: + LOG_ERR("General protection fault (code 0x%lx)", code); + break; + /* IV_PAGE_FAULT skipped, we have a dedicated handler */ + case IV_X87_FPU_FP_ERROR: + LOG_ERR("x87 floating point exception"); + break; + case IV_ALIGNMENT_CHECK: + LOG_ERR("Alignment check (code 0x%lx)", code); + break; + case IV_MACHINE_CHECK: + LOG_ERR("Machine check"); + break; + case IV_SIMD_FP: + LOG_ERR("SIMD floating point exception"); + break; + case IV_VIRT_EXCEPTION: + LOG_ERR("Virtualization exception"); + break; + case IV_SECURITY_EXCEPTION: + LOG_ERR("Security exception"); + break; + default: + break; } - - z_fatal_error(reason, esf); - CODE_UNREACHABLE; } -/* - * PAGE FAULT HANDLING - */ - -#ifdef CONFIG_EXCEPTION_DEBUG -/* Page fault error code flags */ -#define PRESENT BIT(0) -#define WR BIT(1) -#define US BIT(2) -#define RSVD BIT(3) -#define ID BIT(4) -#define PK BIT(5) -#define SGX BIT(15) - #ifdef CONFIG_X86_MMU static bool dump_entry_flags(const char *name, u64_t flags) { @@ -240,6 +279,15 @@ static void dump_mmu_flags(struct x86_page_tables *ptables, uintptr_t addr) } #endif /* CONFIG_X86_MMU */ +/* Page fault error code flags */ +#define PRESENT BIT(0) +#define WR BIT(1) +#define US BIT(2) +#define RSVD BIT(3) +#define ID BIT(4) +#define PK BIT(5) +#define SGX BIT(15) + static void dump_page_fault(z_arch_esf_t *esf) { uintptr_t err, cr2; @@ -248,13 +296,24 @@ static void dump_page_fault(z_arch_esf_t *esf) __asm__ ("mov %%cr2, %0" : "=r" (cr2)); err = esf_get_code(esf); - LOG_ERR("***** CPU Page Fault (error code " PR_UPTR ")", err); + LOG_ERR("Page fault at address 0x%lx (error code 0x%lx)", cr2, err); - LOG_ERR("%s thread %s address " PR_UPTR, - (err & US) != 0U ? "User" : "Supervisor", - (err & ID) != 0U ? "executed" : ((err & WR) != 0U ? - "wrote" : - "read"), cr2); + if ((err & RSVD) != 0) { + LOG_ERR("Reserved bits set in page tables"); + } else if ((err & PRESENT) == 0) { + LOG_ERR("Linear address not present in page tables"); + } else { + LOG_ERR("Access violation: %s thread not allowed to %s", + (err & US) != 0U ? "user" : "supervisor", + (err & ID) != 0U ? "execute" : ((err & WR) != 0U ? + "write" : + "read")); + if ((err & PK) != 0) { + LOG_ERR("Protection key disallowed"); + } else if ((err & SGX) != 0) { + LOG_ERR("SGX access control violation"); + } + } #ifdef CONFIG_X86_MMU #ifdef CONFIG_USERSPACE @@ -269,6 +328,29 @@ static void dump_page_fault(z_arch_esf_t *esf) } #endif /* CONFIG_EXCEPTION_DEBUG */ +FUNC_NORETURN void z_x86_fatal_error(unsigned int reason, + const z_arch_esf_t *esf) +{ +#ifdef CONFIG_EXCEPTION_DEBUG + if (esf != NULL) { + dump_regs(esf); + } +#endif + z_fatal_error(reason, esf); + CODE_UNREACHABLE; +} + +FUNC_NORETURN void z_x86_unhandled_cpu_exception(uintptr_t vector, + const z_arch_esf_t *esf) +{ +#ifdef CONFIG_EXCEPTION_DEBUG + log_exception(vector, esf_get_code(esf)); +#else + ARG_UNUSED(vector); +#endif + z_x86_fatal_error(K_ERR_CPU_EXCEPTION, esf); +} + #ifdef CONFIG_USERSPACE Z_EXC_DECLARE(z_x86_user_string_nlen); diff --git a/arch/x86/core/ia32/fatal.c b/arch/x86/core/ia32/fatal.c index 1bd88214f34..51fe600d5e0 100644 --- a/arch/x86/core/ia32/fatal.c +++ b/arch/x86/core/ia32/fatal.c @@ -93,21 +93,7 @@ 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) { - switch (vector) { - case IV_GENERAL_PROTECTION: - LOG_ERR("General Protection Fault"); - break; - case IV_DEVICE_NOT_AVAILABLE: - LOG_ERR("Floating point unit not enabled"); - break; - default: - LOG_ERR("CPU exception %d", vector); - break; - } - if ((BIT(vector) & _EXC_ERROR_CODE_FAULTS) != 0) { - LOG_ERR("Exception code: 0x%x", pEsf->errorCode); - } - z_x86_fatal_error(K_ERR_CPU_EXCEPTION, pEsf); + z_x86_unhandled_cpu_exception(vector, pEsf); } #define _EXC_FUNC(vector) \ diff --git a/arch/x86/core/intel64/fatal.c b/arch/x86/core/intel64/fatal.c index 01ea0f67d98..2f355e9afc0 100644 --- a/arch/x86/core/intel64/fatal.c +++ b/arch/x86/core/intel64/fatal.c @@ -17,9 +17,7 @@ void z_x86_exception(z_arch_esf_t *esf) z_x86_page_fault_handler(esf); break; default: - LOG_ERR("** CPU Exception %ld (code %ld/0x%lx) **", - esf->vector, esf->code, esf->code); - z_x86_fatal_error(K_ERR_CPU_EXCEPTION, esf); + z_x86_unhandled_cpu_exception(esf->vector, esf); CODE_UNREACHABLE; } } diff --git a/arch/x86/include/kernel_arch_data.h b/arch/x86/include/kernel_arch_data.h index 2aa189e621d..2f9d3f62ad2 100644 --- a/arch/x86/include/kernel_arch_data.h +++ b/arch/x86/include/kernel_arch_data.h @@ -31,6 +31,8 @@ #define IV_ALIGNMENT_CHECK 17 #define IV_MACHINE_CHECK 18 #define IV_SIMD_FP 19 +#define IV_VIRT_EXCEPTION 20 +#define IV_SECURITY_EXCEPTION 30 #define IV_IRQS 32 /* start of vectors available for IRQs */ #define IV_NR_VECTORS 256 /* total number of vectors */ diff --git a/arch/x86/include/kernel_arch_func.h b/arch/x86/include/kernel_arch_func.h index 7537474813f..857913b206f 100644 --- a/arch/x86/include/kernel_arch_func.h +++ b/arch/x86/include/kernel_arch_func.h @@ -47,6 +47,12 @@ void z_x86_early_serial_init(void); void z_x86_paging_init(void); #endif /* CONFIG_X86_MMU */ +/* Called upon CPU exception that is unhandled and hence fatal; dump + * interesting info and call z_x86_fatal_error() + */ +FUNC_NORETURN void z_x86_unhandled_cpu_exception(uintptr_t vector, + const z_arch_esf_t *esf); + /* Called upon unrecoverable error; dump registers and transfer control to * kernel via z_fatal_error() */ diff --git a/include/arch/x86/ia32/segmentation.h b/include/arch/x86/ia32/segmentation.h index 2fa032e7867..c5b8287a2ab 100644 --- a/include/arch/x86/ia32/segmentation.h +++ b/include/arch/x86/ia32/segmentation.h @@ -18,23 +18,6 @@ extern "C" { #endif -/* - * Bitmask used to determine which exceptions result in an error code being - * pushed onto the stack. The following exception vectors push an error code: - * - * Vector Mnemonic Description - * ------ ------- ---------------------- - * 8 #DF Double Fault - * 10 #TS Invalid TSS - * 11 #NP Segment Not Present - * 12 #SS Stack Segment Fault - * 13 #GP General Protection Fault - * 14 #PF Page Fault - * 17 #AC Alignment Check - */ -#define _EXC_ERROR_CODE_FAULTS 0x27d00U - - /* NOTE: We currently do not have definitions for 16-bit segment, currently * assume everything we are working with is 32-bit */