From f3816c1088f3987324f304dc885ec99a5ce43422 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 21 Jan 2023 13:55:26 +0400 Subject: [PATCH] Interrupt handling and ATA driver. --- Makefile | 3 +- cpu/idt.c | 152 +++++++++++++++++++++++++++++++++++++++++++++ cpu/isr.h | 49 +++++++++++++++ cpu/vectors.S | 55 ++++++++++++++++ drivers/ata.c | 80 ++++++++++++++++++++++++ drivers/ata.h | 5 ++ drivers/keyboard.c | 36 +++++++++++ drivers/keyboard.h | 6 ++ kernel.c | 11 +++- lib/mem.c | 12 ++++ lib/mem.h | 5 ++ 11 files changed, 411 insertions(+), 3 deletions(-) create mode 100644 cpu/idt.c create mode 100644 cpu/isr.h create mode 100644 cpu/vectors.S create mode 100644 drivers/ata.c create mode 100644 drivers/ata.h create mode 100644 drivers/keyboard.c create mode 100644 drivers/keyboard.h create mode 100644 lib/mem.c create mode 100644 lib/mem.h diff --git a/Makefile b/Makefile index 4c8b27a..49e5ae7 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,8 @@ user/%: user/%.o user/crt.o image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o console.o drivers/vga.o drivers/uart.o +kernel.bin: kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ + cpu/idt.o cpu/vectors.o lib/mem.o $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ %.o: %.c diff --git a/cpu/idt.c b/cpu/idt.c new file mode 100644 index 0000000..78f41f0 --- /dev/null +++ b/cpu/idt.c @@ -0,0 +1,152 @@ +#include "isr.h" +#include "gdt.h" +#include "../drivers/port.h" +#include "../console.h" + +enum { + IDT_HANDLERS = 256, +}; + +typedef struct { + uint16_t low_offset; + uint16_t selector; + uint8_t always0; + uint8_t type: 4; + uint8_t s: 1; + uint8_t dpl: 2; + uint8_t p: 1; + uint16_t high_offset; +} __attribute__((packed)) idt_gate_t; + +idt_gate_t idt[IDT_HANDLERS]; + +#define low_16(address) (uint16_t)((address) & 0xFFFF) +#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) + +#define STS_IG32 0xE // 32-bit Interrupt Gate +#define STS_TG32 0xF // 32-bit Trap Gate + +void set_idt_gate(int n, _Bool istrap, uint32_t handler, uint8_t dpl) { + idt[n].low_offset = low_16(handler); + idt[n].selector = 0x08; // see GDT + idt[n].always0 = 0; + idt[n].type = istrap ? STS_TG32 : STS_IG32; + idt[n].s = 0; + idt[n].dpl = dpl; + idt[n].p = 1; + idt[n].high_offset = high_16(handler); +} + +// defined in vectors.S +extern const uint32_t default_handlers[]; + +void init_idt() { + if (default_handlers[0] == 0) { + panic("handler table empty\n"); + } + for (int i = 0; i < IDT_HANDLERS; i++) { + set_idt_gate(i, 0, default_handlers[i], 0); + } +} + +const char * const exception_messages[] = { + [0] = "Division By Zero", + [1] = "Debug", + [2] = "Non Maskable Interrupt", + [3] = "Breakpoint", + [4] = "Into Detected Overflow", + [5] = "Out of Bounds", + [6] = "Invalid Opcode", + [7] = "No Coprocessor", + + [8] = "Double Fault", + [9] = "Coprocessor Segment Overrun", + [10] = "Bad TSS", + [11] = "Segment Not Present", + [12] = "Stack Fault", + [13] = "General Protection Fault", + [14] = "Page Fault", + [15] = "Unknown Interrupt", + + [16] = "Coprocessor Fault", + [17] = "Alignment Check", + [18] = "Machine Check", +}; + +#define ARRLEN(a) (sizeof(a) / sizeof(a[0])) + +static isr_t interrupt_handlers[IDT_HANDLERS]; + +void register_interrupt_handler(uint8_t i, isr_t handler) { + interrupt_handlers[i] = handler; +} + +void trap(registers_t *r) { + if (r->int_no < 32) { + const char* msg = "Reserved"; + if (r->int_no < ARRLEN(exception_messages)) { + msg = exception_messages[r->int_no]; + } + panic(msg); + } + + if (interrupt_handlers[r->int_no] != 0) { + isr_t handler = interrupt_handlers[r->int_no]; + handler(r); + } + + // EOI + if (r->int_no >= 40) { + port_byte_out(0xA0, 0x20); /* follower */ + } + if (r->int_no >= 32) { + port_byte_out(0x20, 0x20); /* leader */ + } +} + +static void init_pic() { + // ICW1 + port_byte_out(0x20, 0x11); + port_byte_out(0xA0, 0x11); + + // ICW2 + port_byte_out(0x21, 0x20); + port_byte_out(0xA1, 0x28); + + // ICW3 + port_byte_out(0x21, 0x04); + port_byte_out(0xA1, 0x02); + + // ICW4 + port_byte_out(0x21, 0x01); + port_byte_out(0xA1, 0x01); + + // OCW1 + port_byte_out(0x21, 0x0); + port_byte_out(0xA1, 0x0); +} + +typedef struct { + uint16_t limit; + void* base; +} __attribute__((packed)) idt_register_t; + +static idt_register_t idt_reg; + +void load_idt() { + init_idt(); + + idt_reg.base = &idt; + idt_reg.limit = sizeof(idt) - 1; + asm("lidt (%0)" : : "r"(&idt_reg)); + + init_pic(); +} + +void cli() { + asm("cli"); +} + +void sti() { + asm("sti"); +} diff --git a/cpu/isr.h b/cpu/isr.h new file mode 100644 index 0000000..b708e40 --- /dev/null +++ b/cpu/isr.h @@ -0,0 +1,49 @@ +#pragma once +#include + +enum { + IRQ0 = 32, + IRQ1, + IRQ2, + IRQ3, + IRQ4, + IRQ5, + IRQ6, + IRQ7, + IRQ8, + IRQ9, + IRQ10, + IRQ11, + IRQ12, + IRQ13, + IRQ14, + IRQ15, +}; + +/* Struct which aggregates many registers. + * It matches exactly the pushes on vectors.S. From the bottom: + * - pushed by the processor automatically + * - `push byte`s on the isr-specific code: error code, then int number + * - segment registers + * - all the registers by pusha + */ +typedef struct { + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ + uint32_t gs, fs, es, ds; + uint32_t int_no, err_code; // Interrupt number and error code (if applicable) + uint32_t eip, cs, eflags; // Pushed by the processor automatically + + uint32_t useresp, ss; // Pushed by the processor for userspace interrupts +} registers_t; + +void isr_install(); + +void isr_handler(registers_t *r); + +typedef void (*isr_t)(registers_t *); + +void register_interrupt_handler(uint8_t n, isr_t handler); + +void load_idt(); +void cli(); +void sti(); diff --git a/cpu/vectors.S b/cpu/vectors.S new file mode 100644 index 0000000..b1131fb --- /dev/null +++ b/cpu/vectors.S @@ -0,0 +1,55 @@ + .global trapret + +alltraps: + # Build trap frame. + pushl %ds + pushl %es + pushl %fs + pushl %gs + pushal + + mov $0x10, %ax + mov %ax, %ds + + # Call trap(tf), where tf=%esp + pushl %esp + call trap + add $4, %esp + // execution falls through to trapret + +trapret: + popal + popl %gs + popl %fs + popl %es + popl %ds + addl $0x8, %esp # trapno and errcode + iret + +.macro handler i +vector\i : + .if (!(\i == 8 || (\i >= 10 && \i <= 14) || \i == 17)) + pushl $0 + .endif + pushl $\i + jmp alltraps +.endm + +.altmacro + +.macro irq_insertX number + .section .text + handler \number + + .section .rodata + .long vector\number +.endm + +.section .rodata +.global default_handlers +default_handlers: +.set i,0 +.rept 256 + irq_insertX %i + .set i, i+1 +.endr diff --git a/drivers/ata.c b/drivers/ata.c new file mode 100644 index 0000000..5abee68 --- /dev/null +++ b/drivers/ata.c @@ -0,0 +1,80 @@ +// stolen from https://github.com/dhavalhirdhav/LearnOS/blob/master/drivers/ata/ata.c + +#include + +#include "ata.h" +#include "port.h" + +/* + BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set. +RDY: a 1 means that the controller is ready to accept a command, and the drive is spinning at correct speed.. +WFT: a 1 means that the controller detected a write fault. +SKC: a 1 means that the read/write head is in position (seek completed). +DRQ: a 1 means that the controller is expecting data (for a write) or is sending data (for a read). Don't access the data register while this bit is 0. +COR: a 1 indicates that the controller had to correct data, by using the ECC bytes (error correction code: extra bytes at the end of the sector that allows to verify its integrity and, sometimes, to correct errors). +IDX: a 1 indicates the the controller retected the index mark (which is not a hole on hard-drives). +ERR: a 1 indicates that an error occured. An error code has been placed in the error register. +*/ + +#define STATUS_BSY 0x80 +#define STATUS_RDY 0x40 +#define STATUS_DRQ 0x08 +#define STATUS_DF 0x20 +#define STATUS_ERR 0x01 + +//This is really specific to our OS now, assuming ATA bus 0 master +//Source - OsDev wiki +static void ATA_wait_BSY(); +static void ATA_wait_DRQ(); +void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count) +{ + + ATA_wait_BSY(); + port_byte_out(0x1F6, 0xE0 | ((LBA >>24) & 0xF)); + port_byte_out(0x1F2, sector_count); + port_byte_out(0x1F3, (uint8_t) LBA); + port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7, 0x20); //Send the read command + + uint16_t *target = (uint16_t*) target_address; + + for (int j = 0; j < sector_count; j++) + { + ATA_wait_BSY(); + ATA_wait_DRQ(); + for(int i = 0; i < 256; i++) + target[i] = port_word_in(0x1F0); + target += 256; + } +} + +void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes) +{ + ATA_wait_BSY(); + port_byte_out(0x1F6,0xE0 | ((LBA >>24) & 0xF)); + port_byte_out(0x1F2,sector_count); + port_byte_out(0x1F3, (uint8_t) LBA); + port_byte_out(0x1F4, (uint8_t)(LBA >> 8)); + port_byte_out(0x1F5, (uint8_t)(LBA >> 16)); + port_byte_out(0x1F7,0x30); //Send the write command + + for (int j =0;j + +void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count); +void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes); diff --git a/drivers/keyboard.c b/drivers/keyboard.c new file mode 100644 index 0000000..d7ade6e --- /dev/null +++ b/drivers/keyboard.c @@ -0,0 +1,36 @@ +#include "keyboard.h" +#include "../cpu/isr.h" +#include "../console.h" +#include "port.h" +#include "../lib/mem.h" + +static const char sc_ascii[] = { + '?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '?', '?', 'q', 'w', 'e', 'r', 't', 'y', + 'u', 'i', 'o', 'p', '[', ']', '\n', '?', 'a', 's', 'd', 'f', 'g', + 'h', 'j', 'k', 'l', ';', '\'', '`', '?', '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' ', +}; + +enum { kbd_buf_capacity = 1024 }; + +static void interrupt_handler(registers_t *r) { + uint8_t scancode = port_byte_in(0x60); + if (scancode < sizeof(sc_ascii)) { + char c = sc_ascii[scancode]; + if (kbd_buf_size < kbd_buf_capacity) { + kbd_buf[kbd_buf_size++] = c; + } + char string[] = {c, '\0'}; + printk(string); + } +} + +char* kbd_buf; +unsigned kbd_buf_size; + +void init_keyboard() { + kbd_buf = kmalloc(kbd_buf_capacity); + + register_interrupt_handler(IRQ1, interrupt_handler); +} diff --git a/drivers/keyboard.h b/drivers/keyboard.h new file mode 100644 index 0000000..c13d4bb --- /dev/null +++ b/drivers/keyboard.h @@ -0,0 +1,6 @@ +#pragma once + +void init_keyboard(); + +extern unsigned kbd_buf_size; +extern char *kbd_buf; diff --git a/kernel.c b/kernel.c index 17b5a5c..481c60b 100644 --- a/kernel.c +++ b/kernel.c @@ -1,12 +1,19 @@ #include "console.h" #include "drivers/vga.h" #include "drivers/uart.h" +#include "drivers/keyboard.h" +#include "cpu/isr.h" void _start() { uartinit(); + init_keyboard(); + load_idt(); + sti(); vga_clear_screen(); - printk("\nYABLOKO\n"); - asm("hlt"); + printk("\nYABLOKO\n> "); + while (1) { + asm("hlt"); + } } diff --git a/lib/mem.c b/lib/mem.c new file mode 100644 index 0000000..0bf188b --- /dev/null +++ b/lib/mem.c @@ -0,0 +1,12 @@ +#include "mem.h" + +static void* freeptr; + +void* kmalloc(size_t size) { + if (!freeptr) { + freeptr = (void*)(1<<20); + } + void* result = freeptr; + freeptr += size; + return result; +} diff --git a/lib/mem.h b/lib/mem.h new file mode 100644 index 0000000..1a77610 --- /dev/null +++ b/lib/mem.h @@ -0,0 +1,5 @@ +#pragma once + +typedef unsigned size_t; + +void* kmalloc(size_t size);