From 4202c999fab285257afeb61fec80b3573e6681e6 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 7 Dec 2022 02:41:18 +0200 Subject: [PATCH] Kernel: IRQs are now working :) --- kernel/Makefile | 11 ++- kernel/arch/i386/tty.cpp | 15 ++-- kernel/include/kernel/IDT.h | 26 ++----- kernel/include/kernel/IO.h | 25 +++++++ kernel/include/kernel/PIC.h | 15 ++++ kernel/include/kernel/PIT.h | 12 +++ kernel/include/kernel/PS2.h | 8 ++ kernel/kernel/GDT.cpp | 35 ++++----- kernel/kernel/GDT_asm.S | 16 ++++ kernel/kernel/IDT.cpp | 145 ++++++++++++++++++++++++++---------- kernel/kernel/IDT_asm.S | 15 ++++ kernel/kernel/PIC.cpp | 105 ++++++++++++++++++++++++++ kernel/kernel/PIT.cpp | 53 +++++++++++++ kernel/kernel/PS2.cpp | 23 ++++++ kernel/kernel/kernel.cpp | 23 ++++-- 15 files changed, 431 insertions(+), 96 deletions(-) create mode 100644 kernel/include/kernel/IO.h create mode 100644 kernel/include/kernel/PIC.h create mode 100644 kernel/include/kernel/PIT.h create mode 100644 kernel/include/kernel/PS2.h create mode 100644 kernel/kernel/GDT_asm.S create mode 100644 kernel/kernel/IDT_asm.S create mode 100644 kernel/kernel/PIC.cpp create mode 100644 kernel/kernel/PIT.cpp create mode 100644 kernel/kernel/PS2.cpp diff --git a/kernel/Makefile b/kernel/Makefile index 3fe5e6f7..5809bd09 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -14,7 +14,7 @@ BOOTDIR?=$(EXEC_PREFIX)/boot INCLUDEDIR?=$(PREFIX)/include CFLAGS:=$(CFLAGS) -D__is_kernel -Iinclude -fstack-protector -ffreestanding -Wall -Wextra -Wno-unused-function -CPPFLAGS:=$(CPPFLAGS) +CPPFLAGS:=$(CPPFLAGS) -fno-rtti -fno-exceptions LDFLAGS:=$(LDFLAGS) LIBS:=$(LIBS) -nostdlib -lk -lgcc @@ -32,9 +32,14 @@ BUILDDIR=$(abspath build) KERNEL_OBJS=\ $(KERNEL_ARCH_OBJS) \ kernel/GDT.o \ +kernel/GDT_asm.o \ kernel/IDT.o \ +kernel/IDT_asm.o \ kernel/kernel.o \ kernel/kmalloc.o \ +kernel/PIC.o \ +kernel/PIT.o \ +kernel/PS2.o \ kernel/SSP.o \ OBJS=\ @@ -59,12 +64,12 @@ $(ARCHDIR)/crtn.o \ all: banan-os.kernel banan-os.kernel: always $(OBJS) $(ARCHDIR)/linker.ld - cd $(BUILDDIR) && $(CC) -T ../$(ARCHDIR)/linker.ld -o banan-os.kernel $(CFLAGS) $(LINK_LIST) + cd $(BUILDDIR) && $(CXX) -T ../$(ARCHDIR)/linker.ld -o banan-os.kernel $(CFLAGS) $(CPPFLAGS) $(LINK_LIST) cd $(BUILDDIR) && grub-file --is-x86-multiboot banan-os.kernel $(ARCHDIR)/crtbegin.o $(ARCHDIR)/crtend.o: OBJ=`$(CC) $(CFLAGS) $(LDFLAGS) -print-file-name=$(@F)` && cp "$$OBJ" $(BUILDDIR)/$@ - + .cpp.o: $(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS) $(CPPFLAGS) diff --git a/kernel/arch/i386/tty.cpp b/kernel/arch/i386/tty.cpp index 4c11f86a..2b02d748 100644 --- a/kernel/arch/i386/tty.cpp +++ b/kernel/arch/i386/tty.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -77,19 +78,13 @@ void terminal_clear_line(size_t line) terminal_putentryat(' ', terminal_color, x, line); } - -static inline void outb(uint16_t port, uint8_t value) -{ - asm volatile ("outb %0, %1" : : "a" (value), "Nd" (port)); -} static void terminal_update_cursor() { uint16_t pos = terminal_row * VGA_WIDTH + terminal_col; - - outb(0x3D4, 0x0F); - outb(0x3D5, (uint8_t) (pos & 0xFF)); - outb(0x3D4, 0x0E); - outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); + IO::outb(0x3D4, 0x0F); + IO::outb(0x3D5, (uint8_t) (pos & 0xFF)); + IO::outb(0x3D4, 0x0E); + IO::outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); } void terminal_set_cursor_pos(int x, int y) diff --git a/kernel/include/kernel/IDT.h b/kernel/include/kernel/IDT.h index 07ca1969..ecc0a351 100644 --- a/kernel/include/kernel/IDT.h +++ b/kernel/include/kernel/IDT.h @@ -2,26 +2,12 @@ #include -union GateDescriptor +constexpr uint8_t IRQ_VECTOR_BASE = 0x50; + +namespace IDT { - struct - { - uint16_t offset_lo; - uint16_t selector; - uint8_t reserved; - uint8_t type : 4; - uint8_t zero : 1; - uint8_t dpl : 2; - uint8_t present : 1; - uint16_t offset_hi; - }; - struct - { - uint32_t low; - uint32_t high; - }; - -} __attribute__((packed)); + void initialize(); + void register_irq_handler(uint8_t irq, void (*f)()); -void idt_initialize(); \ No newline at end of file +} \ No newline at end of file diff --git a/kernel/include/kernel/IO.h b/kernel/include/kernel/IO.h new file mode 100644 index 00000000..f81eaca2 --- /dev/null +++ b/kernel/include/kernel/IO.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace IO +{ + + static inline void outb(uint16_t port, uint8_t val) + { + asm volatile("outb %0, %1" : : "a"(val), "Nd"(port)); + } + + static inline uint8_t inb(uint16_t port) + { + uint8_t ret; + asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; + } + + static inline void io_wait() + { + outb(0x80, 0); + } + +} \ No newline at end of file diff --git a/kernel/include/kernel/PIC.h b/kernel/include/kernel/PIC.h new file mode 100644 index 00000000..c4bf9440 --- /dev/null +++ b/kernel/include/kernel/PIC.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace PIC +{ + + void initialize(); + void eoi(uint8_t); + void unmask(uint8_t); + void mask(uint8_t); + + uint16_t get_isr(); + +} \ No newline at end of file diff --git a/kernel/include/kernel/PIT.h b/kernel/include/kernel/PIT.h new file mode 100644 index 00000000..dc7c879d --- /dev/null +++ b/kernel/include/kernel/PIT.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace PIT +{ + + uint64_t ms_since_boot(); + + void initialize(); + +} \ No newline at end of file diff --git a/kernel/include/kernel/PS2.h b/kernel/include/kernel/PS2.h new file mode 100644 index 00000000..3e10966d --- /dev/null +++ b/kernel/include/kernel/PS2.h @@ -0,0 +1,8 @@ +#pragma once + +namespace PS2 +{ + + void initialize(); + +} \ No newline at end of file diff --git a/kernel/kernel/GDT.cpp b/kernel/kernel/GDT.cpp index 040465fc..67d202fa 100644 --- a/kernel/kernel/GDT.cpp +++ b/kernel/kernel/GDT.cpp @@ -10,37 +10,34 @@ struct GDTR static GDTR s_gdtr; static SegmentDesriptor* s_gdt; -void write_gdt_entry_raw(uint8_t index, uint32_t low, uint32_t high) +extern "C" void load_gdt(void* gdt_ptr); + +void write_gdt_entry_raw(uint8_t segment, uint32_t low, uint32_t high) { + uint8_t index = segment >> 3; s_gdt[index].low = low; s_gdt[index].high = high; } -void write_gdt_entry(uint8_t index, SegmentDesriptor descriptor) +void write_gdt_entry(uint8_t segment, SegmentDesriptor descriptor) { - write_gdt_entry_raw(index, descriptor.low, descriptor.high); -} - -static void flush_gdt() -{ - asm volatile("lgdt %0"::"m"(s_gdtr)); + write_gdt_entry_raw(segment, descriptor.low, descriptor.high); } void gdt_initialize() { - constexpr size_t gdt_size = 256; + constexpr uint8_t GDT_SIZE = 5; + + s_gdt = new SegmentDesriptor[GDT_SIZE]; - s_gdt = new SegmentDesriptor[gdt_size]; - s_gdtr.address = s_gdt; - s_gdtr.size = gdt_size * 8 - 1; + s_gdtr.size = GDT_SIZE * 8 - 1; - uint8_t index = 0; - write_gdt_entry(index++, { 0, 0x0000, 0x00, 0x0 }); // null - write_gdt_entry(index++, { 0, 0xFFFF, 0x9A, 0xC }); // kernel code - write_gdt_entry(index++, { 0, 0xFFFF, 0x92, 0xC }); // kernel data - write_gdt_entry(index++, { 0, 0xFFFF, 0xFA, 0xC }); // user code - write_gdt_entry(index++, { 0, 0xFFFF, 0xF2, 0xC }); // user data + write_gdt_entry(0x00, { 0, 0x00000, 0x00, 0x0 }); // null + write_gdt_entry(0x08, { 0, 0xFFFFF, 0x9A, 0xC }); // kernel code + write_gdt_entry(0x10, { 0, 0xFFFFF, 0x92, 0xC }); // kernel data + write_gdt_entry(0x18, { 0, 0xFFFFF, 0xFA, 0xC }); // user code + write_gdt_entry(0x20, { 0, 0xFFFFF, 0xF2, 0xC }); // user data - flush_gdt(); + load_gdt(&s_gdtr); } diff --git a/kernel/kernel/GDT_asm.S b/kernel/kernel/GDT_asm.S new file mode 100644 index 00000000..5dd1fd3a --- /dev/null +++ b/kernel/kernel/GDT_asm.S @@ -0,0 +1,16 @@ +.global load_gdt + +load_gdt: + movl 4(%esp),%eax + lgdt (%eax) + + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + jmp $0x08,$flush + +flush: + ret \ No newline at end of file diff --git a/kernel/kernel/IDT.cpp b/kernel/kernel/IDT.cpp index 793fea88..ed0ca0d0 100644 --- a/kernel/kernel/IDT.cpp +++ b/kernel/kernel/IDT.cpp @@ -1,8 +1,31 @@ #include #include #include +#include #include +union GateDescriptor +{ + struct + { + uint16_t offset_lo; + uint16_t selector; + uint8_t reserved; + uint8_t type : 4; + uint8_t zero : 1; + uint8_t dpl : 2; + uint8_t present : 1; + uint16_t offset_hi; + }; + + struct + { + uint32_t low; + uint32_t high; + }; + +} __attribute__((packed)); + struct IDTR { uint16_t size; @@ -12,6 +35,10 @@ struct IDTR static IDTR s_idtr; static GateDescriptor* s_idt; +static void (*s_irq_handlers[16])(); + +extern "C" void handle_irq(); +extern "C" void handle_irq_common(); #define INTERRUPT_HANDLER(i, msg) \ static void interrupt ## i () \ @@ -42,54 +69,94 @@ INTERRUPT_HANDLER(0x0e, "Page fault") INTERRUPT_HANDLER(0x0f, "Unknown error") INTERRUPT_HANDLER(0x10, "Coprocessor error") -#define REGISTER_HANDLER(i) register_interrupt_handeler(i, interrupt ## i) +#define REGISTER_HANDLER(i) register_interrupt_handler(i, interrupt ## i) -static void flush_idt() +void handle_irq() { - asm volatile("lidt %0"::"m"(s_idtr)); + uint16_t isr = PIC::get_isr(); + if (!isr) { + kprint("Spurious IRQ\n"); + return; + } + + uint8_t irq = 0; + for (uint8_t i = 0; i < 16; ++i) { + if (i == 2) + continue; + if (isr & (1 << i)) { + irq = i; + break; + } + } + + if (s_irq_handlers[irq]) + s_irq_handlers[irq](); + else + kprint("no handler for irq {}\n", irq); + + PIC::eoi(irq); } -static void unimplemented_trap() +namespace IDT { - Kernel::panic("Unhandeled IRQ"); -} -static void register_interrupt_handeler(uint8_t index, void (*f)()) -{ - s_idt[index].low = 0x00080000 | ((uint32_t)(f) & 0x0000ffff); - s_idt[index].high = ((uint32_t)(f) & 0xffff0000) | 0x8e00; - flush_idt(); -} + static void flush_idt() + { + asm volatile("lidt %0"::"m"(s_idtr)); + } -void idt_initialize() -{ - constexpr size_t idt_size = 256; + static void unimplemented_trap() + { + Kernel::panic("Unhandeled IRQ"); + } - s_idt = new GateDescriptor[idt_size]; + static void register_interrupt_handler(uint8_t index, void (*f)()) + { + s_idt[index].low = 0x00080000 | ((uint32_t)(f) & 0x0000ffff); + s_idt[index].high = ((uint32_t)(f) & 0xffff0000) | 0x8e00; + flush_idt(); + } - s_idtr.offset = s_idt; - s_idtr.size = idt_size * 8; + void register_irq_handler(uint8_t irq, void (*f)()) + { + s_irq_handlers[irq] = f; + register_interrupt_handler(IRQ_VECTOR_BASE + irq, handle_irq_common); + } - REGISTER_HANDLER(0x00); - REGISTER_HANDLER(0x01); - REGISTER_HANDLER(0x02); - REGISTER_HANDLER(0x03); - REGISTER_HANDLER(0x04); - REGISTER_HANDLER(0x05); - REGISTER_HANDLER(0x06); - REGISTER_HANDLER(0x07); - REGISTER_HANDLER(0x08); - REGISTER_HANDLER(0x09); - REGISTER_HANDLER(0x0a); - REGISTER_HANDLER(0x0b); - REGISTER_HANDLER(0x0c); - REGISTER_HANDLER(0x0d); - REGISTER_HANDLER(0x0e); - REGISTER_HANDLER(0x0f); - REGISTER_HANDLER(0x10); + void initialize() + { + constexpr size_t idt_size = 256; - for (uint16_t i = 0x11; i < idt_size; i++) - register_interrupt_handeler(i, unimplemented_trap); + s_idt = new GateDescriptor[idt_size]; - flush_idt(); -} + s_idtr.offset = s_idt; + s_idtr.size = idt_size * 8; + + for (uint8_t i = 0xff; i > 0x10; i--) + register_interrupt_handler(i, unimplemented_trap); + + REGISTER_HANDLER(0x00); + REGISTER_HANDLER(0x01); + REGISTER_HANDLER(0x02); + REGISTER_HANDLER(0x03); + REGISTER_HANDLER(0x04); + REGISTER_HANDLER(0x05); + REGISTER_HANDLER(0x06); + REGISTER_HANDLER(0x07); + REGISTER_HANDLER(0x08); + REGISTER_HANDLER(0x09); + REGISTER_HANDLER(0x0a); + REGISTER_HANDLER(0x0b); + REGISTER_HANDLER(0x0c); + REGISTER_HANDLER(0x0d); + REGISTER_HANDLER(0x0e); + REGISTER_HANDLER(0x0f); + REGISTER_HANDLER(0x10); + + for (uint8_t i = 0; i < sizeof(s_irq_handlers) / sizeof(*s_irq_handlers); i++) + s_irq_handlers[i] = nullptr; + + flush_idt(); + } + +} \ No newline at end of file diff --git a/kernel/kernel/IDT_asm.S b/kernel/kernel/IDT_asm.S new file mode 100644 index 00000000..523f1bf5 --- /dev/null +++ b/kernel/kernel/IDT_asm.S @@ -0,0 +1,15 @@ +.globl handle_irq_common + +handle_irq_common: + pusha + pushw %ds + pushw %es + pushw %ss + pushw %ss + popw %ds + popw %es + call handle_irq + popw %es + popw %ds + popa + iret \ No newline at end of file diff --git a/kernel/kernel/PIC.cpp b/kernel/kernel/PIC.cpp new file mode 100644 index 00000000..f9e28023 --- /dev/null +++ b/kernel/kernel/PIC.cpp @@ -0,0 +1,105 @@ +#include +#include +#include + +#define PIC1 0x20 /* IO base address for master PIC */ +#define PIC2 0xA0 /* IO base address for slave PIC */ +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) + +#define PIC_EOI 0x20 /* End-of-interrupt command code */ + +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + + +namespace PIC +{ + + void initialize() + { + // Start the initialization sequence (in cascade mode) + IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + IO::io_wait(); + IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + IO::io_wait(); + + // ICW2 + IO::outb(PIC1_DATA, IRQ_VECTOR_BASE); + IO::io_wait(); + IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08); + IO::io_wait(); + + // ICW3 + IO::outb(PIC1_DATA, 4); + IO::io_wait(); + IO::outb(PIC2_DATA, 2); + IO::io_wait(); + + // ICW4 + IO::outb(PIC1_DATA, ICW4_8086); + IO::io_wait(); + IO::outb(PIC2_DATA, ICW4_8086); + IO::io_wait(); + + // Mask everything + IO::outb(PIC1_DATA, 0xff); + IO::outb(PIC2_DATA, 0xff); + } + + void eoi(uint8_t irq) + { + if (irq >= 8) + IO::outb(PIC2_COMMAND, PIC_EOI); + IO::outb(PIC1_COMMAND, PIC_EOI); + } + + void mask(uint8_t irq) { + uint16_t port; + uint8_t value; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + value = IO::inb(port) | (1 << irq); + IO::outb(port, value); + } + + void unmask(uint8_t irq) { + uint16_t port; + uint8_t value; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + value = IO::inb(port) & ~(1 << irq); + IO::outb(port, value); + } + + uint16_t get_isr() + { + IO::outb(PIC1_COMMAND, 0x0b); + IO::outb(PIC2_COMMAND, 0x0b); + uint8_t isr0 = IO::inb(PIC1_COMMAND); + uint8_t isr1 = IO::inb(PIC2_COMMAND); + return (isr1 << 8) | isr0; + } + +} \ No newline at end of file diff --git a/kernel/kernel/PIT.cpp b/kernel/kernel/PIT.cpp new file mode 100644 index 00000000..ab4d15f3 --- /dev/null +++ b/kernel/kernel/PIT.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#define IRQ_TIMER 0 + +#define TIMER0_CTL 0x40 +#define TIMER1_CTL 0x41 +#define TIMER2_CTL 0x42 +#define PIT_CTL 0x43 + +#define SELECT_CHANNEL0 0x00 +#define SELECT_CHANNEL1 0x40 +#define SELECT_CHANNEL2 0x80 + +#define ACCESS_HI 0x10 +#define ACCESS_LO 0x20 + +#define MODE_SQUARE_WAVE 0x06 + +#define BASE_FREQUENCY 1193182 +#define TICKS_PER_SECOND 1000 + +namespace PIT +{ + static uint64_t s_system_time = 0; + + void clock_handle() + { + PIT::s_system_time++; + } + + uint64_t ms_since_boot() + { + return s_system_time; + } + + void initialize() + { + constexpr uint16_t timer_reload = BASE_FREQUENCY / TICKS_PER_SECOND; + + IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_SQUARE_WAVE); + + IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff); + IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff); + + IDT::register_irq_handler(IRQ_TIMER, clock_handle); + + PIC::unmask(IRQ_TIMER); + } + +} diff --git a/kernel/kernel/PS2.cpp b/kernel/kernel/PS2.cpp new file mode 100644 index 00000000..bf015637 --- /dev/null +++ b/kernel/kernel/PS2.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +#define PS2_IRQ 0x01 + + +namespace PS2 +{ + + void irq_handler() + { + kprint("keyboard\n"); + } + + void initialize() + { + IDT::register_irq_handler(PS2_IRQ, irq_handler); + PIC::unmask(PS2_IRQ); + } + +} \ No newline at end of file diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 2a15d2b5..76e4af55 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -3,8 +3,12 @@ #include #include #include +#include +#include +#include #include #include +#include #include #include @@ -18,21 +22,30 @@ extern "C" void kernel_main(multiboot_info_t* mbi, uint32_t magic) { DISABLE_INTERRUPTS(); - - if (magic != 0x2BADB002) - asm volatile("hlt"); s_multiboot_info = mbi; + if (magic != 0x2BADB002) + goto halt; + terminal_initialize(); kmalloc_initialize(); + PIC::initialize(); gdt_initialize(); - idt_initialize(); + IDT::initialize(); + + PIT::initialize(); + PS2::initialize(); kprint("Hello from the kernel!\n"); - asm volatile("int $14"); + ENABLE_INTERRUPTS(); +halt: + for (;;) + { + asm("hlt"); + } } \ No newline at end of file