summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/acpi.c84
-rw-r--r--src/gdt.c44
-rw-r--r--src/idt.c110
-rw-r--r--src/mem.c26
-rw-r--r--src/memory.c185
-rw-r--r--src/nrvn.c57
-rw-r--r--src/pic.c107
-rw-r--r--src/ps2.c216
-rw-r--r--src/vga.c112
9 files changed, 941 insertions, 0 deletions
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 <acpi.h>
+#include <vga.h>
+#include <mem.h>
+#include <stdbool.h>
+
+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 <gdt.h>
+
+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 <stdbool.h>
+#include <idt.h>
+#include <vga.h>
+#include <mem.h>
+
+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 <mem.h>
+#include <stdint.h>
+
+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 <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <vga.h>
+#include <multiboot.h>
+#include <memory.h>
+#include <mem.h>
+
+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 <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <nrvn.h>
+#include <vga.h>
+#include <idt.h>
+#include <pic.h>
+#include <acpi.h>
+#include <ps2.h>
+#include <multiboot.h>
+#include <mem.h>
+#include <memory.h>
+
+/* 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 <nrvn.h>
+#include <pic.h>
+#include <idt.h>
+#include <vga.h>
+
+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 <stdbool.h>
+#include <nrvn.h>
+#include <acpi.h>
+#include <vga.h>
+
+#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 <nrvn.h>
+#include <vga.h>
+
+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;
+}