mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 04:02:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			897 lines
		
	
	
	
		
			45 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			897 lines
		
	
	
	
		
			45 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <AK/Format.h>
 | |
| #include <AK/RefPtr.h>
 | |
| #include <AK/Types.h>
 | |
| 
 | |
| #include <Kernel/Arch/Interrupts.h>
 | |
| #include <Kernel/Arch/x86/common/Interrupts/PIC.h>
 | |
| #include <Kernel/Interrupts/GenericInterruptHandler.h>
 | |
| #include <Kernel/Interrupts/SharedIRQHandler.h>
 | |
| #include <Kernel/Interrupts/SpuriousInterruptHandler.h>
 | |
| #include <Kernel/Interrupts/UnhandledInterruptHandler.h>
 | |
| #include <Kernel/Panic.h>
 | |
| #include <Kernel/PerformanceManager.h>
 | |
| #include <Kernel/Process.h>
 | |
| #include <Kernel/Random.h>
 | |
| #include <Kernel/Scheduler.h>
 | |
| #include <Kernel/Sections.h>
 | |
| #include <Kernel/Thread.h>
 | |
| #include <Kernel/ThreadTracer.h>
 | |
| 
 | |
| #include <LibC/mallocdefs.h>
 | |
| 
 | |
| #include <Kernel/Arch/CPU.h>
 | |
| #include <Kernel/Arch/PageFault.h>
 | |
| #include <Kernel/Arch/Processor.h>
 | |
| #include <Kernel/Arch/RegisterState.h>
 | |
| #include <Kernel/Arch/SafeMem.h>
 | |
| #include <Kernel/Arch/TrapFrame.h>
 | |
| #include <Kernel/Arch/x86/ISRStubs.h>
 | |
| 
 | |
| extern FlatPtr start_of_unmap_after_init;
 | |
| extern FlatPtr end_of_unmap_after_init;
 | |
| extern FlatPtr start_of_ro_after_init;
 | |
| extern FlatPtr end_of_ro_after_init;
 | |
| extern FlatPtr start_of_kernel_ksyms;
 | |
| extern FlatPtr end_of_kernel_ksyms;
 | |
| 
 | |
| namespace Kernel {
 | |
| 
 | |
| READONLY_AFTER_INIT static DescriptorTablePointer s_idtr;
 | |
| READONLY_AFTER_INIT static IDTEntry s_idt[256];
 | |
| 
 | |
| static GenericInterruptHandler* s_interrupt_handler[GENERIC_INTERRUPT_HANDLERS_COUNT];
 | |
| static GenericInterruptHandler* s_disabled_interrupt_handler[2];
 | |
| 
 | |
| static EntropySource s_entropy_source_interrupts { EntropySource::Static::Interrupts };
 | |
| 
 | |
| // clang-format off
 | |
| 
 | |
| #if ARCH(I386)
 | |
| #define EH_ENTRY(ec, title)                                                    \
 | |
|     extern "C" void title##_asm_entry();                                       \
 | |
|     extern "C" void title##_handler(TrapFrame*) __attribute__((used));         \
 | |
|     NAKED void title##_asm_entry() {                                           \
 | |
|         asm(                                                                   \
 | |
|             "    pusha\n"                                                      \
 | |
|             "    pushl %ds\n"                                                  \
 | |
|             "    pushl %es\n"                                                  \
 | |
|             "    pushl %fs\n"                                                  \
 | |
|             "    pushl %gs\n"                                                  \
 | |
|             "    pushl %ss\n"                                                  \
 | |
|             "    mov $" __STRINGIFY(GDT_SELECTOR_DATA0) ", %ax\n"              \
 | |
|             "    mov %ax, %ds\n"                                               \
 | |
|             "    mov %ax, %es\n"                                               \
 | |
|             "    mov $" __STRINGIFY(GDT_SELECTOR_PROC) ", %ax\n"               \
 | |
|             "    mov %ax, %gs\n"                                               \
 | |
|             "    pushl %esp \n" /* set TrapFrame::regs */                      \
 | |
|             "    subl $" __STRINGIFY(TRAP_FRAME_SIZE - 4) ", %esp \n"          \
 | |
|             "    pushl %esp \n"                                                \
 | |
|             "    cld\n"                                                        \
 | |
|             "    call enter_trap_no_irq \n"                                    \
 | |
|             "    call " #title "_handler\n"                                    \
 | |
|             "    jmp common_trap_exit \n"                                      \
 | |
|         );                                                                     \
 | |
|     }
 | |
| 
 | |
| #define EH_ENTRY_NO_CODE(ec, title)                                            \
 | |
|     extern "C" void title##_asm_entry();                                       \
 | |
|     extern "C" void title##_handler(TrapFrame*) __attribute__((used));         \
 | |
|     NAKED void title##_asm_entry() {                                           \
 | |
|         asm(                                                                   \
 | |
|             "    pushl $0x0\n"                                                 \
 | |
|             "    pusha\n"                                                      \
 | |
|             "    pushl %ds\n"                                                  \
 | |
|             "    pushl %es\n"                                                  \
 | |
|             "    pushl %fs\n"                                                  \
 | |
|             "    pushl %gs\n"                                                  \
 | |
|             "    pushl %ss\n"                                                  \
 | |
|             "    mov $" __STRINGIFY(GDT_SELECTOR_DATA0) ", %ax\n"              \
 | |
|             "    mov %ax, %ds\n"                                               \
 | |
|             "    mov %ax, %es\n"                                               \
 | |
|             "    mov $" __STRINGIFY(GDT_SELECTOR_PROC) ", %ax\n"               \
 | |
|             "    mov %ax, %gs\n"                                               \
 | |
|             "    pushl %esp \n" /* set TrapFrame::regs */                      \
 | |
|             "    subl $" __STRINGIFY(TRAP_FRAME_SIZE - 4) ", %esp \n"          \
 | |
|             "    pushl %esp \n"                                                \
 | |
|             "    cld\n"                                                        \
 | |
|             "    call enter_trap_no_irq \n"                                    \
 | |
|             "    call " #title "_handler\n"                                    \
 | |
|             "    jmp common_trap_exit \n"                                      \
 | |
|         );                                                                     \
 | |
|     }
 | |
| 
 | |
| #elif ARCH(X86_64)
 | |
| #define EH_ENTRY(ec, title)                                                    \
 | |
|     extern "C" void title##_asm_entry();                                       \
 | |
|     extern "C" void title##_handler(TrapFrame*) __attribute__((used));         \
 | |
|     NAKED void title##_asm_entry() {                                           \
 | |
