232 lines
6.2 KiB
C
232 lines
6.2 KiB
C
/*
|
|
* Copyright (c) 2013-2014 Wind River Systems, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* @brief Kernel fatal error handler
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <kernel_internal.h>
|
|
#include <zephyr/drivers/interrupt_controller/sysapic.h>
|
|
#include <zephyr/arch/x86/ia32/segmentation.h>
|
|
#include <zephyr/arch/syscall.h>
|
|
#include <ia32/exception.h>
|
|
#include <inttypes.h>
|
|
#include <zephyr/arch/common/exc_handle.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <x86_mmu.h>
|
|
#include <zephyr/kernel/mm.h>
|
|
|
|
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
|
|
|
#ifdef CONFIG_DEBUG_COREDUMP
|
|
unsigned int z_x86_exception_vector;
|
|
#endif
|
|
|
|
__weak void z_debug_fatal_hook(const struct arch_esf *esf) { ARG_UNUSED(esf); }
|
|
|
|
__pinned_func
|
|
void z_x86_spurious_irq(const struct arch_esf *esf)
|
|
{
|
|
int vector = z_irq_controller_isr_vector_get();
|
|
|
|
if (vector >= 0) {
|
|
LOG_ERR("IRQ vector: %d", vector);
|
|
}
|
|
|
|
z_x86_fatal_error(K_ERR_SPURIOUS_IRQ, esf);
|
|
}
|
|
|
|
__pinned_func
|
|
void arch_syscall_oops(void *ssf)
|
|
{
|
|
struct _x86_syscall_stack_frame *ssf_ptr =
|
|
(struct _x86_syscall_stack_frame *)ssf;
|
|
struct arch_esf oops = {
|
|
.eip = ssf_ptr->eip,
|
|
.cs = ssf_ptr->cs,
|
|
.eflags = ssf_ptr->eflags
|
|
};
|
|
|
|
if (oops.cs == USER_CODE_SEG) {
|
|
oops.esp = ssf_ptr->esp;
|
|
}
|
|
|
|
z_x86_fatal_error(K_ERR_KERNEL_OOPS, &oops);
|
|
}
|
|
|
|
extern void (*_kernel_oops_handler)(void);
|
|
NANO_CPU_INT_REGISTER(_kernel_oops_handler, NANO_SOFT_IRQ,
|
|
Z_X86_OOPS_VECTOR / 16, Z_X86_OOPS_VECTOR, 3);
|
|
|
|
#if CONFIG_EXCEPTION_DEBUG
|
|
__pinned_func
|
|
FUNC_NORETURN static void generic_exc_handle(unsigned int vector,
|
|
const struct arch_esf *pEsf)
|
|
{
|
|
#ifdef CONFIG_DEBUG_COREDUMP
|
|
z_x86_exception_vector = vector;
|
|
#endif
|
|
|
|
z_x86_unhandled_cpu_exception(vector, pEsf);
|
|
}
|
|
|
|
#define _EXC_FUNC(vector) \
|
|
__pinned_func \
|
|
FUNC_NORETURN __used static void handle_exc_##vector(const struct arch_esf *pEsf) \
|
|
{ \
|
|
generic_exc_handle(vector, pEsf); \
|
|
}
|
|
|
|
#define Z_EXC_FUNC_CODE(vector, dpl) \
|
|
_EXC_FUNC(vector) \
|
|
_EXCEPTION_CONNECT_CODE(handle_exc_##vector, vector, dpl)
|
|
|
|
#define Z_EXC_FUNC_NOCODE(vector, dpl) \
|
|
_EXC_FUNC(vector) \
|
|
_EXCEPTION_CONNECT_NOCODE(handle_exc_##vector, vector, dpl)
|
|
|
|
/* Necessary indirection to ensure 'vector' is expanded before we expand
|
|
* the handle_exc_##vector
|
|
*/
|
|
#define EXC_FUNC_NOCODE(vector, dpl) \
|
|
Z_EXC_FUNC_NOCODE(vector, dpl)
|
|
|
|
#define EXC_FUNC_CODE(vector, dpl) \
|
|
Z_EXC_FUNC_CODE(vector, dpl)
|
|
|
|
EXC_FUNC_NOCODE(IV_DIVIDE_ERROR, 0);
|
|
EXC_FUNC_NOCODE(IV_NON_MASKABLE_INTERRUPT, 0);
|
|
EXC_FUNC_NOCODE(IV_OVERFLOW, 0);
|
|
EXC_FUNC_NOCODE(IV_BOUND_RANGE, 0);
|
|
EXC_FUNC_NOCODE(IV_INVALID_OPCODE, 0);
|
|
EXC_FUNC_NOCODE(IV_DEVICE_NOT_AVAILABLE, 0);
|
|
#ifndef CONFIG_X86_ENABLE_TSS
|
|
EXC_FUNC_NOCODE(IV_DOUBLE_FAULT, 0);
|
|
#endif
|
|
EXC_FUNC_CODE(IV_INVALID_TSS, 0);
|
|
EXC_FUNC_CODE(IV_SEGMENT_NOT_PRESENT, 0);
|
|
EXC_FUNC_CODE(IV_STACK_FAULT, 0);
|
|
EXC_FUNC_CODE(IV_GENERAL_PROTECTION, 0);
|
|
EXC_FUNC_NOCODE(IV_X87_FPU_FP_ERROR, 0);
|
|
EXC_FUNC_CODE(IV_ALIGNMENT_CHECK, 0);
|
|
EXC_FUNC_NOCODE(IV_MACHINE_CHECK, 0);
|
|
#endif
|
|
|
|
_EXCEPTION_CONNECT_CODE(z_x86_page_fault_handler, IV_PAGE_FAULT, 0);
|
|
|
|
#ifdef CONFIG_X86_ENABLE_TSS
|
|
static __pinned_noinit volatile struct arch_esf _df_esf;
|
|
|
|
/* Very tiny stack; just enough for the bogus error code pushed by the CPU
|
|
* and a frame pointer push by the compiler. All df_handler_top does is
|
|
* shuffle some data around with 'mov' statements and then 'iret'.
|
|
*/
|
|
static __pinned_noinit char _df_stack[8];
|
|
|
|
static FUNC_NORETURN __used void df_handler_top(void);
|
|
|
|
#ifdef CONFIG_X86_KPTI
|
|
extern char z_trampoline_stack_end[];
|
|
#endif
|
|
|
|
Z_GENERIC_SECTION(.tss)
|
|
struct task_state_segment _main_tss = {
|
|
.ss0 = DATA_SEG,
|
|
#ifdef CONFIG_X86_KPTI
|
|
/* Stack to land on when we get a soft/hard IRQ in user mode.
|
|
* In a special kernel page that, unlike all other kernel pages,
|
|
* is marked present in the user page table.
|
|
*/
|
|
.esp0 = (uint32_t)&z_trampoline_stack_end
|
|
#endif
|
|
};
|
|
|
|
/* Special TSS for handling double-faults with a known good stack */
|
|
Z_GENERIC_SECTION(.tss)
|
|
struct task_state_segment _df_tss = {
|
|
.esp = (uint32_t)(_df_stack + sizeof(_df_stack)),
|
|
.cs = CODE_SEG,
|
|
.ds = DATA_SEG,
|
|
.es = DATA_SEG,
|
|
.ss = DATA_SEG,
|
|
.eip = (uint32_t)df_handler_top,
|
|
.cr3 = (uint32_t)
|
|
K_MEM_PHYS_ADDR(POINTER_TO_UINT(&z_x86_kernel_ptables[0]))
|
|
};
|
|
|
|
__pinned_func
|
|
static __used void df_handler_bottom(void)
|
|
{
|
|
/* We're back in the main hardware task on the interrupt stack */
|
|
unsigned int reason = K_ERR_CPU_EXCEPTION;
|
|
|
|
/* Restore the top half so it is runnable again */
|
|
_df_tss.esp = (uint32_t)(_df_stack + sizeof(_df_stack));
|
|
_df_tss.eip = (uint32_t)df_handler_top;
|
|
|
|
LOG_ERR("Double Fault");
|
|
#ifdef CONFIG_THREAD_STACK_INFO
|
|
/* To comply with MISRA 13.2 rule necessary to exclude code that depends
|
|
* on the order of evaluation of function arguments.
|
|
* Create 2 variables to store volatile data from the structure _df_esf
|
|
*/
|
|
uint32_t df_esf_esp = _df_esf.esp;
|
|
uint16_t df_esf_cs = _df_esf.cs;
|
|
|
|
if (z_x86_check_stack_bounds(df_esf_esp, 0, df_esf_cs)) {
|
|
reason = K_ERR_STACK_CHK_FAIL;
|
|
}
|
|
#endif
|
|
z_x86_fatal_error(reason, (struct arch_esf *)&_df_esf);
|
|
}
|
|
|
|
__pinned_func
|
|
static FUNC_NORETURN __used void df_handler_top(void)
|
|
{
|
|
/* State of the system when the double-fault forced a task switch
|
|
* will be in _main_tss. Set up a struct arch_esf and copy system state into
|
|
* it
|
|
*/
|
|
_df_esf.esp = _main_tss.esp;
|
|
_df_esf.ebp = _main_tss.ebp;
|
|
_df_esf.ebx = _main_tss.ebx;
|
|
_df_esf.esi = _main_tss.esi;
|
|
_df_esf.edi = _main_tss.edi;
|
|
_df_esf.edx = _main_tss.edx;
|
|
_df_esf.eax = _main_tss.eax;
|
|
_df_esf.ecx = _main_tss.ecx;
|
|
_df_esf.errorCode = 0;
|
|
_df_esf.eip = _main_tss.eip;
|
|
_df_esf.cs = _main_tss.cs;
|
|
_df_esf.eflags = _main_tss.eflags;
|
|
|
|
/* Restore the main IA task to a runnable state */
|
|
_main_tss.esp = (uint32_t)(K_KERNEL_STACK_BUFFER(
|
|
z_interrupt_stacks[0]) + CONFIG_ISR_STACK_SIZE);
|
|
_main_tss.cs = CODE_SEG;
|
|
_main_tss.ds = DATA_SEG;
|
|
_main_tss.es = DATA_SEG;
|
|
_main_tss.ss = DATA_SEG;
|
|
_main_tss.eip = (uint32_t)df_handler_bottom;
|
|
_main_tss.cr3 = k_mem_phys_addr(z_x86_kernel_ptables);
|
|
_main_tss.eflags = 0U;
|
|
|
|
/* NT bit is set in EFLAGS so we will task switch back to _main_tss
|
|
* and run df_handler_bottom
|
|
*/
|
|
__asm__ volatile ("iret");
|
|
CODE_UNREACHABLE;
|
|
}
|
|
|
|
/* Configure a task gate descriptor in the IDT for the double fault
|
|
* exception
|
|
*/
|
|
_X86_IDT_TSS_REGISTER(DF_TSS, -1, -1, IV_DOUBLE_FAULT, 0);
|
|
|
|
#endif /* CONFIG_X86_ENABLE_TSS */
|