From 4c6e7a4bcaa50029db4129b12739f6e53a97a15d Mon Sep 17 00:00:00 2001 From: David Karoly Date: Thu, 24 Feb 2022 20:41:57 +0100 Subject: [PATCH] kernel/arm: implement prefetch abort handler Change-Id: I39c330a44ad36ede41c7727017dd733d07288be9 Reviewed-on: https://review.haiku-os.org/c/haiku/+/4983 Tested-by: Commit checker robot Reviewed-by: Fredrik Holmqvist --- .../private/system/arch/arm/arch_cpu_defs.h | 3 + src/system/kernel/arch/arm/arch_exceptions.S | 2 +- src/system/kernel/arch/arm/arch_int.cpp | 78 +++++++++++++++++-- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/headers/private/system/arch/arm/arch_cpu_defs.h b/headers/private/system/arch/arm/arch_cpu_defs.h index c4c6d74e9c..bc07e0b32f 100644 --- a/headers/private/system/arch/arm/arch_cpu_defs.h +++ b/headers/private/system/arch/arm/arch_cpu_defs.h @@ -11,6 +11,9 @@ #define SPINLOCK_PAUSE() do {} while (false) +#define CPSR_MODE_MASK 0x1f +#define CPSR_MODE_USR 0x10 + #define FSR_WNR 0x800 #endif /* _SYSTEM_ARCH_ARM_DEFS_H */ diff --git a/src/system/kernel/arch/arm/arch_exceptions.S b/src/system/kernel/arch/arm/arch_exceptions.S index a7a792dc3e..a3c4edb3f7 100644 --- a/src/system/kernel/arch/arm/arch_exceptions.S +++ b/src/system/kernel/arch/arm/arch_exceptions.S @@ -175,7 +175,7 @@ FUNCTION(arm_prefetch_abort): nop /* Make absolutely sure any pending */ nop /* imprecise aborts have occurred. */ #endif - add lr, lr, #4 + sub lr, lr, #4 PUSHFRAMEINSVC mov r0, sp diff --git a/src/system/kernel/arch/arm/arch_int.cpp b/src/system/kernel/arch/arm/arch_int.cpp index 1fde42b741..e8f3d1a6bd 100644 --- a/src/system/kernel/arch/arm/arch_int.cpp +++ b/src/system/kernel/arch/arm/arch_int.cpp @@ -279,7 +279,7 @@ extern "C" void arch_arm_data_abort(struct iframe *frame) { Thread *thread = thread_get_current_thread(); - bool isUser = (frame->spsr & 0x1f) == 0x10; + bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR; int32 fsr = arm_get_fsr(); addr_t far = arm_get_far(); bool isWrite = (fsr & FSR_WNR) == FSR_WNR; @@ -365,12 +365,80 @@ arch_arm_data_abort(struct iframe *frame) extern "C" void -arch_arm_prefetch_abort(struct iframe *iframe) +arch_arm_prefetch_abort(struct iframe *frame) { - print_iframe("Prefetch Abort", iframe); - IFrameScope scope(iframe); + Thread *thread = thread_get_current_thread(); + bool isUser = (frame->spsr & CPSR_MODE_MASK) == CPSR_MODE_USR; + addr_t newip = 0; - panic("not handled!"); +#ifdef TRACE_ARCH_INT + print_iframe("Prefetch Abort", frame); + dprintf("thread: %s\n", thread->name); +#endif + + IFrameScope scope(frame); + + if (debug_debugger_running()) { + // If this CPU or this thread has a fault handler, we're allowed to be + // here. + if (thread != NULL) { + cpu_ent* cpu = &gCPU[smp_get_current_cpu()]; + + if (cpu->fault_handler != 0) { + debug_set_page_fault_info(frame->pc, frame->pc, 0); + frame->svc_sp = cpu->fault_handler_stack_pointer; + frame->pc = cpu->fault_handler; + return; + } + + if (thread->fault_handler != 0) { + kprintf("ERROR: thread::fault_handler used in kernel " + "debugger!\n"); + debug_set_page_fault_info(frame->pc, frame->pc, 0); + frame->pc = reinterpret_cast(thread->fault_handler); + return; + } + } + + // otherwise, not really + panic("page fault in debugger without fault handler! Prefetch abort at %p\n", + (void *)frame->pc); + return; + } else if ((frame->spsr & (1 << 7)) != 0) { + // interrupts disabled + + // If a page fault handler is installed, we're allowed to be here. + uintptr_t handler = reinterpret_cast(thread->fault_handler); + if (thread && thread->fault_handler != 0) { + if (frame->pc != handler) { + frame->pc = handler; + return; + } + + // The fault happened at the fault handler address. This is a + // certain infinite loop. + panic("page fault, interrupts disabled, fault handler loop. " + "Prefetch abort at %p\n", (void*)frame->pc); + } + + // If we are not running the kernel startup the page fault was not + // allowed to happen and we must panic. + panic("page fault, but interrupts were disabled. Prefetch abort at %p\n", + (void *)frame->pc); + return; + } else if (thread != NULL && thread->page_faults_allowed < 1) { + panic("page fault not allowed at this place. Prefetch abort at %p\n", + (void *)frame->pc); + return; + } + + enable_interrupts(); + + vm_page_fault(frame->pc, frame->pc, false, true, isUser, &newip); + + if (newip != 0) { + frame->pc = newip; + } }