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 --- Makefile | 29 +++++++ asm/boot.asm | 161 +++++++++++++++++++++++++++++++++++++++ asm/long.asm | 171 +++++++++++++++++++++++++++++++++++++++++ include/acpi.h | 143 ++++++++++++++++++++++++++++++++++ include/gdt.h | 10 +++ include/idt.h | 17 +++++ include/mem.h | 11 +++ include/memory.h | 13 ++++ include/multiboot.h | 173 +++++++++++++++++++++++++++++++++++++++++ include/nrvn.h | 22 ++++++ include/pic.h | 9 +++ include/ps2.h | 9 +++ include/vga.h | 38 +++++++++ linker.ld | 62 +++++++++++++++ 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 +++++++++++++++++++++++++++ 23 files changed, 1809 insertions(+) create mode 100644 Makefile create mode 100644 asm/boot.asm create mode 100644 asm/long.asm create mode 100644 include/acpi.h create mode 100644 include/gdt.h create mode 100644 include/idt.h create mode 100644 include/mem.h create mode 100644 include/memory.h create mode 100644 include/multiboot.h create mode 100644 include/nrvn.h create mode 100644 include/pic.h create mode 100644 include/ps2.h create mode 100644 include/vga.h create mode 100644 linker.ld 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 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 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 +#include + +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 +#include + +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 +#include +#include + +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 + +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 +#include + +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 + +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 +#include +#include + +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 + +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 +#include + +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 +#include + +/* 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 +#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