From 30d6e8f850d2fe26fffdeef0c38fc627ef8bab9a Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Wed, 13 Dec 2023 13:24:48 +0100 Subject: initial commit currently with: booted via multiboot 1 protected mode to long mode boostrap code vga used for outputting gdt and idt set up identity paging for the whole memory reported by multiboot pic and ps/2 set up acpi code exists but is broken --- src/acpi.c | 84 +++++++++++++++++++++++ src/gdt.c | 44 ++++++++++++ src/idt.c | 110 ++++++++++++++++++++++++++++++ src/mem.c | 26 +++++++ src/memory.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nrvn.c | 57 ++++++++++++++++ src/pic.c | 107 +++++++++++++++++++++++++++++ src/ps2.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vga.c | 112 +++++++++++++++++++++++++++++++ 9 files changed, 941 insertions(+) create mode 100644 src/acpi.c create mode 100644 src/gdt.c create mode 100644 src/idt.c create mode 100644 src/mem.c create mode 100644 src/memory.c create mode 100644 src/nrvn.c create mode 100644 src/pic.c create mode 100644 src/ps2.c create mode 100644 src/vga.c (limited to 'src') diff --git a/src/acpi.c b/src/acpi.c new file mode 100644 index 0000000..b4bb05e --- /dev/null +++ b/src/acpi.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include + +static bool checksum_rsdp(const struct rsdp *rsdp) { + size_t chksm = 0; + for (uint8_t *byte = (uint8_t*) rsdp; byte < (uint8_t*)rsdp + sizeof(*rsdp); byte++) + chksm += *byte; + return (chksm & 0xFF) == 0; +} + +static bool checksum_xsdp(const struct xsdp *rsdp) { + return true; + size_t chksm = 0; + for (uint8_t *byte = (uint8_t*) rsdp; byte < (uint8_t*)rsdp + sizeof(*rsdp); byte++) + chksm += *byte; + return (chksm & 0xFF) == 0; +} + +static bool checksum_sdt(const struct acpi_sdt_header *header) { + uint8_t chksm = 0; + for (size_t i = 0; i < header->len; i++) { + chksm += ((int8_t *) header)[i]; + } + return chksm == 0; +} + +void print_rsdp(const struct rsdp *rsdp) { + vga_puts("sig: "); + vga_putsn(rsdp->signature, 8); + vga_putc('\n'); + + vga_puts("checksum: "); + vga_putn(rsdp->checksum); + vga_putc(' '); + vga_puts(checksum_rsdp(rsdp) ? "PASS" : "FAIL"); + vga_putc('\n'); + + vga_puts("oem id: "); + vga_putsn(rsdp->oemid, 6); + vga_putc('\n'); + + vga_puts("revision: "); + vga_putn(rsdp->revision); + vga_putc('\n'); + + vga_puts("addr: "); + vga_putx(rsdp->rsdt_address); + vga_putc('\n'); +} + +struct fadt *find_facp(struct xsdt *root_sdt) { + if (root_sdt == NULL) + return NULL; + size_t entries = (root_sdt->header.len - sizeof(root_sdt->header)) / 4; + for (size_t i = 0; i < entries; i++) { + struct acpi_sdt_header *header = (struct acpi_sdt_header *) root_sdt->sdt_pointers[i]; + if (memcmp(header->signature, "FACP", 4) == 0 && checksum_sdt(header)) { + return (struct fadt *) header; + } + } + return NULL; +} + +struct xsdp *find_rsdp() { + struct xsdp *rsdp = NULL; + + char *ebda = (char*)((uintptr_t) *(uint16_t *)0x40e << 4); + for (char *ptr = ebda; ptr < ebda + 128; ptr += 16) { + if (memcmp(ptr, "RSD PTR ", 8) == 0 && checksum_xsdp((struct xsdp*) ptr)) { + rsdp = (struct xsdp *) ptr; + } + } + + for (char *ptr = (char*)0x000E0000; ptr < (char*)0x000FFFFF; ptr += 16) { + if (memcmp(ptr, "RSD PTR ", 8) == 0 && checksum_xsdp((struct xsdp*) ptr)) { + if (rsdp == NULL) + rsdp = (struct xsdp *) ptr; + } + } + //print_rsdp(rsdp); + return rsdp; +} diff --git a/src/gdt.c b/src/gdt.c new file mode 100644 index 0000000..22489ac --- /dev/null +++ b/src/gdt.c @@ -0,0 +1,44 @@ +#include + +struct gdt_entry { + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle_low; + uint8_t access; + uint8_t gran; + uint8_t base_middle_high; + uint32_t base_high; + uint32_t reseved; +} __attribute__((packed)); + +struct gdt { + uint16_t limit; + uint64_t base; +} __attribute__((packed)); + +struct gdt_entry gdt[3]; +struct gdt gdt_ptr; + +extern void gdt_flush(); + +void gdt_set_gate(size_t num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran) { + gdt[num] = (struct gdt_entry) { + .base_low = base & 0xFFFF, + .base_middle_low = (base >> 16) & 0xFF, + .base_middle_high = (base >> 24) & 0xFF, + .base_high = (base >> 32) & 0xFFFFFFFF, + .limit_low = limit & 0xFFFF, + .gran = ((limit >> 16) & 0x0F) | (gran & 0xF0), + .access = access + }; +} + +void gdt_install() { + gdt_ptr.limit = (sizeof(struct gdt_entry) * 3) - 1; + gdt_ptr.base = (uint64_t) &gdt; + + gdt_set_gate(0, 0, 0, 0, 0); + gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); + gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); + gdt_flush(); +} diff --git a/src/idt.c b/src/idt.c new file mode 100644 index 0000000..a78847d --- /dev/null +++ b/src/idt.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +extern void idt_load(); + +struct idt_entry { + uint16_t offset_low; + uint16_t sel; + uint8_t ist; + uint8_t flags; + uint16_t offset_middle; + uint32_t offset_high; + uint32_t reserved; +} __attribute__((packed)); + +struct idt { + uint16_t limit; + uint64_t base; +} __attribute__((packed)); + +struct idt_entry idt[256]; +struct idt idt_ptr; +static void (*handlers[256])(uint64_t, uint64_t) = { 0 }; +static const char *exception_msg[] = { + "Division By Zero Exception", + "Debug Exception", + "Non Maskable Interrupt Exception", + "Breakpoint Exception", + "Into Detected Overflow Exception", + "Out of Bounds Exception", + "Invalid Opcode Exception", + "No Coprocessor Exception", + "Double Fault Exception", + "Coprocessor Segment Overrun Exception", + "Bad TSS Exception", + "Segment Not Present Exception", + "Stack Fault Exception", + "General Protection Fault Exception", + "Page Fault Exception", + "Unknown Interrupt Exception", + "Coprocessor Fault Exception", + "Alignment Check Exception (486+)", + "Machine Check Exception (Pentium/586+)", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", + "Reserved Exceptions", +}; + + +void exception_handler(uint64_t num, uint64_t error) { + vga_puts("\n\neto bleh: "); + vga_puts(exception_msg[num]); + vga_puts("\nerror info: "); + vga_putx(error); + while (1); +} + +void interrupt_handler(uint64_t num, uint64_t data) { + handlers[num](num, data); +} + +void idt_set_isr(uint8_t num, uint64_t offset, uint16_t sel, uint8_t flags) { + idt[num] = (struct idt_entry) { + .offset_low = offset & 0xFFFF, + .offset_middle = (offset >> 16) & 0xFFFF, + .offset_high = (offset >> 32) & 0xFFFFFFFF, + .sel = sel, + .ist = 0, + .flags = flags, + .reserved = 0, + }; +} + +void idt_init() { + idt_ptr.limit = (sizeof(struct idt_entry) * 256) - 1; + idt_ptr.base = (uint64_t) &idt; + memset(idt, 0, (sizeof(struct idt_entry) * 256)); + idt_load(); + + for (size_t i = 0; i < 32; i++) { + idt_set_isr(i, isr_table[i], 0x08, 0x8E); + handlers[i] = exception_handler; + } +} + +bool idt_register_isr(size_t num, void (*func)(uint64_t, uint64_t)) { + if (handlers[num]) + return false; + handlers[num] = func; + return true; +} + +bool idt_remove_isr(size_t num, void (*func)(uint64_t, uint64_t)) { + if (handlers[num] != func) + return false; + handlers[num] = NULL; + return true; +} diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..dd302fb --- /dev/null +++ b/src/mem.c @@ -0,0 +1,26 @@ +#include +#include + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) { + for (size_t i = 0; i < n; i++) + ((char *) dest)[i] = ((char *) src)[i]; + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) { + for (size_t i = 0; i < n; i++) + ((char *) dest)[i] = ((char *) src)[i]; + return dest; +} + +void *memset(void *s, int c, size_t n) { + for (size_t i = 0; i < n; i++) + ((char *) s)[i] = c; + return s; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + while (n && *(uint8_t*)s1 == *(uint8_t*)s2) + n--, s1++, s2++; + return n ? *(uint8_t*)s1 - *(uint8_t*)s2 : 0; +} diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..eac0718 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,185 @@ +#include +#include +#include + +#include +#include +#include +#include + +extern uint64_t multiboot_magic; +extern struct multiboot_info *multiboot_header; +extern void *kernel_end; + +extern uint64_t pml4[512]; + +struct memory { + struct memory *prev, *next; + bool free; + size_t size; + uint8_t block[]; +} *blocklist; + +void print_blocks() { + struct memory *block = NULL; + for (block = blocklist; block; block = block->next) { + vga_puts("block at: "); + vga_putx((size_t)block); + vga_puts(", size: "); + vga_putx(block->size); + if (block->free) + vga_puts(", free"); + vga_putc('\n'); + } +} + +void *kmalloc_a(size_t size, size_t alignment) { + struct memory *block = NULL; + for (block = blocklist; block; block = block->next) { + if (!block->free || block->size < size + sizeof(struct memory)) + continue; + size_t offset = (uintptr_t)block->block % alignment; + if (offset) { + if (block->size < (2 * sizeof(struct memory)) + size + (alignment - offset)) { + continue; + } + + struct memory *new_block = (void *)block + (alignment - offset); + *new_block = (struct memory) { + .size = block->size - (alignment - offset), + .free = true, + .prev = block, + .next = block->next, + }; + block->next = new_block; + block->size = alignment - offset; + + block = new_block; + break; + } + } + if (!block) { + print_blocks(); + vga_puts("\n\noom"); + while(1); + } + + struct memory *nextblock = (void *)block->block + size; + *nextblock = (struct memory) { + .size = block->size - size, + .free = true, + .prev = block, + .next = block->next + }; + block->next = nextblock; + block->size = size; + block->free = false; + return block->block; +} + +void *kmalloc(size_t size) { + struct memory *block = NULL; + for (block = blocklist; block && (!block->free || block->size < size + sizeof(struct memory)); block = block->next); + if (!block) { + print_blocks(); + vga_puts("\n\noom"); + while(1); + } + + struct memory *nextblock = (void *)block->block + size; + *nextblock = (struct memory) { + .size = block->size - size, + .free = true, + .prev = block, + .next = block->next + }; + block->next = nextblock; + block->size = size; + block->free = false; + + return block->block; +} + +void kfree(void *ptr) { + struct memory *header = ptr - sizeof(struct memory); + if (header->next && header->next == (void*)header->block + header->size && header->next->free) { + header->size += header->next->size + sizeof(struct memory); + header->next = header->next->next; + if (header->next) + header->next->prev = header; + } + if (header->prev && header == (void*)header->prev->block + header->prev->size && header->prev->free) { + header->prev->size += header->size + sizeof(struct memory); + header->prev->next = header->next; + if (header->next) + header->next->prev = header->prev; + header = header->prev; + } + + header->free = true; +} + +uint64_t alloc_page() { + uint64_t *page = kmalloc_a(sizeof(uint64_t) * 512, 0x1000); + memset(page, 0, sizeof(uint64_t) * 512); + return (uint64_t)page | 0x3; +} + +uint64_t *addr_to_page(uint64_t addr) { + size_t index = (addr >> 39) & 0x1ff; + if (!(pml4[index] & 0x1)) + pml4[index] = alloc_page(); + + uint64_t *pdpr = (uint64_t*)(pml4[index] & ~0xfff); + index = (addr >> 30) & 0x1ff; + if (!(pdpr[index] & 0x1)) + pdpr[index] = alloc_page(); + + uint64_t *pd = (uint64_t*)(pdpr[index] & ~0xfff); + index = (addr >> 21) & 0x1ff; + if (!(pd[index] & 0x1)) + pd[index] = alloc_page(); + + uint64_t *pt = (uint64_t*)(pd[index] & ~0xfff); + index = (addr >> 12) & 0x1ff; + return &pt[index]; +}; + +bool paging_init() { + blocklist = (void *)0x7e00; + *blocklist = (struct memory) { + .size = 0x7ffff - 0x7e00, + .free = true + }; + + if (multiboot_magic != 0x2badb002) + return false; + + for (size_t i = 0; i < multiboot_header->mmap_length / sizeof(struct multiboot_mmap_entry); i++) { + struct multiboot_mmap_entry *mmap = &((struct multiboot_mmap_entry *) (uintptr_t) multiboot_header->mmap_addr)[i]; + + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE && mmap->addr > 0x1000) { + struct memory *header = mmap->addr > (uint64_t)&kernel_end ? (void*) mmap->addr : (void *)&kernel_end; + + // map the first header, in case we need to use this region for paging itself + uint64_t *page = addr_to_page((uintptr_t) header); + *page = ((uintptr_t)header & ~0xfff) | 0x3; + + *header = (struct memory) { + .size = mmap->len, + .free = true + }; + struct memory *last; + for (last = blocklist; last->next; last = last->next); + last->next = header; + header->prev = last; + } + + size_t end = mmap->addr + mmap->len; + for (size_t addr = mmap->addr & ~0xfff; addr < end; addr += 0x1000) { + uint64_t *page = addr_to_page(addr); + *page = addr | 0x3; + } + } + return true; +} diff --git a/src/nrvn.c b/src/nrvn.c new file mode 100644 index 0000000..84b297f --- /dev/null +++ b/src/nrvn.c @@ -0,0 +1,57 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Check if the compiler thinks you are targeting the wrong operating system. */ +#if defined(__linux__) +#error "You are not using a cross-compiler, you will most certainly run into trouble" +#endif + +/* This tutorial will only work for the 32-bit ix86 targets. */ +//#if !defined(__i386__) | !defined(__x86_64__) +//#error "This tutorial needs to be compiled with a ix86-elf compiler" +//#endif + +void kernel_main(void) { + vga_init(); + vga_puts("hello world\n"); + vga_puts("welcome to nirvanna\n\n"); + + vga_puts("initializing interrupt tables..."); + idt_init(); + vga_puts("interrupt tables initialized\n"); + + + vga_puts("initializing the pic..."); + pic_init(); + vga_puts("pic initialized\n"); + + vga_puts("initializing memory pages... "); + if (paging_init()) { + vga_puts("memory pages allocated\n"); + } else { + vga_puts("failed to allocate memory pages\n"); + asm volatile ("cli"); + asm volatile ("hlt"); + } + + // TODO -- fix acpi + + vga_puts("initializing ps/2...\n"); + if (ps2_init(NULL)) + vga_puts("ps/2 initialized.\n"); + else + vga_puts("ps/2 initialization failed.\n"); + + while (1); +} diff --git a/src/pic.c b/src/pic.c new file mode 100644 index 0000000..ad2c15d --- /dev/null +++ b/src/pic.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +extern void toggle_interrupts(bool enable); + +#define pic1 0x20 +#define pic2 0xA0 +#define pic1_d (pic1 + 1) +#define pic2_d (pic2 + 2) + +void pic_remap(uint32_t offset1, uint32_t offset2) { + uint8_t a1, a2; + + a1 = inb(pic1_d); + a2 = inb(pic2_d); + + outb(pic1, 0x10 | 0x01); + io_wait(); + outb(pic2, 0x10 | 0x01); + io_wait(); + outb(pic1_d, offset1); + io_wait(); + outb(pic2_d, offset2); + io_wait(); + outb(pic1_d, 4); + io_wait(); + outb(pic2_d, 2); + io_wait(); + + outb(pic1_d, 0x01); + io_wait(); + outb(pic2_d, 0x01); + io_wait(); + + outb(pic1_d, a1); + outb(pic2_d, a2); +} + +void pic_set_mask(uint8_t irq_line) { + uint32_t port; + uint8_t value; + + if (irq_line < 8) + port = pic1_d; + else { + port = pic2_d; + irq_line -= 8; + } + value = inb(port) | (1 << irq_line); + outb(port, value); +} + +void pic_clear_mask(uint8_t irq_line) { + uint32_t port; + uint8_t value; + + if (irq_line < 8) + port = pic1_d; + else { + port = pic2_d; + irq_line -= 8; + } + value = inb(port) & ~(1 << irq_line); + outb(port, value); +} + +void pic_set_all_masks(void) { + outb(pic1_d, 0xFF); + outb(pic2_d, 0xFF); +} + +void pic_clear_all_masks(void) { + outb(pic1_d, 0x00); + outb(pic2_d, 0x00); +} + +void pic_eoi(uint8_t irq) { + if (irq >= 8) + outb(pic2, 0x20); + outb(pic1, 0x20); +} + +void keyboard_handler(uint64_t num, uint64_t _unused) { + (void)num; + (void)_unused; + uint8_t key = inb(0x60); + vga_puts("key pressed: "); + vga_putx(key); + vga_putc('\n'); + pic_eoi(0x1); +} + +void pic_init(void) { + pic_set_all_masks(); + pic_clear_mask(1); + //pic_clear_mask(12); + pic_remap(32, 40); + + for (size_t i = 32; i < 48; i++) + idt_set_isr(i, isr_table[i], 0x08, 0x8E); + + idt_register_isr(33, keyboard_handler); + + toggle_interrupts(true); +} diff --git a/src/ps2.c b/src/ps2.c new file mode 100644 index 0000000..6491aed --- /dev/null +++ b/src/ps2.c @@ -0,0 +1,216 @@ +#include +#include +#include +#include + +#define data_port 0x60 +#define status_reg 0x64 +#define cmd_reg 0x64 + +#define config_rcmd 0x20 +#define config_wcmd 0x60 +#define ctrl_test_cmd 0xaa + +static inline void print_port_status(bool second_port, const char *msg) { + vga_puts("ps/2 port "); + vga_puts(second_port ? "2 " : "1 "); + vga_puts(msg); +} + +static inline void send_cmd(uint8_t cmd) { + while (inb(status_reg) & 0x2); + outb(cmd_reg, cmd); +} + +static inline bool read_data(uint8_t *data_out) { + size_t counter = 50; + while (!(inb(status_reg) & 0x1) && --counter > 0); + if (counter == 0) + return false; + *data_out = inb(data_port); + return true; +} + +static inline void send_data(uint8_t data, bool second_device) { + if (second_device) + send_cmd(0xd4); + while (inb(status_reg) & 0x2); + outb(data_port, data); +} + +// TODO: ps/2 mouse interface is weird. +static bool interface_test(bool second_device) { + uint8_t rply; + + // Test port + send_cmd(second_device ? 0xa9 : 0xab); + read_data(&rply); + switch (rply) { + case 0x00: + print_port_status(second_device, "port test PASS\n"); + break; + case 0x01: + print_port_status(second_device, "FAIL clock line stuck low\n"); + return false; + case 0x02: + print_port_status(second_device, "FAIL clock line stuck high\n"); + return false; + case 0x03: + print_port_status(second_device, "FAIL data line stuck low\n"); + return false; + case 0x04: + print_port_status(second_device, "FAIL data line stuck data\n"); + return false; + } + + // Re-enable interface + send_cmd(second_device ? 0xa8 : 0xae); + + // reset and self test device + send_data(0xff, second_device); + if (!read_data(&rply)) { + print_port_status(second_device, "self-test FAIL: no reply\n"); + return false; + } + + switch (rply) { + case 0xFA: + if (read_data(&rply), rply == 0xaa) { + print_port_status(second_device, "self-test PASS\n"); + } else { + print_port_status(second_device, "self-test FAIL"); + vga_putx(rply); + vga_putc('\n'); + return false; + } + (void) inb(data_port); // PS/2 mouses send their id, we need to flush + break; + case 0xFC: + print_port_status(second_device, "self-test FAIL: "); + vga_putx(rply); + vga_putc('\n'); + return false; + default: + print_port_status(second_device, "self-test unknown reply: "); + vga_putx(rply); + vga_putc('\n'); + return false; + } + + // Disable device + print_port_status(second_device, "disabling scanning.\n"); + send_data(0xf5, second_device); + if (!read_data(&rply)) { + print_port_status( second_device, "failed to disable: "); + return false; + } + + if (rply != 0xfa && rply != 0xaa) { + print_port_status( second_device, "failed to disable: "); + vga_putx(rply); + vga_putc('\n'); + return false; + } + + print_port_status(second_device, "scanning disabled.\n"); + + print_port_status(second_device, "detecting device type.\n"); + send_data(0xf2, second_device); + if (read_data(&rply), rply != 0xfa) { + print_port_status(second_device, "failed to identify: "); + vga_putx(rply); + vga_putc('\n'); + return false; + } + print_port_status(second_device, "id ack: "); + vga_putx((read_data(&rply), rply)); + vga_putc(' '); + if (read_data(&rply)) + vga_putx(rply); + vga_putc('\n'); + + // Enable device + send_data(0xf4, second_device); + if (read_data(&rply), rply != 0xfa) { + print_port_status(second_device, "failed to enable: "); + vga_putx(rply); + vga_putc('\n'); + return false; + } + + print_port_status(second_device, "scanning enabled.\n"); + + return true; +} + +bool ps2_init(struct fadt *fadt) { + if (fadt != NULL && !(fadt->boot_arch_flags & 0x2)) { + vga_puts("fadt boot arch bit 1 not set: "); + vga_putx(fadt->boot_arch_flags); + vga_putc(' '); + vga_putx(((char*)fadt)[109]); + vga_putc('\n'); + } + + uint8_t rply; + + // Disable ps/2 devices + vga_puts("disabling first device\n"); + send_cmd(0xad); + vga_puts("disabling second device\n"); + send_cmd(0xa7); + + // flush output buffer + (void) inb(data_port); + + // set controller config, clearing bits 0, 1, and 6 + send_cmd(config_rcmd); + uint8_t config = (read_data(&rply), rply) & ~0b01000011; + + // check if dual channel is already set + bool dual_channel = !(config & 0x5); + + send_cmd(config_wcmd); + send_data(config, false); + + // perform controller self test + send_cmd(ctrl_test_cmd); + if (read_data(&rply), rply != 0x55) { + vga_puts("ps/2 controller self test failed: "); + vga_putx(rply); + vga_putc('\n'); + return false; + } + + // re-set config in case the self tests reset the device + send_cmd(config_wcmd); + send_data(config, false); + + // determine if there are two ports + if (!dual_channel) { + // attempt to enable second channel + send_cmd(0xa8); + + // check if configuration show second port + // and disable it again if it shows. + send_cmd(0x20); + if ((dual_channel = !((read_data(&rply), rply) & 0x5))) + send_cmd(0xa7); + } + + // enable and reset devices + if (!interface_test(false)) + vga_puts("failed to test port 1 interface\n"); + + if (dual_channel && !interface_test(true)) + vga_puts("failed to test port 2 interface\n"); + + // re enabled bits 1 and 2 (interrupts) + send_cmd(0x20); + config = (read_data(&rply), rply) | 0x3; + + send_cmd(0x60); + send_data(config, false); + + return true; +} diff --git a/src/vga.c b/src/vga.c new file mode 100644 index 0000000..150af9b --- /dev/null +++ b/src/vga.c @@ -0,0 +1,112 @@ +#include +#include + +static size_t vga_x, vga_y; +static uint8_t vga_color; +static uint16_t *vga_buffer; + +void vga_init(void) { + vga_x = vga_y = 0; + vga_set_color(VGA_LIGHT_MAGENTA, VGA_BLACK); + vga_buffer = (uint16_t *)0xB8000; + for (size_t y = 0; y < VGA_HEIGHT; y++) + for (size_t x = 0; x < VGA_WIDTH; x++) + vga_buffer[y * VGA_WIDTH + x] = ' ' | (vga_color << 8); +} + +void vga_scroll() { + if (vga_y < VGA_HEIGHT) + return; + + for (size_t y = 0; y < VGA_HEIGHT - 1; y++) { + for (size_t x = 0; x < VGA_WIDTH; x++) { + const size_t from = (y + 1) * VGA_WIDTH + x; + const size_t to = y * VGA_WIDTH + x; + vga_buffer[to] = vga_buffer[from]; + } + } + vga_y = VGA_HEIGHT - 1; + for (size_t x = 0; x < VGA_HEIGHT; x++) { + vga_buffer[vga_y * VGA_WIDTH + x] = ' ' | (vga_color << 8); + } +} + +void vga_update_cursor(void) { + size_t index = vga_y * VGA_WIDTH + vga_x; + + outb(0x3D4, 14); + outb(0x3D5, index >> 8); + outb(0x3D4, 15); + outb(0x3D5, index); +} + +void vga_putn(const size_t number) { + char digits[32] = { 0 }; + size_t num = number, count = 0; + do { + digits[count] = (num % 10) + '0'; + count++; + } while ((num /= 10) > 0); + + do { + vga_putc(digits[count]); + } while (count-- > 0); +} + +void vga_putx(const size_t hex) { + char digits[32] = { 0 }; + size_t num = hex, count = 0; + do { + digits[count] = "0123456789abcdef"[num % 16]; + count++; + } while ((num /= 16) > 0); + + vga_puts("0x"); + do { + vga_putc(digits[count]); + } while (count-- > 0); +} + +void vga_putc(const char c) { + switch (c) { + case '\n': + vga_x = 0; + vga_y++; + break; + case '\b': + if (vga_x > 0) + vga_x--; + break; + case '\r': + vga_x = 0; + break; + case '\t': + break; + default: + // Any character greater than and including a space, is a printable character + if (c >= ' ') + vga_buffer[vga_y * VGA_WIDTH + vga_x++] = (uint16_t) c | ((uint16_t) vga_color << 8); + } + + if (vga_x >= VGA_WIDTH) { + vga_x = 0; + vga_y++; + } + + vga_scroll(); + vga_update_cursor(); +} + +void vga_puts(const char *s) { + while (*s) + vga_putc(*s++); +} + +void vga_putsn(const char *s, const size_t len) { + for (size_t i = 0; i < len; i++) + vga_putc(s[i]); +} + +void vga_set_color(const uint8_t fg, const uint8_t bg) { + vga_color = fg | bg << 4; +} -- cgit v1.2.3