blob: 2769af002deac129ae800c0dac1a4f24187f6034 [file] [log] [blame]
/*
* 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 <exc_handle.h>
#include <logging/log.h>
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
return esf->rsp;
#else
return esf->esp;
#endif
}
static inline uintptr_t esf_get_code(const z_arch_esf_t *esf)
{
#ifdef CONFIG_X86_64
return esf->code;
#else
return esf->errorCode;
#endif
}
#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 (arch_is_in_isr()) {
/* We were servicing an interrupt */
start = (uintptr_t)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;
}
/*
* 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)
{
if ((flags & Z_X86_MMU_P) == 0) {
LOG_ERR("%s: Non-present", name);
return false;
} else {
LOG_ERR("%s: 0x%016llx %s, %s, %s", name, flags,
flags & MMU_ENTRY_WRITE ?
"Writable" : "Read-only",
flags & MMU_ENTRY_USER ?
"User" : "Supervisor",
flags & MMU_ENTRY_EXECUTE_DISABLE ?
"Execute Disable" : "Execute Enabled");
return true;
}
}
static void dump_mmu_flags(struct x86_page_tables *ptables, uintptr_t addr)
{
u64_t entry;
#ifdef CONFIG_X86_64
entry = *z_x86_get_pml4e(ptables, addr);
if (!dump_entry_flags("PML4E", entry)) {
return;
}
entry = *z_x86_pdpt_get_pdpte(z_x86_pml4e_get_pdpt(entry), addr);
if (!dump_entry_flags("PDPTE", entry)) {
return;
}
#else
/* 32-bit doesn't have anything interesting in the PDPTE except
* the present bit
*/
entry = *z_x86_get_pdpte(ptables, addr);
if ((entry & Z_X86_MMU_P) == 0) {
LOG_ERR("PDPTE: Non-present");
return;
}
#endif
entry = *z_x86_pd_get_pde(z_x86_pdpte_get_pd(entry), addr);
if (!dump_entry_flags(" PDE", entry)) {
return;
}
entry = *z_x86_pt_get_pte(z_x86_pde_get_pt(entry), addr);
if (!dump_entry_flags(" PTE", entry)) {
return;
}
}
#endif /* CONFIG_X86_MMU */
static void dump_page_fault(z_arch_esf_t *esf)
{
uintptr_t err, cr2;
/* See Section 6.15 of the IA32 Software Developer's Manual vol 3 */
__asm__ ("mov %%cr2, %0" : "=r" (cr2));
err = esf_get_code(esf);
LOG_ERR("***** CPU Page Fault (error code " PR_UPTR ")", err);
LOG_ERR("%s thread %s address " PR_UPTR,
(err & US) != 0U ? "User" : "Supervisor",
(err & ID) != 0U ? "executed" : ((err & WR) != 0U ?
"wrote" :
"read"), cr2);
#ifdef CONFIG_X86_MMU
#ifdef CONFIG_USERSPACE
if (err & US) {
dump_mmu_flags(z_x86_thread_page_tables_get(_current), cr2);
} else
#endif /* CONFIG_USERSPACE */
{
dump_mmu_flags(&z_x86_kernel_ptables, cr2);
}
#endif /* CONFIG_X86_MMU */
}
#endif /* CONFIG_EXCEPTION_DEBUG */
#ifdef CONFIG_USERSPACE
Z_EXC_DECLARE(z_x86_user_string_nlen);
static const struct z_exc_handle exceptions[] = {
Z_EXC_HANDLE(z_x86_user_string_nlen)
};
#endif
void z_x86_page_fault_handler(z_arch_esf_t *esf)
{
#ifdef CONFIG_USERSPACE
int i;
for (i = 0; i < ARRAY_SIZE(exceptions); i++) {
if ((void *)esf->eip >= exceptions[i].start &&
(void *)esf->eip < exceptions[i].end) {
esf->eip = (unsigned int)(exceptions[i].fixup);
return;
}
}
#endif
#ifdef CONFIG_EXCEPTION_DEBUG
dump_page_fault(esf);
#endif
#ifdef CONFIG_THREAD_STACK_INFO
if (z_x86_check_stack_bounds(esf_get_sp(esf), 0, esf->cs)) {
z_x86_fatal_error(K_ERR_STACK_CHK_FAIL, esf);
}
#endif
z_x86_fatal_error(K_ERR_CPU_EXCEPTION, esf);
CODE_UNREACHABLE;
}