From 54f26c506e747f2c28df472e0e39de881c2f1cfe Mon Sep 17 00:00:00 2001 From: xamidev Date: Sat, 27 Dec 2025 13:52:05 +0100 Subject: [PATCH] 1000Hz PIC timer working + IDT dispatch/handler fixes --- Makefile | 2 +- debug.gdb | 3 +- src/idt/idt.S | 135 +++++++++++++++++++++++++++++++++++------------ src/idt/idt.c | 10 ++++ src/idt/idt.h | 2 +- src/kernel.h | 3 ++ src/kmain.c | 21 +++----- src/time/timer.c | 74 ++++++++++++++++++++++++++ src/time/timer.h | 6 +++ 9 files changed, 204 insertions(+), 52 deletions(-) create mode 100644 src/time/timer.c create mode 100644 src/time/timer.h diff --git a/Makefile b/Makefile index 472d432..641b1d7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ build: rm -f *.o - x86_64-elf-gcc -g -c -I src src/idt/idt.c src/mem/utils.c src/mem/gdt.c src/io/serial.c src/io/term.c src/io/printf.c src/kmain.c -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel + x86_64-elf-gcc -g -c -I src src/time/timer.c src/idt/idt.c src/mem/utils.c src/mem/gdt.c src/io/serial.c src/io/term.c src/io/printf.c src/kmain.c -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o nasm -f elf64 src/idt/idt.S -o idt_stub.o x86_64-elf-ld -o pepperk -T linker.ld *.o diff --git a/debug.gdb b/debug.gdb index 8518633..616ded2 100644 --- a/debug.gdb +++ b/debug.gdb @@ -1,2 +1,3 @@ target remote localhost:1234 -set disassembly-flavor intel \ No newline at end of file +set disassembly-flavor intel +display/8i $rip \ No newline at end of file diff --git a/src/idt/idt.S b/src/idt/idt.S index 7adf2bb..7cf344b 100644 --- a/src/idt/idt.S +++ b/src/idt/idt.S @@ -34,22 +34,22 @@ interrupt_stub: ; executed when the interrupt happened. ; (except rsp because it will already be saved in the iret frame) - push rax - push rbx - push rcx - push rdx - push rsi - push rdi - push rsp - push rbp - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 + push qword rax + push qword rbx + push qword rcx + push qword rdx + push qword rsi + push qword rdi + ;push qword rsp + push qword rbp + push qword r8 + push qword r9 + push qword r10 + push qword r11 + push qword r12 + push qword r13 + push qword r14 + push qword r15 ; Put stack pointer as first argument of our function mov rdi, rsp @@ -57,22 +57,22 @@ interrupt_stub: ; What the function returns (new stack pointer) is saved in rbp mov rsp, rax - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop rbp - pop rsp - pop rdi - pop rsi - pop rdx - pop rcx - pop rbx - pop rax + pop qword r15 + pop qword r14 + pop qword r13 + pop qword r12 + pop qword r11 + pop qword r10 + pop qword r9 + pop qword r8 + pop qword rbp + ;pop qword rsp + pop qword rdi + pop qword rsi + pop qword rdx + pop qword rcx + pop qword rbx + pop qword rax ; Removing the error code and vector number so stack doesn't ; get corrupted @@ -80,7 +80,7 @@ interrupt_stub: ; Restore ss, rsp, rflags, cs, rip of code that was executing ; before the interrupt - iret + iretq ; Vector handlers will be 16-byte aligned so that we can loop over them ; like * 16 to get each one's address @@ -233,4 +233,71 @@ vector_21_handler: push qword 21 jmp interrupt_stub -; The others are reserved (22->31) or external (32->255) interrupts \ No newline at end of file +; The others are reserved (22->31) or external (32->255) interrupts + +align 16 +vector_22_handler: + push qword 0 + push qword 22 + jmp interrupt_stub + +align 16 +vector_23_handler: + push qword 0 + push qword 23 + jmp interrupt_stub + +align 16 +vector_24_handler: + push qword 0 + push qword 24 + jmp interrupt_stub + +align 16 +vector_25_handler: + push qword 0 + push qword 25 + jmp interrupt_stub + +align 16 +vector_26_handler: + push qword 0 + push qword 26 + jmp interrupt_stub + +align 16 +vector_27_handler: + push qword 0 + push qword 27 + jmp interrupt_stub + +align 16 +vector_28_handler: + push qword 0 + push qword 28 + jmp interrupt_stub + +align 16 +vector_29_handler: + push qword 0 + push qword 29 + jmp interrupt_stub + +align 16 +vector_30_handler: + push qword 0 + push qword 30 + jmp interrupt_stub + +align 16 +vector_31_handler: + push qword 0 + push qword 31 + jmp interrupt_stub + +; PIT timer +align 16 +vector_32_handler: + push qword 0 + push qword 32 + jmp interrupt_stub \ No newline at end of file diff --git a/src/idt/idt.c b/src/idt/idt.c index e13b7ab..10df147 100644 --- a/src/idt/idt.c +++ b/src/idt/idt.c @@ -9,6 +9,9 @@ struct idtr idt_reg; // Address to our first interrupt handler extern char vector_0_handler[]; +// Timer ticks +extern uint64_t ticks; + void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl) { uint64_t handler_addr = (uint64_t)handler; @@ -119,6 +122,13 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) serial_kputs("kernel: idt: Control Protection Exception!\n"); break; + case 32: + serial_kputs("Tick!"); + ticks++; + // Send an EOI so that we can continue having interrupts + outb(0x20, 0x20); + break; + default: serial_kputs("kernel: idt: Unexpected interrupt\n"); break; diff --git a/src/idt/idt.h b/src/idt/idt.h index 37b6e32..3b61abb 100644 --- a/src/idt/idt.h +++ b/src/idt/idt.h @@ -36,7 +36,7 @@ struct cpu_status_t uint64_t r9; uint64_t r8; uint64_t rbp; - uint64_t rsp; + //uint64_t rsp; uint64_t rdi; uint64_t rsi; uint64_t rdx; diff --git a/src/kernel.h b/src/kernel.h index c3769ea..1ded436 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -7,4 +7,7 @@ enum ErrorCodes EIO }; +#define CLEAR_INTERRUPTS __asm__ volatile("cli") +#define SET_INTERRUPTS __asm__ volatile("sti") + #endif diff --git a/src/kmain.c b/src/kmain.c index 9616db4..ffb1474 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -7,6 +7,8 @@ #include "mem/gdt.h" #include "mem/utils.h" #include "idt/idt.h" +#include "kernel.h" +#include "time/timer.h" // Limine version used __attribute__((used, section(".limine_requests"))) @@ -36,20 +38,6 @@ static void hcf() } } -static inline void trigger_div0(void) -{ - asm volatile ( - "mov $1, %%rax\n" - "xor %%rdx, %%rdx\n" - "xor %%rcx, %%rcx\n" // divisor = 0 - "idiv %%rcx\n" - : - : - : "rax", "rcx", "rdx" - ); -} - - // This is our entry point void kmain() { @@ -63,12 +51,15 @@ void kmain() if (serial_init()) kputs("kernel: serial: error: Cannot init serial communication!"); + CLEAR_INTERRUPTS; gdt_init(); idt_init(); + timer_init(); + SET_INTERRUPTS; // Draw something printf("%s, %s!", "Hello", "world"); - trigger_div0(); + //printf("%d", 4/0); hcf(); } diff --git a/src/time/timer.c b/src/time/timer.c new file mode 100644 index 0000000..41415b4 --- /dev/null +++ b/src/time/timer.c @@ -0,0 +1,74 @@ +#include +#include "../io/serial.h" + +/* +For now, the timer module will be using the PIC. +Even though it's quite old, it's still supported by newer CPUs +and it will be precise enough for what we'll do. Also it's easier +to implement than ACPI etc. (we may upgrade to ACPI when we're +interested in multi-core functionnality like SMP) +*/ + +volatile uint64_t ticks = 0; + +void pic_remap() +{ + uint8_t master_mask = inb(0x21); + uint8_t slave_mask = inb(0xA1); + + // ICW1: start initialization + outb(0x20, 0x11); + outb(0xA0, 0x11); + + // ICW2: vector offsets + outb(0x21, 0x20); // Master PIC -> 0x20 + outb(0xA1, 0x28); // Slave PIC -> 0x28 + + // ICW3: tell Master about Slave at IRQ2 (0000 0100) + outb(0x21, 0x04); + // ICW3: tell Slave its cascade identity (0000 0010) + outb(0xA1, 0x02); + + // ICW4: 8086 mode + outb(0x21, 0x01); + outb(0xA1, 0x01); + + // Restore saved masks + outb(0x21, master_mask); + outb(0xA1, slave_mask); +} + +void pic_enable() +{ + // Enabling IRQ0 (unmasking it) but not the others + uint8_t mask = inb(0x21); + mask &= ~(1 << 0); // Clear bit 0 (IRQ0) + outb(0x21, mask); +} + +/* +Base frequency = 1.193182 MHz +1 tick per ms (divide by 1000) = roughly 1193 Hz +*/ +void pit_init() +{ + uint32_t frequency = 1000; // 1 kHz + uint32_t divisor = 1193182 / frequency; + + // Set PIT to mode 3, channel 0 + outb(0x43, 0x36); // 0x36 + + // Send divisor (low byte, then high byte) + outb(0x40, divisor & 0xFF); + outb(0x40, (divisor >> 8) & 0xFF); +} + +void timer_init() +{ + // Remapping the PIC, because at startup it conflicts with + // the reserved IRQs we have for faults/exceptions etc. + // so we move its IRQ0 to something not reserved (32) + pic_remap(); + pic_enable(); + pit_init(); +} \ No newline at end of file diff --git a/src/time/timer.h b/src/time/timer.h new file mode 100644 index 0000000..0c47d5a --- /dev/null +++ b/src/time/timer.h @@ -0,0 +1,6 @@ +#ifndef TIMER_H +#define TIMER_H + +void timer_init(); + +#endif \ No newline at end of file