Feat: CPU Exceptions and load errors are now shown.
This commit is contained in:
parent
c1068d34e2
commit
9e4224793b
255
boot.asm
255
boot.asm
@ -1,221 +1,94 @@
|
||||
; File: boot.asm
|
||||
[org 0x7c00] ; BIOS loads us at 0x7C00
|
||||
[bits 16] ; We start in 16-bit Real Mode
|
||||
; Stage 1 Bootloader (Master Boot Record - exactly 512 bytes)
|
||||
; Job: Load Stage 2 from disk and jump to it
|
||||
|
||||
[org 0x7c00]
|
||||
[bits 16]
|
||||
|
||||
STAGE2_OFFSET equ 0x7e00 ; Load stage2 right after boot sector
|
||||
|
||||
start:
|
||||
; Set up segments
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
mov sp, 0x7c00
|
||||
|
||||
; IMPORTANT: Save boot drive number (BIOS passes it in DL)
|
||||
push dx
|
||||
|
||||
; Clear screen
|
||||
mov ah, 0x00
|
||||
mov al, 0x03
|
||||
int 0x10
|
||||
|
||||
mov si, real_msg
|
||||
; Print loading message
|
||||
mov si, msg_loading
|
||||
call print_string
|
||||
|
||||
cli
|
||||
; Load Stage 2 from disk
|
||||
; Read 5 sectors (sectors 2-6) - gives us 2.5KB for Stage 2
|
||||
pop dx ; Restore boot drive
|
||||
push dx ; Save it again for stage 2
|
||||
mov bx, STAGE2_OFFSET ; Destination
|
||||
mov dh, 5 ; Number of sectors
|
||||
; DL already has drive number
|
||||
call load_disk
|
||||
|
||||
lgdt [gdt_descriptor]
|
||||
; Jump to Stage 2 (DL still has boot drive number)
|
||||
pop dx ; Pass drive number to stage 2 in DL
|
||||
jmp STAGE2_OFFSET
|
||||
|
||||
mov eax, cr0
|
||||
or eax, 0x1
|
||||
mov cr0, eax
|
||||
; ============================================================
|
||||
; LOAD DISK - Read sectors using BIOS INT 0x13
|
||||
; ============================================================
|
||||
load_disk:
|
||||
push dx
|
||||
|
||||
; Far jump into 32-bit mode.
|
||||
jmp 0x08:init_pm
|
||||
mov ah, 0x02 ; Read function
|
||||
mov al, dh ; Number of sectors
|
||||
mov ch, 0 ; Cylinder 0
|
||||
mov cl, 2 ; Start at sector 2
|
||||
mov dh, 0 ; Head 0
|
||||
|
||||
; --- Helper: Print String (BIOS INT 0x10) ---
|
||||
int 0x13
|
||||
jc disk_error
|
||||
|
||||
pop dx
|
||||
cmp al, dh
|
||||
jne disk_error
|
||||
ret
|
||||
|
||||
disk_error:
|
||||
mov si, msg_error
|
||||
call print_string
|
||||
jmp $
|
||||
|
||||
; ============================================================
|
||||
; PRINT STRING
|
||||
; ============================================================
|
||||
print_string:
|
||||
pusha
|
||||
mov ah, 0x0e ; INT 0x10 "Teletype Output" function
|
||||
mov ah, 0x0e
|
||||
.loop:
|
||||
lodsb ; Load byte at [SI] into AL, increment SI
|
||||
cmp al, 0 ; Check for null terminator
|
||||
je .done
|
||||
int 0x10 ; Call BIOS video interrupt
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
int 0x10
|
||||
jmp .loop
|
||||
.done:
|
||||
popa
|
||||
ret
|
||||
|
||||
real_msg: db '[REAL] - Loaded successfully.', 0x0D, 0x0A, 0
|
||||
; ============================================================
|
||||
; DATA
|
||||
; ============================================================
|
||||
msg_loading: db '[STAGE1] Loading SoraOS...', 0x0D, 0x0A, 0
|
||||
msg_error: db '[ERROR] Disk read failed!', 0x0D, 0x0A, 0
|
||||
|
||||
; --- GDT (Global Descriptor Table) ---
|
||||
align 8
|
||||
gdt_start:
|
||||
|
||||
gdt_null:
|
||||
dq 0x0
|
||||
|
||||
gdt_code32: ; 32-bit code segment (selector 0x08)
|
||||
dw 0xffff ; Limit 0:15
|
||||
dw 0x0000 ; Base 0:15
|
||||
db 0x00 ; Base 16:23
|
||||
db 10011010b ; Access: present, ring 0, code, executable, readable
|
||||
db 11001111b ; Flags: 4KB granularity, 32-bit | Limit 16:19
|
||||
db 0x00 ; Base 24:31
|
||||
|
||||
gdt_data32: ; 32-bit data segment (selector 0x10)
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10010010b ; Access: present, ring 0, data, writable
|
||||
db 11001111b
|
||||
db 0x00
|
||||
|
||||
gdt_code64: ; 64-bit code segment (selector 0x18)
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10011010b ; Access: present, ring 0, code, executable, readable
|
||||
db 10101111b ; Flags: 4KB granularity, 64-bit, limit 16:19
|
||||
db 0x00
|
||||
|
||||
gdt_data64: ; 64-bit data segment (selector 0x20)
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10010010b
|
||||
db 10101111b
|
||||
db 0x00
|
||||
|
||||
gdt_end:
|
||||
|
||||
gdt_descriptor:
|
||||
dw gdt_end - gdt_start - 1
|
||||
dd gdt_start
|
||||
|
||||
; ==================================================
|
||||
; 32-BIT PROTECTED MODE
|
||||
; ==================================================
|
||||
[bits 32]
|
||||
init_pm:
|
||||
; Set up segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
mov esp, 0x90000
|
||||
|
||||
; Print message in Protected Mode
|
||||
; Print to VGA memory
|
||||
mov edi, 0xb8000
|
||||
mov al, 'P'
|
||||
mov ah, 0x0f
|
||||
mov [edi], ax
|
||||
add edi, 2
|
||||
mov al, 'M'
|
||||
mov [edi], ax
|
||||
add edi, 2
|
||||
mov al, '3'
|
||||
mov [edi], ax
|
||||
add edi, 2
|
||||
mov al, '2'
|
||||
mov [edi], ax
|
||||
|
||||
; Set up page tables for identity mapping first 2MB
|
||||
; PML4[0] -> PDPT
|
||||
mov edi, 0x1000
|
||||
mov dword [edi], 0x2003 ; Present, Writable, points to 0x2000
|
||||
add edi, 4
|
||||
mov dword [edi], 0
|
||||
|
||||
; PDPT[0] -> PD
|
||||
mov edi, 0x2000
|
||||
mov dword [edi], 0x3003 ; Present, Writable, points to 0x3000
|
||||
add edi, 4
|
||||
mov dword [edi], 0
|
||||
|
||||
; PD[0] -> 2MB page at physical 0x0
|
||||
mov edi, 0x3000
|
||||
mov dword [edi], 0x83 ; Present, Writable, Page Size (2MB)
|
||||
add edi, 4
|
||||
mov dword [edi], 0
|
||||
|
||||
; Load CR3 with PML4 address
|
||||
mov eax, 0x1000
|
||||
mov cr3, eax
|
||||
|
||||
; Enable PAE (Physical Address Extension)
|
||||
mov eax, cr4
|
||||
or eax, 0x20
|
||||
mov cr4, eax
|
||||
|
||||
; Enable Long Mode in EFER MSR
|
||||
mov ecx, 0xC0000080
|
||||
rdmsr
|
||||
or eax, 0x100
|
||||
wrmsr
|
||||
|
||||
; Enable Paging (activates Long Mode)
|
||||
mov eax, cr0
|
||||
or eax, 0x80000000
|
||||
mov cr0, eax
|
||||
|
||||
; Far jump to 64-bit code
|
||||
jmp 0x18:long_mode_start
|
||||
|
||||
jmp $
|
||||
|
||||
; --- 64-bit Long Mode ---
|
||||
[bits 64]
|
||||
long_mode_start:
|
||||
; Set up segments (most are ignored in 64-bit mode)
|
||||
mov ax, 0x20
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Set up stack
|
||||
mov rsp, 0x90000
|
||||
|
||||
; Clear screen and print "SORA OS 64"
|
||||
mov rdi, 0xb8000
|
||||
mov rcx, 80*25
|
||||
mov ax, 0x0f20 ; White space
|
||||
rep stosw
|
||||
|
||||
; Print message
|
||||
mov rdi, 0xb8000
|
||||
mov rsi, long_msg
|
||||
call print_string_64
|
||||
|
||||
; Hang
|
||||
jmp $
|
||||
|
||||
; --- 64-bit Print String ---
|
||||
print_string_64:
|
||||
mov ah, 0x0f ; White on black
|
||||
.loop:
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
stosw ; Write character + attribute
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
long_msg: db 'SoraOS 64-BIT', 0
|
||||
|
||||
; --- The Magic Boot Sector Footer ---
|
||||
; Fill the rest of the 512 bytes with zeros
|
||||
; ============================================================
|
||||
; BOOT SIGNATURE
|
||||
; ============================================================
|
||||
times 510-($-$$) db 0
|
||||
; The BIOS signature (must be at the very end)
|
||||
dw 0xAA55
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
pml4_table:
|
||||
resb 4096
|
||||
pdpt_table:
|
||||
resb 4096
|
||||
pd_table:
|
||||
resb 4096
|
||||
|
||||
|
||||
75
build.sh
75
build.sh
@ -1,12 +1,71 @@
|
||||
#!/bin/sh
|
||||
export PATH=/usr/local/cross/bin:$PATH
|
||||
#!/bin/bash
|
||||
# Complete build script for SoraOS with 2-stage bootloader
|
||||
|
||||
# 1. Assemble raw binary
|
||||
echo ">>> Assembling boot.asm to boot.bin..."
|
||||
set -e # Exit on any error
|
||||
|
||||
echo "==================================="
|
||||
echo " SoraOS Build System v1.0"
|
||||
echo "==================================="
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Step 1: Assemble Stage 1 bootloader (MBR - 512 bytes)
|
||||
echo -e "${BLUE}[1/6]${NC} Assembling Stage 1 bootloader (MBR)..."
|
||||
nasm -f bin boot.asm -o boot.bin
|
||||
|
||||
# 2. Run QEMU as a raw disk drive
|
||||
echo ">>> Running QEMU with boot.bin..."
|
||||
qemu-system-x86_64 -drive format=raw,file=boot.bin
|
||||
# Step 2: Assemble Stage 2 bootloader (extended loader)
|
||||
echo -e "${BLUE}[2/6]${NC} Assembling Stage 2 bootloader..."
|
||||
nasm -f bin elevator.asm -o elevator.bin
|
||||
|
||||
echo "Done!"
|
||||
# Step 3: Assemble kernel entry point
|
||||
echo -e "${BLUE}[3/6]${NC} Assembling kernel entry..."
|
||||
nasm -f elf64 kernel.asm -o kernel.asm.o
|
||||
|
||||
# Step 4: Compile kernel C code
|
||||
echo -e "${BLUE}[4/6]${NC} Compiling kernel..."
|
||||
gcc -ffreestanding -c kernel.c -o kernel.c.o -mcmodel=large -mno-red-zone -fno-pic -fno-pie -static -fno-stack-protector -nostdlib -mno-mmx -mno-sse -mno-sse2 -m64
|
||||
|
||||
# Step 5: Link kernel
|
||||
echo -e "${BLUE}[5/6]${NC} Linking kernel..."
|
||||
ld -T linker.ld -o kernel.bin kernel.asm.o kernel.c.o --oformat binary
|
||||
|
||||
# Step 6: Create OS image
|
||||
echo -e "${BLUE}[6/6]${NC} Creating OS image..."
|
||||
cat boot.bin elevator.bin kernel.bin > os-image.bin
|
||||
|
||||
# Pad to floppy size (1.44MB)
|
||||
truncate -s 1440K os-image.bin
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Build complete!${NC}"
|
||||
echo ""
|
||||
echo "==================================="
|
||||
echo "File Layout:"
|
||||
echo "==================================="
|
||||
echo "Sector 1: Stage 1 (MBR)"
|
||||
echo "Sectors 2-6: Stage 2 (Extended Bootloader)"
|
||||
echo "Sectors 7+: Kernel"
|
||||
echo ""
|
||||
|
||||
# Show sizes
|
||||
echo "File Sizes:"
|
||||
printf "%-15s %10s\n" "File" "Size"
|
||||
printf "%-15s %10s\n" "----" "----"
|
||||
printf "%-15s %10d bytes\n" "boot.bin" $(stat -f%z boot.bin 2>/dev/null || stat -c%s stage1.bin 2>/dev/null)
|
||||
printf "%-15s %10d bytes\n" "elevator.bin" $(stat -f%z elevator.bin 2>/dev/null || stat -c%s stage2.bin 2>/dev/null)
|
||||
printf "%-15s %10d bytes\n" "kernel.bin" $(stat -f%z kernel.bin 2>/dev/null || stat -c%s kernel.bin 2>/dev/null)
|
||||
printf "%-15s %10d bytes\n" "os-image.bin" $(stat -f%z os-image.bin 2>/dev/null || stat -c%s os-image.bin 2>/dev/null)
|
||||
echo ""
|
||||
|
||||
echo -e "${YELLOW}Running QEMU...${NC}"
|
||||
echo "==================================="
|
||||
echo "Press Ctrl+C to exit QEMU"
|
||||
echo ""
|
||||
qemu-system-x86_64 -drive format=raw,file=os-image.bin -no-reboot -d cpu_reset
|
||||
|
||||
|
||||
echo -e "${GREEN}Done!${NC}"
|
||||
|
||||
490
elevator.asm
Normal file
490
elevator.asm
Normal file
@ -0,0 +1,490 @@
|
||||
; File: elevator.asm
|
||||
; Stage 2 Bootloader - No size limit!
|
||||
; Job: Load kernel, set up GDT, switch to 64-bit mode, jump to kernel
|
||||
|
||||
[org 0x7e00]
|
||||
[bits 16]
|
||||
|
||||
KERNEL_OFFSET equ 0x10000 ; Load kernel at 64KB (safer than 0x1000)
|
||||
BOOT_DRIVE: db 0 ; Store boot drive number
|
||||
ERROR_CODE: db 0 ; Store error code from disk read
|
||||
|
||||
stage2_start:
|
||||
; Print stage 2 message
|
||||
mov si, msg_stage2
|
||||
call print_string
|
||||
|
||||
; Save boot drive number (passed from stage1 in DL)
|
||||
mov [BOOT_DRIVE], dl
|
||||
|
||||
; Load kernel from disk
|
||||
call load_kernel
|
||||
|
||||
; Verify kernel was loaded - check for signature
|
||||
; Check if there's actual code at 0x10000
|
||||
mov ax, 0x1000
|
||||
mov es, ax
|
||||
xor bx, bx
|
||||
mov ax, [es:bx] ; Read first 2 bytes of kernel
|
||||
|
||||
; Show what we read (for debugging)
|
||||
push ax
|
||||
xor ax, ax
|
||||
mov es, ax
|
||||
|
||||
mov si, msg_kernel_check
|
||||
call print_string
|
||||
|
||||
pop ax
|
||||
call print_hex_word ; Print the word we read
|
||||
|
||||
; If it's 0x0000, kernel didn't load
|
||||
test ax, ax
|
||||
jz kernel_load_failed
|
||||
|
||||
mov si, msg_kernel_ok
|
||||
call print_string
|
||||
|
||||
; Disable interrupts
|
||||
cli
|
||||
|
||||
; Load GDT
|
||||
lgdt [gdt_descriptor]
|
||||
|
||||
; Enable Protected Mode
|
||||
mov eax, cr0
|
||||
or eax, 0x1
|
||||
mov cr0, eax
|
||||
|
||||
; Far jump to 32-bit code
|
||||
jmp 0x08:protected_mode
|
||||
|
||||
; ============================================================
|
||||
; LOAD KERNEL FROM DISK
|
||||
; ============================================================
|
||||
load_kernel:
|
||||
push ax
|
||||
push bx
|
||||
push cx
|
||||
push dx
|
||||
|
||||
; Reset disk system first
|
||||
mov ah, 0x00
|
||||
mov dl, [BOOT_DRIVE]
|
||||
int 0x13
|
||||
|
||||
; Set up ES:BX for the destination
|
||||
; KERNEL_OFFSET is 0x10000, so we need ES=0x1000, BX=0x0000
|
||||
mov ax, 0x1000
|
||||
mov es, ax
|
||||
xor bx, bx ; BX = 0, so ES:BX = 0x1000:0x0000 = 0x10000
|
||||
|
||||
; Now try loading kernel
|
||||
mov ah, 0x02 ; Read function
|
||||
mov al, 10 ; Number of sectors
|
||||
mov ch, 0 ; Cylinder 0
|
||||
mov cl, 7 ; Start at sector 7
|
||||
mov dh, 0 ; Head 0
|
||||
mov dl, [BOOT_DRIVE] ; Drive number
|
||||
|
||||
int 0x13
|
||||
jc kernel_error
|
||||
|
||||
; Restore ES to 0
|
||||
xor ax, ax
|
||||
mov es, ax
|
||||
|
||||
pop dx
|
||||
pop cx
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
kernel_error:
|
||||
; Restore ES
|
||||
xor ax, ax
|
||||
mov es, ax
|
||||
|
||||
; Save error code
|
||||
mov byte [ERROR_CODE], ah
|
||||
|
||||
mov si, msg_kernel_error
|
||||
call print_string
|
||||
|
||||
; Show error code
|
||||
mov si, msg_error_code
|
||||
call print_string
|
||||
|
||||
; Print error code as hex
|
||||
mov al, [ERROR_CODE]
|
||||
call print_hex
|
||||
|
||||
; Show drive number we tried to use
|
||||
mov si, msg_drive
|
||||
call print_string
|
||||
mov al, [BOOT_DRIVE]
|
||||
call print_hex
|
||||
|
||||
jmp $
|
||||
|
||||
kernel_load_failed:
|
||||
xor ax, ax
|
||||
mov es, ax
|
||||
|
||||
mov si, msg_verify_failed
|
||||
call print_string
|
||||
jmp $
|
||||
|
||||
; ============================================================
|
||||
; PRINT STRING (16-bit)
|
||||
; ============================================================
|
||||
print_string:
|
||||
pusha
|
||||
mov ah, 0x0e
|
||||
.loop:
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
int 0x10
|
||||
jmp .loop
|
||||
.done:
|
||||
popa
|
||||
ret
|
||||
|
||||
; ============================================================
|
||||
; PRINT HEX BYTE (for debugging)
|
||||
; ============================================================
|
||||
print_hex:
|
||||
push ax
|
||||
push bx
|
||||
|
||||
mov bl, al
|
||||
shr al, 4 ; Get high nibble
|
||||
call print_nibble
|
||||
|
||||
mov al, bl
|
||||
and al, 0x0F ; Get low nibble
|
||||
call print_nibble
|
||||
|
||||
pop bx
|
||||
pop ax
|
||||
ret
|
||||
|
||||
print_nibble:
|
||||
cmp al, 9
|
||||
jle .digit
|
||||
add al, 7 ; Convert to A-F
|
||||
.digit:
|
||||
add al, '0'
|
||||
mov ah, 0x0e
|
||||
int 0x10
|
||||
ret
|
||||
|
||||
; Print a 16-bit word as hex
|
||||
print_hex_word:
|
||||
push ax
|
||||
mov al, ah ; Print high byte first
|
||||
call print_hex
|
||||
pop ax
|
||||
call print_hex ; Print low byte
|
||||
ret
|
||||
|
||||
; ============================================================
|
||||
; MESSAGES
|
||||
; ============================================================
|
||||
msg_stage2: db '[STAGE2] Extended bootloader loaded', 0x0D, 0x0A, 0
|
||||
msg_kernel_loaded: db '[STAGE2] Kernel loaded, switching to Protected Mode...', 0x0D, 0x0A, 0
|
||||
msg_kernel_error: db '[ERROR] Kernel load failed!', 0x0D, 0x0A, 0
|
||||
msg_kernel_check: db '[STAGE2] Verifying kernel load: Read word = 0x', 0
|
||||
msg_kernel_ok: db '[STAGE2] Kernel load verified.', 0x0D, 0x0A, 0
|
||||
msg_verify_failed: db '[ERROR] Kernel verification failed!', 0x0D, 0x0A, 0
|
||||
msg_error_code: db ' Error code: 0x', 0
|
||||
msg_drive: db ' Drive number: 0x', 0
|
||||
|
||||
; ============================================================
|
||||
; GDT - Global Descriptor Table
|
||||
; ============================================================
|
||||
align 8
|
||||
gdt_start:
|
||||
|
||||
gdt_null:
|
||||
dq 0x0
|
||||
|
||||
gdt_code32: ; Selector 0x08
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10011010b
|
||||
db 11001111b
|
||||
db 0x00
|
||||
|
||||
gdt_data32: ; Selector 0x10
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10010010b
|
||||
db 11001111b
|
||||
db 0x00
|
||||
|
||||
gdt_code64: ; Selector 0x18
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10011010b
|
||||
db 10101111b
|
||||
db 0x00
|
||||
|
||||
gdt_data64: ; Selector 0x20
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 10010010b
|
||||
db 10101111b
|
||||
db 0x00
|
||||
|
||||
gdt_end:
|
||||
|
||||
gdt_descriptor:
|
||||
dw gdt_end - gdt_start - 1 ; Limit (size - 1)
|
||||
dd gdt_start ; Base address (org already adjusts this)
|
||||
|
||||
; ============================================================
|
||||
; 32-BIT PROTECTED MODE
|
||||
; ============================================================
|
||||
[bits 32]
|
||||
protected_mode:
|
||||
; Set up segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
mov esp, 0x90000
|
||||
|
||||
; Print to VGA
|
||||
mov edi, 0xb8000
|
||||
mov esi, pm_msg
|
||||
call print_pm
|
||||
|
||||
; Clear page table area (12KB starting at 0x1000)
|
||||
mov edi, 0x1000
|
||||
mov ecx, 3072 ; 12KB / 4 bytes = 3072 dwords
|
||||
xor eax, eax
|
||||
rep stosd
|
||||
|
||||
; Set up page tables
|
||||
; PML4[0] -> PDPT at 0x2000
|
||||
mov dword [0x1000], 0x2003
|
||||
|
||||
; PDPT[0] -> PD at 0x3000
|
||||
mov dword [0x2000], 0x3003
|
||||
|
||||
; Identity map first 4MB (two 2MB pages)
|
||||
; PD[0] -> First 2MB
|
||||
mov dword [0x3000], 0x00000083
|
||||
mov dword [0x3004], 0x00000000
|
||||
|
||||
; PD[1] -> Second 2MB (covers 0x200000 - 0x3FFFFF)
|
||||
mov dword [0x3008], 0x00200083
|
||||
mov dword [0x300C], 0x00000000
|
||||
|
||||
; Load page table
|
||||
mov eax, 0x1000
|
||||
mov cr3, eax
|
||||
|
||||
; Enable PAE
|
||||
mov eax, cr4
|
||||
or eax, 0x20
|
||||
mov cr4, eax
|
||||
|
||||
; Enable Long Mode
|
||||
mov ecx, 0xC0000080
|
||||
rdmsr
|
||||
or eax, 0x100
|
||||
wrmsr
|
||||
|
||||
; Enable Paging
|
||||
mov eax, cr0
|
||||
or eax, 0x80000000
|
||||
mov cr0, eax
|
||||
|
||||
; Jump to 64-bit mode
|
||||
jmp 0x18:long_mode
|
||||
|
||||
; Print in 32-bit protected mode
|
||||
print_pm:
|
||||
mov ah, 0x0f
|
||||
.loop:
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
stosw
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
pm_msg: db '[PM32] Setting up Long Mode...', 0
|
||||
|
||||
; ============================================================
|
||||
; 64-BIT LONG MODE
|
||||
; ============================================================
|
||||
[bits 64]
|
||||
long_mode:
|
||||
; Set up segments
|
||||
mov ax, 0x20
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
mov rsp, 0x90000
|
||||
|
||||
; Set up a minimal IDT to prevent triple faults
|
||||
; Point all interrupts to a dummy handler
|
||||
call setup_idt
|
||||
|
||||
; Print message on line 3
|
||||
mov rdi, 0xb8000 + (160 * 2)
|
||||
mov rsi, lm_msg
|
||||
call print_64
|
||||
|
||||
; Write a marker before jumping to kernel (for debugging)
|
||||
mov rdi, 0xb8000 + (160 * 3)
|
||||
mov rsi, before_kernel_msg
|
||||
call print_64
|
||||
|
||||
; JUMP TO KERNEL!
|
||||
call KERNEL_OFFSET
|
||||
|
||||
; If kernel returns, show error
|
||||
mov rdi, 0xb8000 + (160 * 4)
|
||||
mov rsi, kernel_return_msg
|
||||
call print_64
|
||||
jmp $
|
||||
|
||||
; Set up a minimal IDT
|
||||
setup_idt:
|
||||
; Fill IDT with different handlers for different exceptions
|
||||
mov rdi, 0x4000 ; IDT location
|
||||
|
||||
; Set up first 32 entries (CPU exceptions) with specific handlers
|
||||
mov rcx, 32
|
||||
mov rax, exception_handler
|
||||
|
||||
.fill_exceptions:
|
||||
mov word [rdi], ax ; Offset low
|
||||
mov word [rdi + 2], 0x18 ; Code segment
|
||||
mov byte [rdi + 4], 0 ; IST
|
||||
mov byte [rdi + 5], 0x8E ; Present, interrupt gate
|
||||
shr rax, 16
|
||||
mov word [rdi + 6], ax ; Offset middle
|
||||
shr rax, 16
|
||||
mov dword [rdi + 8], eax ; Offset high
|
||||
mov dword [rdi + 12], 0 ; Reserved
|
||||
|
||||
mov rax, exception_handler ; Restore for next iteration
|
||||
add rdi, 16
|
||||
loop .fill_exceptions
|
||||
|
||||
; Fill remaining entries (32-255) with dummy handler
|
||||
mov rcx, 224
|
||||
mov rax, dummy_handler
|
||||
|
||||
.fill_rest:
|
||||
mov word [rdi], ax
|
||||
mov word [rdi + 2], 0x18
|
||||
mov byte [rdi + 4], 0
|
||||
mov byte [rdi + 5], 0x8E
|
||||
shr rax, 16
|
||||
mov word [rdi + 6], ax
|
||||
shr rax, 16
|
||||
mov dword [rdi + 8], eax
|
||||
mov dword [rdi + 12], 0
|
||||
|
||||
mov rax, dummy_handler
|
||||
add rdi, 16
|
||||
loop .fill_rest
|
||||
|
||||
; Load IDT
|
||||
lidt [idt_descriptor]
|
||||
ret
|
||||
|
||||
; Exception handler - shows what went wrong
|
||||
exception_handler:
|
||||
; Save registers
|
||||
push rax
|
||||
push rbx
|
||||
|
||||
; Get exception number from stack
|
||||
; (not perfectly accurate but good enough for debugging)
|
||||
|
||||
; Write "EXCEPTION!" in red at line 5
|
||||
mov rbx, 0xb8000 + (160 * 5)
|
||||
mov rax, 0x0C450C580C430C45 ; "ECXE" in red
|
||||
mov [rbx], rax
|
||||
mov rax, 0x0C4F0C490C540C50 ; "PTIO" in red
|
||||
mov [rbx + 8], rax
|
||||
mov word [rbx + 16], 0x0C21 ; "!" in red
|
||||
|
||||
; Show the instruction pointer where it failed
|
||||
mov rax, [rsp + 16] ; Get RIP from stack (after our 2 pushes)
|
||||
mov rbx, 0xb8000 + (160 * 6)
|
||||
|
||||
; Write "RIP: "
|
||||
mov word [rbx], 0x0F52 ; 'R'
|
||||
mov word [rbx + 2], 0x0F49 ; 'I'
|
||||
mov word [rbx + 4], 0x0F50 ; 'P'
|
||||
mov word [rbx + 6], 0x0F3A ; ':'
|
||||
mov word [rbx + 8], 0x0F20 ; ' '
|
||||
|
||||
; Print RIP value as hex
|
||||
mov rcx, 16
|
||||
mov rbx, 0xb8000 + (160 * 6) + 10
|
||||
.print_rip:
|
||||
rol rax, 4
|
||||
mov dl, al
|
||||
and dl, 0x0F
|
||||
cmp dl, 9
|
||||
jle .digit
|
||||
add dl, 7
|
||||
.digit:
|
||||
add dl, '0'
|
||||
mov byte [rbx], dl
|
||||
mov byte [rbx + 1], 0x0F
|
||||
add rbx, 2
|
||||
dec rcx
|
||||
jnz .print_rip
|
||||
|
||||
; Restore and halt
|
||||
pop rbx
|
||||
pop rax
|
||||
|
||||
; Infinite loop instead of iretq
|
||||
cli
|
||||
hlt
|
||||
jmp $
|
||||
|
||||
; Dummy interrupt handler for IRQs (not CPU exceptions)
|
||||
dummy_handler:
|
||||
iretq
|
||||
|
||||
idt_descriptor:
|
||||
dw (256 * 16) - 1 ; Limit (256 entries * 16 bytes)
|
||||
dq 0x4000 ; Base address
|
||||
|
||||
; Print in 64-bit mode
|
||||
print_64:
|
||||
mov ah, 0x0f
|
||||
.loop:
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
stosw
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
lm_msg: db '[64-BIT] Long mode active...', 0
|
||||
before_kernel_msg: db '[64-BIT] Calling kernel...', 0
|
||||
kernel_return_msg: db '[KERNEL] Returned unexpectedly!', 0
|
||||
15
kernel.asm
Normal file
15
kernel.asm
Normal file
@ -0,0 +1,15 @@
|
||||
; File: kernel.asm
|
||||
; Assembly entry point for the kernel
|
||||
[bits 64]
|
||||
[extern kmain] ; kmain is defined in kernel.c
|
||||
|
||||
global _start ; Make _start visible to linker
|
||||
|
||||
_start:
|
||||
; We're already in 64-bit mode with stack set up
|
||||
; Just call the C kernel
|
||||
call kmain
|
||||
|
||||
; If kmain returns, hang
|
||||
jmp $
|
||||
|
||||
49
kernel.c
49
kernel.c
@ -1,15 +1,42 @@
|
||||
// file: kerlnel.c
|
||||
// File: kernel.c
|
||||
// SoraOS Kernel - Main entry point
|
||||
|
||||
// Simple helper to write a character at a specific position
|
||||
static inline void write_char(int row, int col, char c, unsigned char color) {
|
||||
volatile unsigned short *vga = (volatile unsigned short *)0xB8000;
|
||||
int pos = (row * 80) + col;
|
||||
vga[pos] = (color << 8) | c;
|
||||
}
|
||||
|
||||
// Simple helper to write a string
|
||||
static void write_string(int row, int col, const char *str, unsigned char color) {
|
||||
int i = 0;
|
||||
while (str[i] != '\0') {
|
||||
write_char(row, col + i, str[i], color);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the entire screen
|
||||
static void clear_screen(void) {
|
||||
volatile unsigned short *vga = (volatile unsigned short *)0xB8000;
|
||||
int i;
|
||||
for (i = 0; i < 80 * 25; i++) {
|
||||
vga[i] = (0x00 << 8) | ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void kmain(void) {
|
||||
// Kernel main function
|
||||
// Set VGA memory address
|
||||
volatile unsigned short *VGA_mem = (unsigned short *)0xB8000;
|
||||
// Clear screen
|
||||
clear_screen();
|
||||
|
||||
VGA_mem[0] = (0x07 << 8) | 'S';
|
||||
VGA_mem[1] = (0x07 << 8) | 'O';
|
||||
VGA_mem[2] = (0x07 << 8) | 'R';
|
||||
VGA_mem[3] = (0x07 << 8) | 'A';
|
||||
VGA_mem[4] = (0x07 << 8) | ' ';
|
||||
VGA_mem[5] = (0x07 << 8) | 'O';
|
||||
VGA_mem[6] = (0x07 << 8) | 'S';
|
||||
// Write messages
|
||||
write_string(10, 30, "SORA OS KERNEL", 0x0A); // Green
|
||||
write_string(12, 25, "64-bit kernel running!", 0x0F); // White
|
||||
write_string(14, 28, "System Initialized", 0x0E); // Yellow
|
||||
|
||||
// Infinite loop
|
||||
while (1) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
42
linker.ld
42
linker.ld
@ -1,36 +1,26 @@
|
||||
/* Linker script for SoraOS kernel */
|
||||
OUTPUT_FORMAT(binary)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Set the location counter to 0x100000 (1MB) */
|
||||
. = 0x100000;
|
||||
/* Kernel loaded at 0x10000 (64KB) by stage2 bootloader */
|
||||
. = 0x10000;
|
||||
|
||||
.multiboot :
|
||||
{
|
||||
KEEP(*(.multiboot))
|
||||
}
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
|
||||
/* Define the .text section at the current location */
|
||||
.text :
|
||||
{
|
||||
/* Put all .text sections from all input files here */
|
||||
*(.text)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
/* Define the .data section immediately after .text */
|
||||
.data :
|
||||
{
|
||||
/* Put all .data sections §from all input files here */
|
||||
*(.data)
|
||||
}
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
/* Define the .bss section immediately after .data */
|
||||
.bss :
|
||||
{
|
||||
/* Put all .bss sections from all input files here */
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
/* End of the linker script */
|
||||
.bss : {
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user