Moved the 32-bit page fault handler to arch_int.cpp, use it for x86_64.
A proper page fault handler was required for areas that were not locked into the kernel address space. This enables the boot process to get up to the point of trying to find the boot volume.
This commit is contained in:
@@ -405,8 +405,9 @@ void x86_set_mtrrs(uint8 defaultType, const x86_mtrr_info* infos,
|
||||
void x86_init_fpu();
|
||||
bool x86_check_feature(uint32 feature, enum x86_feature_type type);
|
||||
void* x86_get_double_fault_stack(int32 cpu, size_t* _size);
|
||||
void x86_double_fault_exception(struct iframe* frame);
|
||||
void x86_page_fault_exception_double_fault(struct iframe* frame);
|
||||
int32 x86_double_fault_get_cpu(void);
|
||||
|
||||
void x86_page_fault_exception(struct iframe* iframe);
|
||||
|
||||
#ifndef __x86_64__
|
||||
|
||||
@@ -414,7 +415,8 @@ void x86_fnsave(void* fpuState);
|
||||
void x86_frstor(const void* fpuState);
|
||||
void x86_fnsave_swap(void* oldFpuState, const void* newFpuState);
|
||||
void x86_set_task_gate(int32 cpu, int32 n, int32 segment);
|
||||
int32 x86_double_fault_get_cpu(void);
|
||||
void x86_double_fault_exception(struct iframe* frame);
|
||||
void x86_page_fault_exception_double_fault(struct iframe* frame);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -346,87 +346,6 @@ x86_page_fault_exception_double_fault(struct iframe* frame)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
page_fault_exception(struct iframe* frame)
|
||||
{
|
||||
Thread *thread = thread_get_current_thread();
|
||||
addr_t cr2 = x86_read_cr2();
|
||||
addr_t newip;
|
||||
|
||||
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(cr2, frame->ip,
|
||||
(frame->error_code & 0x2) != 0
|
||||
? DEBUG_PAGE_FAULT_WRITE : 0);
|
||||
frame->ip = cpu->fault_handler;
|
||||
frame->bp = cpu->fault_handler_stack_pointer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread->fault_handler != 0) {
|
||||
kprintf("ERROR: thread::fault_handler used in kernel "
|
||||
"debugger!\n");
|
||||
debug_set_page_fault_info(cr2, frame->ip,
|
||||
(frame->error_code & 0x2) != 0
|
||||
? DEBUG_PAGE_FAULT_WRITE : 0);
|
||||
frame->ip = thread->fault_handler;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, not really
|
||||
panic("page fault in debugger without fault handler! Touching "
|
||||
"address %p from eip %p\n", (void *)cr2, (void *)frame->ip);
|
||||
return;
|
||||
} else if ((frame->flags & 0x200) == 0) {
|
||||
// interrupts disabled
|
||||
|
||||
// If a page fault handler is installed, we're allowed to be here.
|
||||
// TODO: Now we are generally allowing user_memcpy() with interrupts
|
||||
// disabled, which in most cases is a bug. We should add some thread
|
||||
// flag allowing to explicitly indicate that this handling is desired.
|
||||
if (thread && thread->fault_handler != 0) {
|
||||
if (frame->ip != thread->fault_handler) {
|
||||
frame->ip = thread->fault_handler;
|
||||
return;
|
||||
}
|
||||
|
||||
// The fault happened at the fault handler address. This is a
|
||||
// certain infinite loop.
|
||||
panic("page fault, interrupts disabled, fault handler loop. "
|
||||
"Touching address %p from eip %p\n", (void*)cr2,
|
||||
(void*)frame->ip);
|
||||
}
|
||||
|
||||
// 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. Touching address "
|
||||
"%p from eip %p\n", (void *)cr2, (void *)frame->ip);
|
||||
return;
|
||||
} else if (thread != NULL && thread->page_faults_allowed < 1) {
|
||||
panic("page fault not allowed at this place. Touching address "
|
||||
"%p from eip %p\n", (void *)cr2, (void *)frame->ip);
|
||||
return;
|
||||
}
|
||||
|
||||
enable_interrupts();
|
||||
|
||||
vm_page_fault(cr2, frame->ip,
|
||||
(frame->error_code & 0x2) != 0, // write access
|
||||
(frame->error_code & 0x4) != 0, // userland
|
||||
&newip);
|
||||
if (newip != 0) {
|
||||
// the page fault handler wants us to modify the iframe to set the
|
||||
// IP the cpu will return to to be this ip
|
||||
frame->ip = newip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
arch_int_init(struct kernel_args *args)
|
||||
{
|
||||
@@ -720,7 +639,7 @@ arch_int_init(struct kernel_args *args)
|
||||
table[11] = fatal_exception; // Segment Not Present (#NP)
|
||||
table[12] = fatal_exception; // Stack Fault Exception (#SS)
|
||||
table[13] = unexpected_exception; // General Protection Exception (#GP)
|
||||
table[14] = page_fault_exception; // Page-Fault Exception (#PF)
|
||||
table[14] = x86_page_fault_exception; // Page-Fault Exception (#PF)
|
||||
table[16] = unexpected_exception; // x87 FPU Floating-Point Error (#MF)
|
||||
table[17] = unexpected_exception; // Alignment Check Exception (#AC)
|
||||
table[18] = fatal_exception; // Machine-Check Exception (#MC)
|
||||
|
||||
@@ -96,16 +96,6 @@ unexpected_exception(iframe* frame)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
page_fault_exception(iframe* frame)
|
||||
{
|
||||
addr_t cr2 = x86_read_cr2();
|
||||
|
||||
panic("page fault exception at ip %#lx on %#lx, error code %#lx\n",
|
||||
frame->ip, cr2, frame->error_code);
|
||||
}
|
||||
|
||||
|
||||
/*! Returns the virtual IDT address for CPU \a cpu. */
|
||||
void*
|
||||
x86_get_idt(int32 cpu)
|
||||
@@ -164,7 +154,7 @@ arch_int_init(kernel_args* args)
|
||||
table[11] = fatal_exception; // Segment Not Present (#NP)
|
||||
table[12] = fatal_exception; // Stack Fault Exception (#SS)
|
||||
table[13] = unexpected_exception; // General Protection Exception (#GP)
|
||||
table[14] = page_fault_exception; // Page-Fault Exception (#PF)
|
||||
table[14] = x86_page_fault_exception; // Page-Fault Exception (#PF)
|
||||
table[16] = unexpected_exception; // x87 FPU Floating-Point Error (#MF)
|
||||
table[17] = unexpected_exception; // Alignment Check Exception (#AC)
|
||||
table[18] = fatal_exception; // Machine-Check Exception (#MC)
|
||||
|
||||
@@ -697,7 +697,6 @@ x86_get_double_fault_stack(int32 cpu, size_t* _size)
|
||||
}
|
||||
|
||||
|
||||
#ifndef __x86_64__
|
||||
/*! Returns the index of the current CPU. Can only be called from the double
|
||||
fault handler.
|
||||
*/
|
||||
@@ -705,9 +704,8 @@ int32
|
||||
x86_double_fault_get_cpu(void)
|
||||
{
|
||||
uint32 stack = x86_get_stack_frame();
|
||||
return (stack - (uint32)sDoubleFaultStacks) / kDoubleFaultStackSize;
|
||||
return (stack - (addr_t)sDoubleFaultStacks) / kDoubleFaultStackSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <team.h>
|
||||
#include <thread.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_priv.h>
|
||||
|
||||
#include <arch/cpu.h>
|
||||
#include <arch/int.h>
|
||||
@@ -90,6 +92,87 @@ hardware_interrupt(struct iframe* frame)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
x86_page_fault_exception(struct iframe* frame)
|
||||
{
|
||||
Thread* thread = thread_get_current_thread();
|
||||
addr_t cr2 = x86_read_cr2();
|
||||
addr_t newip;
|
||||
|
||||
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(cr2, frame->ip,
|
||||
(frame->error_code & 0x2) != 0
|
||||
? DEBUG_PAGE_FAULT_WRITE : 0);
|
||||
frame->ip = cpu->fault_handler;
|
||||
frame->bp = cpu->fault_handler_stack_pointer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread->fault_handler != 0) {
|
||||
kprintf("ERROR: thread::fault_handler used in kernel "
|
||||
"debugger!\n");
|
||||
debug_set_page_fault_info(cr2, frame->ip,
|
||||
(frame->error_code & 0x2) != 0
|
||||
? DEBUG_PAGE_FAULT_WRITE : 0);
|
||||
frame->ip = thread->fault_handler;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, not really
|
||||
panic("page fault in debugger without fault handler! Touching "
|
||||
"address %p from ip %p\n", (void*)cr2, (void*)frame->ip);
|
||||
return;
|
||||
} else if ((frame->flags & 0x200) == 0) {
|
||||
// interrupts disabled
|
||||
|
||||
// If a page fault handler is installed, we're allowed to be here.
|
||||
// TODO: Now we are generally allowing user_memcpy() with interrupts
|
||||
// disabled, which in most cases is a bug. We should add some thread
|
||||
// flag allowing to explicitly indicate that this handling is desired.
|
||||
if (thread && thread->fault_handler != 0) {
|
||||
if (frame->ip != thread->fault_handler) {
|
||||
frame->ip = thread->fault_handler;
|
||||
return;
|
||||
}
|
||||
|
||||
// The fault happened at the fault handler address. This is a
|
||||
// certain infinite loop.
|
||||
panic("page fault, interrupts disabled, fault handler loop. "
|
||||
"Touching address %p from ip %p\n", (void*)cr2,
|
||||
(void*)frame->ip);
|
||||
}
|
||||
|
||||
// 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. Touching address "
|
||||
"%p from ip %p\n", (void*)cr2, (void*)frame->ip);
|
||||
return;
|
||||
} else if (thread != NULL && thread->page_faults_allowed < 1) {
|
||||
panic("page fault not allowed at this place. Touching address "
|
||||
"%p from ip %p\n", (void*)cr2, (void*)frame->ip);
|
||||
return;
|
||||
}
|
||||
|
||||
enable_interrupts();
|
||||
|
||||
vm_page_fault(cr2, frame->ip,
|
||||
(frame->error_code & 0x2) != 0, // write access
|
||||
(frame->error_code & 0x4) != 0, // userland
|
||||
&newip);
|
||||
if (newip != 0) {
|
||||
// the page fault handler wants us to modify the iframe to set the
|
||||
// IP the cpu will return to this ip
|
||||
frame->ip = newip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user