diff --git a/bootloader/boot.asm b/bootloader/boot.asm index 189f613..3fc3b50 100644 --- a/bootloader/boot.asm +++ b/bootloader/boot.asm @@ -1,67 +1,230 @@ +; ============================================================================== +; boot.asm - First Stage Bootloader (CHS Based) +; ============================================================================== + [BITS 16] [ORG 0x7C00] start: - cli + cli ; Disable interrupts + + mov [bootdev], dl ; Save boot device number (from BIOS in DL) + + ; Setup stack safely below EBDA area (choose 0x0000:0x7A00) + xor ax, ax ; AX = 0 + mov ss, ax ; Stack segment = 0x0000 + mov sp, 0x7A00 ; Stack offset = 0x7A00 + + ; Initialize DS, ES for zero-based segments xor ax, ax mov ds, ax mov es, ax - mov ss, ax - mov sp, 0x7C00 + ; Load second-stage bootloader (boot1.asm) to 0x7E00 + mov si, 0x7E00 ; Destination offset (ES already zero, so physical 0x7E00) + mov al, 1 ; Number of sectors to read (adjust if needed) + mov cl, 1 ; Starting sector (CHS - sector number, 1-based) + call read_chs + jc disk_error ; Jump if carry set (read error) + + ; Load kernel to 0x100000 (1 MB) + mov si, 0x0000 ; Destination offset + mov ax, 0x1000 ; ES = 0x1000 (0x1000:0x0000 = 1 MB) + mov es, ax + xor bx, bx + mov al, 16 ; Number of sectors for kernel (example) + mov cl, 2 ; Starting sector (adjust as per your disk layout) + call read_chs + jc disk_error + + ; Memory Validation: Verify checksum of second stage bootloader + mov si, 0x7E00 ; Start of second stage + mov cx, 512 ; Size in bytes (adjust if more sectors loaded) + call verify_checksum + jc disk_error ; Jump if checksum fails + + ; Enable A20 line call enable_a20 + jc a20_error ; Jump if A20 enable fails + + ; Setup Global Descriptor Table call setup_gdt + + ; Switch to protected mode and jump to second stage at 0x08:0x7E00 call switch_to_pm -; ---------------------- -; A20 Gate Enable (Fast method) +disk_error: + mov si, disk_error_msg + call print_string_16 + jmp halt + +a20_error: + mov si, a20_error_msg + call print_string_16 + jmp halt + +; ---------------------------------------------------------------- +; Verify Checksum Routine +; Uses SI = start address, CX = byte count +; Simple XOR checksum over bytes, expects result 0 + +verify_checksum: + push ax + push bx + push di + mov di, si + xor al, al + xor bx, bx + +.verify_loop: + lodsb + xor bl, al + loop .verify_loop + + test bl, bl + jz .checksum_ok + stc ; Set carry on checksum error + jmp .done + +.checksum_ok: + clc ; Clear carry on success + +.done: + pop di + pop bx + pop ax + ret + +; ---------------------------------------------------------------- +; CHS Disk Read Routine +; AL = number of sectors +; CL = starting sector (1-based) +; SI = destination offset (Segment:ES already set) +read_chs: + pusha + push dx + mov cx, 5 +.retry: + mov ah, 0x02 ; BIOS read sector + mov dl, [bootdev] ; Drive number + int 0x13 + + jc .error ; Carry flag set = error + pop dx + popa + ret + +.error: + dec cx + jz disk_error + xor ah, ah + int 0x13 + jmp .retry + +; ---------------------------------------------------------------- enable_a20: + ; Fast A20 gate method in al, 0x92 or al, 0x02 out 0x92, al + ; Fallback method (keyboard controller) + jc .fallbackenable_a20: + ; Fast A20 method + in al, 0x92 + or al, 0x02 + and al, 0xFE ; Clear bit 0 to avoid fast A20 issues on some systems + out 0x92, al + ; Verify A20 + call check_a20 + jnc .done + ; Fallback method + mov al, 0xAD + out 0x64, al + ; ... (rest of keyboard controller code) +.done: ret -; ---------------------- -; Set up a minimal GDT +check_a20: + in al, 0x64 ; Read keyboard controller status + test al, 0x02 ; Check if input buffer is full + jnz check_a20 ; Wait until empty + + mov al, 0xD0 ; Command: read output port + out 0x64, al + +.wait_output: + in al, 0x60 ; Read output port value + test al, 0x02 ; Check A20 gate bit (bit 1) + jnz .a20_enabled ; If set, A20 enabled + + xor al, al ; Clear carry to indicate failure + stc ; Set carry for failure, jc will jump + + ret + +.a20_enabled: + clc ; Clear carry flag to indicate success + ret + +.fallback: + mov al, 0xAD + out 0x64, al + mov al, 0xD0 + out 0x64, al + in al, 0x60 + or al, 0x02 + mov al, 0xD1 + out 0x64, al + mov al, 0xAE + out 0x64, al + ret + +; ---------------------------------------------------------------- gdt_start: - dq 0x0000000000000000 ; null descriptor - dq 0x00CF9A000000FFFF ; code segment descriptor - dq 0x00CF92000000FFFF ; data segment descriptor + dq 0x0000000000000000 ; Null descriptor + dq 0x00CF9A000000FFFF ; 32-bit code segment (selector 0x08) + dq 0x00CF92000000FFFF ; 32-bit data segment (selector 0x10) + dq 0x00009A000000FFFF ; 16-bit code segment for real mode (selector 0x18) gdt_descriptor: - dw gdt_end - gdt_start - 1 ; size of GDT - dd gdt_start ; address of GDT - + dw gdt_end - gdt_start - 1 + dd gdt_start gdt_end: setup_gdt: lgdt [gdt_descriptor] ret -; ---------------------- -; Switch to Protected Mode +; ---------------------------------------------------------------- switch_to_pm: cli mov eax, cr0 or eax, 1 mov cr0, eax - jmp 0x08:protected_mode_entry -; ---------------------- -; 32-bit Protected Mode Entry Point -[BITS 32] -protected_mode_entry: - mov ax, 0x10 ; data segment selector - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - mov esp, 0x90000 + jmp 0x08:0x7E00 - ; Kernel is assumed to be loaded at 0x100000 - jmp 0x08:0x100000 +; ---------------------------------------------------------------- +print_string_16: +.loop: + lodsb + or al, al + jz .done + mov ah, 0x0E + int 0x10 + jmp .loop +.done: + ret + +disk_error_msg db "Disk error!", 0 +a20_error_msg db "A20 error!", 0 + +halt: + cli + hlt + +bootdev db 0 +sectors_per_track dw 63 +heads_per_cylinder dw 255 -; ---------------------- -; Boot signature times 510 - ($ - $$) db 0 dw 0xAA55 diff --git a/bootloader/boot.bin b/bootloader/boot.bin deleted file mode 100644 index ed8cf05..0000000 Binary files a/bootloader/boot.bin and /dev/null differ diff --git a/bootloader/boot1.asm b/bootloader/boot1.asm new file mode 100644 index 0000000..439beb7 --- /dev/null +++ b/bootloader/boot1.asm @@ -0,0 +1,182 @@ +; ============================================================================== +; boot1.asm - Second Stage Bootloader (Fixed Real Mode Transition) +; ============================================================================== + +[BITS 32] +global _start +extern kmain + +_start: + ; Set up segments + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; Stack (must be identity-mapped) + mov esp, 0x90000 + + ; CPU Feature Detection: check CPUID support + pushfd ; Save flags + pop eax + mov ecx, eax + xor eax, 1 << 21 ; Flip ID bit + push eax + popfd + pushfd + pop eax + xor eax, ecx + jz .no_cpuid ; CPUID unsupported if no change + + ; CPUID supported, verify features + mov eax, 1 + cpuid + ; Check for paging support (bit 31 of edx) + test edx, 1 << 31 + jz .no_paging_support + + ; Additional CPU feature checks could be added here + + jmp .cpuid_check_done + +.no_cpuid: + mov si, no_cpuid_msg + call print_string_16 + jmp halt + +.no_paging_support: + mov si, no_paging_msg + call print_string_16 + jmp halt + +.cpuid_check_done: + + ; Temporarily switch back to real mode + cli + mov eax, cr0 + and eax, 0x7FFFFFFE ; Clear PE & PG bits + mov cr0, eax + jmp 0x18:real_mode_entry + +; ---------------------------------------------------------------- +[BITS 16] +real_mode_entry: + ; Real mode for BIOS access (E820, VESA) + xor ax, ax + mov es, ax + + ; VESA call + mov di, VbeControllerInfo + mov ax, 0x4F00 + int 0x10 + jc vesa_error + + ; E820 memory map + xor ebx, ebx + mov edx, 0x534D4150 + mov di, MemoryMapBuffer + mov [MemoryMapEntries], dword 0 + +.e820_loop: + mov eax, 0xE820 + mov ecx, 24 + int 0x15 + jc e820_error + add di, 24 + inc dword [MemoryMapEntries] + test ebx, ebx + jnz .e820_loop + jmp .e820_done + +e820_error: + mov si, e820_error_msg + call print_string_16 + jmp halt + +vesa_error: + mov si, vesa_error_msg + call print_string_16 + + ; Fallback: set VGA text mode 3 and continue + mov ah, 0x00 ; BIOS Set Video Mode function + mov al, 0x03 ; VGA 80x25 text mode + int 0x10 + + ; Clear screen + mov ah, 0x06 ; Scroll up function + mov al, 0 ; Clear entire screen + mov bh, 0x07 ; Text attribute (gray on black) + mov cx, 0 ; Upper-left corner + mov dx, 0x184F ; Lower-right corner + int 0x10 + + jmp .e820_done ; Continue booting without VESA graphics + +.e820_done: + ; Back to protected mode + cli + mov eax, cr0 + or eax, 1 + mov cr0, eax + jmp 0x08:protected_entry + +; ---------------------------------------------------------------- +print_string_16: +.loop: + lodsb + or al, al + jz .done + mov ah, 0x0E + int 0x10 + jmp .loop +.done: + ret + +e820_error_msg db "E820 Failed!", 0 +vesa_error_msg db "VESA Failed!", 0 +no_cpuid_msg db "No CPUID support detected!", 0 +no_paging_msg db "CPU lacks paging support!", 0 + +; ---------------------------------------------------------------- +[BITS 32] +protected_entry: + ; Paging setup + xor eax, eax + mov edi, page_directory + mov ecx, 1024 + rep stosd + mov edi, page_table + rep stosd + + mov dword [page_directory], page_table | 0x3 + mov ecx, 1024 + mov edi, page_table + mov eax, 0x00000003 +.fill_pages: + mov [edi], eax + add eax, 0x1000 + add edi, 4 + loop .fill_pages + + mov eax, page_directory + mov cr3, eax + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + jmp kmain + +; ---------------------------------------------------------------- +; Data buffers and variables must be appropriately defined in your data section +MemoryMapBuffer times 128 db 0 ; 128*24 bytes reserved for E820 memory map (adjust size as needed) +MemoryMapEntries dd 0 +VbeControllerInfo times 512 db 0 ; Buffer for VESA controller info (adjust size as needed) + +; Define page directory and page table aligned as needed (in your data section) +align 4096 +page_directory times 1024 dd 0 +align 4096 +page_table times 1024 dd 0 + diff --git a/bootloader/linker.ld b/bootloader/linker.ld index e422078..4e7add9 100644 --- a/bootloader/linker.ld +++ b/bootloader/linker.ld @@ -1,10 +1,9 @@ -ENTRY(start) +ENTRY(kmain) SECTIONS { . = 1M; .text : { - *(.multiboot) *(.text*) } diff --git a/kernel/keyboard.c b/kernel/keyboard.c index d590b73..928eda8 100644 --- a/kernel/keyboard.c +++ b/kernel/keyboard.c @@ -20,34 +20,40 @@ static const char scancode_map[128] = { }; // Interrupt handler for IRQ1 -void keyboard_callback() { - uint8_t scancode = inb(KEYBOARD_DATA_PORT); +void keyboard_callback(void) { + uint8_t scancode = inb(0x60); - // Ignore key releases (scancodes with high bit set) - if (scancode & 0x80) - return; - - char c = scancode_map[scancode]; - if (c && buffer_index < sizeof(key_buffer) - 1) { - key_buffer[buffer_index++] = c; - terminal_putchar(c); // Echo to terminal (optional) + // Only handle key press (ignore key release) + if (!(scancode & 0x80)) { + char c = scancode_map[scancode]; + if (c && buffer_index < sizeof(key_buffer) - 1) { + key_buffer[buffer_index++] = c; + terminal_putchar(c); + } } + + // Send End of Interrupt (EOI) to the PIC + outb(0x20, 0x20); } + void keyboard_init() { register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21) } // Blocking read (returns one char) char keyboard_get_char() { - while (buffer_index == 0); - char c = key_buffer[0]; + while (buffer_index == 0); // Busy wait - // Shift buffer left + char c; + __asm__ __volatile__("cli"); + c = key_buffer[0]; for (uint8_t i = 1; i < buffer_index; i++) { key_buffer[i - 1] = key_buffer[i]; } buffer_index--; + __asm__ __volatile__("sti"); return c; } + diff --git a/kernel/kmain.c b/kernel/kmain.c index 09d8b4c..05ea503 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -11,6 +11,7 @@ #include "kmalloc.h" #include "timer.h" #include "utils.h" +#include "keyboard.h" #define LPT1 0x378 @@ -54,6 +55,10 @@ void kmain(void) { timer_init(100); // 100 Hz (10 ms interval) serial_write("Timer initialized.\n"); + terminal_write("Initializing keyboard...\n"); + keyboard_init(); + serial_write("Keyboard initialized.\n"); + terminal_write("Getting memory map...\n"); memory_map_entry_t mmap[32]; uint32_t mmap_size = get_memory_map(mmap, 32); diff --git a/kernel/timer.c b/kernel/timer.c index 03e539c..ab4a859 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -4,17 +4,20 @@ #include "terminal.h" #include "stdio.h" -static uint32_t tick = 0; +static volatile uint32_t tick = 0; void timer_callback(void) { tick++; - - // Print every 100 ticks for debugging purposes + if (tick % 100 == 0) { - char tick_msg[50]; - snprintf(tick_msg, sizeof(tick_msg), "Tick count: %u\n", tick); - terminal_write(tick_msg); + char buf[16]; + itoa(tick, buf, 10); + terminal_write("Tick count: "); + terminal_write(buf); + terminal_write("\n"); } + + outb(0x20, 0x20); // EOI to PIC } void timer_init(uint32_t frequency) { @@ -29,4 +32,4 @@ void timer_init(uint32_t frequency) { uint32_t timer_get_ticks(void) { return tick; -} \ No newline at end of file +}