diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 28e9451a..12f1a041 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES src/memory/MemoryManager.cpp src/boot/Init.cpp src/arch/Serial.cpp + src/arch/Timer.cpp ) if("${ARCH}" MATCHES "x86_64") @@ -13,6 +14,7 @@ if("${ARCH}" MATCHES "x86_64") src/arch/x86_64/Serial.cpp src/arch/x86_64/MMU.cpp src/arch/x86_64/CPU.cpp + src/arch/x86_64/Timer.cpp ) endif() diff --git a/kernel/src/arch/CPU.h b/kernel/src/arch/CPU.h index e2d2313c..daad9bd0 100644 --- a/kernel/src/arch/CPU.h +++ b/kernel/src/arch/CPU.h @@ -9,8 +9,13 @@ namespace CPU const char* platform_string(); void platform_init(); + void platform_finish_init(); [[noreturn]] void efficient_halt(); void switch_kernel_stack(u64 top); + + void enable_interrupts(); + void disable_interrupts(); + void wait_for_interrupt(); } \ No newline at end of file diff --git a/kernel/src/arch/Timer.cpp b/kernel/src/arch/Timer.cpp new file mode 100644 index 00000000..c06b003c --- /dev/null +++ b/kernel/src/arch/Timer.cpp @@ -0,0 +1,80 @@ +#include "arch/Timer.h" + +static u64 timer_ticks = 0; +static u64 boot_timestamp; + +namespace Timer +{ + void tick() + { + timer_ticks++; + } + + usize ticks() + { + return ticks_ms() / 1000; + } + + usize ticks_ms() + { + return timer_ticks / ARCH_TIMER_FREQ; + } + + usize ticks_us() // We want a bit of precision; if there are 10 ticks/ms, do not return the truncated ms value * + // 1000, but ticks * 100 (1000/10), which is more precise + { + if (ARCH_TIMER_FREQ > 1000) [[unlikely]] + return timer_ticks / (ARCH_TIMER_FREQ / 1000); + return timer_ticks * (1000 / ARCH_TIMER_FREQ); + } + + usize ticks_ns() + { + return ticks_us() * 1000; + } + + usize boot() + { + return boot_timestamp; + } + + usize boot_ms() + { + return boot_timestamp * MS_PER_SECOND; + } + + usize boot_us() + { + return boot_timestamp * US_PER_SECOND; + } + + usize boot_ns() + { + return boot_timestamp * NS_PER_SECOND; + } + + usize clock() + { + return boot() + ticks(); + } + + usize clock_ms() + { + return boot_ms() + ticks_ms(); + } + + usize clock_us() + { + return boot_us() + ticks_us(); + } + + usize clock_ns() + { + return boot_ns() + ticks_ns(); + } + + void init() + { + arch_init(); + } +} \ No newline at end of file diff --git a/kernel/src/arch/Timer.h b/kernel/src/arch/Timer.h new file mode 100644 index 00000000..d2895de3 --- /dev/null +++ b/kernel/src/arch/Timer.h @@ -0,0 +1,33 @@ +#pragma once +#include + +extern const usize ARCH_TIMER_FREQ; // How many timer ticks in one millisecond? + +static const usize MS_PER_SECOND = 1000; +static const usize US_PER_SECOND = MS_PER_SECOND * 1000; +static const usize NS_PER_SECOND = US_PER_SECOND * 1000; + +namespace Timer +{ + void tick(); + + usize raw_ticks(); + + usize ticks(); + usize ticks_ms(); + usize ticks_us(); + usize ticks_ns(); + + usize boot(); + usize boot_ms(); + usize boot_us(); + usize boot_ns(); + + usize clock(); + usize clock_ms(); + usize clock_us(); + usize clock_ns(); + + void arch_init(); + void init(); +} \ No newline at end of file diff --git a/kernel/src/arch/x86_64/CPU.asm b/kernel/src/arch/x86_64/CPU.asm index f59fca57..c5b37810 100644 --- a/kernel/src/arch/x86_64/CPU.asm +++ b/kernel/src/arch/x86_64/CPU.asm @@ -149,4 +149,5 @@ _isr18: ; machine check (#MC) ISR 19 ; SIMD floating-point exception (#XM) ISR 20 ; virtualization exception (#VE) ISR_ERROR 21 ; control-protection exception (#CP) -; ISR 22-31 reserved \ No newline at end of file +; ISR 22-31 reserved +IRQ 32, 0 ; timer interrupt \ No newline at end of file diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index c9a2010f..8f77a652 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -1,5 +1,7 @@ #include "arch/x86_64/CPU.h" #include "arch/Serial.h" +#include "arch/Timer.h" +#include "arch/x86_64/IO.h" #include #include #include @@ -105,6 +107,59 @@ static void setup_gdt() load_tr(0x2b); } +// PIC code + +#define PIC1_COMMAND 0x20 +#define PIC1_DATA 0x21 +#define PIC2_COMMAND 0xA0 +#define PIC2_DATA 0xA1 +#define PIC_EOI 0x20 + +#define ICW1_INIT 0x10 +#define ICW1_ICW4 0x01 +#define ICW4_8086 0x01 + +#define io_delay() IO::outb(0x80, 0) + +static void remap_pic() +{ + IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + io_delay(); + IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_delay(); + + IO::outb(PIC1_DATA, 0x20); + io_delay(); + + IO::outb(PIC2_DATA, 0x28); + io_delay(); + + IO::outb(PIC1_DATA, 4); + io_delay(); + IO::outb(PIC2_DATA, 2); + io_delay(); + + IO::outb(PIC1_DATA, ICW4_8086); + io_delay(); + IO::outb(PIC2_DATA, ICW4_8086); + io_delay(); + + IO::outb(PIC1_DATA, 0b11111110); + io_delay(); + IO::outb(PIC2_DATA, 0b11111111); +} + +static void pic_eoi(unsigned char irq) +{ + if (irq >= 8) IO::outb(PIC2_COMMAND, PIC_EOI); + IO::outb(PIC1_COMMAND, PIC_EOI); +} + +static void pic_eoi(Registers* regs) +{ + pic_eoi((unsigned char)(regs->error)); // On IRQs, the error code is the IRQ number +} + // IDT code and definitions struct IDTEntry @@ -160,6 +215,7 @@ static void idt_add_handler(short num, void* handler, u8 type_attr) #define INT(x) extern "C" void _isr##x() #define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate) +#define IRQ(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_InterruptGate) INT(0); INT(1); @@ -181,6 +237,7 @@ INT(18); INT(19); INT(20); INT(21); +INT(32); static void setup_idt() { @@ -206,6 +263,7 @@ static void setup_idt() TRAP(19); TRAP(20); TRAP(21); + IRQ(32); static IDTR idtr; idtr.limit = 0x0FFF; @@ -249,6 +307,11 @@ extern "C" void handle_x86_exception([[maybe_unused]] Registers* regs) extern "C" void arch_interrupt_entry(Registers* regs) { if (regs->isr < 32) handle_x86_exception(regs); + else if (regs->isr == 32) + { + Timer::tick(); + pic_eoi(regs); + } else { Serial::println("IRQ catched! Halting."); @@ -310,6 +373,26 @@ namespace CPU setup_idt(); } + void platform_finish_init() + { + remap_pic(); + } + + void enable_interrupts() + { + asm volatile("sti"); + } + + void disable_interrupts() + { + asm volatile("cli"); + } + + void wait_for_interrupt() + { + asm volatile("hlt"); + } + [[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using the // "hlt" instruction, which puts the CPU into a low-power idle state until the // next interrupt arrives... and we disable interrupts beforehand. diff --git a/kernel/src/arch/x86_64/Entry.asm b/kernel/src/arch/x86_64/Entry.asm index 92eee7f3..62dee46c 100644 --- a/kernel/src/arch/x86_64/Entry.asm +++ b/kernel/src/arch/x86_64/Entry.asm @@ -3,4 +3,5 @@ extern _start _main: xor rbp, rbp + cld call _start \ No newline at end of file diff --git a/kernel/src/arch/x86_64/Timer.cpp b/kernel/src/arch/x86_64/Timer.cpp new file mode 100644 index 00000000..da8dc528 --- /dev/null +++ b/kernel/src/arch/x86_64/Timer.cpp @@ -0,0 +1,16 @@ +#include "arch/Timer.h" +#include "arch/x86_64/IO.h" + +#define PIT_CHANNEL_0 0x40 + +const u64 base_frequency = 1193182; +const usize ARCH_TIMER_FREQ = 10; + +void Timer::arch_init() +{ + constexpr u16 divisor = (uint16_t)(base_frequency / (ARCH_TIMER_FREQ * 1000)); + static_assert(divisor >= 100); + IO::outb(PIT_CHANNEL_0, (uint8_t)(divisor & 0xFF)); + IO::outb(0x80, 0); // short delay + IO::outb(PIT_CHANNEL_0, (uint8_t)((divisor & 0xFF00) >> 8)); +} \ No newline at end of file diff --git a/kernel/src/boot/Init.cpp b/kernel/src/boot/Init.cpp index 46791f34..ab7a0ee1 100644 --- a/kernel/src/boot/Init.cpp +++ b/kernel/src/boot/Init.cpp @@ -20,6 +20,8 @@ void Init::check_magic() void Init::early_init() { + CPU::disable_interrupts(); + Framebuffer::init(); MemoryManager::init(); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 75ea1339..4442ceef 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,6 +1,7 @@ #include "arch/CPU.h" #include "arch/MMU.h" #include "arch/Serial.h" +#include "arch/Timer.h" #include "boot/Init.h" #include "memory/MemoryManager.h" #include "video/Framebuffer.h" @@ -92,9 +93,20 @@ extern "C" [[noreturn]] void _start() Serial::println("Successfully unmapped address"); - *ptr = 16; + Timer::init(); - Serial::println("ERROR: Still here after page fault"); + CPU::platform_finish_init(); + + CPU::enable_interrupts(); + + usize start = 0; + + while (1) + { + while ((Timer::ticks_ms() - start) < 20) { CPU::wait_for_interrupt(); } + start = Timer::ticks_ms(); + Serial::printf("%8zu milliseconds have passed!\n", start); + } CPU::efficient_halt(); } \ No newline at end of file diff --git a/luna/Format.cpp b/luna/Format.cpp index 02eb0ff6..6f3a2e56 100644 --- a/luna/Format.cpp +++ b/luna/Format.cpp @@ -185,7 +185,8 @@ static bool is_integer_format_specifier(char c) static usize to_string(usize value, usize base, char* buf, usize max, bool uppercase) { usize i = 0; - if (!value && max) + if (!max) return 0; + if (!value) { buf[i] = '0'; return 1;