|         asm(                                                                   \
 | |
|             "    pushq %r15\n"                                                 \
 | |
|             "    pushq %r14\n"                                                 \
 | |
|             "    pushq %r13\n"                                                 \
 | |
|             "    pushq %r12\n"                                                 \
 | |
|             "    pushq %r11\n"                                                 \
 | |
|             "    pushq %r10\n"                                                 \
 | |
|             "    pushq %r9\n"                                                  \
 | |
|             "    pushq %r8\n"                                                  \
 | |
|             "    pushq %rax\n"                                                 \
 | |
|             "    pushq %rcx\n"                                                 \
 | |
|             "    pushq %rdx\n"                                                 \
 | |
|             "    pushq %rbx\n"                                                 \
 | |
|             "    pushq %rsp\n"                                                 \
 | |
|             "    pushq %rbp\n"                                                 \
 | |
|             "    pushq %rsi\n"                                                 \
 | |
|             "    pushq %rdi\n"                                                 \
 | |
|             "    pushq %rsp \n" /* set TrapFrame::regs */                      \
 | |
|             "    subq $" __STRINGIFY(TRAP_FRAME_SIZE - 8) ", %rsp \n"          \
 | |
|             "    subq $0x8, %rsp\n" /* align stack */                          \
 | |
|             "    lea 0x8(%rsp), %rdi \n"                                       \
 | |
|             "    cld\n"                                                        \
 | |
|             "    call enter_trap_no_irq \n"                                    \
 | |
|             "    lea 0x8(%rsp), %rdi \n"                                       \
 | |
|             "    call " #title "_handler\n"                                    \
 | |
|             "    addq $0x8, %rsp\n" /* undo alignment */                       \
 | |
|             "    jmp common_trap_exit \n"                                      \
 | |
|         );                                                                     \
 | |
|     }
 | |
| 
 | |
| #define EH_ENTRY_NO_CODE(ec, title)                                            \
 | |
|     extern "C" void title##_handler(TrapFrame*) __attribute__((used));         \
 | |
|     extern "C" void title##_asm_entry();                                       \
 | |
|     NAKED void title##_asm_entry() {                                           \
 | |
|         asm(                                                                   \
 | |
|             "    pushq $0x0\n"                                                 \
 | |
|             "    pushq %r15\n"                                                 \
 | |
|             "    pushq %r14\n"                                                 \
 | |
|             "    pushq %r13\n"                                                 \
 | |
|             "    pushq %r12\n"                                                 \
 | |
|             "    pushq %r11\n"                                                 \
 | |
|             "    pushq %r10\n"                                                 \
 | |
|             "    pushq %r9\n"                                                  \
 | |
|             "    pushq %r8\n"                                                  \
 | |
|             "    pushq %rax\n"                                                 \
 | |
|             "    pushq %rcx\n"                                                 \
 | |
|             "    pushq %rdx\n"                                                 \
 | |
|             "    pushq %rbx\n"                                                 \
 | |
|             "    pushq %rsp\n"                                                 \
 | |
|             "    pushq %rbp\n"                                                 \
 | |
|             "    pushq %rsi\n"                                                 \
 | |
|             "    pushq %rdi\n"                                                 \
 | |
|             "    pushq %rsp \n" /* set TrapFrame::regs */                      \
 | |
|             "    subq $" __STRINGIFY(TRAP_FRAME_SIZE - 8) ", %rsp \n"          \
 | |
|             "    movq %rsp, %rdi \n"                                           \
 | |
|             "    cld\n"                                                        \
 | |
|             "    call enter_trap_no_irq \n"                                    \
 | |
|             "    movq %rsp, %rdi \n"                                           \
 | |
|             "    call " #title "_handler\n"                                    \
 | |
|             "    jmp common_trap_exit \n"                                      \
 | |
|         );                                                                     \
 | |
|     }
 | |
| #endif
 | |
| 
 | |
| // clang-format on
 | |
| 
 | |
| void dump_registers(RegisterState const& regs)
 | |
| {
 | |
| #if ARCH(I386)
 | |
|     u16 ss;
 | |
|     u32 esp;
 | |
| 
 | |
|     if (!(regs.cs & 3)) {
 | |
|         ss = regs.ss;
 | |
|         esp = regs.esp;
 | |
|     } else {
 | |
|         ss = regs.userspace_ss;
 | |
|         esp = regs.userspace_esp;
 | |
|     }
 | |
| #else
 | |
|     u64 rsp;
 | |
| 
 | |
|     if (!(regs.cs & 3))
 | |
|         rsp = regs.rsp;
 | |
|     else
 | |
|         rsp = regs.userspace_rsp;
 | |
| #endif
 | |
| 
 | |
|     dbgln("Exception code: {:04x} (isr: {:04x})", regs.exception_code, regs.isr_number);
 | |
| #if ARCH(I386)
 | |
|     dbgln("    pc={:#04x}:{:p} eflags={:p}", (u16)regs.cs, regs.eip, regs.eflags);
 | |
|     dbgln(" stack={:#04x}:{:p}", ss, esp);
 | |
|     dbgln("    ds={:#04x} es={:#04x} fs={:#04x} gs={:#04x}", (u16)regs.ds, (u16)regs.es, (u16)regs.fs, (u16)regs.gs);
 | |
|     dbgln("   eax={:p} ebx={:p} ecx={:p} edx={:p}", regs.eax, regs.ebx, regs.ecx, regs.edx);
 | |
|     dbgln("   ebp={:p} esp={:p} esi={:p} edi={:p}", regs.ebp, regs.esp, regs.esi, regs.edi);
 | |
|     dbgln("   cr0={:p} cr2={:p} cr3={:p} cr4={:p}", read_cr0(), read_cr2(), read_cr3(), read_cr4());
 | |
| #else
 | |
|     dbgln("    pc={:#04x}:{:p} rflags={:p}", (u16)regs.cs, regs.rip, regs.rflags);
 | |
|     dbgln(" stack={:p}", rsp);
 | |
|     // FIXME: Add fs_base and gs_base here
 | |
|     dbgln("   rax={:p} rbx={:p} rcx={:p} rdx={:p}", regs.rax, regs.rbx, regs.rcx, regs.rdx);
 | |
|     dbgln("   rbp={:p} rsp={:p} rsi={:p} rdi={:p}", regs.rbp, regs.rsp, regs.rsi, regs.rdi);
 | |
|     dbgln("    r8={:p}  r9={:p} r10={:p} r11={:p}", regs.r8, regs.r9, regs.r10, regs.r11);
 | |
|     dbgln("   r12={:p} r13={:p} r14={:p} r15={:p}", regs.r12, regs.r13, regs.r14, regs.r15);
 | |
|     dbgln("   cr0={:p} cr2={:p} cr3={:p} cr4={:p}", read_cr0(), read_cr2(), read_cr3(), read_cr4());
 | |
| #endif
 | |
| }
 | |
| 
 | |
| EH_ENTRY_NO_CODE(6, illegal_instruction);
 | |
| void illegal_instruction_handler(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
|     handle_crash(*trap->regs, "Illegal instruction", SIGILL);
 | |
| }
 | |
| 
 | |
| EH_ENTRY_NO_CODE(0, divide_error);
 | |
| void divide_error_handler(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
|     handle_crash(*trap->regs, "Divide error", SIGFPE);
 | |
| }
 | |
| 
 | |
| EH_ENTRY(13, general_protection_fault);
 | |
| void general_protection_fault_handler(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
|     handle_crash(*trap->regs, "General protection fault", SIGSEGV);
 | |
| }
 | |
| 
 | |
| // 7: FPU not available exception
 | |
| EH_ENTRY_NO_CODE(7, fpu_exception);
 | |
| void fpu_exception_handler(TrapFrame*)
 | |
