Skip to content

Interrupt Handling

NtinosTheGamer2324 edited this page Dec 11, 2025 · 1 revision

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:

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

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

%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

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

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

#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

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

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.

#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

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

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

void critical_section_enter(void) {
    cli();
}

void critical_section_exit(void) {
    sti();
}

Next Steps

Clone this wiki locally