summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile29
-rw-r--r--asm/boot.asm161
-rw-r--r--asm/long.asm171
-rw-r--r--include/acpi.h143
-rw-r--r--include/gdt.h10
-rw-r--r--include/idt.h17
-rw-r--r--include/mem.h11
-rw-r--r--include/memory.h13
-rw-r--r--include/multiboot.h173
-rw-r--r--include/nrvn.h22
-rw-r--r--include/pic.h9
-rw-r--r--include/ps2.h9
-rw-r--r--include/vga.h38
-rw-r--r--linker.ld62
-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
23 files changed, 1809 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b6abe96
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+all: outdir out/nrvn.bin
+
+out/nrvn.bin: out/boot.o out/nrvn.o out/long.o out/vga.o out/idt.o out/mem.o out/acpi.o out/ps2.o out/pic.o out/memory.o
+ x86_64-pc-elf-gcc -T linker.ld -o $@ -ffreestanding -nostdlib -lgcc $^ -g
+
+out/boot.o: asm/boot.asm
+ nasm -felf64 $^ -o $@
+
+out/long.o: asm/long.asm
+ nasm -felf64 $^ -o $@
+
+out/%.o: src/%.c
+ x86_64-pc-elf-gcc -c $^ -o $@ -I include -ffreestanding -Wall -Wextra -g
+
+iso: all
+ cp out/nrvn.bin grub/boot/nrvn.bin
+ grub-mkrescue -o out/nrvn.iso grub
+
+run: iso
+ qemu-system-x86_64 -cdrom out/nrvn.iso
+
+debug: all
+ qemu-system-x86_64 -kernel out/nrvn.bin -s -S
+
+outdir:
+ mkdir -p out
+
+clean:
+ rm -rf out
diff --git a/asm/boot.asm b/asm/boot.asm
new file mode 100644
index 0000000..ae68761
--- /dev/null
+++ b/asm/boot.asm
@@ -0,0 +1,161 @@
+[BITS 32]
+
+; Declare constants for the multiboot header.
+MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
+MEMINFO equ 1 << 1 ; provide memory map
+MBFLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
+MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
+CHECKSUM equ -(MAGIC + MBFLAGS) ; checksum of above, to prove we are multiboot
+
+; Declare a multiboot header that marks the program as a kernel. These are magic
+; values that are documented in the multiboot standard. The bootloader will
+; search for this signature in the first 8 KiB of the kernel file, aligned at a
+; 32-bit boundary. The signature is in its own section so the header can be
+; forced to be within the first 8 KiB of the kernel file.
+section .multiboot
+align 4
+ dd MAGIC
+ dd MBFLAGS
+ dd CHECKSUM
+
+section .bootstrap
+
+present equ 1 << 7
+not_sys equ 1 << 4
+exec equ 1 << 3
+cd equ 1 << 2
+rw equ 1 << 1
+access equ 1 << 0
+
+gran_4k equ 1 << 7
+sz_32 equ 1 << 6
+long_m equ 1 << 5
+
+global gdt.ptr
+gdt:
+.null: equ $ - gdt
+ dq 0
+.code: equ $ - gdt
+ dd 0xffff
+ db 0
+ db present | not_sys | exec | rw
+ db gran_4k | long_m | 0xf
+ db 0
+.data: equ $ - gdt
+ dd 0xffff
+ db 0
+ db present | not_sys | rw
+ db gran_4k | sz_32 | 0xf
+ db 0
+.ptr:
+ dw $ - gdt - 1
+ dq gdt
+
+global bootstrap
+bootstrap:
+
+ mov dword [multiboot_magic], eax
+ mov dword [multiboot_header], ebx
+
+ mov eax, cr0
+ and eax, ~(1 << 31)
+ mov cr0, eax
+
+ mov edi, pml4 ;0x1000
+ mov cr3, edi
+ xor eax, eax
+ mov ecx, 1024
+ rep stosd
+
+ mov edi, 0x2000
+ mov ecx, 4096
+ rep stosd
+
+ mov edi, cr3
+
+ mov dword [edi], 0x2003
+ mov edi, 0x2000
+ mov dword [edi], 0x3003
+ add edi, 0x1000
+ mov dword [edi], 0x4003
+ mov dword [edi + 8], 0x5003
+ add edi, 0x1000
+
+ mov ebx, 0x00000003
+ mov ecx, 512
+
+.set_entry:
+ mov dword [edi], ebx
+ add ebx, 0x1000
+ add edi, 8
+ loop .set_entry
+
+ mov edi, 0x5000
+ mov ecx, 512
+
+.set_entry_2:
+ mov dword [edi], ebx
+ add ebx, 0x1000
+ add edi, 8
+ loop .set_entry_2
+
+ mov eax, cr4
+ or eax, 1 << 5
+ mov cr4, eax
+
+ mov ecx, 0xc0000080
+ rdmsr
+ or eax, 1 << 8 | 1 << 0
+ wrmsr
+
+ mov eax, cr0
+ or eax, 1 << 31
+ mov cr0, eax
+
+ lgdt [gdt.ptr]
+
+ mov ax, gdt.data
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+
+ extern start
+ jmp gdt.code:start
+
+global set_pml4
+set_pml4:
+ mov ecx, 0xC000080
+ rdmsr
+ and eax, ~(1 << 8)
+ wrmsr
+
+ mov eax, cr0
+ and eax, ~(1 << 31)
+ mov cr0, eax
+
+ mov cr3, edi
+
+ mov ecx, 0xC000080
+ rdmsr
+ or eax, 1 << 8
+ wrmsr
+
+ mov eax, cr0
+ or eax, 1 << 31
+ mov cr0, eax
+
+ retf
+
+section .data
+align 16
+global multiboot_header
+multiboot_header: dq 0
+global multiboot_magic
+multiboot_magic: dq 0
+
+section .bss
+global pml4
+align 4096
+pml4: resq 512
diff --git a/asm/long.asm b/asm/long.asm
new file mode 100644
index 0000000..829d582
--- /dev/null
+++ b/asm/long.asm
@@ -0,0 +1,171 @@
+; The multiboot standard does not define the value of the stack pointer register
+; (esp) and it is up to the kernel to provide a stack. This allocates room for a
+; small stack by creating a symbol at the bottom of it, then allocating 16384
+; bytes for it, and finally creating a symbol at the top. The stack grows
+; downwards on x86. The stack is in its own section so it can be marked nobits,
+; which means the kernel file is smaller because it does not contain an
+; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
+; System V ABI standard and de-facto extensions. The compiler will assume the
+; stack is properly aligned and failure to align the stack will result in
+; undefined behavior.
+section .bss
+ align 16
+stack_bottom:
+ resb 16384 ; 16 KiB
+stack_top:
+
+; The linker script specifies _start as the entry point to the kernel and the
+; bootloader will jump to this position once the kernel has been loaded. It
+; doesn't make sense to return from this function as the bootloader is gone.
+; Declare _start as a function symbol with the given symbol size.
+section .text
+
+global idt_load
+extern idt_ptr
+idt_load:
+ lidt [idt_ptr]
+ ret
+
+%macro isr 1-*
+ %rep %0
+ isr_%+%1:
+ cli
+ push qword 0
+ push qword %1
+ jmp isr_common
+ %rotate 1
+ %endrep
+%endmacro
+
+%macro isr_err 1-*
+ %rep %0
+ isr_%+%1:
+ cli
+ push qword %1
+ jmp isr_common
+ %rotate 1
+ %endrep
+%endmacro
+
+isr 0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 31
+isr_err 8, 10, 11, 12, 13, 14, 17, 21, 29, 30
+isr 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
+
+extern interrupt_handler
+isr_common:
+ push rdi
+ mov rdi, [rsp + 8]
+ push rsi
+ mov rsi, [rsp + 24]
+ push rax
+ push rdx
+ push rcx
+ push r8
+ push r9
+ push r10
+ push r11
+
+ call interrupt_handler
+
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop rcx
+ pop rdx
+ pop rax
+ pop rsi
+ pop rdi
+
+ add rsp, 16
+ iretq
+
+global isr_table
+isr_table:
+ %assign i 0
+ %rep 48
+ dq isr_%+i
+ %assign i i+1
+ %endrep
+
+global toggle_interrupts
+toggle_interrupts:
+ cmp rdi, 0x0
+ je .disable
+ sti
+ ret
+.disable
+ cli
+ ret
+
+global start:function (start.end - start)
+start:
+ ; The bootloader has loaded us into 32-bit protected mode on a x86
+ ; machine. Interrupts are disabled. Paging is disabled. The processor
+ ; state is as defined in the multiboot standard. The kernel has full
+ ; control of the CPU. The kernel can only make use of hardware features
+ ; and any code it provides as part of itself. There's no printf
+ ; function, unless the kernel provides its own <stdio.h> header and a
+ ; printf implementation. There are no security restrictions, no
+ ; safeguards, no debugging mechanisms, only what the kernel provides
+ ; itself. It has absolute and complete power over the
+ ; machine.
+ mov rsp, stack_top
+
+ ; To set up a stack, we set the esp register to point to the top of our
+ ; stack (as it grows downwards on x86 systems). This is necessarily done
+ ; in assembly as languages such as C cannot function without a stack.
+ ;mov rsp, stack_top
+
+ ; This is a good place to initialize crucial processor state before the
+ ; high-level kernel is entered. It's best to minimize the early
+ ; environment where crucial features are offline. Note that the
+ ; processor is not fully initialized yet: Features such as floating
+ ; point instructions and instruction set extensions are not initialized
+ ; yet. The GDT should be loaded here. Paging should be enabled here.
+ ; C++ features such as global constructors and exceptions will require
+ ; runtime support to work as well.
+
+ ; Enter the high-level kernel. The ABI requires the stack is 16-byte
+ ; aligned at the time of the call instruction (which afterwards pushes
+ ; the return pointer of size 4 bytes). The stack was originally 16-byte
+ ; aligned above and we've since pushed a multiple of 16 bytes to the
+ ; stack since (pushed 0 bytes so far) and the alignment is thus
+ ; preserved and the call is well defined.
+ ; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
+
+ mov rax, 0x1
+ cpuid
+ test edx, 1 << 25
+ jz .no_sse
+
+ mov rax, cr0
+ and ax, 0xfffb
+ or ax, 0x2
+ mov cr0, rax
+ mov rax, cr4
+ or ax, 3 << 9
+ mov cr4, rax
+
+.no_sse:
+ extern kernel_main
+ call kernel_main
+
+ ; If the system has nothing more to do, put the computer into an
+ ; infinite loop. To do that:
+ ; 1) Disable interrupts with cli (clear interrupt enable in eflags).
+ ; They are already disabled by the bootloader, so this is not needed.
+ ; Mind that you might later enable interrupts and return from
+ ; kernel_main (which is sort of nonsensical to do).
+ ; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
+ ; Since they are disabled, this will lock up the computer.
+ ; 3) Jump to the hlt instruction if it ever wakes up due to a
+ ; non-maskable interrupt occurring or due to system management mode.
+ int 0
+.hang: hlt
+ jmp .hang
+.end:
+
+section .end
+global kernel_end
+kernel_end:
diff --git a/include/acpi.h b/include/acpi.h
new file mode 100644
index 0000000..1c68ef4
--- /dev/null
+++ b/include/acpi.h
@@ -0,0 +1,143 @@
+#ifndef _ACPI_H_
+#define _ACPI_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+struct rsdp {
+ char signature[8];
+ uint8_t checksum;
+ char oemid[6];
+ uint8_t revision;
+ uint32_t rsdt_address;
+} __attribute__((packed));
+
+struct xsdp {
+ char signature[8];
+ uint8_t checksum;
+ char oemid[6];
+ uint8_t revision;
+ uint32_t rsdt_address; // deprecated since version 2.0
+
+ uint32_t length;
+ uint64_t xsdt_address;
+ uint8_t extended_checksum;
+ uint8_t reserved[3];
+} __attribute__((packed));
+
+struct acpi_sdt_header {
+ char signature[4];
+ uint32_t len;
+ uint8_t rev;
+ uint8_t checksum;
+ char oemid[6];
+ char oem_tableid[8];
+ uint32_t oem_revision;
+ uint32_t creatorid;
+ uint32_t creator_rev;
+} __attribute__((packed));
+
+struct rsdt {
+ struct acpi_sdt_header header;
+ uint32_t sdt_pointers[];
+} __attribute__((packed));
+
+struct xsdt {
+ struct acpi_sdt_header header;
+ uint64_t sdt_pointers[];
+} __attribute__((packed));
+
+struct generic_address {
+ uint8_t address_space;
+ uint8_t bid_width;
+ uint8_t bit_offset;
+ uint8_t access_size;
+ uint64_t address;
+} __attribute__((packed));
+
+struct fadt {
+ struct acpi_sdt_header header;
+ uint32_t firmware_ctrl;
+ uint32_t dsdt;
+
+ uint8_t reserved;
+
+ uint8_t prevered_power_management_profile;
+ uint16_t sci_interrupt;
+ uint32_t smi_command_port;
+ uint8_t acpi_enable;
+ uint8_t acpi_disable;
+ uint8_t s4_bios_req;
+ uint8_t pstate_control;
+
+ uint32_t pm1a_event_block;
+ uint32_t pm1b_event_block;
+
+ uint32_t pm1a_control_block;
+ uint32_t pm1b_control_block;
+
+ uint32_t pm2_control_block;
+
+ uint32_t pm_timer_block;
+
+ uint32_t gpe0_block;
+ uint32_t gpe1_block;
+
+ uint8_t pm1_event_length;
+ uint8_t pm1_control_length;
+ uint8_t pm2_control_length;
+ uint8_t pm_timer_length;
+
+ uint8_t gpe0_length;
+ uint8_t gpe1_length;
+ uint8_t gpe1_base;
+
+ uint8_t cstate_control;
+
+ uint16_t worst_c2_latency;
+ uint16_t worst_c3_latency;
+
+ uint16_t flush_size;
+ uint16_t flush_stride;
+
+ uint8_t duty_offset;
+ uint8_t duty_width;
+
+ uint8_t day_alarm;
+ uint8_t month_alarm;
+ uint8_t century;
+
+ uint16_t boot_arch_flags;
+
+ uint8_t reserved_2;
+ uint32_t flags;
+
+ struct generic_address reset_reg;
+
+ uint8_t reset_valued;
+ uint8_t reserved_3[3];
+
+ uint64_t x_firmware_control;
+ uint64_t x_dsdt;
+
+ struct generic_address x_pm1a_event_block;
+ struct generic_address x_pm1b_event_block;
+ struct generic_address x_pm1a_control_block;
+ struct generic_address x_pm1b_control_block;
+ struct generic_address x_pm2_control_block;
+ struct generic_address x_pm_timer_block;
+ struct generic_address x_gpe0_block;
+ struct generic_address x_gpe1_block;
+} __attribute__((packed));
+
+struct xsdp *find_rsdp();
+struct fadt *find_facp(struct xsdt *root_sdt);
+void print_rsdp(const struct rsdp *rsdp);
+
+static inline struct xsdt *get_rsdt(struct xsdp *rsdp) {
+ if (rsdp == NULL || rsdp->revision == 0)
+ return NULL;
+ return (struct xsdt *) rsdp->xsdt_address;
+}
+
+#endif
diff --git a/include/gdt.h b/include/gdt.h
new file mode 100644
index 0000000..63ff08b
--- /dev/null
+++ b/include/gdt.h
@@ -0,0 +1,10 @@
+#ifndef _GDT_H_
+#define _GDT_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+void gdt_set_gate(size_t num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran);
+void gdt_install();
+
+#endif
diff --git a/include/idt.h b/include/idt.h
new file mode 100644
index 0000000..587419b
--- /dev/null
+++ b/include/idt.h
@@ -0,0 +1,17 @@
+#ifndef _IDT_H_
+#define _IDT_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+void idt_set_isr(uint8_t num, uint64_t offset, uint16_t sel, uint8_t flags);
+void idt_init();
+
+extern uint64_t isr_table[32];
+
+
+bool idt_register_isr(size_t num, void (*func)(uint64_t, uint64_t));
+bool idt_remove_isr(size_t num, void (*func)(uint64_t, uint64_t));
+
+#endif
diff --git a/include/mem.h b/include/mem.h
new file mode 100644
index 0000000..ee68165
--- /dev/null
+++ b/include/mem.h
@@ -0,0 +1,11 @@
+#ifndef _MEM_H_
+#define _MEM_H_
+
+#include <stddef.h>
+
+void *memcpy(void *restrict dest, const void *restrict src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+
+#endif
diff --git a/include/memory.h b/include/memory.h
new file mode 100644
index 0000000..b349e62
--- /dev/null
+++ b/include/memory.h
@@ -0,0 +1,13 @@
+#ifndef _PAGE_H_
+#define _PAGE_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+
+void *kmalloc(size_t size);
+void *kmalloc_a(size_t size, size_t alignment);
+void kfree(void *ptr);
+void print_blocks();
+bool paging_init();
+
+#endif
diff --git a/include/multiboot.h b/include/multiboot.h
new file mode 100644
index 0000000..d2be87b
--- /dev/null
+++ b/include/multiboot.h
@@ -0,0 +1,173 @@
+#ifndef _MULTIBOOT_H_
+#define _MULTIBOOT_H_
+
+#include <stdint.h>
+
+struct multiboot_header
+{
+ uint32_t magic;
+
+ uint32_t flags;
+
+ /* The above fields plus this one must equal 0 mod 2^32. */
+ uint32_t checksum;
+
+ /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
+ uint32_t header_addr;
+ uint32_t load_addr;
+ uint32_t load_end_addr;
+ uint32_t bss_end_addr;
+ uint32_t entry_addr;
+
+ /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
+ uint32_t mode_type;
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+};
+
+/* The symbol table for a.out. */
+struct multiboot_aout_symbol_table
+{
+ uint32_t tabsize;
+ uint32_t strsize;
+ uint32_t addr;
+ uint32_t reserved;
+};
+
+/* The section header table for ELF. */
+struct multiboot_elf_section_header_table
+{
+ uint32_t num;
+ uint32_t size;
+ uint32_t addr;
+ uint32_t shndx;
+};
+
+struct multiboot_info
+{
+ /* Multiboot info version number */
+ uint32_t flags;
+
+ /* Available memory from BIOS */
+ uint32_t mem_lower;
+ uint32_t mem_upper;
+
+ /* "root" partition */
+ uint32_t boot_device;
+
+ /* Kernel command line */
+ uint32_t cmdline;
+
+ /* Boot-Module list */
+ uint32_t mods_count;
+ uint32_t mods_addr;
+
+ union
+ {
+ struct multiboot_aout_symbol_table aout_sym;
+ struct multiboot_elf_section_header_table elf_sec;
+ } u;
+
+ /* Memory Mapping buffer */
+ uint32_t mmap_length;
+ uint32_t mmap_addr;
+
+ /* Drive Info buffer */
+ uint32_t drives_length;
+ uint32_t drives_addr;
+
+ /* ROM configuration table */
+ uint32_t config_table;
+
+ /* Boot Loader Name */
+ uint32_t boot_loader_name;
+
+ /* APM table */
+ uint32_t apm_table;
+
+ /* Video */
+ uint32_t vbe_control_info;
+ uint32_t vbe_mode_info;
+ uint16_t vbe_mode;
+ uint16_t vbe_interface_seg;
+ uint16_t vbe_interface_off;
+ uint16_t vbe_interface_len;
+
+ uint64_t framebuffer_addr;
+ uint32_t framebuffer_pitch;
+ uint32_t framebuffer_width;
+ uint32_t framebuffer_height;
+ uint8_t framebuffer_bpp;
+#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
+#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
+#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
+ uint8_t framebuffer_type;
+ union
+ {
+ struct
+ {
+ uint32_t framebuffer_palette_addr;
+ uint16_t framebuffer_palette_num_colors;
+ };
+ struct
+ {
+ uint8_t framebuffer_red_field_position;
+ uint8_t framebuffer_red_mask_size;
+ uint8_t framebuffer_green_field_position;
+ uint8_t framebuffer_green_mask_size;
+ uint8_t framebuffer_blue_field_position;
+ uint8_t framebuffer_blue_mask_size;
+ };
+ };
+};
+
+struct multiboot_color
+{
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+};
+
+struct multiboot_mmap_entry
+{
+ uint32_t size;
+ uint64_t addr;
+ uint64_t len;
+#define MULTIBOOT_MEMORY_AVAILABLE 1
+#define MULTIBOOT_MEMORY_RESERVED 2
+#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
+#define MULTIBOOT_MEMORY_NVS 4
+#define MULTIBOOT_MEMORY_BADRAM 5
+ uint32_t type;
+} __attribute__((packed));
+
+struct multiboot_mod_list
+{
+ /* the memory used goes from bytes ’mod_start’ to ’mod_end-1’ inclusive */
+ uint32_t mod_start;
+ uint32_t mod_end;
+
+ /* Module command line */
+ uint32_t cmdline;
+
+ /* padding to take it to 16 bytes (must be zero) */
+ uint32_t pad;
+};
+
+/* APM BIOS info. */
+struct multiboot_apm_info
+{
+ uint16_t version;
+ uint16_t cseg;
+ uint32_t offset;
+ uint16_t cseg_16;
+ uint16_t dseg;
+ uint16_t flags;
+ uint16_t cseg_len;
+ uint16_t cseg_16_len;
+ uint16_t dseg_len;
+};
+
+#endif
+
diff --git a/include/nrvn.h b/include/nrvn.h
new file mode 100644
index 0000000..abb03b4
--- /dev/null
+++ b/include/nrvn.h
@@ -0,0 +1,22 @@
+#ifndef _NRVN_H_
+#define _NRVN_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static inline void outb(uint16_t port, uint8_t val) {
+ asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port) : "memory");
+}
+
+static inline uint8_t inb(uint16_t port) {
+ uint8_t ret;
+ asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port) : "memory");
+ return ret;
+}
+
+static inline void io_wait(void) {
+ outb(0x80, 0);
+}
+
+#endif
diff --git a/include/pic.h b/include/pic.h
new file mode 100644
index 0000000..08ec6e7
--- /dev/null
+++ b/include/pic.h
@@ -0,0 +1,9 @@
+#ifndef _PIC_H_
+#define _PIC_H_
+
+#include <stdint.h>
+
+void pic_init(void);
+void pic_eoi(uint8_t irq);
+
+#endif
diff --git a/include/ps2.h b/include/ps2.h
new file mode 100644
index 0000000..29c1f09
--- /dev/null
+++ b/include/ps2.h
@@ -0,0 +1,9 @@
+#ifndef _PS2_H_
+#define _PS2_H_
+
+#include <stdbool.h>
+#include <acpi.h>
+
+bool ps2_init(struct fadt *fadt);
+
+#endif
diff --git a/include/vga.h b/include/vga.h
new file mode 100644
index 0000000..6d74671
--- /dev/null
+++ b/include/vga.h
@@ -0,0 +1,38 @@
+#ifndef _VGA_H_
+#define _VGA_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Hardware text mode color constants. */
+enum vga_color {
+ VGA_BLACK = 0,
+ VGA_BLUE = 1,
+ VGA_GREEN = 2,
+ VGA_CYAN = 3,
+ VGA_RED = 4,
+ VGA_MAGENTA = 5,
+ VGA_BROWN = 6,
+ VGA_LIGHT_GREY = 7,
+ VGA_DARK_GREY = 8,
+ VGA_LIGHT_BLUE = 9,
+ VGA_LIGHT_GREEN = 10,
+ VGA_LIGHT_CYAN = 11,
+ VGA_LIGHT_RED = 12,
+ VGA_LIGHT_MAGENTA = 13,
+ VGA_LIGHT_BROWN = 14,
+ VGA_WHITE = 15,
+};
+
+static const size_t VGA_WIDTH = 80;
+static const size_t VGA_HEIGHT = 25;
+
+void vga_init(void);
+void vga_putn(const size_t num);
+void vga_putx(const size_t hex);
+void vga_putc(const char c);
+void vga_puts(const char *s);
+void vga_putsn(const char *s, const size_t len);
+void vga_set_color(const uint8_t fg, const uint8_t bg);
+
+#endif
diff --git a/linker.ld b/linker.ld
new file mode 100644
index 0000000..87fb6c4
--- /dev/null
+++ b/linker.ld
@@ -0,0 +1,62 @@
+/* The bootloader will look at this image and start execution at the symbol
+ designated as the entry point. */
+ENTRY(bootstrap)
+
+/* Tell where the various sections of the object files will be put in the final
+ kernel image. */
+SECTIONS
+{
+ /* It used to be universally recommended to use 1MiB as a start offset,
+ as it was effectively guaranteed to be available under BIOS systems.
+ However, UEFI has made things more complicated, and experimental data
+ strongly suggests that 2M is a safer place to load. In 2016, a new
+ feature was introduced to the multiboot2 spec to inform bootloaders
+ that a kernel can be loaded anywhere within a range of addresses and
+ will be able to relocations to itself to run from such a loader-selected
+ address, in order to give the loader freedom in selecting a span of
+ memory which is verified to be available by the firmware, in order to
+ work around this issue. This does not use that feature, so 2M was
+ chosen as a safer option than the traditional 1M. */
+ . = 2M;
+
+ .bootstrap BLOCK(4K) : ALIGN(4K)
+ {
+ *(.multiboot)
+ *(.bootstrap)
+ }
+
+ /* First put the multiboot header, as it is required to be put very early
+ early in the image or the bootloader won't recognize the file format.
+ Next we'll put the .text section. */
+ .text BLOCK(4K) : ALIGN(4K)
+ {
+ *(.text)
+ }
+
+ /* Read-only data. */
+ .rodata BLOCK(4K) : ALIGN(4K)
+ {
+ *(.rodata)
+ }
+
+ /* Read-write data (initialized) */
+ .data BLOCK(4K) : ALIGN(4K)
+ {
+ *(.data)
+ }
+
+ /* Read-write data (uninitialized) and stack */
+ .bss BLOCK(4K) : ALIGN(4K)
+ {
+ *(COMMON)
+ *(.bss)
+ }
+
+ .end BLOCK(4K) : ALIGN(4K)
+ {
+ *(.end)
+ }
+
+ /* The compiler may produce other sections, by default it will put them in
+ a segment with the same name. Simply add stuff here as needed. */
+}
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;
+}