# Interrupt Handling ModuOS implements a complete interrupt handling system for CPU exceptions and hardware interrupts. ## Overview ``` Hardware Event → PIC → CPU → IDT Lookup → ISR/IRQ Handler → Handle Interrupt → IRET → Resume Execution ``` ## Interrupt Descriptor Table (IDT) **File**: `src/arch/AMD64/interrupts/idt.c` ### IDT Structure The IDT maps interrupt vectors to handler functions: ```c struct idt_entry { uint16_t offset_low; // Handler address bits 0-15 uint16_t selector; // Code segment selector uint8_t ist; // Interrupt Stack Table uint8_t type_attr; // Type and attributes uint16_t offset_mid; // Handler address bits 16-31 uint32_t offset_high; // Handler address bits 32-63 uint32_t zero; // Reserved } __attribute__((packed)); struct idt_entry idt[256]; // 256 interrupt vectors ``` ### IDT Initialization ```c void idt_init(void) { // 1. Zero out IDT memset(&idt, 0, sizeof(idt)); // 2. Set up fault handlers (0-31) for (int i = 0; i < 32; i++) { idt_set_gate(i, isr_table[i], 0x08, 0x8E); } // 3. Set up IRQ handlers (32-47) for (int i = 32; i < 48; i++) { idt_set_gate(i, irq_table[i-32], 0x08, 0x8E); } // 4. Load IDT idtr.limit = sizeof(idt) - 1; idtr.base = (uint64_t)&idt; __asm__ volatile("lidt %0" : : "m"(idtr)); } ``` ## CPU Exceptions (Faults) **Vectors 0-31**: CPU-generated exceptions ### Exception Table | Vector | Name | Type | Error Code | |--------|------|------|------------| | 0 | Divide by Zero | Fault | No | | 1 | Debug | Fault/Trap | No | | 2 | Non-Maskable Interrupt | Interrupt | No | | 3 | Breakpoint | Trap | No | | 4 | Overflow | Trap | No | | 5 | Bound Range Exceeded | Fault | No | | 6 | Invalid Opcode | Fault | No | | 7 | Device Not Available | Fault | No | | 8 | Double Fault | Abort | Yes | | 10 | Invalid TSS | Fault | Yes | | 11 | Segment Not Present | Fault | Yes | | 12 | Stack-Segment Fault | Fault | Yes | | 13 | General Protection Fault | Fault | Yes | | 14 | Page Fault | Fault | Yes | | 16 | x87 FPU Error | Fault | No | | 17 | Alignment Check | Fault | Yes | | 18 | Machine Check | Abort | No | | 19 | SIMD Floating-Point | Fault | No | ### Fault Handler Implementation **File**: `src/arch/AMD64/interrupts/fault.asm` ```nasm %macro ISR_NOERRCODE 1 global isr%1 isr%1: push 0 ; Dummy error code push %1 ; Interrupt number jmp isr_common %endmacro %macro ISR_ERRCODE 1 global isr%1 isr%1: push %1 ; Interrupt number jmp isr_common %endmacro isr_common: ; Save all registers push rax push rbx push rcx push rdx push rsi push rdi push rbp push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15 ; Call C handler mov rdi, rsp ; Pass stack pointer call fault_handler ; Restore registers pop r15 pop r14 ; ... (all registers) pop rax ; Clean up error code and int number add rsp, 16 iretq ``` ### Fault Handler (C) **File**: `src/kernel/fault.c` ```c void fault_handler(registers_t *regs) { const char *exception_messages[] = { "Division By Zero", "Debug", "Non Maskable Interrupt", // ... (all 32 exceptions) }; if (regs->int_no < 32) { VGA_print("EXCEPTION: "); VGA_print(exception_messages[regs->int_no]); VGA_print("\n"); // Special handling for page fault if (regs->int_no == 14) { uint64_t faulting_address; __asm__ volatile("mov %%cr2, %0" : "=r"(faulting_address)); // Analyze error code and address } panic("Unrecoverable exception"); } } ``` ## Hardware Interrupts (IRQs) **Vectors 32-47**: Hardware device interrupts ### IRQ Mapping | IRQ | Vector | Device | |-----|--------|--------| | 0 | 32 | PIT (Timer) | | 1 | 33 | Keyboard | | 2 | 34 | Cascade (PIC2) | | 3 | 35 | COM2 | | 4 | 36 | COM1 | | 5 | 37 | LPT2 | | 6 | 38 | Floppy | | 7 | 39 | LPT1 | | 8 | 40 | RTC | | 9 | 41 | Free | | 10 | 42 | Free | | 11 | 43 | Free | | 12 | 44 | PS/2 Mouse | | 13 | 45 | FPU | | 14 | 46 | Primary ATA | | 15 | 47 | Secondary ATA | ### IRQ Handler Registration **File**: `src/arch/AMD64/interrupts/irq.c` ```c typedef void (*irq_handler_t)(registers_t *regs); static irq_handler_t irq_handlers[16]; void irq_register_handler(int irq, irq_handler_t handler) { irq_handlers[irq] = handler; } void irq_handler(registers_t *regs) { int irq = regs->int_no - 32; if (irq_handlers[irq]) { irq_handlers[irq](regs); } // Send EOI to PIC pic_send_eoi(irq); } ``` ## Programmable Interrupt Controller (PIC) **File**: `src/arch/AMD64/interrupts/pic.c` ### 8259 PIC Architecture ``` ┌──────────┐ ┌──────────┐ │ PIC1 │ Cascade │ PIC2 │ │ (Master) ├────────►│ (Slave) │ │ IRQ 0-7 │ IRQ 2 │ IRQ 8-15 │ └────┬─────┘ └────┬─────┘ │ │ └────────┬───────────┘ │ CPU INTR Pin ``` ### PIC Ports ```c #define PIC1_COMMAND 0x20 #define PIC1_DATA 0x21 #define PIC2_COMMAND 0xA0 #define PIC2_DATA 0xA1 #define PIC_EOI 0x20 // End of Interrupt ``` ### PIC Initialization ```c void pic_init(void) { // ICW1: Start initialization outb(PIC1_COMMAND, 0x11); outb(PIC2_COMMAND, 0x11); // ICW2: Set vector offsets outb(PIC1_DATA, 0x20); // Master: IRQ 0-7 → INT 32-39 outb(PIC2_DATA, 0x28); // Slave: IRQ 8-15 → INT 40-47 // ICW3: Set cascade outb(PIC1_DATA, 0x04); // Master has slave on IRQ2 outb(PIC2_DATA, 0x02); // Slave cascade identity // ICW4: 8086 mode outb(PIC1_DATA, 0x01); outb(PIC2_DATA, 0x01); // Unmask all IRQs outb(PIC1_DATA, 0x00); outb(PIC2_DATA, 0x00); } ``` ### Sending EOI ```c void pic_send_eoi(int irq) { if (irq >= 8) { outb(PIC2_COMMAND, PIC_EOI); // Slave PIC } outb(PIC1_COMMAND, PIC_EOI); // Master PIC } ``` ## Timer (PIT) **File**: `src/arch/AMD64/interrupts/timer.c` ### Programmable Interval Timer The PIT generates periodic interrupts for scheduling. ```c #define PIT_CHANNEL0 0x40 #define PIT_COMMAND 0x43 #define PIT_FREQUENCY 1193182 // Base frequency void pit_init(uint32_t frequency) { uint32_t divisor = PIT_FREQUENCY / frequency; // Command: Channel 0, Lo/Hi byte, Rate generator outb(PIT_COMMAND, 0x36); // Set divisor outb(PIT_CHANNEL0, divisor & 0xFF); outb(PIT_CHANNEL0, (divisor >> 8) & 0xFF); // Register IRQ0 handler irq_register_handler(0, timer_handler); } void timer_handler(registers_t *regs) { ticks++; scheduler_tick(); // Trigger process scheduling } ``` ## System Calls **Vector 0x80**: Software interrupt for syscalls ### Syscall Entry **File**: `src/arch/AMD64/syscall/syscall_entry.asm` ```nasm global syscall_handler syscall_handler: ; Save registers push rbp push rbx push r12 push r13 push r14 push r15 ; RAX = syscall number ; RDI, RSI, RDX, RCX, R8, R9 = arguments call syscall_dispatch ; Restore registers pop r15 pop r14 pop r13 pop r12 pop rbx pop rbp iretq ``` ## Interrupt Flag Management ### Enabling/Disabling Interrupts ```c static inline void cli(void) { __asm__ volatile("cli"); // Clear interrupt flag } static inline void sti(void) { __asm__ volatile("sti"); // Set interrupt flag } static inline uint64_t save_irq_state(void) { uint64_t rflags; __asm__ volatile("pushfq; pop %0" : "=r"(rflags)); cli(); return rflags; } static inline void restore_irq_state(uint64_t rflags) { __asm__ volatile("push %0; popfq" : : "r"(rflags)); } ``` ## Critical Sections ```c void critical_section_enter(void) { cli(); } void critical_section_exit(void) { sti(); } ``` ## Next Steps - [Process Management](Process-Management.md) - Scheduler interrupts - [System Calls](System-Calls.md) - Syscall interface - [Boot Process](Boot-Process.md) - IDT setup