123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- #include "types.h"
- #include "kmalloc.h"
- #include "i386.h"
- #include "Assertions.h"
- #include "Process.h"
- #include "MemoryManager.h"
- #include "IRQHandler.h"
- #include "PIC.h"
- #include "Scheduler.h"
- //#define PAGE_FAULT_DEBUG
- struct DescriptorTablePointer {
- word size;
- void* address;
- } PACKED;
- static DescriptorTablePointer s_idtr;
- static DescriptorTablePointer s_gdtr;
- static Descriptor* s_idt;
- static Descriptor* s_gdt;
- static IRQHandler** s_irq_handler;
- static Vector<word, KmallocEternalAllocator>* s_gdt_freelist;
- static word s_gdtLength;
- word gdt_alloc_entry()
- {
- ASSERT(s_gdt_freelist);
- ASSERT(!s_gdt_freelist->is_empty());
- return s_gdt_freelist->take_last();
- }
- void gdt_free_entry(word entry)
- {
- s_gdt_freelist->append(entry);
- }
- extern "C" void handle_irq();
- extern "C" void asm_irq_entry();
- asm(
- ".globl asm_irq_entry\n"
- "asm_irq_entry: \n"
- " pusha\n"
- " pushw %ds\n"
- " pushw %es\n"
- " pushw %ss\n"
- " pushw %ss\n"
- " popw %ds\n"
- " popw %es\n"
- " call handle_irq\n"
- " popw %es\n"
- " popw %ds\n"
- " popa\n"
- " iret\n"
- );
- #define EH_ENTRY(ec) \
- extern "C" void exception_ ## ec ## _handler(RegisterDumpWithExceptionCode&); \
- extern "C" void exception_ ## ec ## _entry(); \
- asm( \
- ".globl exception_" # ec "_entry\n" \
- "exception_" # ec "_entry: \n" \
- " pusha\n" \
- " pushw %ds\n" \
- " pushw %es\n" \
- " pushw %fs\n" \
- " pushw %gs\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " popw %ds\n" \
- " popw %es\n" \
- " popw %fs\n" \
- " popw %gs\n" \
- " mov %esp, %eax\n" \
- " call exception_" # ec "_handler\n" \
- " popw %gs\n" \
- " popw %gs\n" \
- " popw %fs\n" \
- " popw %es\n" \
- " popw %ds\n" \
- " popa\n" \
- " add $0x4, %esp\n" \
- " iret\n" \
- );
- #define EH_ENTRY_NO_CODE(ec) \
- extern "C" void exception_ ## ec ## _handler(RegisterDump&); \
- extern "C" void exception_ ## ec ## _entry(); \
- asm( \
- ".globl exception_" # ec "_entry\n" \
- "exception_" # ec "_entry: \n" \
- " pusha\n" \
- " pushw %ds\n" \
- " pushw %es\n" \
- " pushw %fs\n" \
- " pushw %gs\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " pushw %ss\n" \
- " popw %ds\n" \
- " popw %es\n" \
- " popw %fs\n" \
- " popw %gs\n" \
- " mov %esp, %eax\n" \
- " call exception_" # ec "_handler\n" \
- " popw %gs\n" \
- " popw %gs\n" \
- " popw %fs\n" \
- " popw %es\n" \
- " popw %ds\n" \
- " popa\n" \
- " iret\n" \
- );
- // 6: Invalid Opcode
- EH_ENTRY_NO_CODE(6);
- void exception_6_handler(RegisterDump& regs)
- {
- kprintf("%s invalid opcode: %u(%s)\n", current->is_ring0() ? "Kernel" : "Process", current->pid(), current->name().characters());
- word ss;
- dword esp;
- if (current->is_ring0()) {
- ss = regs.ds;
- esp = regs.esp;
- } else {
- ss = regs.ss_if_crossRing;
- esp = regs.esp_if_crossRing;
- }
- kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
- kprintf("stk=%w:%x\n", ss, esp);
- kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
- kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
- if (current->is_ring0()) {
- kprintf("Oh shit, we've crashed in ring 0 :(\n");
- HANG;
- }
- HANG;
- current->crash();
- }
- // 7: FPU not available exception
- EH_ENTRY_NO_CODE(7);
- void exception_7_handler(RegisterDump& regs)
- {
- (void)regs;
- asm volatile("clts");
- if (g_last_fpu_process == current)
- return;
- if (g_last_fpu_process) {
- asm volatile("fnsave %0":"=m"(g_last_fpu_process->fpu_state()));
- } else {
- asm volatile("fnclex");
- }
- g_last_fpu_process = current;
- if (current->has_used_fpu()) {
- asm volatile("frstor %0"::"m"(current->fpu_state()));
- } else {
- asm volatile("fninit");
- current->set_has_used_fpu(true);
- }
- #ifdef FPU_EXCEPTION_DEBUG
- kprintf("%s FPU not available exception: %u(%s)\n", current->is_ring0() ? "Kernel" : "Process", current->pid(), current->name().characters());
- word ss;
- dword esp;
- if (current->is_ring0()) {
- ss = regs.ds;
- esp = regs.esp;
- } else {
- ss = regs.ss_if_crossRing;
- esp = regs.esp_if_crossRing;
- }
- kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
- kprintf("stk=%w:%x\n", ss, esp);
- kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
- kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
- #endif
- }
- // 13: General Protection Fault
- EH_ENTRY(13);
- void exception_13_handler(RegisterDumpWithExceptionCode& regs)
- {
- kprintf("%s GPF: %u(%s)\n", current->is_ring0() ? "Kernel" : "User", current->pid(), current->name().characters());
- word ss;
- dword esp;
- if (current->is_ring0()) {
- ss = regs.ds;
- esp = regs.esp;
- } else {
- ss = regs.ss_if_crossRing;
- esp = regs.esp_if_crossRing;
- }
- kprintf("exception code: %w\n", regs.exception_code);
- kprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
- kprintf("stk=%w:%x\n", ss, esp);
- kprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
- kprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
- if (current->is_ring0()) {
- kprintf("Oh shit, we've crashed in ring 0 :(\n");
- HANG;
- }
- current->crash();
- }
- // 14: Page Fault
- EH_ENTRY(14);
- void exception_14_handler(RegisterDumpWithExceptionCode& regs)
- {
- ASSERT(current);
- dword faultAddress;
- asm ("movl %%cr2, %%eax":"=a"(faultAddress));
- dword fault_page_directory;
- asm ("movl %%cr3, %%eax":"=a"(fault_page_directory));
- dbgprintf("%s(%u): ring%u %s page fault in PD=%x, %s L%x\n",
- current->name().characters(),
- current->pid(),
- regs.cs & 3,
- regs.exception_code & 1 ? "PV" : "NP",
- fault_page_directory,
- regs.exception_code & 2 ? "write" : "read",
- faultAddress);
- word ss;
- dword esp;
- if (current->is_ring0()) {
- ss = regs.ds;
- esp = regs.esp;
- } else {
- ss = regs.ss_if_crossRing;
- esp = regs.esp_if_crossRing;
- }
- auto dump_registers_and_code = [&] {
- dbgprintf("exception code: %w\n", regs.exception_code);
- dbgprintf("pc=%w:%x ds=%w es=%w fs=%w gs=%w\n", regs.cs, regs.eip, regs.ds, regs.es, regs.fs, regs.gs);
- dbgprintf("stk=%w:%x\n", ss, esp);
- dbgprintf("eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
- dbgprintf("ebp=%x esp=%x esi=%x edi=%x\n", regs.ebp, esp, regs.esi, regs.edi);
- if (current->validate_read((void*)regs.eip, 8)) {
- byte* codeptr = (byte*)regs.eip;
- dbgprintf("code: %b %b %b %b %b %b %b %b\n",
- codeptr[0],
- codeptr[1],
- codeptr[2],
- codeptr[3],
- codeptr[4],
- codeptr[5],
- codeptr[6],
- codeptr[7]
- );
- }
- };
- #ifdef PAGE_FAULT_DEBUG
- dump_registers_and_code();
- #endif
- auto response = MM.handle_page_fault(PageFault(regs.exception_code, LinearAddress(faultAddress)));
- if (response == PageFaultResponse::ShouldCrash) {
- kprintf("%s(%u) unrecoverable page fault, %s laddr=%p\n",
- current->name().characters(),
- current->pid(),
- regs.exception_code & 2 ? "write" : "read",
- faultAddress);
- dump_registers_and_code();
- current->crash();
- } else if (response == PageFaultResponse::Continue) {
- #ifdef PAGE_FAULT_DEBUG
- dbgprintf("Continuing after resolved page fault\n");
- #endif
- } else {
- ASSERT_NOT_REACHED();
- }
- }
- #define EH(i, msg) \
- static void _exception ## i () \
- { \
- kprintf(msg"\n"); \
- dword cr0, cr2, cr3, cr4; \
- asm ("movl %%cr0, %%eax":"=a"(cr0)); \
- asm ("movl %%cr2, %%eax":"=a"(cr2)); \
- asm ("movl %%cr3, %%eax":"=a"(cr3)); \
- asm ("movl %%cr4, %%eax":"=a"(cr4)); \
- kprintf("CR0=%x CR2=%x CR3=%x CR4=%x\n", cr0, cr2, cr3, cr4); \
- HANG; \
- }
- EH(0, "Divide error")
- EH(1, "Debug exception")
- EH(2, "Unknown error")
- EH(3, "Breakpoint")
- 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")
- static void write_raw_gdt_entry(word selector, dword low, dword high)
- {
- word i = (selector & 0xfffc) >> 3;
- s_gdt[i].low = low;
- s_gdt[i].high = high;
- if (i > s_gdtLength) {
- s_gdtr.size = (s_gdtLength + 1) * 8;
- }
- }
- void write_gdt_entry(word selector, Descriptor& descriptor)
- {
- write_raw_gdt_entry(selector, descriptor.low, descriptor.high);
- }
- Descriptor& get_gdt_entry(word selector)
- {
- word i = (selector & 0xfffc) >> 3;
- return *(Descriptor*)(&s_gdt[i]);
- }
- void flush_gdt()
- {
- s_gdtr.address = s_gdt;
- s_gdtr.size = (s_gdtLength * 8) - 1;
- asm("lgdt %0"::"m"(s_gdtr));
- }
- void gdt_init()
- {
- s_gdt = static_cast<Descriptor*>(kmalloc_eternal(sizeof(Descriptor) * 256));
- s_gdtLength = 5;
- s_gdt_freelist = new Vector<word, KmallocEternalAllocator>();
- s_gdt_freelist->ensure_capacity(256);
- for (size_t i = s_gdtLength; i < 256; ++i)
- s_gdt_freelist->append(i * 8);
- s_gdtLength = 256;
- s_gdtr.address = s_gdt;
- s_gdtr.size = (s_gdtLength * 8) - 1;
- write_raw_gdt_entry(0x0000, 0x00000000, 0x00000000);
- write_raw_gdt_entry(0x0008, 0x0000ffff, 0x00cf9a00);
- write_raw_gdt_entry(0x0010, 0x0000ffff, 0x00cf9200);
- write_raw_gdt_entry(0x0018, 0x0000ffff, 0x00cffa00);
- write_raw_gdt_entry(0x0020, 0x0000ffff, 0x00cff200);
- flush_gdt();
- }
- static void unimp_trap()
- {
- kprintf("Unhandled IRQ.");
- HANG;
- }
- void register_irq_handler(byte irq, IRQHandler& handler)
- {
- ASSERT(!s_irq_handler[irq]);
- s_irq_handler[irq] = &handler;
- register_interrupt_handler(IRQ_VECTOR_BASE + irq, asm_irq_entry);
- }
- void unregister_irq_handler(byte irq, IRQHandler& handler)
- {
- ASSERT(s_irq_handler[irq] == &handler);
- s_irq_handler[irq] = nullptr;
- }
- void register_interrupt_handler(byte index, void (*f)())
- {
- s_idt[index].low = 0x00080000 | LSW((f));
- s_idt[index].high = ((dword)(f) & 0xffff0000) | 0x8e00;
- flush_idt();
- }
- void register_user_callable_interrupt_handler(byte index, void (*f)())
- {
- s_idt[index].low = 0x00080000 | LSW((f));
- s_idt[index].high = ((dword)(f) & 0xffff0000) | 0xef00;
- flush_idt();
- }
- void flush_idt()
- {
- asm("lidt %0"::"m"(s_idtr));
- }
- /* If an 8259 gets cranky, it'll generate a spurious IRQ7.
- * ATM I don't have a clear grasp on when/why this happens,
- * so I ignore them and assume it makes no difference.
- */
- extern "C" void irq7_handler();
- asm(
- ".globl irq7_handler \n"
- "irq7_handler: \n"
- " iret\n"
- );
- void idt_init()
- {
- s_idt = static_cast<Descriptor*>(kmalloc_eternal(sizeof(Descriptor) * 256));
- s_idtr.address = s_idt;
- s_idtr.size = 0x100 * 8;
- for (byte i = 0xff; i > 0x10; --i)
- register_interrupt_handler(i, unimp_trap);
- register_interrupt_handler(0x00, _exception0);
- register_interrupt_handler(0x01, _exception1);
- register_interrupt_handler(0x02, _exception2);
- register_interrupt_handler(0x03, _exception3);
- register_interrupt_handler(0x04, _exception4);
- register_interrupt_handler(0x05, _exception5);
- register_interrupt_handler(0x06, exception_6_entry);
- register_interrupt_handler(0x07, exception_7_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, exception_13_entry);
- register_interrupt_handler(0x0e, exception_14_entry);
- register_interrupt_handler(0x0f, _exception15);
- register_interrupt_handler(0x10, _exception16);
- register_interrupt_handler(0x57, irq7_handler);
- s_irq_handler = static_cast<IRQHandler**>(kmalloc_eternal(sizeof(IRQHandler*) * 16));
- for (byte i = 0; i < 16; ++i) {
- s_irq_handler[i] = nullptr;
- }
- flush_idt();
- }
- void load_task_register(word selector)
- {
- asm("ltr %0"::"r"(selector));
- }
- void handle_irq()
- {
- word isr = PIC::get_isr();
- if (!isr) {
- kprintf("Spurious IRQ\n");
- return;
- }
- byte irq = 0;
- for (byte i = 0; i < 16; ++i) {
- if (i == 2)
- continue;
- if (isr & (1 << i)) {
- irq = i;
- break;
- }
- }
- if (s_irq_handler[irq])
- s_irq_handler[irq]->handle_irq();
- PIC::eoi(irq);
- }
- void __assertion_failed(const char* msg, const char* file, unsigned line, const char* func)
- {
- asm volatile("cli");
- kprintf("ASSERTION FAILED: %s\n%s:%u in %s\n", msg, file, line, func);
- asm volatile("hlt");
- for (;;);
- }
|