| {
 | |
|     // Just clear the TS flag. We've already restored the FPU state eagerly.
 | |
|     // FIXME: It would be nice if we didn't have to do this at all.
 | |
|     asm volatile("clts");
 | |
| }
 | |
| 
 | |
| // 14: Page Fault
 | |
| EH_ENTRY(14, page_fault);
 | |
| void page_fault_handler(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
| 
 | |
|     // NOTE: Once we've extracted the faulting address from CR2,
 | |
|     //       we can re-enable interrupts.
 | |
|     auto fault_address = read_cr2();
 | |
|     sti();
 | |
| 
 | |
|     auto& regs = *trap->regs;
 | |
| 
 | |
|     if constexpr (PAGE_FAULT_DEBUG) {
 | |
|         u32 fault_page_directory = read_cr3();
 | |
|         dbgln("CPU #{} ring {} {} page fault in PD={:#x}, {}{} {}",
 | |
|             Processor::is_initialized() ? Processor::current_id() : 0,
 | |
|             regs.cs & 3,
 | |
|             regs.exception_code & 1 ? "PV" : "NP",
 | |
|             fault_page_directory,
 | |
|             regs.exception_code & 8 ? "reserved-bit " : "",
 | |
|             regs.exception_code & 2 ? "write" : "read",
 | |
|             VirtualAddress(fault_address));
 | |
| 
 | |
|         dump_registers(regs);
 | |
|     }
 | |
| 
 | |
|     bool faulted_in_kernel = !(regs.cs & 3);
 | |
| 
 | |
|     if (faulted_in_kernel && Processor::current_in_irq()) {
 | |
|         // If we're faulting in an IRQ handler, first check if we failed
 | |
|         // due to safe_memcpy, safe_strnlen, or safe_memset. If we did,
 | |
|         // gracefully continue immediately. Because we're in an IRQ handler
 | |
|         // we can't really try to resolve the page fault in a meaningful
 | |
|         // way, so we need to do this before calling into
 | |
|         // MemoryManager::handle_page_fault, which would just bail and
 | |
|         // request a crash
 | |
|         if (handle_safe_access_fault(regs, fault_address))
 | |
|             return;
 | |
|     }
 | |
| 
 | |
|     auto current_thread = Thread::current();
 | |
| 
 | |
|     if (current_thread) {
 | |
|         current_thread->set_handling_page_fault(true);
 | |
|         PerformanceManager::add_page_fault_event(*current_thread, regs);
 | |
|     }
 | |
| 
 | |
|     ScopeGuard guard = [current_thread] {
 | |
|         if (current_thread)
 | |
|             current_thread->set_handling_page_fault(false);
 | |
|     };
 | |
| 
 | |
|     VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() };
 | |
| 
 | |
