#include #include #include #include "interrupt.h" #include #include #define NUM_ENTRIES 0x100 struct InterruptDescriptor { uint32_t offset; uint32_t selector; uint8_t gate_type; uint8_t ring; bool present; }; struct IDT { uint16_t size; uint64_t* base; } __attribute__((packed)); static uint64_t entries[NUM_ENTRIES]; struct IDT idt; enum EntryTypes { DivisionByZero = 0x00, _Reserved0 = 0x01, NMIInterrupt = 0x02, Breakpoint = 0x03, Overflow = 0x04, BoundsExceeded = 0x05, InvalidOpcode = 0x06, DeviceNotAvail = 0x07, DoubleFault = 0x08, CoprocSegORun = 0x09, InvalidTSS = 0x0A, SegNotPresent = 0x0B, StackSegFault = 0x0C, GenerProtFault = 0x0D, PageFault = 0x0E, _Reserved1 = 0x0F, x87FPUError = 0x10, AlignmentCheck = 0x11, MachineCheck = 0x12, SIMDFloatExcep = 0x13, }; static void encodeIdtEntry(uint8_t *target, struct InterruptDescriptor source) { // Encode the present bit if (source.present) { target[5] |= 0x80; } else { // Not present, return return; } // Encode the offset target[0] = (source.offset >> 0x00) & 0xFF; target[1] = (source.offset >> 0x08) & 0xFF; target[6] = (source.offset >> 0x10) & 0xFF; target[7] = (source.offset >> 0x18) & 0xFF; // Encode the base target[2] = (source.selector >> 0x00) & 0xFF; target[3] = (source.selector >> 0x08) & 0xFF; // Encode the gate type target[5] |= source.gate_type; // Encode the DPL target[5] |= (source.ring << 5); } static void add_entry(struct InterruptDescriptor entry, uint8_t index) { uint64_t descriptor = 0; encodeIdtEntry((uint8_t*) &descriptor, entry); entries[index] = descriptor; } static void load_idt() { idt.size = NUM_ENTRIES * 8 - 1; idt.base = entries; asm ( "lidt %0" : : "m"(idt) ); asm ( "sti" ); } struct InterruptDescriptor generate_entry(bool present, void isr(struct interrupt_frame*), uint8_t gate_type, uint8_t ring, uint8_t selector) { struct InterruptDescriptor entry; entry.present = present; entry.offset = (uint32_t)isr; entry.gate_type = gate_type; entry.ring = ring; entry.selector = selector; return entry; } static inline void outb(uint16_t port, uint8_t val) { __asm__ volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory"); /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). * The outb %al, %dx encoding is the only option for all other cases. * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ } void idt_init() { outb(0x21,0xfd); outb(0xa1,0xff); struct InterruptDescriptor entry; entry = generate_entry(true, exception, 0xF, 0, 0x8); for (int i = 0; i < 0x20; i++) { add_entry(entry, i); } entry = generate_entry(true, irq, 0xF, 0, 0x8); for (int i = 0x20; i < 0x28; i++) { add_entry(entry, i); } entry = generate_entry(true, divide_by_zero, 0xF, 0, 0x8); add_entry(entry, DivisionByZero); entry = generate_entry(true, general_protection_fault, 0xF, 0, 0x8); add_entry(entry, GenerProtFault); entry = generate_entry(true, double_fault, 0xF, 0, 0x8); add_entry(entry, DoubleFault); struct InterruptDescriptor print_entry; print_entry.present = true; print_entry.offset = (uint32_t)print; print_entry.gate_type = 0xE; print_entry.ring = 3; print_entry.selector = 0x8; add_entry(print_entry, 0x80); struct InterruptDescriptor input_entry; input_entry.present = true; input_entry.offset = (uint32_t)input; input_entry.gate_type = 0xE; input_entry.ring = 0; input_entry.selector = 0x8; add_entry(input_entry, 0x81); load_idt(); tss.ss0 = 0x10; asm("mov %%esp, %0" : "=r" (tss.esp0)); asm("mov $0x28, %ax"); asm("ltr %ax"); }