diff options
Diffstat (limited to 'src/kernel/arch/i386')
33 files changed, 0 insertions, 1462 deletions
diff --git a/src/kernel/arch/i386/ata.c b/src/kernel/arch/i386/ata.c deleted file mode 100644 index 56344e4..0000000 --- a/src/kernel/arch/i386/ata.c +++ /dev/null @@ -1,151 +0,0 @@ -#include <kernel/arch/i386/ata.h> -#include <kernel/arch/i386/port_io.h> -#include <kernel/panic.h> -#include <stdbool.h> - -static struct { - enum { - DEV_UNKNOWN, - DEV_PATA, - DEV_PATAPI, - } type; - uint32_t sectors; -} ata_drives[4]; - -enum { - DATA = 0, - FEAT = 1, - SCNT = 2, - LBAlo = 3, - LBAmid = 4, - LBAhi = 5, - DRV = 6, - CMD = 7, - STATUS = 7, - - /* note: the OSDev wiki uses a different base port for the control port - * however i can just use this offset and stuff will just work tm */ - CTRL = 0x206, -}; // offsets - -// get I/O port base for drive -static uint16_t ata_iobase(int drive) { - bool secondary = drive&2; - return secondary ? 0x170 : 0x1F0; -} - -static void ata_400ns(void) { - uint16_t base = ata_iobase(0); // doesn't matter - for (int i = 0; i < 4; i++) - port_in8(base + STATUS); -} - -static void ata_driveselect(int drive, int block) { - uint8_t v = 0xE0; - if (drive&1) // slave? - v |= 0x10; // set drive number bit - // TODO account for block - port_out8(ata_iobase(drive) + DRV, v); -} - -static void ata_softreset(int drive) { - uint16_t iobase = ata_iobase(drive); - port_out8(iobase + CTRL, 4); - port_out8(iobase + CTRL, 0); - ata_400ns(); - - uint16_t timeout = 10000; - while (--timeout) { // TODO separate polling function - uint8_t v = port_in8(iobase + STATUS); - if (v & 0x80) continue; // still BSY, continue - if (v & 0x40) break; // RDY, break - // TODO check for ERR - } -} - -static void ata_detecttype(int drive) { - ata_softreset(drive); - ata_driveselect(drive, 0); - ata_400ns(); - switch (port_in8(ata_iobase(drive) + LBAmid)) { - case 0: - ata_drives[drive].type = DEV_PATA; - break; - case 0x14: - ata_drives[drive].type = DEV_PATAPI; - break; - default: - ata_drives[drive].type = DEV_UNKNOWN; - break; - } -} - -static bool ata_identify(int drive) { - uint16_t iobase = ata_iobase(drive); - uint16_t data[256]; - uint8_t v; - - ata_driveselect(drive, 0); - for (int i = 2; i < 6; i++) - port_out8(iobase + i, 0); - switch (ata_drives[drive].type) { - case DEV_PATA: - port_out8(iobase + CMD, 0xEC); // IDENTIFY - break; - case DEV_PATAPI: - port_out8(iobase + CMD, 0xA1); // IDENTIFY PACKET DEVICE - break; - default: panic_invalid_state(); - } - - v = port_in8(iobase + STATUS); - if (v == 0) return false; // nonexistent drive - while (port_in8(iobase + STATUS) & 0x80); - - /* pool until bit 3 (DRQ) or 0 (ERR) is set */ - while (!((v = port_in8(iobase + STATUS) & 0x9))); - if (v & 1) return false; /* ERR was set, bail */ - - for (int i = 0; i < 256; i++) - data[i] = port_in16(iobase); - ata_drives[drive].sectors = data[60] | (data[61] << 16); - return true; -} - -void ata_init(void) { - for (int i = 0; i < 4; i++) { - ata_detecttype(i); - if (ata_drives[i].type == DEV_PATA) - ata_identify(i); - } -} - -bool ata_available(int drive) { - return ata_drives[drive].type != DEV_UNKNOWN; -} - -int ata_read(int drive, uint32_t lba, void *buf) { - assert(ata_drives[drive].type == DEV_PATA); - int iobase = ata_iobase(drive); - - ata_driveselect(drive, lba); - port_out8(iobase + FEAT, 0); // supposedly pointless - port_out8(iobase + SCNT, 1); // sector count - port_out8(iobase + LBAlo, lba); - port_out8(iobase + LBAmid, lba >> 8); - port_out8(iobase + LBAhi, lba >> 16); - port_out8(iobase + CMD, 0x20); // READ SECTORS - - for (;;) { // TODO separate polling function - uint8_t v = port_in8(iobase + STATUS); - if (v & 0x80) continue; // still BSY, continue - if (v & 0x40) break; // RDY, break - // TODO check for ERR - } - - uint16_t *b = buf; - for (int i = 0; i < 256; i++) - b[i] = port_in16(iobase); - - return 512; -} diff --git a/src/kernel/arch/i386/ata.h b/src/kernel/arch/i386/ata.h deleted file mode 100644 index 82f4f81..0000000 --- a/src/kernel/arch/i386/ata.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include <stdbool.h> -#include <stdint.h> - -void ata_init(void); -bool ata_available(int drive); -int ata_read(int drive, uint32_t lba, void *buf); diff --git a/src/kernel/arch/i386/boot.c b/src/kernel/arch/i386/boot.c deleted file mode 100644 index 754a327..0000000 --- a/src/kernel/arch/i386/boot.c +++ /dev/null @@ -1,53 +0,0 @@ -#include <kernel/arch/generic.h> -#include <kernel/arch/i386/ata.h> -#include <kernel/arch/i386/boot.h> -#include <kernel/arch/i386/driver/fsroot.h> -#include <kernel/arch/i386/driver/ps2.h> -#include <kernel/arch/i386/driver/serial.h> -#include <kernel/arch/i386/gdt.h> -#include <kernel/arch/i386/interrupts/idt.h> -#include <kernel/arch/i386/interrupts/irq.h> -#include <kernel/arch/i386/multiboot.h> -#include <kernel/arch/i386/tty/tty.h> -#include <kernel/main.h> -#include <kernel/mem/alloc.h> -#include <kernel/panic.h> - -static void find_init(struct multiboot_info *multiboot, struct kmain_info *info) -{ - struct multiboot_mod *module = &multiboot->mods[0]; - if (multiboot->mods_count < 1) { - kprintf("can't find init! "); - panic_invalid_state(); - } - info->init.at = module->start; - info->init.size = module->end - module->start; - -} - -void kmain_early(struct multiboot_info *multiboot) { - struct kmain_info info; - - tty_init(); - kprintf("gdt..."); - gdt_init(); - kprintf("idt..."); - idt_init(); - kprintf("irq..."); - irq_init(); - - info.memtop = (void*) (multiboot->mem_upper * 1024); - find_init(multiboot, &info); - kprintf("mem...\n"); - mem_init(&info); - - kprintf("rootfs..."); - vfs_root_init(); - ps2_init(); - serial_init(); - - kprintf("ata..."); - ata_init(); - - kmain(info); -} diff --git a/src/kernel/arch/i386/boot.h b/src/kernel/arch/i386/boot.h deleted file mode 100644 index 544f02d..0000000 --- a/src/kernel/arch/i386/boot.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include <kernel/arch/i386/multiboot.h> - -void kmain_early(struct multiboot_info *multiboot); diff --git a/src/kernel/arch/i386/boot.s b/src/kernel/arch/i386/boot.s deleted file mode 100644 index 743c6d6..0000000 --- a/src/kernel/arch/i386/boot.s +++ /dev/null @@ -1,34 +0,0 @@ -.section .text -.global _start -.type _start, @function -_start: - mov $_stack_top, %esp - call sysenter_setup - push %ebx // address of the Multiboot struct - call kmain_early - -.global cpu_shutdown -.type cpu_shutdown, @function -cpu_shutdown: -/* This quits QEMU. While I couldn't find this officially documented anywhere, - * it is used by QEMU in tests/tcg/i386/system/boot.S (as of commit 40d6ee), so - * I assume that this is safe-ish to use */ - mov $0x604, %edx - mov $0x2000, %eax - outw %ax, %dx - -.global halt_cpu -.type halt_cpu, @function -halt_cpu: - cli -1: hlt - jmp 1b - - -.global cpu_pause -.type cpu_pause, @function -cpu_pause: - sti - hlt - cli - ret diff --git a/src/kernel/arch/i386/debug.c b/src/kernel/arch/i386/debug.c deleted file mode 100644 index b1920db..0000000 --- a/src/kernel/arch/i386/debug.c +++ /dev/null @@ -1,18 +0,0 @@ -#include <kernel/arch/generic.h> - -void *debug_caller(size_t depth) { - void **ebp; - asm("mov %%ebp, %0" - : "=r" (ebp)); - while (depth--) { - if (!ebp) return NULL; - ebp = *ebp; - } - return ebp[1]; -} - -void debug_stacktrace(void) { - for (size_t i = 0; i < 16; i++) { - kprintf(" k/%08x\n", (uintptr_t)debug_caller(i)); - } -} diff --git a/src/kernel/arch/i386/driver/fsroot.c b/src/kernel/arch/i386/driver/fsroot.c deleted file mode 100644 index 8b1b307..0000000 --- a/src/kernel/arch/i386/driver/fsroot.c +++ /dev/null @@ -1,146 +0,0 @@ -#include <kernel/arch/i386/ata.h> -#include <kernel/mem/virt.h> -#include <kernel/panic.h> -#include <kernel/proc.h> -#include <kernel/util.h> -#include <kernel/arch/i386/driver/fsroot.h> -#include <shared/mem.h> -#include <stdbool.h> - -enum { - HANDLE_ROOT, - HANDLE_VGA, - HANDLE_ATA_ROOT, - HANDLE_ATA, - _SKIP = HANDLE_ATA + 4, -}; - -static bool exacteq(struct vfs_request *req, const char *str) { - size_t len = strlen(str); - assert(req->input.kern); - return req->input.len == len && !memcmp(req->input.buf_kern, str, len); -} - -/* truncates the length */ -static void req_preprocess(struct vfs_request *req, size_t max_len) { - if (req->offset < 0) { - // TODO negative offsets - req->offset = 0; - } - - if (req->offset >= capped_cast32(max_len)) { - req->input.len = 0; - req->output.len = 0; - req->offset = max_len; - return; - } - - req->input.len = min(req->input.len, max_len - req->offset); - req->output.len = min(req->output.len, max_len - req->offset); - - assert(req->input.len + req->offset <= max_len); - assert(req->input.len + req->offset <= max_len); -} - - -static int handle(struct vfs_request *req) { - assert(req->caller); - int id = (int)req->id; - switch (req->type) { - case VFSOP_OPEN: - if (exacteq(req, "/")) return HANDLE_ROOT; - if (exacteq(req, "/vga")) return HANDLE_VGA; - - if (exacteq(req, "/ata/")) return HANDLE_ATA_ROOT; - if (exacteq(req, "/ata/0")) - return ata_available(0) ? HANDLE_ATA+0 : -1; - if (exacteq(req, "/ata/1")) - return ata_available(1) ? HANDLE_ATA+1 : -1; - if (exacteq(req, "/ata/2")) - return ata_available(2) ? HANDLE_ATA+2 : -1; - if (exacteq(req, "/ata/3")) - return ata_available(3) ? HANDLE_ATA+3 : -1; - - return -1; - - case VFSOP_READ: - switch (id) { - case HANDLE_ROOT: { - // TODO document directory read format - const char src[] = - "vga\0" - "com1\0" - "ps2\0" - "ata/"; - req_preprocess(req, sizeof src); - virt_cpy_to(req->caller->pages, req->output.buf, - src + req->offset, req->output.len); - return req->output.len; - } - case HANDLE_VGA: { - char *vga = (void*)0xB8000; - req_preprocess(req, 80*25*2); - virt_cpy_to(req->caller->pages, req->output.buf, - vga + req->offset, req->output.len); - return req->output.len; - } - case HANDLE_ATA_ROOT: { - char list[8] = {}; - size_t len = 0; - for (int i = 0; i < 4; i++) { - if (ata_available(i)) { - list[len] = '0' + i; - len += 2; - } - } - req_preprocess(req, len); - virt_cpy_to(req->caller->pages, req->output.buf, - list + req->offset, req->output.len); - return req->output.len; - } - case HANDLE_ATA: case HANDLE_ATA+1: - case HANDLE_ATA+2: case HANDLE_ATA+3: { - if (req->offset < 0) return 0; - char buf[512]; - uint32_t sector = req->offset / 512; - size_t len = min(req->output.len, 512 - ((size_t)req->offset & 511)); - ata_read(id - HANDLE_ATA, sector, buf); - virt_cpy_to(req->caller->pages, req->output.buf, buf, len); - return len; - } - default: panic_invalid_state(); - } - - case VFSOP_WRITE: - switch (id) { - case HANDLE_VGA: { - void *vga = (void*)0xB8000; - req_preprocess(req, 80*25*2); - virt_cpy_from(req->caller->pages, vga + req->offset, - req->input.buf, req->input.len); - return req->input.len; - } - default: return -1; - } - - case VFSOP_CLOSE: - return 0; - - default: panic_invalid_state(); - } -} - -static void accept(struct vfs_request *req) { - if (req->caller) { - vfsreq_finish_short(req, handle(req)); - } else { - vfsreq_finish_short(req, -1); - } -} - -static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { - return true; -} - -static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); -void vfs_root_init(void) { vfs_mount_root_register("", &backend); } diff --git a/src/kernel/arch/i386/driver/fsroot.h b/src/kernel/arch/i386/driver/fsroot.h deleted file mode 100644 index dd72202..0000000 --- a/src/kernel/arch/i386/driver/fsroot.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void vfs_root_init(void); diff --git a/src/kernel/arch/i386/driver/ps2.c b/src/kernel/arch/i386/driver/ps2.c deleted file mode 100644 index 341a7d3..0000000 --- a/src/kernel/arch/i386/driver/ps2.c +++ /dev/null @@ -1,61 +0,0 @@ -#include <kernel/arch/i386/driver/ps2.h> -#include <kernel/arch/i386/interrupts/irq.h> -#include <kernel/mem/virt.h> -#include <kernel/panic.h> -#include <kernel/vfs/request.h> -#include <shared/container/ring.h> -#include <shared/mem.h> - -#define BACKLOG_CAPACITY 64 -static volatile uint8_t backlog_buf[BACKLOG_CAPACITY]; -static volatile ring_t backlog = {(void*)backlog_buf, BACKLOG_CAPACITY, 0, 0}; - -static void accept(struct vfs_request *req); -static bool is_ready(struct vfs_backend *self); - -static struct vfs_request *blocked_on = NULL; -static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); -void ps2_init(void) { vfs_mount_root_register("/ps2", &backend); } - - -void ps2_recv(uint8_t s) { - ring_put1b((void*)&backlog, s); - if (blocked_on) { - accept(blocked_on); - blocked_on = NULL; - vfs_backend_tryaccept(&backend); - } -} - -static void accept(struct vfs_request *req) { - // when you fix something here go also fix it in the COM1 driver - static uint8_t buf[32]; // pretty damn stupid - int ret; - bool valid; - switch (req->type) { - case VFSOP_OPEN: - valid = req->input.len == 0; - vfsreq_finish_short(req, valid ? 0 : -1); - break; - case VFSOP_READ: - if (ring_size((void*)&backlog) == 0) { - // nothing to read - blocked_on = req; - } else if (req->caller) { - ret = clamp(0, req->output.len, sizeof buf); - ret = ring_get((void*)&backlog, buf, ret); - virt_cpy_to(req->caller->pages, req->output.buf, buf, ret); - vfsreq_finish_short(req, ret); - } else { - vfsreq_finish_short(req, -1); - } - break; - default: - vfsreq_finish_short(req, -1); - break; - } -} - -static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { - return blocked_on == NULL; -} diff --git a/src/kernel/arch/i386/driver/ps2.h b/src/kernel/arch/i386/driver/ps2.h deleted file mode 100644 index 54e8fb2..0000000 --- a/src/kernel/arch/i386/driver/ps2.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include <stdint.h> - -void ps2_recv(uint8_t s); -void ps2_init(void); diff --git a/src/kernel/arch/i386/driver/serial.c b/src/kernel/arch/i386/driver/serial.c deleted file mode 100644 index dac5b75..0000000 --- a/src/kernel/arch/i386/driver/serial.c +++ /dev/null @@ -1,114 +0,0 @@ -#include <kernel/arch/i386/driver/serial.h> -#include <kernel/arch/i386/interrupts/irq.h> -#include <kernel/arch/i386/port_io.h> -#include <kernel/mem/virt.h> -#include <kernel/panic.h> -#include <shared/container/ring.h> -#include <shared/mem.h> -#include <stdint.h> - -#define BACKLOG_CAPACITY 64 -static volatile uint8_t backlog_buf[BACKLOG_CAPACITY]; -static volatile ring_t backlog = {(void*)backlog_buf, BACKLOG_CAPACITY, 0, 0}; - -static const int COM1 = 0x3f8; - -static void accept(struct vfs_request *req); -static bool is_ready(struct vfs_backend *self); - -static struct vfs_request *blocked_on = NULL; -static struct vfs_backend backend = BACKEND_KERN(is_ready, accept); -void serial_init(void) { vfs_mount_root_register("/com1", &backend); } - - -static void serial_selftest(void) { - char b = 0x69; - port_out8(COM1 + 4, 0b00011110); // enable loopback mode - port_out8(COM1, b); - assert(port_in8(COM1) == b); -} - -void serial_preinit(void) { - // see https://www.sci.muni.cz/docs/pc/serport.txt - // set baud rate divisor - port_out8(COM1 + 3, 0b10000000); // enable DLAB - port_out8(COM1 + 0, 0x01); // divisor = 1 (low byte) - port_out8(COM1 + 1, 0x00); // (high byte) - - port_out8(COM1 + 3, 0b00000011); // 8 bits, no parity, one stop bit - port_out8(COM1 + 1, 0x01); // enable the Data Ready IRQ - port_out8(COM1 + 2, 0b11000111); // enable FIFO with 14-bit trigger level (???) - - serial_selftest(); - - port_out8(COM1 + 4, 0b00001111); // enable everything in the MCR -} - - -void serial_irq(void) { - ring_put1b((void*)&backlog, port_in8(COM1)); - if (blocked_on) { - accept(blocked_on); - blocked_on = NULL; - vfs_backend_tryaccept(&backend); - } -} - - -static void serial_putchar(char c) { - while ((port_in8(COM1 + 5) & 0x20) == 0); // wait for THRE - port_out8(COM1, c); -} - -void serial_write(const char *buf, size_t len) { - for (size_t i = 0; i < len; i++) - serial_putchar(buf[i]); -} - - -static void accept(struct vfs_request *req) { - static char buf[32]; - int ret; - bool valid; - switch (req->type) { - case VFSOP_OPEN: - valid = req->input.len == 0; - vfsreq_finish_short(req, valid ? 0 : -1); - break; - case VFSOP_READ: - if (ring_size((void*)&backlog) == 0) { - /* nothing to read, join queue */ - assert(!req->postqueue_next); - struct vfs_request **slot = &blocked_on; - while (*slot) - slot = &(*slot)->postqueue_next; - *slot = req; - } else if (req->caller) { - ret = clamp(0, req->output.len, sizeof buf); - ret = ring_get((void*)&backlog, buf, ret); - virt_cpy_to(req->caller->pages, req->output.buf, buf, ret); - vfsreq_finish_short(req, ret); - } else { - vfsreq_finish_short(req, -1); - } - break; - case VFSOP_WRITE: - if (req->caller) { - struct virt_iter iter; - virt_iter_new(&iter, req->input.buf, req->input.len, - req->caller->pages, true, false); - while (virt_iter_next(&iter)) - serial_write(iter.frag, iter.frag_len); - ret = iter.prior; - } else ret = -1; - vfsreq_finish_short(req, ret); - break; - default: - vfsreq_finish_short(req, -1); - break; - } -} - -static bool is_ready(struct vfs_backend __attribute__((unused)) *self) { - return true; -} diff --git a/src/kernel/arch/i386/driver/serial.h b/src/kernel/arch/i386/driver/serial.h deleted file mode 100644 index 6a4876e..0000000 --- a/src/kernel/arch/i386/driver/serial.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include <kernel/vfs/request.h> -#include <stdbool.h> -#include <stddef.h> - -void serial_preinit(void); -void serial_irq(void); -void serial_write(const char *buf, size_t len); - -void serial_init(void); diff --git a/src/kernel/arch/i386/gdt.h b/src/kernel/arch/i386/gdt.h deleted file mode 100644 index 5e40cd7..0000000 --- a/src/kernel/arch/i386/gdt.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -enum { - SEG_null, - // order dictated by SYSENTER - SEG_r0code, - SEG_r0data, - SEG_r3code, - SEG_r3data, - SEG_TSS, - - SEG_end -}; - -void gdt_init(void); -void gdt_farjump(int segment); diff --git a/src/kernel/arch/i386/gdt/farjump.s b/src/kernel/arch/i386/gdt/farjump.s deleted file mode 100644 index 2885d2b..0000000 --- a/src/kernel/arch/i386/gdt/farjump.s +++ /dev/null @@ -1,8 +0,0 @@ -.section .text -.global gdt_farjump -.type gdt_farjump, @function -gdt_farjump: - /* retf pops off the return address and code segment off the stack. - * it turns out that in the i386 cdecl calling convention they're in - * the correct place already. */ - retf diff --git a/src/kernel/arch/i386/gdt/gdt.c b/src/kernel/arch/i386/gdt/gdt.c deleted file mode 100644 index 3521052..0000000 --- a/src/kernel/arch/i386/gdt/gdt.c +++ /dev/null @@ -1,130 +0,0 @@ -#include <kernel/arch/generic.h> -#include <kernel/arch/i386/gdt.h> -#include <shared/mem.h> -#include <stdbool.h> -#include <stdint.h> - -extern char _isr_mini_stack; - -struct gdt_entry { - uint64_t limit_low : 16; - uint64_t base_low : 24; - uint64_t accessed : 1; // set by the processor - // CODE | DATA - uint64_t rw : 1; // readable? | writeable? - uint64_t conforming : 1; // conforming? | expands down? - uint64_t code : 1; // 1 | 0 - - uint64_t codeordata : 1; // 1 for everything other than TSS and LDT - uint64_t ring : 2; - uint64_t present : 1; // always 1 - uint64_t limit_high : 4; - uint64_t available : 1; // ??? - uint64_t long_mode : 1; - uint64_t x32 : 1; - uint64_t gran : 1; // 1 - 4kb, 0 - 1b - uint64_t base_high : 8; -} __attribute__((packed)); - -struct tss_entry { - uint32_t _unused0; - uint32_t esp0; // kernel mode stack pointer - uint32_t ss0; // kernel mode stack segment - uint8_t _unused1[0x5c]; -} __attribute__((packed)); - -struct lgdt_arg { - uint16_t limit; - uint32_t base; -} __attribute__((packed)); - -__attribute__((section(".shared"))) -static struct gdt_entry GDT[SEG_end]; -__attribute__((section(".shared"))) -static struct tss_entry TSS; -static struct lgdt_arg lgdt_arg; // probably doesn't need to be global - -static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code); -static void gdt_prepare(void); -static void gdt_load(void); - - -static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code) { - *entry = (struct gdt_entry) { - // set up the identity mapping - .limit_low = 0xFFFF, - .limit_high = 0xF, - .gran = 1, // 4KB * 0xFFFFF = (almost) 4GB - .base_low = 0, - .base_high = 0, - - .ring = ring, - .code = code, - - .accessed = 0, - .rw = 1, - .conforming = 0, - .codeordata = 1, - .present = 1, - .long_mode = 0, // ??? - .available = 1, // ??? - .x32 = 1, - }; -} - -static void gdt_prepare(void) { - GDT[SEG_null].present = 0; - - gdt_fillout(&GDT[SEG_r0code], 0, true); - gdt_fillout(&GDT[SEG_r0data], 0, false); - gdt_fillout(&GDT[SEG_r3code], 3, true); - gdt_fillout(&GDT[SEG_r3data], 3, false); - - // tss - memset(&TSS, 0, sizeof(TSS)); - TSS.ss0 = SEG_r0data << 3; // kernel data segment - TSS.esp0 = (uintptr_t) &_isr_mini_stack; - - GDT[SEG_TSS] = (struct gdt_entry) { - .limit_low = sizeof(TSS), - .limit_high = sizeof(TSS) >> 16, - .gran = 0, - .base_low = (uintptr_t) &TSS, - .base_high = ((uintptr_t) &TSS) >> 24, - - .accessed = 1, // 1 for TSS - .rw = 0, // 1 busy / 0 not busy - .conforming = 0, // 0 for TSS - .code = 1, // 32bit - .codeordata = 0, // is a system entry - .ring = 3, - .present = 1, - .available = 0, // 0 for TSS - .long_mode = 0, - .x32 = 0, // idk - }; -} - -static void gdt_load(void) { - lgdt_arg.limit = sizeof(GDT) - 1; - lgdt_arg.base = (uintptr_t) &GDT; - asm("lgdt (%0)" - : : "r" (&lgdt_arg) : "memory"); - - asm("ltr %%ax" - : : "a" (SEG_TSS << 3 | 3) : "memory"); - - // update all segment registers - gdt_farjump(SEG_r0code << 3); - asm("mov %0, %%ds;" - "mov %0, %%ss;" - "mov %0, %%es;" - "mov %0, %%fs;" - "mov %0, %%gs;" - : : "r" (SEG_r0data << 3) : "memory"); -} - -void gdt_init(void) { - gdt_prepare(); - gdt_load(); -} diff --git a/src/kernel/arch/i386/interrupts/idt.c b/src/kernel/arch/i386/interrupts/idt.c deleted file mode 100644 index cd2adfd..0000000 --- a/src/kernel/arch/i386/interrupts/idt.c +++ /dev/null @@ -1,68 +0,0 @@ -#include <kernel/arch/i386/gdt.h> -#include <kernel/arch/i386/interrupts/idt.h> -#include <kernel/arch/i386/interrupts/isr.h> -#include <kernel/panic.h> -#include <stdbool.h> -#include <stdint.h> - -struct idt_entry { - uint16_t offset_low ; - uint16_t code_seg ; - uint8_t zero ; // unused, has to be 0 - uint8_t type : 4; // 16/32 bit, task/interrupt/task gate - uint8_t storage : 1; // 0 for interrupt/trap gates - uint8_t ring : 2; - uint8_t present : 1; - uint16_t offset_high ; -} __attribute__((packed)); - -// is exactly the same as lgdt_arg, i should combine them into a single struct -// later -struct lidt_arg { - uint16_t limit; - uint32_t base; -} __attribute__((packed)); - -__attribute__((section(".shared"))) -static struct idt_entry IDT[256]; -static struct lidt_arg lidt_arg; - -static void idt_prepare(void); -static void idt_load(void); -static void idt_test(void); - - -static void idt_prepare(void) { - uintptr_t offset; - for (int i = 0; i < 256; i++) { - offset = (uintptr_t) &_isr_stubs + i * 8; - - IDT[i] = (struct idt_entry) { - .offset_low = offset, - .offset_high = offset >> 16, - .code_seg = SEG_r0code << 3, - .zero = 0, - .present = 1, - .ring = 0, - .storage = 0, - .type = 0xE, // 32-bit interrupt gate - }; - } -} - -static void idt_load(void) { - lidt_arg.limit = sizeof(IDT) - 1; - lidt_arg.base = (uintptr_t) &IDT; - asm("lidt (%0)" : : "r" (&lidt_arg) : "memory"); -} - -static void idt_test(void) { - asm("int $0x34" : : : "memory"); - assert(isr_test_interrupt_called); -} - -void idt_init(void) { - idt_prepare(); - idt_load(); - idt_test(); -} diff --git a/src/kernel/arch/i386/interrupts/idt.h b/src/kernel/arch/i386/interrupts/idt.h deleted file mode 100644 index 6576cf9..0000000 --- a/src/kernel/arch/i386/interrupts/idt.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void idt_init(void); diff --git a/src/kernel/arch/i386/interrupts/irq.c b/src/kernel/arch/i386/interrupts/irq.c deleted file mode 100644 index 5918169..0000000 --- a/src/kernel/arch/i386/interrupts/irq.c +++ /dev/null @@ -1,33 +0,0 @@ -#include <kernel/arch/i386/interrupts/irq.h> -#include <kernel/arch/i386/port_io.h> -#include <stdint.h> - -static const int PIC1 = 0x20; -static const int PIC2 = 0xA0; - -void irq_init(void) { - port_out8(PIC1, 0x11); /* start init sequence */ - port_out8(PIC2, 0x11); - - port_out8(PIC1+1, 0x20); /* interrupt offsets */ - port_out8(PIC2+1, 0x30); - - port_out8(PIC1+1, 0x4); /* just look at the osdev wiki lol */ - port_out8(PIC2+1, 0x2); - - port_out8(PIC1+1, 0x1); /* 8086 mode */ - port_out8(PIC2+1, 0x1); - - uint8_t mask = 0xff; - mask &= ~(1 << 1); // keyboard - mask &= ~(1 << 4); // COM1 - - port_out8(PIC1+1, mask); - port_out8(PIC2+1, 0xff); -} - -void irq_eoi(uint8_t line) { - port_out8(PIC1, 0x20); - if (line >= 8) - port_out8(PIC2, 0x20); -} diff --git a/src/kernel/arch/i386/interrupts/irq.h b/src/kernel/arch/i386/interrupts/irq.h deleted file mode 100644 index f523154..0000000 --- a/src/kernel/arch/i386/interrupts/irq.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include <stdbool.h> -#include <stdint.h> - -void irq_init(void); -void irq_eoi(uint8_t line); diff --git a/src/kernel/arch/i386/interrupts/isr.c b/src/kernel/arch/i386/interrupts/isr.c deleted file mode 100644 index 2d893cb..0000000 --- a/src/kernel/arch/i386/interrupts/isr.c +++ /dev/null @@ -1,38 +0,0 @@ -#include <kernel/arch/i386/driver/ps2.h> -#include <kernel/arch/i386/driver/serial.h> -#include <kernel/arch/i386/interrupts/irq.h> -#include <kernel/arch/i386/interrupts/isr.h> -#include <kernel/arch/i386/port_io.h> -#include <kernel/arch/generic.h> -#include <kernel/panic.h> -#include <kernel/proc.h> -#include <stdbool.h> -#include <stdint.h> - -bool isr_test_interrupt_called = false; - -void isr_stage3(int interrupt) { - switch (interrupt) { - case 0x08: // double fault - kprintf("#DF"); - panic_invalid_state(); - case 0x34: - isr_test_interrupt_called = true; - return; - - case 0x21: // keyboard irq - ps2_recv(port_in8(0x60)); - irq_eoi(1); - return; - - case 0x24: // COM1 irq - serial_irq(); - irq_eoi(1); - return; - - default: - // TODO check if the exception was in the kernel - process_kill(process_current, interrupt); - process_switch_any(); - } -} diff --git a/src/kernel/arch/i386/interrupts/isr.h b/src/kernel/arch/i386/interrupts/isr.h deleted file mode 100644 index 3189538..0000000 --- a/src/kernel/arch/i386/interrupts/isr.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include <stdbool.h> - -extern bool isr_test_interrupt_called; // used in the self-test in idt.c -extern const char _isr_stubs; - -void isr_stage3(int interrupt); diff --git a/src/kernel/arch/i386/interrupts/isr_stub.s b/src/kernel/arch/i386/interrupts/isr_stub.s deleted file mode 100644 index fdbae6f..0000000 --- a/src/kernel/arch/i386/interrupts/isr_stub.s +++ /dev/null @@ -1,64 +0,0 @@ -.section .shared - -.global _isr_stubs -_isr_stubs: -.rept 256 - .set _stub_start, . - - cli - call _isr_stage2 - - .if . - _stub_start > 8 - .error "isr stubs over maximum size" - .abort - .endif - .align 8 -.endr - -_isr_stage2: - // pushal order, without %esp - push %eax - push %ecx - push %edx - push %ebx - push %ebp - push %esi - push %edi - - // convert the return address into the vector nr - mov 28(%esp), %eax - add $-_isr_stubs, %eax - shr $3, %eax - - // disable paging, if present - mov %cr0, %ebx - push %ebx // push original cr0 - and $0x7FFFFFFF, %ebx - mov %ebx, %cr0 - - mov %esp, %ebp - mov $_isr_big_stack, %esp - push %eax // push the vector nr - call isr_stage3 - - mov %ebp, %esp - pop %eax // restore old cr0 - mov %eax, %cr0 - - // restore registers - pop %edi - pop %esi - pop %ebp - pop %ebx - pop %edx - pop %ecx - pop %eax - - add $4, %esp // skip call's return address - iret - -.align 8 -// TODO overflow check -.skip 64 // seems to be enough -.global _isr_mini_stack -_isr_mini_stack: diff --git a/src/kernel/arch/i386/multiboot.h b/src/kernel/arch/i386/multiboot.h deleted file mode 100644 index 1d6718f..0000000 --- a/src/kernel/arch/i386/multiboot.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include <stdint.h> - -#ifndef __CHECKER__ -_Static_assert(sizeof(void*) == 4, - "this code assumes that pointers have 4 bytes"); -#endif - -struct multiboot_mod { - void *start; - void *end; - const char *str; - uint32_t _reserved; -} __attribute__((packed)); - -struct multiboot_info { - uint32_t flag_mem : 1; - uint32_t flag_boot_device : 1; - uint32_t flag_cmdline : 1; - uint32_t flag_mods : 1; - uint32_t _flag_other : 28; // unimplemented - - uint32_t mem_lower; - uint32_t mem_upper; - - uint32_t boot_device; - - const char *cmdline; - - uint32_t mods_count; - struct multiboot_mod *mods; - - // [...] -} __attribute__((packed)); diff --git a/src/kernel/arch/i386/multiboot.s b/src/kernel/arch/i386/multiboot.s deleted file mode 100644 index dc19b36..0000000 --- a/src/kernel/arch/i386/multiboot.s +++ /dev/null @@ -1,12 +0,0 @@ -.set MAGIC, 0x1BADB002 - -/* 1<<0 - align modules on page boundaries. */ -.set FLAGS, 1<<0 -.set CHECKSUM, -(MAGIC + FLAGS) - -.section .multiboot -.align 4 -multiboot_header: - .long MAGIC - .long FLAGS - .long CHECKSUM diff --git a/src/kernel/arch/i386/pagedir.c b/src/kernel/arch/i386/pagedir.c deleted file mode 100644 index e607cc7..0000000 --- a/src/kernel/arch/i386/pagedir.c +++ /dev/null @@ -1,206 +0,0 @@ -#include <kernel/arch/generic.h> -#include <kernel/mem/alloc.h> -#include <shared/mem.h> -#include <stdint.h> - -/* <heat> nitpick: I highly recommend you dont use bitfields for paging - * structures - * <heat> you can't change them atomically and the way they're layed out - * in memory is implementation defined iirc - */ -struct pagetable_entry { - uint32_t present : 1; - uint32_t writeable : 1; - uint32_t user : 1; - uint32_t writethru : 1; - uint32_t uncached : 1; - uint32_t dirty : 1; - uint32_t always0 : 1; // memory type thing? - uint32_t global : 1; - uint32_t _unused : 3; - uint32_t address : 21; -}; - -struct pagedir_entry { - uint32_t present : 1; - uint32_t _writeable: 1; // don't use! not checked by multiple functions here - uint32_t _user : 1; // ^ - uint32_t writethru : 1; - uint32_t uncached : 1; - uint32_t accessed : 1; - uint32_t always0 : 1; - uint32_t large : 1; // 4 MiB instead of 4 KiB - uint32_t _unused : 3; - uint32_t address : 21; -} __attribute__((packed)); - -struct pagedir { - struct pagedir_entry e[1024]; -} __attribute__((packed)); - - -struct pagedir *pagedir_new(void) { - struct pagedir *dir = page_alloc(1); - for (int i = 0; i < 1024; i++) - dir->e[i].present = 0; - return dir; -} - -void pagedir_free(struct pagedir *dir) { - // assumes all user pages are unique and can be freed - struct pagetable_entry *pt; - void *page; - - for (int i = 0; i < 1024; i++) { - if (!dir->e[i].present) continue; - - pt = (void*)(dir->e[i].address << 11); - - for (int j = 0; j < 1024; j++) { - if (!pt[j].present) continue; - if (!pt[j].user) continue; - - page = (void*)(pt[j].address << 11); - page_free(page, 1); - } - page_free(pt, 1); - } - page_free(dir, 1); -} - -static struct pagetable_entry* -get_entry(struct pagedir *dir, const void __user *virt) { - uint32_t pd_idx = ((uintptr_t)virt) >> 22; - uint32_t pt_idx = ((uintptr_t)virt) >> 12 & 0x03FF; - struct pagetable_entry *pagetable; - - if (!dir->e[pd_idx].present) return NULL; - - pagetable = (void*)(dir->e[pd_idx].address << 11); - return &pagetable[pt_idx]; -} - -void *pagedir_unmap(struct pagedir *dir, void __user *virt) { - void *phys = pagedir_virt2phys(dir, virt, false, false); - struct pagetable_entry *page = get_entry(dir, virt); - page->present = false; - return phys; -} - -void pagedir_map(struct pagedir *dir, void __user *virt, void *phys, - bool user, bool writeable) -{ - uintptr_t virt_cast = (uintptr_t) virt; - uint32_t pd_idx = virt_cast >> 22; - uint32_t pt_idx = virt_cast >> 12 & 0x03FF; - struct pagetable_entry *pagetable; - - if (dir->e[pd_idx].present) { - pagetable = (void*) (dir->e[pd_idx].address << 11); - } else { - pagetable = page_alloc(1); - for (int i = 0; i < 1024; i++) - pagetable[i].present = 0; - - dir->e[pd_idx] = (struct pagedir_entry) { - .present = 1, - ._writeable= 1, - ._user = 1, - .writethru = 1, - .uncached = 0, - .accessed = 0, - .always0 = 0, - .large = 0, - ._unused = 0, - .address = (uintptr_t) pagetable >> 11 - }; - } - - pagetable[pt_idx] = (struct pagetable_entry) { - .present = 1, - .writeable = writeable, - .user = user, - .writethru = 1, - .uncached = 0, - .dirty = 0, - .always0 = 0, - .global = 0, - ._unused = 0, - .address = (uintptr_t) phys >> 11 - }; -} - -void pagedir_switch(struct pagedir *dir) { - asm volatile("mov %0, %%cr3;" : : "r" (dir) : "memory"); -} - -// creates a new pagedir with exact copies of the user pages -struct pagedir *pagedir_copy(const struct pagedir *orig) { - struct pagedir *clone = page_alloc(1); - struct pagetable_entry *orig_pt, *clone_pt; - void *orig_page, *clone_page; - - for (int i = 0; i < 1024; i++) { - clone->e[i] = orig->e[i]; - if (!orig->e[i].present) continue; - - orig_pt = (void*)(orig->e[i].address << 11); - clone_pt = page_alloc(1); - clone->e[i].address = (uintptr_t) clone_pt >> 11; - - for (int j = 0; j < 1024; j++) { - clone_pt[j] = orig_pt[j]; - if (!orig_pt[j].present) continue; - if (!orig_pt[j].user) continue; - // i could use .global? - - orig_page = (void*)(orig_pt[j].address << 11); - clone_page = page_alloc(1); - clone_pt[j].address = (uintptr_t) clone_page >> 11; - - memcpy(clone_page, orig_page, PAGE_SIZE); - } - } - - return clone; -} - -bool pagedir_iskern(struct pagedir *dir, const void __user *virt) { - struct pagetable_entry *page = get_entry(dir, virt); - return page && page->present && !page->user; -} - -void *pagedir_virt2phys(struct pagedir *dir, const void __user *virt, - bool user, bool writeable) -{ - struct pagetable_entry *page; - uintptr_t phys; - page = get_entry(dir, virt); - if (!page || !page->present) return NULL; - if (user && !page->user) return NULL; - if (writeable && !page->writeable) return NULL; - - phys = page->address << 11; - phys |= ((uintptr_t)virt) & 0xFFF; - return (void*)phys; -} - -void __user *pagedir_findfree(struct pagedir *dir, char __user *start, size_t len) { - struct pagetable_entry *page; - char __user *iter; - start = (userptr_t)(((uintptr_t __force)start + PAGE_MASK) & ~PAGE_MASK); // round up to next page - iter = start; - - while (iter < (char __user *)0xFFF00000) { // TODO better boundary - page = get_entry(dir, iter); - if (page && page->present) { - start = iter + PAGE_SIZE; - } else { - if ((size_t)(iter + PAGE_SIZE - start) >= len) - return start; - } - iter += PAGE_SIZE; - } - - return NULL; -} diff --git a/src/kernel/arch/i386/port_io.h b/src/kernel/arch/i386/port_io.h deleted file mode 100644 index eac9331..0000000 --- a/src/kernel/arch/i386/port_io.h +++ /dev/null @@ -1,22 +0,0 @@ -#include <stdint.h> - -static inline void port_out8(uint16_t port, uint8_t val) { - asm volatile("outb %0, %1" : : "a" (val), "Nd" (port)); -} - -static inline void port_out16(uint16_t port, uint16_t val) { - asm volatile("outw %0, %1" : : "a" (val), "Nd" (port)); -} - -static inline uint8_t port_in8(uint16_t port) { - uint8_t val; - asm volatile("inb %1, %0" : "=a" (val) : "Nd" (port)); - return val; -} - -static inline uint16_t port_in16(uint16_t port) { - uint16_t val; - asm volatile("inw %1, %0" : "=a" (val) : "Nd" (port)); - return val; -} - diff --git a/src/kernel/arch/i386/registers.h b/src/kernel/arch/i386/registers.h deleted file mode 100644 index 9f481d3..0000000 --- a/src/kernel/arch/i386/registers.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include <shared/types.h> -#include <stdint.h> - -struct registers { - /* those are in the order of pushad/popad - so you can load/save this - * struct in (almost) one instruction */ - uint32_t edi, esi; - userptr_t ebp, esp; - uint32_t ebx, edx, ecx, eax; - - // not part of pushad/popad, but still useful - userptr_t eip; -} __attribute__((__packed__)); - -// saves a return value according to the SysV ABI -static inline uint64_t regs_savereturn(struct registers *regs, uint64_t value) { - regs->eax = value; - regs->edx = value >> 32; - return value; -} diff --git a/src/kernel/arch/i386/sysenter.c b/src/kernel/arch/i386/sysenter.c deleted file mode 100644 index 5837474..0000000 --- a/src/kernel/arch/i386/sysenter.c +++ /dev/null @@ -1,26 +0,0 @@ -#include <kernel/arch/generic.h> -#include <kernel/arch/i386/sysenter.h> -#include <kernel/proc.h> -#include <shared/syscalls.h> - -struct registers _sysexit_regs; - -void sysexit(struct registers regs) { - _sysexit_regs = regs; - _sysexit_regs.ecx = (uintptr_t) regs.esp; - _sysexit_regs.edx = (uintptr_t) regs.eip; - _sysexit_real(); - __builtin_unreachable(); -} - -_Noreturn void sysenter_stage2(void) { - struct registers *regs = &process_current->regs; - - *regs = _sysexit_regs; // save the registers - regs->esp = (userptr_t) regs->ecx; // fix them up - regs->eip = (userptr_t) regs->edx; - - _syscall(regs->eax, regs->ebx, - regs->esi, regs->edi, (uintptr_t)regs->ebp); - process_switch_any(); -} diff --git a/src/kernel/arch/i386/sysenter.h b/src/kernel/arch/i386/sysenter.h deleted file mode 100644 index b88c186..0000000 --- a/src/kernel/arch/i386/sysenter.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// sysenter.c -extern struct registers _sysexit_regs; -_Noreturn void sysenter_stage2(void); - -// sysenter.s -void _sysexit_real(void); diff --git a/src/kernel/arch/i386/sysenter.s b/src/kernel/arch/i386/sysenter.s deleted file mode 100644 index d04f839..0000000 --- a/src/kernel/arch/i386/sysenter.s +++ /dev/null @@ -1,89 +0,0 @@ -/* arch/i386/gdt.c */ -.set SEG_r0code, 1 -.set SEG_r3code, 3 -.set SEG_r3data, 4 - -.set IA32_SYSENTER_CS, 0x174 -.set IA32_SYSENTER_ESP, 0x175 -.set IA32_SYSENTER_EIP, 0x176 - -.section .text -.global sysenter_setup -.type sysenter_setup, @function -sysenter_setup: - xor %edx, %edx - - mov $(SEG_r0code << 3), %eax - mov $IA32_SYSENTER_CS, %ecx - wrmsr - - mov $IA32_SYSENTER_ESP, %ecx - mov $0, %eax // unused - wrmsr - - mov $IA32_SYSENTER_EIP, %ecx - mov $sysenter_stage1, %eax - wrmsr - - ret - - -.section .shared - -.global stored_eax -stored_eax: -.long 0 - -.global _sysexit_real -.type _sysexit_real, @function -_sysexit_real: - mov $(SEG_r3data << 3 | 3), %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - - // restore the registers - mov $_sysexit_regs, %esp - pop %edi - pop %esi - pop %ebp - add $4, %esp - pop %ebx - pop %edx - pop %ecx - pop %eax - - // enable paging - mov %eax, stored_eax - mov %cr0, %eax - or $0x80000000, %eax - mov %eax, %cr0 - mov stored_eax, %eax - - sysexit - -sysenter_stage1: - cli /* prevent random IRQs in the middle of kernel code */ - - // disable paging - // I don't want to damage any of the registers passed in by the user, - // so i'm using ESP as a temporary register. At this point there's nothing - // useful in it, it's == _bss_end. - mov %cr0, %esp - and $0x7FFFFFFF, %esp // disable paging - mov %esp, %cr0 - - // save the registers - mov $(_sysexit_regs + 32), %esp - push %eax - push %ecx - push %edx - push %ebx - push $0x0 // pushal pushes %esp here, but that's worthless - push %ebp - push %esi - push %edi - - mov $_bss_end, %esp - jmp sysenter_stage2 diff --git a/src/kernel/arch/i386/tty/tty.c b/src/kernel/arch/i386/tty/tty.c deleted file mode 100644 index 9451059..0000000 --- a/src/kernel/arch/i386/tty/tty.c +++ /dev/null @@ -1,26 +0,0 @@ -#include <kernel/arch/generic.h> -#include <kernel/arch/i386/driver/serial.h> -#include <kernel/arch/i386/tty/tty.h> -#include <shared/printf.h> - -void tty_init(void) { - vga_clear(); - serial_preinit(); - - vga_write("\x03 ", 2); // cp437 heart - serial_write("<3 ", 3); -} - -static void backend(void __attribute__((unused)) *arg, const char *buf, size_t len) { - vga_write(buf, len); - serial_write(buf, len); -} - -int kprintf(const char *fmt, ...) { - int ret; - va_list argp; - va_start(argp, fmt); - ret = __printf_internal(fmt, argp, backend, NULL); - va_end(argp); - return ret; -} diff --git a/src/kernel/arch/i386/tty/tty.h b/src/kernel/arch/i386/tty/tty.h deleted file mode 100644 index b96003d..0000000 --- a/src/kernel/arch/i386/tty/tty.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include <stddef.h> - -void vga_write(const char *buf, size_t len); -void vga_clear(void); - -void tty_init(void); diff --git a/src/kernel/arch/i386/tty/vga.c b/src/kernel/arch/i386/tty/vga.c deleted file mode 100644 index 9efd326..0000000 --- a/src/kernel/arch/i386/tty/vga.c +++ /dev/null @@ -1,33 +0,0 @@ -#include <kernel/arch/i386/tty/tty.h> - -struct vga_cell { - unsigned char c; - unsigned char style; -} __attribute__((__packed__)); - -static const size_t vga_len = 80 * 25; -static struct vga_cell *vga = (void*) 0xB8000; -static size_t vga_pos = 0; - -static void vga_scroll(void) { - for (size_t i = 0; i < vga_len - 80; i++) - vga[i] = vga[i + 80]; - vga_pos -= 80; -} - -static void vga_putchar(char c) { - if (vga_pos >= vga_len - 80) - vga_scroll(); - vga[vga_pos++].c = c; -} - -void vga_write(const char *buf, size_t len) { - for (size_t i = 0; i < len; i++) - vga_putchar(buf[i]); -} - -void vga_clear(void) { - for (size_t i = 0; i < vga_len; i++) - vga[i].c = ' '; - vga_pos = 0; -} |