|     if (!faulted_in_kernel) {
 | |
|         bool has_valid_stack_pointer = current_thread->process().address_space().with([&](auto& space) {
 | |
|             return MM.validate_user_stack(*space, userspace_sp);
 | |
|         });
 | |
|         if (!has_valid_stack_pointer) {
 | |
|             dbgln("Invalid stack pointer: {}", userspace_sp);
 | |
|             return handle_crash(regs, "Bad stack on page fault", SIGSEGV);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     PageFault fault { regs.exception_code, VirtualAddress { fault_address } };
 | |
|     auto response = MM.handle_page_fault(fault);
 | |
| 
 | |
|     if (response == PageFaultResponse::ShouldCrash || response == PageFaultResponse::OutOfMemory || response == PageFaultResponse::BusError) {
 | |
|         if (faulted_in_kernel && handle_safe_access_fault(regs, fault_address)) {
 | |
|             // If this would be a ring0 (kernel) fault and the fault was triggered by
 | |
|             // safe_memcpy, safe_strnlen, or safe_memset then we resume execution at
 | |
|             // the appropriate _fault label rather than crashing
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (response == PageFaultResponse::BusError && current_thread->has_signal_handler(SIGBUS)) {
 | |
|             current_thread->send_urgent_signal_to_self(SIGBUS);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (response != PageFaultResponse::OutOfMemory && current_thread) {
 | |
|             if (current_thread->has_signal_handler(SIGSEGV)) {
 | |
|                 current_thread->send_urgent_signal_to_self(SIGSEGV);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         dbgln("Unrecoverable page fault, {}{}{} address {}",
 | |
|             regs.exception_code & PageFaultFlags::ReservedBitViolation ? "reserved bit violation / " : "",
 | |
|             regs.exception_code & PageFaultFlags::InstructionFetch ? "instruction fetch / " : "",
 | |
|             regs.exception_code & PageFaultFlags::Write ? "write to" : "read from",
 | |
|             VirtualAddress(fault_address));
 | |
|         constexpr FlatPtr malloc_scrub_pattern = explode_byte(MALLOC_SCRUB_BYTE);
 | |
|         constexpr FlatPtr free_scrub_pattern = explode_byte(FREE_SCRUB_BYTE);
 | |
|         constexpr FlatPtr kmalloc_scrub_pattern = explode_byte(KMALLOC_SCRUB_BYTE);
 | |
|         constexpr FlatPtr kfree_scrub_pattern = explode_byte(KFREE_SCRUB_BYTE);
 | |
|         if (response == PageFaultResponse::BusError) {
 | |
|             dbgln("Note: Address {} is an access to an undefined memory range of an Inode-backed VMObject", VirtualAddress(fault_address));
 | |
|         } else if ((fault_address & 0xffff0000) == (malloc_scrub_pattern & 0xffff0000)) {
 | |
|             dbgln("Note: Address {} looks like it may be uninitialized malloc() memory", VirtualAddress(fault_address));
 | |
|         } else if ((fault_address & 0xffff0000) == (free_scrub_pattern & 0xffff0000)) {
 | |
|             dbgln("Note: Address {} looks like it may be recently free()'d memory", VirtualAddress(fault_address));
 | |
|         } else if ((fault_address & 0xffff0000) == (kmalloc_scrub_pattern & 0xffff0000)) {
 | |
|             dbgln("Note: Address {} looks like it may be uninitialized kmalloc() memory", VirtualAddress(fault_address));
 | |
|         } else if ((fault_address & 0xffff0000) == (kfree_scrub_pattern & 0xffff0000)) {
 | |
|             dbgln("Note: Address {} looks like it may be recently kfree()'d memory", VirtualAddress(fault_address));
 | |
|         } else if (fault_address < 4096) {
 | |
|             dbgln("Note: Address {} looks like a possible nullptr dereference", VirtualAddress(fault_address));
 | |
|         } else if constexpr (SANITIZE_PTRS) {
 | |
|             constexpr FlatPtr refptr_scrub_pattern = explode_byte(REFPTR_SCRUB_BYTE);
 | |
|             constexpr FlatPtr nonnullrefptr_scrub_pattern = explode_byte(NONNULLREFPTR_SCRUB_BYTE);
 | |
|             constexpr FlatPtr ownptr_scrub_pattern = explode_byte(OWNPTR_SCRUB_BYTE);
 | |
|             constexpr FlatPtr nonnullownptr_scrub_pattern = explode_byte(NONNULLOWNPTR_SCRUB_BYTE);
 | |
|             constexpr FlatPtr lockrefptr_scrub_pattern = explode_byte(LOCKREFPTR_SCRUB_BYTE);
 | |
|             constexpr FlatPtr nonnulllockrefptr_scrub_pattern = explode_byte(NONNULLLOCKREFPTR_SCRUB_BYTE);
 | |
| 
 | |
|             if ((fault_address & 0xffff0000) == (refptr_scrub_pattern & 0xffff0000)) {
 | |
|                 dbgln("Note: Address {} looks like it may be a recently destroyed LockRefPtr", VirtualAddress(fault_address));
 | |
|             } else if ((fault_address & 0xffff0000) == (nonnullrefptr_scrub_pattern & 0xffff0000)) {
 | |
|                 dbgln("Note: Address {} looks like it may be a recently destroyed NonnullLockRefPtr", VirtualAddress(fault_address));
 | |
|             } else if ((fault_address & 0xffff0000) == (ownptr_scrub_pattern & 0xffff0000)) {
 | |
|                 dbgln("Note: Address {} looks like it may be a recently destroyed OwnPtr", VirtualAddress(fault_address));
 | |
|             } else if ((fault_address & 0xffff0000) == (nonnullownptr_scrub_pattern & 0xffff0000)) {
 | |
|                 dbgln("Note: Address {} looks like it may be a recently destroyed NonnullOwnPtr", VirtualAddress(fault_address));
 | |
|             } else if ((fault_address & 0xffff0000) == (lockrefptr_scrub_pattern & 0xffff0000)) {
 | |
|                 dbgln("Note: Address {} looks like it may be a recently destroyed LockRefPtr", VirtualAddress(fault_address));
 | |
|             } else if ((fault_address & 0xffff0000) == (nonnulllockrefptr_scrub_pattern & 0xffff0000)) {
 | |
|                 dbgln("Note: Address {} looks like it may be a recently destroyed NonnullLockRefPtr", VirtualAddress(fault_address));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (current_thread) {
 | |
|             auto& current_process = current_thread->process();
 | |
|             if (current_process.is_user_process()) {
 | |
|                 auto fault_address_string = KString::formatted("{:p}", fault_address);
 | |
|                 auto fault_address_view = fault_address_string.is_error() ? ""sv : fault_address_string.value()->view();
 | |
|                 (void)current_process.try_set_coredump_property("fault_address"sv, fault_address_view);
 | |
|                 (void)current_process.try_set_coredump_property("fault_type"sv, fault.type() == PageFault::Type::PageNotPresent ? "NotPresent"sv : "ProtectionViolation"sv);
 | |
|                 StringView fault_access;
 | |
|                 if (fault.is_instruction_fetch())
 | |
|                     fault_access = "Execute"sv;
 | |
|                 else
 | |
|                     fault_access = fault.access() == PageFault::Access::Read ? "Read"sv : "Write"sv;
 | |
|                 (void)current_process.try_set_coredump_property("fault_access"sv, fault_access);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (response == PageFaultResponse::BusError)
 | |
|             return handle_crash(regs, "Page Fault (Bus Error)", SIGBUS, false);
 | |
|         return handle_crash(regs, "Page Fault", SIGSEGV, response == PageFaultResponse::OutOfMemory);
 | |
|     } else if (response == PageFaultResponse::Continue) {
 | |
|         dbgln_if(PAGE_FAULT_DEBUG, "Continuing after resolved page fault");
 | |
|     } else {
 | |
|         VERIFY_NOT_REACHED();
 | |
|     }
 | |
| }
 | |
| 
 | |
| EH_ENTRY_NO_CODE(1, debug);
 | |
| void debug_handler(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
|     auto& regs = *trap->regs;
 | |
|     auto current_thread = Thread::current();
 | |
|     auto& process = current_thread->process();
 | |
|     if ((regs.cs & 3) == 0) {
 | |
|         PANIC("Debug exception in ring 0");
 | |
|     }
 | |
|     constexpr u8 REASON_SINGLESTEP = 14;
 | |
|     auto debug_status = read_dr6();
 | |
|     auto should_trap_mask = (1 << REASON_SINGLESTEP) | 0b1111;
 | |
|     if ((debug_status & should_trap_mask) == 0)
 | |
|         return;
 | |
|     if (auto tracer = process.tracer()) {
 | |
|         tracer->set_regs(regs);
 | |
|     }
 | |
|     current_thread->send_urgent_signal_to_self(SIGTRAP);
 | |
|     write_dr6(debug_status & ~(should_trap_mask));
 | |
| }
 | |
| 
 | |
| EH_ENTRY_NO_CODE(3, breakpoint);
 | |
| void breakpoint_handler(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
|     auto& regs = *trap->regs;
 | |
|     auto current_thread = Thread::current();
 | |
|     auto& process = current_thread->process();
 | |
|     if ((regs.cs & 3) == 0) {
 | |
|         PANIC("Breakpoint trap in ring 0");
 | |
|     }
 | |
|     if (auto tracer = process.tracer()) {
 | |
|         tracer->set_regs(regs);
 | |
|     }
 | |
|     current_thread->send_urgent_signal_to_self(SIGTRAP);
 | |
| }
 | |
| 
 | |
| #define EH(i, msg)                                                                                            \
 | |
|     static void _exception##i()                                                                               \
 | |
|     {                                                                                                         \
 | |
|         dbgln("{}", msg);                                                                                     \
 | |
|         PANIC("cr0={:08x} cr2={:08x} cr3={:08x} cr4={:08x}", read_cr0(), read_cr2(), read_cr3(), read_cr4()); \
 | |
|     }
 | |
| 
 | |
| EH(2, "Unknown error")
 | |
| EH(4, "Overflow")
 | |
| EH(5, "Bounds check")
 | |
| EH(8, "Double fault")
 | |
| EH(9, "Coprocessor segment overrun")
 | |
| EH(10, "Invalid TSS")
 | |
| EH(11, "Segment not present")
 | |
| EH(12, "Stack exception")
 | |
| EH(15, "Unknown error")
 | |
| EH(16, "Coprocessor error")
 | |
| 
 | |
| extern "C" void pre_init_finished(void) __attribute__((used));
 | |
| extern "C" void post_init_finished(void) __attribute__((used));
 | |
| extern "C" void handle_interrupt(TrapFrame*) __attribute__((used));
 | |
| 
 | |
| extern "C" UNMAP_AFTER_INIT void pre_init_finished(void)
 | |
| {
 | |
|     VERIFY(g_scheduler_lock.is_locked_by_current_processor());
 | |
| 
 | |
|     // Because init_finished() will wait on the other APs, we need
 | |
|     // to release the scheduler lock so that the other APs can also get
 | |
|     // to this point
 | |
| 
 | |
|     // The target flags will get restored upon leaving the trap
 | |
|     Scheduler::leave_on_first_switch(processor_interrupts_state());
 | |
| }
 | |
| 
 | |
| extern "C" UNMAP_AFTER_INIT void post_init_finished(void)
 | |
| {
 | |
|     // We need to re-acquire the scheduler lock before a context switch
 | |
|     // transfers control into the idle loop, which needs the lock held
 | |
|     Scheduler::prepare_for_idle_loop();
 | |
| }
 | |
| 
 | |
| void handle_interrupt(TrapFrame* trap)
 | |
| {
 | |
|     clac();
 | |
|     auto& regs = *trap->regs;
 | |
| 
 | |
|     GenericInterruptHandler* handler = nullptr;
 | |
|     // Note: we declare interrupt service routine offset 0x20 to 0x2f as
 | |
|     // reserved for when the PIC is disabled, so we can still route spurious
 | |
|     // IRQs to a different interrupt handlers at different location.
 | |
|     if (regs.isr_number >= pic_disabled_vector_base && regs.isr_number <= pic_disabled_vector_end) {
 | |
|         u8 irq = (u8)(regs.isr_number - pic_disabled_vector_base);
 | |
|         if (irq == 7) {
 | |
|             handler = s_disabled_interrupt_handler[0];
 | |
|         } else if (irq == 15) {
 | |
|             handler = s_disabled_interrupt_handler[1];
 | |
|         }
 | |
|     } else {
 | |
|         VERIFY(regs.isr_number >= IRQ_VECTOR_BASE && regs.isr_number <= (IRQ_VECTOR_BASE + GENERIC_INTERRUPT_HANDLERS_COUNT));
 | |
|         u8 irq = (u8)(regs.isr_number - IRQ_VECTOR_BASE);
 | |
|         s_entropy_source_interrupts.add_random_event(irq);
 | |
|         handler = s_interrupt_handler[irq];
 | |
|     }
 | |
|     VERIFY(handler);
 | |
|     handler->increment_call_count();
 | |
|     handler->handle_interrupt(regs);
 | |
|     handler->eoi();
 | |
| }
 | |
| 
 | |
| DescriptorTablePointer const& get_idtr()
 | |
| {
 | |
|     return s_idtr;
 | |
| }
 | |
| 
 | |
| static void unimp_trap()
 | |
| {
 | |
|     PANIC("Unhandled IRQ");
 | |
| }
 | |
| 
 | |
| GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number)
 | |
| {
 | |
|     auto*& handler_slot = s_interrupt_handler[interrupt_number];
 | |
|     VERIFY(handler_slot != nullptr);
 | |
|     return *handler_slot;
 | |
| }
 | |
| 
 | |
| static void revert_to_unused_handler(u8 interrupt_number)
 | |
| {
 | |
|     auto handler = new UnhandledInterruptHandler(interrupt_number);
 | |
|     handler->register_interrupt_handler();
 | |
| }
 | |
| 
 | |
| void register_disabled_interrupt_handler(u8 number, GenericInterruptHandler& handler)
 | |
| {
 | |
|     if (number == 15) {
 | |
|         s_disabled_interrupt_handler[0] = &handler;
 | |
|         return;
 | |
|     } else if (number == 7) {
 | |
|         s_disabled_interrupt_handler[1] = &handler;
 | |
|         return;
 | |
|     }
 | |
|     VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| void register_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler)
 | |
| {
 | |
|     VERIFY(interrupt_number < GENERIC_INTERRUPT_HANDLERS_COUNT);
 | |
|     auto*& handler_slot = s_interrupt_handler[interrupt_number];
 | |
|     if (handler_slot != nullptr) {
 | |
|         if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) {
 | |
|             if (handler_slot) {
 | |
|                 auto* unhandled_handler = static_cast<UnhandledInterruptHandler*>(handler_slot);
 | |
|                 unhandled_handler->unregister_interrupt_handler();
 | |
|                 delete unhandled_handler;
 | |
|             }
 | |
|             handler_slot = &handler;
 | |
|             return;
 | |
|         }
 | |
|         if (handler_slot->is_shared_handler() && !handler_slot->is_sharing_with_others()) {
 | |
|             VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler);
 | |
|             static_cast<SharedIRQHandler*>(handler_slot)->register_handler(handler);
 | |
|             return;
 | |
|         }
 | |
|         if (!handler_slot->is_shared_handler()) {
 | |
|             if (handler_slot->type() == HandlerType::SpuriousInterruptHandler) {
 | |
|                 static_cast<SpuriousInterruptHandler*>(handler_slot)->register_handler(handler);
 | |
|                 return;
 | |
|             }
 | |
|             VERIFY(handler_slot->type() == HandlerType::IRQHandler);
 | |
|             auto& previous_handler = *handler_slot;
 | |
|             handler_slot = nullptr;
 | |
|             SharedIRQHandler::initialize(interrupt_number);
 | |
|             VERIFY(handler_slot);
 | |
|             static_cast<SharedIRQHandler*>(handler_slot)->register_handler(previous_handler);
 | |
|             static_cast<SharedIRQHandler*>(handler_slot)->register_handler(handler);
 | |
|             return;
 | |
|         }
 | |
|         VERIFY_NOT_REACHED();
 | |
|     } else {
 | |
|         handler_slot = &handler;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void unregister_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler)
 | |
| {
 | |
|     auto*& handler_slot = s_interrupt_handler[interrupt_number];
 | |
|     VERIFY(handler_slot != nullptr);
 | |
|     if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) {
 | |
|         dbgln("Trying to unregister unused handler (?)");
 | |
|         return;
 | |
|     }
 | |
|     if (handler_slot->is_shared_handler() && !handler_slot->is_sharing_with_others()) {
 | |
|         VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler);
 | |
|         auto* shared_handler = static_cast<SharedIRQHandler*>(handler_slot);
 | |
|         shared_handler->unregister_handler(handler);
 | |
|         if (!shared_handler->sharing_devices_count()) {
 | |
|             handler_slot = nullptr;
 | |
|             revert_to_unused_handler(interrupt_number);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if (!handler_slot->is_shared_handler()) {
 | |
|         VERIFY(handler_slot->type() == HandlerType::IRQHandler);
 | |
|         handler_slot = nullptr;
 | |
|         revert_to_unused_handler(interrupt_number);
 | |
|         return;
 | |
|     }
 | |
|     VERIFY_NOT_REACHED();
 | |
| }
 | |
| 
 | |
| UNMAP_AFTER_INIT void register_interrupt_handler(u8 index, void (*handler)())
 | |
| {
 | |
|     // FIXME: Is the Gate Type really required to be an Interrupt
 | |
|     // FIXME: What's up with that storage segment 0?
 | |
|     s_idt[index] = IDTEntry((FlatPtr)handler, GDT_SELECTOR_CODE0, IDTEntryType::InterruptGate32, 0, 0);
 | |
| }
 | |
| 
 | |
| UNMAP_AFTER_INIT void register_user_callable_interrupt_handler(u8 index, void (*handler)())
 | |
| {
 | |
|     // FIXME: Is the Gate Type really required to be a Trap
 | |
|     // FIXME: What's up with that storage segment 0?
 | |
|     s_idt[index] = IDTEntry((FlatPtr)handler, GDT_SELECTOR_CODE0, IDTEntryType::TrapGate32, 0, 3);
 | |
| }
 | |
| 
 | |
| UNMAP_AFTER_INIT void flush_idt()
 | |
| {
 | |
|     asm("lidt %0" ::"m"(s_idtr));
 | |
| }
 | |
| 
 | |
| UNMAP_AFTER_INIT void initialize_interrupts()
 | |
| {
 | |
|     s_idtr.address = s_idt;
 | |
|     s_idtr.limit = 256 * sizeof(IDTEntry) - 1;
 | |
| 
 | |
|     register_interrupt_handler(0x00, divide_error_asm_entry);
 | |
|     register_user_callable_interrupt_handler(0x01, debug_asm_entry);
 | |
|     register_interrupt_handler(0x02, _exception2);
 | |
|     register_user_callable_interrupt_handler(0x03, breakpoint_asm_entry);
 | |
|     register_interrupt_handler(0x04, _exception4);
 | |
|     register_interrupt_handler(0x05, _exception5);
 | |
|     register_interrupt_handler(0x06, illegal_instruction_asm_entry);
 | |
|     register_interrupt_handler(0x07, fpu_exception_asm_entry);
 | |
|     register_interrupt_handler(0x08, _exception8);
 | |
|     register_interrupt_handler(0x09, _exception9);
 | |
|     register_interrupt_handler(0x0a, _exception10);
 | |
|     register_interrupt_handler(0x0b, _exception11);
 | |
|     register_interrupt_handler(0x0c, _exception12);
 | |
|     register_interrupt_handler(0x0d, general_protection_fault_asm_entry);
 | |
|     register_interrupt_handler(0x0e, page_fault_asm_entry);
 | |
|     register_interrupt_handler(0x0f, _exception15);
 | |
|     register_interrupt_handler(0x10, _exception16);
 | |
| 
 | |
|     for (u8 i = 0x11; i < 0x20; i++)
 | |
|         register_interrupt_handler(i, unimp_trap);
 | |
| 
 | |
|     dbgln("Initializing unhandled interrupt handlers");
 | |
|     register_interrupt_handler(0x20, interrupt_32_asm_entry);
 | |
|     register_interrupt_handler(0x21, interrupt_33_asm_entry);
 | |
|     register_interrupt_handler(0x22, interrupt_34_asm_entry);
 | |
|     register_interrupt_handler(0x23, interrupt_35_asm_entry);
 | |
|     register_interrupt_handler(0x24, interrupt_36_asm_entry);
 | |
|     register_interrupt_handler(0x25, interrupt_37_asm_entry);
 | |
|     register_interrupt_handler(0x26, interrupt_38_asm_entry);
 | |
|     register_interrupt_handler(0x27, interrupt_39_asm_entry);
 | |
|     register_interrupt_handler(0x28, interrupt_40_asm_entry);
 | |
|     register_interrupt_handler(0x29, interrupt_41_asm_entry);
 | |
|     register_interrupt_handler(0x2a, interrupt_42_asm_entry);
 | |
|     register_interrupt_handler(0x2b, interrupt_43_asm_entry);
 | |
|     register_interrupt_handler(0x2c, interrupt_44_asm_entry);
 | |
|     register_interrupt_handler(0x2d, interrupt_45_asm_entry);
 | |
|     register_interrupt_handler(0x2e, interrupt_46_asm_entry);
 | |
|     register_interrupt_handler(0x2f, interrupt_47_asm_entry);
 | |
|     register_interrupt_handler(0x30, interrupt_48_asm_entry);
 | |
|     register_interrupt_handler(0x31, interrupt_49_asm_entry);
 | |
|     register_interrupt_handler(0x32, interrupt_50_asm_entry);
 | |
|     register_interrupt_handler(0x33, interrupt_51_asm_entry);
 | |
|     register_interrupt_handler(0x34, interrupt_52_asm_entry);
 | |
|     register_interrupt_handler(0x35, interrupt_53_asm_entry);
 | |
|     register_interrupt_handler(0x36, interrupt_54_asm_entry);
 | |
|     register_interrupt_handler(0x37, interrupt_55_asm_entry);
 | |
|     register_interrupt_handler(0x38, interrupt_56_asm_entry);
 | |
|     register_interrupt_handler(0x39, interrupt_57_asm_entry);
 | |
|     register_interrupt_handler(0x3a, interrupt_58_asm_entry);
 | |
|     register_interrupt_handler(0x3b, interrupt_59_asm_entry);
 | |
|     register_interrupt_handler(0x3c, interrupt_60_asm_entry);
 | |
|     register_interrupt_handler(0x3d, interrupt_61_asm_entry);
 | |
|     register_interrupt_handler(0x3e, interrupt_62_asm_entry);
 | |
|     register_interrupt_handler(0x3f, interrupt_63_asm_entry);
 | |
|     register_interrupt_handler(0x40, interrupt_64_asm_entry);
 | |
|     register_interrupt_handler(0x41, interrupt_65_asm_entry);
 | |
|     register_interrupt_handler(0x42, interrupt_66_asm_entry);
 | |
|     register_interrupt_handler(0x43, interrupt_67_asm_entry);
 | |
|     register_interrupt_handler(0x44, interrupt_68_asm_entry);
 | |
|     register_interrupt_handler(0x45, interrupt_69_asm_entry);
 | |
|     register_interrupt_handler(0x46, interrupt_70_asm_entry);
 | |
|     register_interrupt_handler(0x47, interrupt_71_asm_entry);
 | |
|     register_interrupt_handler(0x48, interrupt_72_asm_entry);
 | |
|     register_interrupt_handler(0x49, interrupt_73_asm_entry);
 | |
|     register_interrupt_handler(0x4a, interrupt_74_asm_entry);
 | |
|     register_interrupt_handler(0x4b, interrupt_75_asm_entry);
 | |
|     register_interrupt_handler(0x4c, interrupt_76_asm_entry);
 | |
|     register_interrupt_handler(0x4d, interrupt_77_asm_entry);
 | |
|     register_interrupt_handler(0x4e, interrupt_78_asm_entry);
 | |
|     register_interrupt_handler(0x4f, interrupt_79_asm_entry);
 | |
|     register_interrupt_handler(0x50, interrupt_80_asm_entry);
 | |
|     register_interrupt_handler(0x51, interrupt_81_asm_entry);
 | |
|     register_interrupt_handler(0x52, interrupt_82_asm_entry);
 | |
|     register_interrupt_handler(0x53, interrupt_83_asm_entry);
 | |
|     register_interrupt_handler(0x54, interrupt_84_asm_entry);
 | |
|     register_interrupt_handler(0x55, interrupt_85_asm_entry);
 | |
|     register_interrupt_handler(0x56, interrupt_86_asm_entry);
 | |
|     register_interrupt_handler(0x57, interrupt_87_asm_entry);
 | |
|     register_interrupt_handler(0x58, interrupt_88_asm_entry);
 | |
|     register_interrupt_handler(0x59, interrupt_89_asm_entry);
 | |
|     register_interrupt_handler(0x5a, interrupt_90_asm_entry);
 | |
|     register_interrupt_handler(0x5b, interrupt_91_asm_entry);
 | |
|     register_interrupt_handler(0x5c, interrupt_92_asm_entry);
 | |
|     register_interrupt_handler(0x5d, interrupt_93_asm_entry);
 | |
|     register_interrupt_handler(0x5e, interrupt_94_asm_entry);
 | |
|     register_interrupt_handler(0x5f, interrupt_95_asm_entry);
 | |
|     register_interrupt_handler(0x60, interrupt_96_asm_entry);
 | |
|     register_interrupt_handler(0x61, interrupt_97_asm_entry);
 | |
|     register_interrupt_handler(0x62, interrupt_98_asm_entry);
 | |
|     register_interrupt_handler(0x63, interrupt_99_asm_entry);
 | |
|     register_interrupt_handler(0x64, interrupt_100_asm_entry);
 | |
|     register_interrupt_handler(0x65, interrupt_101_asm_entry);
 | |
|     register_interrupt_handler(0x66, interrupt_102_asm_entry);
 | |
|     register_interrupt_handler(0x67, interrupt_103_asm_entry);
 | |
|     register_interrupt_handler(0x68, interrupt_104_asm_entry);
 | |
|     register_interrupt_handler(0x69, interrupt_105_asm_entry);
 | |
|     register_interrupt_handler(0x6a, interrupt_106_asm_entry);
 | |
|     register_interrupt_handler(0x6b, interrupt_107_asm_entry);
 | |
|     register_interrupt_handler(0x6c, interrupt_108_asm_entry);
 | |
|     register_interrupt_handler(0x6d, interrupt_109_asm_entry);
 | |
|     register_interrupt_handler(0x6e, interrupt_110_asm_entry);
 | |
|     register_interrupt_handler(0x6f, interrupt_111_asm_entry);
 | |
|     register_interrupt_handler(0x70, interrupt_112_asm_entry);
 | |
|     register_interrupt_handler(0x71, interrupt_113_asm_entry);
 | |
|     register_interrupt_handler(0x72, interrupt_114_asm_entry);
 | |
|     register_interrupt_handler(0x73, interrupt_115_asm_entry);
 | |
|     register_interrupt_handler(0x74, interrupt_116_asm_entry);
 | |
|     register_interrupt_handler(0x75, interrupt_117_asm_entry);
 | |
|     register_interrupt_handler(0x76, interrupt_118_asm_entry);
 | |
|     register_interrupt_handler(0x77, interrupt_119_asm_entry);
 | |
|     register_interrupt_handler(0x78, interrupt_120_asm_entry);
 | |
|     register_interrupt_handler(0x79, interrupt_121_asm_entry);
 | |
|     register_interrupt_handler(0x7a, interrupt_122_asm_entry);
 | |
|     register_interrupt_handler(0x7b, interrupt_123_asm_entry);
 | |
|     register_interrupt_handler(0x7c, interrupt_124_asm_entry);
 | |
|     register_interrupt_handler(0x7d, interrupt_125_asm_entry);
 | |
|     register_interrupt_handler(0x7e, interrupt_126_asm_entry);
 | |
|     register_interrupt_handler(0x7f, interrupt_127_asm_entry);
 | |
|     register_interrupt_handler(0x80, interrupt_128_asm_entry);
 | |
|     register_interrupt_handler(0x81, interrupt_129_asm_entry);
 | |
|     register_interrupt_handler(0x82, interrupt_130_asm_entry);
 | |
|     register_interrupt_handler(0x83, interrupt_131_asm_entry);
 | |
|     register_interrupt_handler(0x84, interrupt_132_asm_entry);
 | |
|     register_interrupt_handler(0x85, interrupt_133_asm_entry);
 | |
|     register_interrupt_handler(0x86, interrupt_134_asm_entry);
 | |
|     register_interrupt_handler(0x87, interrupt_135_asm_entry);
 | |
|     register_interrupt_handler(0x88, interrupt_136_asm_entry);
 | |
|     register_interrupt_handler(0x89, interrupt_137_asm_entry);
 | |
|     register_interrupt_handler(0x8a, interrupt_138_asm_entry);
 | |
|     register_interrupt_handler(0x8b, interrupt_139_asm_entry);
 | |
|     register_interrupt_handler(0x8c, interrupt_140_asm_entry);
 | |
|     register_interrupt_handler(0x8d, interrupt_141_asm_entry);
 | |
|     register_interrupt_handler(0x8e, interrupt_142_asm_entry);
 | |
|     register_interrupt_handler(0x8f, interrupt_143_asm_entry);
 | |
|     register_interrupt_handler(0x90, interrupt_144_asm_entry);
 | |
|     register_interrupt_handler(0x91, interrupt_145_asm_entry);
 | |
|     register_interrupt_handler(0x92, interrupt_146_asm_entry);
 | |
|     register_interrupt_handler(0x93, interrupt_147_asm_entry);
 | |
|     register_interrupt_handler(0x94, interrupt_148_asm_entry);
 | |
|     register_interrupt_handler(0x95, interrupt_149_asm_entry);
 | |
|     register_interrupt_handler(0x96, interrupt_150_asm_entry);
 | |
|     register_interrupt_handler(0x97, interrupt_151_asm_entry);
 | |
|     register_interrupt_handler(0x98, interrupt_152_asm_entry);
 | |
|     register_interrupt_handler(0x99, interrupt_153_asm_entry);
 | |
|     register_interrupt_handler(0x9a, interrupt_154_asm_entry);
 | |
|     register_interrupt_handler(0x9b, interrupt_155_asm_entry);
 | |
|     register_interrupt_handler(0x9c, interrupt_156_asm_entry);
 | |
|     register_interrupt_handler(0x9d, interrupt_157_asm_entry);
 | |
|     register_interrupt_handler(0x9e, interrupt_158_asm_entry);
 | |
|     register_interrupt_handler(0x9f, interrupt_159_asm_entry);
 | |
|     register_interrupt_handler(0xa0, interrupt_160_asm_entry);
 | |
|     register_interrupt_handler(0xa1, interrupt_161_asm_entry);
 | |
|     register_interrupt_handler(0xa2, interrupt_162_asm_entry);
 | |
|     register_interrupt_handler(0xa3, interrupt_163_asm_entry);
 | |
|     register_interrupt_handler(0xa4, interrupt_164_asm_entry);
 | |
|     register_interrupt_handler(0xa5, interrupt_165_asm_entry);
 | |
|     register_interrupt_handler(0xa6, interrupt_166_asm_entry);
 | |
|     register_interrupt_handler(0xa7, interrupt_167_asm_entry);
 | |
|     register_interrupt_handler(0xa8, interrupt_168_asm_entry);
 | |
|     register_interrupt_handler(0xa9, interrupt_169_asm_entry);
 | |
|     register_interrupt_handler(0xaa, interrupt_170_asm_entry);
 | |
|     register_interrupt_handler(0xab, interrupt_171_asm_entry);
 | |
|     register_interrupt_handler(0xac, interrupt_172_asm_entry);
 | |
|     register_interrupt_handler(0xad, interrupt_173_asm_entry);
 | |
|     register_interrupt_handler(0xae, interrupt_174_asm_entry);
 | |
|     register_interrupt_handler(0xaf, interrupt_175_asm_entry);
 | |
|     register_interrupt_handler(0xb0, interrupt_176_asm_entry);
 | |
|     register_interrupt_handler(0xb1, interrupt_177_asm_entry);
 | |
|     register_interrupt_handler(0xb2, interrupt_178_asm_entry);
 | |
|     register_interrupt_handler(0xb3, interrupt_179_asm_entry);
 | |
|     register_interrupt_handler(0xb4, interrupt_180_asm_entry);
 | |
|     register_interrupt_handler(0xb5, interrupt_181_asm_entry);
 | |
|     register_interrupt_handler(0xb6, interrupt_182_asm_entry);
 | |
|     register_interrupt_handler(0xb7, interrupt_183_asm_entry);
 | |
|     register_interrupt_handler(0xb8, interrupt_184_asm_entry);
 | |
|     register_interrupt_handler(0xb9, interrupt_185_asm_entry);
 | |
|     register_interrupt_handler(0xba, interrupt_186_asm_entry);
 | |
|     register_interrupt_handler(0xbb, interrupt_187_asm_entry);
 | |
|     register_interrupt_handler(0xbc, interrupt_188_asm_entry);
 | |
|     register_interrupt_handler(0xbd, interrupt_189_asm_entry);
 | |
|     register_interrupt_handler(0xbe, interrupt_190_asm_entry);
 | |
|     register_interrupt_handler(0xbf, interrupt_191_asm_entry);
 | |
|     register_interrupt_handler(0xc0, interrupt_192_asm_entry);
 | |
|     register_interrupt_handler(0xc1, interrupt_193_asm_entry);
 | |
|     register_interrupt_handler(0xc2, interrupt_194_asm_entry);
 | |
|     register_interrupt_handler(0xc3, interrupt_195_asm_entry);
 | |
|     register_interrupt_handler(0xc4, interrupt_196_asm_entry);
 | |
|     register_interrupt_handler(0xc5, interrupt_197_asm_entry);
 | |
|     register_interrupt_handler(0xc6, interrupt_198_asm_entry);
 | |
|     register_interrupt_handler(0xc7, interrupt_199_asm_entry);
 | |
|     register_interrupt_handler(0xc8, interrupt_200_asm_entry);
 | |
|     register_interrupt_handler(0xc9, interrupt_201_asm_entry);
 | |
|     register_interrupt_handler(0xca, interrupt_202_asm_entry);
 | |
|     register_interrupt_handler(0xcb, interrupt_203_asm_entry);
 | |
|     register_interrupt_handler(0xcc, interrupt_204_asm_entry);
 | |
|     register_interrupt_handler(0xcd, interrupt_205_asm_entry);
 | |
|     register_interrupt_handler(0xce, interrupt_206_asm_entry);
 | |
|     register_interrupt_handler(0xcf, interrupt_207_asm_entry);
 | |
|     register_interrupt_handler(0xd0, interrupt_208_asm_entry);
 | |
|     register_interrupt_handler(0xd1, interrupt_209_asm_entry);
 | |
|     register_interrupt_handler(0xd2, interrupt_210_asm_entry);
 | |
|     register_interrupt_handler(0xd3, interrupt_211_asm_entry);
 | |
|     register_interrupt_handler(0xd4, interrupt_212_asm_entry);
 | |
|     register_interrupt_handler(0xd5, interrupt_213_asm_entry);
 | |
|     register_interrupt_handler(0xd6, interrupt_214_asm_entry);
 | |
|     register_interrupt_handler(0xd7, interrupt_215_asm_entry);
 | |
|     register_interrupt_handler(0xd8, interrupt_216_asm_entry);
 | |
|     register_interrupt_handler(0xd9, interrupt_217_asm_entry);
 | |
|     register_interrupt_handler(0xda, interrupt_218_asm_entry);
 | |
|     register_interrupt_handler(0xdb, interrupt_219_asm_entry);
 | |
|     register_interrupt_handler(0xdc, interrupt_220_asm_entry);
 | |
|     register_interrupt_handler(0xdd, interrupt_221_asm_entry);
 | |
|     register_interrupt_handler(0xde, interrupt_222_asm_entry);
 | |
|     register_interrupt_handler(0xdf, interrupt_223_asm_entry);
 | |
|     register_interrupt_handler(0xe0, interrupt_224_asm_entry);
 | |
|     register_interrupt_handler(0xe1, interrupt_225_asm_entry);
 | |
|     register_interrupt_handler(0xe2, interrupt_226_asm_entry);
 | |
|     register_interrupt_handler(0xe3, interrupt_227_asm_entry);
 | |
|     register_interrupt_handler(0xe4, interrupt_228_asm_entry);
 | |
|     register_interrupt_handler(0xe5, interrupt_229_asm_entry);
 | |
|     register_interrupt_handler(0xe6, interrupt_230_asm_entry);
 | |
|     register_interrupt_handler(0xe7, interrupt_231_asm_entry);
 | |
|     register_interrupt_handler(0xe8, interrupt_232_asm_entry);
 | |
|     register_interrupt_handler(0xe9, interrupt_233_asm_entry);
 | |
|     register_interrupt_handler(0xea, interrupt_234_asm_entry);
 | |
|     register_interrupt_handler(0xeb, interrupt_235_asm_entry);
 | |
|     register_interrupt_handler(0xec, interrupt_236_asm_entry);
 | |
|     register_interrupt_handler(0xed, interrupt_237_asm_entry);
 | |
|     register_interrupt_handler(0xee, interrupt_238_asm_entry);
 | |
|     register_interrupt_handler(0xef, interrupt_239_asm_entry);
 | |
|     register_interrupt_handler(0xf0, interrupt_240_asm_entry);
 | |
|     register_interrupt_handler(0xf1, interrupt_241_asm_entry);
 | |
|     register_interrupt_handler(0xf2, interrupt_242_asm_entry);
 | |
|     register_interrupt_handler(0xf3, interrupt_243_asm_entry);
 | |
|     register_interrupt_handler(0xf4, interrupt_244_asm_entry);
 | |
|     register_interrupt_handler(0xf5, interrupt_245_asm_entry);
 | |
|     register_interrupt_handler(0xf6, interrupt_246_asm_entry);
 | |
|     register_interrupt_handler(0xf7, interrupt_247_asm_entry);
 | |
|     register_interrupt_handler(0xf8, interrupt_248_asm_entry);
 | |
|     register_interrupt_handler(0xf9, interrupt_249_asm_entry);
 | |
|     register_interrupt_handler(0xfa, interrupt_250_asm_entry);
 | |
|     register_interrupt_handler(0xfb, interrupt_251_asm_entry);
 | |
|     register_interrupt_handler(0xfc, interrupt_252_asm_entry);
 | |
|     register_interrupt_handler(0xfd, interrupt_253_asm_entry);
 | |
|     register_interrupt_handler(0xfe, interrupt_254_asm_entry);
 | |
|     register_interrupt_handler(0xff, interrupt_255_asm_entry);
 | |
| 
 | |
|     for (u8 i = 0; i < GENERIC_INTERRUPT_HANDLERS_COUNT; ++i) {
 | |
|         auto* handler = new UnhandledInterruptHandler(i);
 | |
|         handler->register_interrupt_handler();
 | |
|     }
 | |
| 
 | |
|     flush_idt();
 | |
| }
 | |
| 
 | |
| }
 | 
