diff --git a/Makefile b/Makefile index 16e3103..6f6dda8 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,15 @@ CC=i686-elf-gcc +NASM=nasm .PHONY: iso run clean %.o: %.c $(CC) -c $< -o $@ -std=gnu99 -mgeneral-regs-only -ffreestanding -O2 -Wall -Wextra -pedantic -I. -bootstrap.o: bootstrap.s - i686-elf-as bootstrap.s -o bootstrap.o +bootstrap.o: bootstrap.asm + $(NASM) -felf32 bootstrap.asm -o bootstrap.o -kernel.bin: bootstrap.o gfx/terminal.o std/std.o std/kstd.o cpu/idt.o cpu/pic.o cpu/exception.o kernel.o +kernel.bin: bootstrap.o gfx/terminal.o ps2/keyboard.o std/std.o std/kstd.o cpu/idt.o cpu/pic.o cpu/exception.o kernel.o $(CC) -T linker.ld -o $@ -ffreestanding -O2 -nostdlib -lgcc $^ iso: kernel.bin diff --git a/bootstrap.asm b/bootstrap.asm new file mode 100644 index 0000000..3cb618a --- /dev/null +++ b/bootstrap.asm @@ -0,0 +1,118 @@ +; Declare constants for the multiboot header. +MBALIGN equ 1 << 0 ; align loaded modules on page boundaries +MEMINFO equ 1 << 1 ; provide memory map +FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field +MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header +CHECKSUM equ -(MAGIC + FLAGS) ; 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 FLAGS + dd CHECKSUM + +; 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: + +section .data +gdt_ptr: + dw gdt_end - gdt - 1 + dq gdt +gdt: + ; generated using https://wiki.osdev.org/GDT_Tutorial#Some_stuff_to_make_your_life_easy + ; signature for create_descriptor function: void create_descriptor(uint32_t base, uint32_t limit, uint16_t flag) + dq 0x0000000000000000 ; create_descriptor(0, 0, 0); + dq 0x00CF9A000000FFFF ; create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL0)); + dq 0x00CF92000000FFFF ; create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL0)); +gdt_end: + + +; 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 _start:function (_start.end - _start) +load_gdt: + lgdt [gdt_ptr] + ; Reload CS register containing code selector: + JMP 0x08:.reload_CS ; 0x08 is a stand-in for your code segment +.reload_CS: + ; Reload data segment registers: + MOV AX, 0x10 ; 0x10 is a stand-in for your data segment + MOV DS, AX + MOV ES, AX + MOV FS, AX + MOV GS, AX + MOV SS, AX + RET +_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. + + ; 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 esp, 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. + + call load_gdt + + ; 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: _kmain + extern kmain + call kmain + + ; 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 + ; kmain (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. + cli +.hang: hlt + jmp .hang +.end: diff --git a/bootstrap.s b/bootstrap.s deleted file mode 100644 index 327c491..0000000 --- a/bootstrap.s +++ /dev/null @@ -1,126 +0,0 @@ -/* Declare constants for the multiboot header. */ -.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ -.set MEMINFO, 1<<1 /* provide memory map */ -.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ -.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ -.set CHECKSUM, -(MAGIC + FLAGS) /* 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 -.long MAGIC -.long FLAGS -.long CHECKSUM - -/* -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: -.skip 16384 # 16 KiB -stack_top: - -.section .data -gdt_ptr: -.word gdt_end - gdt - 1 -.long gdt -gdt: -// generated using https://wiki.osdev.org/GDT_Tutorial#Some_stuff_to_make_your_life_easy -// signature for create_descriptor function: void create_descriptor(uint32_t base, uint32_t limit, uint16_t flag) -.quad 0x0000000000000000 // create_descriptor(0, 0, 0); -.quad 0x00CF9A000000FFFF // create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL0)); -.quad 0x00CF92000000FFFF // create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL0)); -.quad 0x00CFFA000000FFFF // create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL3)); -.quad 0x00CFF2000000FFFF // create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL3)); -gdt_end: - -/* -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. -*/ -.section .text -.global _start -.type _start, @function -_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. - */ - - /* - To set up a stack, we set the esp register to point to the top of the - 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 $stack_top, %esp - - /* - 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. - */ - - lgdt gdt_ptr - - /* - 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 pushed a multiple of 16 bytes to the - stack since (pushed 0 bytes so far), so the alignment has thus been - preserved and the call is well defined. - */ - call kmain - - /* - 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 - kmain (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. - */ - cli -1: hlt - jmp 1b - -/* -Set the size of the _start symbol to the current location '.' minus its start. -This is useful when debugging or when you implement call tracing. -*/ -.size _start, . - _start - diff --git a/cpu/exception.c b/cpu/exception.c index 5e72520..d101a15 100644 --- a/cpu/exception.c +++ b/cpu/exception.c @@ -7,6 +7,7 @@ void generic_exception_handler(struct interrupt_descriptor_32 *frame, uint8_t ir kprintf("---- PANIC -------------\n"); kprintf("Kernel panic due to exception\n"); kprintf("IRQ %d\n", (int)irq); + kprintf("selector %d\n", (int)frame->selector); kprintf("------------------------\n"); kabort(); } @@ -103,7 +104,7 @@ static void isr17(struct interrupt_descriptor_32 *frame) { __attribute__((interrupt)) static void isr18(struct interrupt_descriptor_32 *frame) { - generic_exception_handler(frame, 17); + generic_exception_handler(frame, 18); } diff --git a/cpu/pic.c b/cpu/pic.c index 7df471c..b27cce6 100644 --- a/cpu/pic.c +++ b/cpu/pic.c @@ -4,9 +4,6 @@ void pic_remap(uint8_t offset1, uint8_t offset2) { uint8_t a1, a2; - a1 = inb(PIC1_DATA); // save masks - a2 = inb(PIC2_DATA); - outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) io_wait(); outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); @@ -25,8 +22,9 @@ void pic_remap(uint8_t offset1, uint8_t offset2) { outb(PIC2_DATA, ICW4_8086); io_wait(); - outb(PIC1_DATA, a1); // restore saved masks. - outb(PIC2_DATA, a2); + // mask all irqs + outb(PIC1_DATA, 0xFF); + outb(PIC2_DATA, 0xFF); } void pic_send_eoi(uint8_t irq) diff --git a/kernel.c b/kernel.c index 76e60ed..6539190 100644 --- a/kernel.c +++ b/kernel.c @@ -1,10 +1,14 @@ #include #include #include + +#include "std/kstd.h" #include "gfx/terminal.h" #include "cpu/pic.h" +#include "cpu/io.h" #include "cpu/idt.h" -#include "std/kstd.h" +#include "cpu/exception.h" +#include "ps2/keyboard.h" #if defined(__linux__) #error "You are not using a cross-compiler, you will most certainly run into trouble" @@ -27,10 +31,15 @@ void kmain(void) { exception_handlers_init(); kprintf("\r[ OK ]\n"); + kprintf("[ ] Initializing keyboard"); + keyboard_init(); + kprintf("\r[ OK ]\n"); + kprintf("[ ] Initializing IDT"); idt_init(); kprintf("\r[ OK ]\n"); - terminal_writestring("Initialization finished.\n"); -} + kprintf("Initialization finished.\n\n"); + for (;;) {} +} diff --git a/ps2/keyboard.c b/ps2/keyboard.c new file mode 100644 index 0000000..f40dea9 --- /dev/null +++ b/ps2/keyboard.c @@ -0,0 +1,65 @@ +#include + +#include "std/kstd.h" +#include "ps2/keyboard.h" +#include "cpu/pic.h" +#include "cpu/idt.h" +#include "cpu/io.h" + +const char us_map[128] = { + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', + '9', '0', '-', '=', '\b', + '\t', + 'q', 'w', 'e', 'r', + 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', + 0, // 29 ctrl + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0, // 42 left shift + '\\', 'z', 'x', 'c', 'v', 'b', 'n', + 'm', ',', '.', '/', 0, // 54 right shift + '*', + 0, // 56 alt + ' ', + 0, // 58 caps lock + 0, // 59 F1 key + 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 68 F10 key + 0, // 69 num lock + 0, // 70 scroll lock + 0, // 71 home key + 0, // 72 up arrow + 0, // 73 page up + '-', + 0, // 75 left arrow + 0, + 0, // 77 right arrow + '+', + 0, // 79 end key + 0, // 80 down arrow + 0, // 81 page down + 0, // 82 insert key + 0, // 83 delete key + 0, 0, 0, + 0, // 87 F11 key + 0, // 88 F12 key + 0 +}; + +__attribute__((interrupt)) +static void irq(struct interrupt_descriptor_32 *frame) { + char scancode = inb(0x60); + + if (scancode & 128) { + goto end; // ignore release scancode + } + + terminal_putchar(us_map[scancode]); +end: + pic_send_eoi(1); +} + +void keyboard_init() { + pic_irq_clear_mask(1); + idt_register_handler(33, (size_t)irq); + inb(0x60); +} diff --git a/ps2/keyboard.h b/ps2/keyboard.h new file mode 100644 index 0000000..9717375 --- /dev/null +++ b/ps2/keyboard.h @@ -0,0 +1,6 @@ +#ifndef _KEYBOARD_H +#define _KEYBOARD_H + +void keyboard_init(); + +#endif diff --git a/std/kstd.c b/std/kstd.c index 2f3e89a..c4f962c 100644 --- a/std/kstd.c +++ b/std/kstd.c @@ -4,7 +4,7 @@ #include "std/std.h" #include "gfx/terminal.h" -int putchar(int ic) { +int kputchar(int ic) { char c = (char) ic; terminal_write(&c, sizeof(c)); return ic; @@ -13,7 +13,7 @@ int putchar(int ic) { static bool print(const char* data, size_t length) { const unsigned char* bytes = (const unsigned char*) data; for (size_t i = 0; i < length; i++) { - if (putchar(bytes[i]) == (-1)) { // EOF?? + if (kputchar(bytes[i]) == (-1)) { // EOF?? return false; } } diff --git a/std/kstd.h b/std/kstd.h index a5d9911..a80202d 100644 --- a/std/kstd.h +++ b/std/kstd.h @@ -3,7 +3,7 @@ #include -int putchar(int ic); +int kputchar(int ic); int kprintf(const char* restrict format, ...); void kputs(const char* data); void kabort();