diff options
| author | andromeda <andromeda@lenovo> | 2026-03-06 16:53:18 +0100 |
|---|---|---|
| committer | andromeda <andromeda@lenovo> | 2026-03-06 16:53:18 +0100 |
| commit | d35463e1959cb64828c3166527b06e4a79cbea5e (patch) | |
| tree | 65017b84447f63eaaac8326c6d18f5c5e34e6bac /bootler | |
| parent | fdf5bb9daf8574b7f993e2e38e400c35aedc3b96 (diff) | |
reorganise
Diffstat (limited to 'bootler')
| -rw-r--r-- | bootler/CHANGELOG.md | 7 | ||||
| -rw-r--r-- | bootler/README.md | 43 | ||||
| -rw-r--r-- | bootler/asm/boot.asm | 286 | ||||
| -rw-r--r-- | bootler/asm/kernel.asm | 23 | ||||
| -rw-r--r-- | bootler/package.nix | 49 |
5 files changed, 408 insertions, 0 deletions
diff --git a/bootler/CHANGELOG.md b/bootler/CHANGELOG.md new file mode 100644 index 0000000..eff4a62 --- /dev/null +++ b/bootler/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## [0.1.0] - 2026-03-06 + +### Added + +- initialised CHANGELOG.md diff --git a/bootler/README.md b/bootler/README.md new file mode 100644 index 0000000..bf50ce2 --- /dev/null +++ b/bootler/README.md @@ -0,0 +1,43 @@ +# bootler + +hobby bootloader, 1-stage, legacy mode :p + +run with `nix run git+https://git.mtgmonkey.net/andromeda/bootler#bootler` + +### memory map + +``` +. . +: : +| longmode kernel | ++------ 0x00010000 ------+ +| | +| | ++------ 0x00009000 ------+ +| IDT | ++------ 0x00008000 ------+ +| | +| | ++------ 0x00007E00 ------+ +| bootloader (boot.asm) | ++------ 0x00007C00 ------+ +| stack | ++------ 0x00005000 ------+ +| PT | ++------ 0x00004000 ------+ +| PDT | ++------ 0x00003000 ------+ +| PDPT | ++------ 0x00002000 ------+ +| PML4T | ++------ 0x00001000 ------+ +| | +| | ++------ 0x00000500 ------+ +| bios stuff | ++------ 0x00000000 ------+ +``` + +--- + +this project follows [Common Changelog](https://common-changelog.org) guidelines diff --git a/bootler/asm/boot.asm b/bootler/asm/boot.asm new file mode 100644 index 0000000..5291d58 --- /dev/null +++ b/bootler/asm/boot.asm @@ -0,0 +1,286 @@ +; bootler +; legacy bootloader in nasm-flavoured asm +; 2026 Andromeda +; GPL licensed + +LOAD_ADDR equ 0x7C00 + +KERNEL_START equ 2 ; first sector on disk to load kernel from; 1 indexed +KERNEL_SIZE equ 1 ; length of kernel in sectors +KERNEL_LOAD_ADDR_ES equ 0x1000 ; kernel to be loaded at es * 0x10 + 0x0000 + +PAGE_TABLE_LOAD_ADDR equ 0x1000 ; start of page table; 4 * pt size +PAGE_TABLE_SIZE equ 0x1000 ; size of page table in bytes + +; 64 bit IDT stuff +IDT_ADDR equ 0x8000 ; bottom of IDT +IDT_SIZE equ 0x1000 ; size of IDT + +IDT_SEGMENT equ 0x0008 ; gdt code segment to jump to for idt calls + +; magic numbers and things +A20_LINE_ENABLE equ 0x2401 ; magic number in ax to enable a20 line with 0x15 +BIOS_INT_DISK_OP equ 0x13 +BIOS_INT_MEMORY_OP equ 0x15 + +CR0_PE equ 1 << 0 ; Protected Mode Enabled +CR0_PG equ 1 << 31 ; Paging + +CR4_PAE equ 1 << 5 ; Physical Address Extension +CR4_PGE equ 1 << 7 ; Page Global Enabled + +MSR_IA32_EFER equ 0xC0000080 ; Extended Feature Enable model-specific Register + +[bits 16] +[org LOAD_ADDR] + +jmp 0x0000:.cs_reset ; in case it loads us at 0x7C00:0x0000 +.cs_reset: + +xor ax, ax +mov ss, ax +mov sp, LOAD_ADDR ; stack builds down, loader faces up +mov ds, ax +mov es, ax +mov fs, ax +mov gs, ax +cld + +; activate a20 +mov ax, A20_LINE_ENABLE +int BIOS_INT_MEMORY_OP +jc error +mov si, msg_a20_line +call print + +; prep to read disk +mov al, KERNEL_SIZE ; size in sectors, min. 1 +mov ch, 0 ; cylinder +mov cl, KERNEL_START ; start sector, 1-indexed +mov dh, 0 ; head +; dl set by bios +mov bx, KERNEL_LOAD_ADDR_ES ; load addr es:bx +mov es, bx +xor bx, bx + +; read disk +mov ah, 0x02 +int BIOS_INT_DISK_OP +jc error +mov si, msg_disk_read +call print + +; zero es +push ax +xor ax, ax +mov es, ax +pop ax + +mov edi, PAGE_TABLE_LOAD_ADDR +jmp lm_load + +; expects: +; es:edi -> 16kb for page table +; ss:esp -> stack +; heavily based on https://wiki.osdev.org/Entering_Long_Mode_Directly +lm_load: + ; clear page table + push di + mov ecx, PAGE_TABLE_SIZE + xor eax, eax + cld + rep stosd ; clear space for all 4 tables + pop di + + ; PML4TE0 + lea eax, [es:di + PAGE_TABLE_SIZE] + or eax, 11b + mov [es:di], eax + + ; PDPT0E0 + lea eax, [es:di + 2 * PAGE_TABLE_SIZE] + or eax, 11b + mov [es:di + PAGE_TABLE_SIZE], eax + + ; PDT0E0 + lea eax, [es:di + 3 * PAGE_TABLE_SIZE] + or eax, 11b + mov [es:di + 2 * PAGE_TABLE_SIZE], eax + + ; PT0 + push di + lea di, [di + 3 * PAGE_TABLE_SIZE] + mov eax, 0x11 + + .set_pt_entry + mov [es:di], eax + add eax, 0x1000 + add di, 8 + cmp eax, 0x200000 ; size of page + jb .set_pt_entry + + pop di + + ; disable interrupts from PIC by masking all interrupts + mov al, 0xFF ; mask + out 0xA1, al + out 0x21, al + + ; PAE, PGE + mov eax, CR4_PAE | CR4_PGE + mov cr4, eax + + ; cr3 -> PML4TE0 + mov edx, edi + mov cr3, edx + + ; highs LME bit in EFER + mov ecx, MSR_IA32_EFER + rdmsr + or eax, 0x00000100 + wrmsr + + ; activate long mode + mov ebx, cr0 + or ebx, CR0_PG | CR0_PE + mov cr0, ebx + + cli + lgdt [gdt_descriptor] + + jmp 0x08:lm_start + +error: + mov si, .msg + call print + .msg db "err ", 0x00 + ret + +done: + hlt + jmp done + +print: + pushad + .loop + lodsb + test al, al + je .done + mov ah, 0x0E + int 0x10 + jmp .loop + .done + popad + ret + +; TODO make readable (yoinked from https://wiki.osdev.org/Entering_Long_Mode_Directly) +gdt_data: + dq 0x0000000000000000 + dq 0x00209A0000000000 + dq 0x0000920000000000 + +gdt_descriptor: + dw $ - gdt_data - 1 ; size + dd gdt_data ; start + +[bits 64] + +; long mode entry +lm_start: + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; set up serial device + ; TODO poll and whatnot (in rust??) + mov dx, 0x3F8 + + ; qemu -no-graphic overlaps serial and bios output without this + mov al, 0x0D + out dx, al + mov al, 0x0A + out dx, al + + ; initialize IDT at IDT_ADDR + call idt_init + + int3 ; silly test + + jmp KERNEL_LOAD_ADDR_ES * 0x10 + +idt_init: + ; clear space for idt + mov rdi, IDT_ADDR + push di + mov rcx, IDT_SIZE / 4 + xor rax, rax + cld + rep stosd ; clear double word for each rcx + pop di + + push rdx ; dx contains the output device + ; nothing between here and the `pop rdx` will be able to print + + ; breakpoint + mov rdi, 0x8E ; present, no dpl, interrupt + mov rsi, 0x03 ; breakpoint interrupt + lea rdx, [int_hdl_dummy] + call mk_idt_entry + + pop rdx ; get back dx output device + + lidt [idt_descriptor] + + ret + +int_hdl_dummy: + mov rsi, .msg + call lm_print + iretq + .msg db "INTdummy", 0x0D, 0x0A, 0x00 + +; rdi = flags +; rsi = entry number +; rdx -> handler +mk_idt_entry: + mov rax, rdx + mov rcx, rsi + + shl rcx, 4 ; offset = entry number * 16 + add rcx, IDT_ADDR + + mov word [rcx + 0], ax ; offset + mov word [rcx + 2], IDT_SEGMENT ; code segment + mov word [rcx + 5], di + mov byte [rcx + 4], 0 ; IST (0 = none) + shr rax, 16 + mov word [rcx + 6], ax ; offset + shr rax, 16 + mov dword [rcx + 8], eax ; offset + mov dword [rcx + 12], 0 ; reserved + ret + +lm_print: + .loop + mov al, [rsi] + test al, al + jz .done + out dx, al + inc si + jmp .loop + .done: + ret + +idt_descriptor: + dw IDT_SIZE - 1 ; size + dq IDT_ADDR ; start + +msg_a20_line db "a20 line", 0x0D, 0x0A, 0x00 +msg_disk_read db "disk", 0x0D, 0x0A, 0x00 + +times 510-($-$$) db 0 ; 2 bytes less now +db 0x55 +db 0xAA diff --git a/bootler/asm/kernel.asm b/bootler/asm/kernel.asm new file mode 100644 index 0000000..b4cbb30 --- /dev/null +++ b/bootler/asm/kernel.asm @@ -0,0 +1,23 @@ +[bits 64] + +mov al, 'K' +out dx, al +mov al, 'e' +out dx, al +mov al, 'r' +out dx, al +mov al, 'n' +out dx, al +mov al, 'e' +out dx, al +mov al, 'l' +out dx, al +mov al, 0x0D +out dx, al +mov al, 0x0A +out dx, al +mov al, 0x00 +out dx, al +.done + hlt + jmp .done diff --git a/bootler/package.nix b/bootler/package.nix new file mode 100644 index 0000000..4e18816 --- /dev/null +++ b/bootler/package.nix @@ -0,0 +1,49 @@ +{ + stdenv, + nasm, + qemu, + ... +}: let + bootImg = "boot"; +in + stdenv.mkDerivation { + pname = "bootler"; + version = "0.1.0"; + src = ./.; + buildPhase = '' + ${nasm}/bin/nasm asm/boot.asm -o boot.bin + ${nasm}/bin/nasm asm/kernel.asm -o dummy.bin + + dd if=/dev/zero of=${bootImg} bs=512 count=2 + dd if=boot.bin of=${bootImg} conv=notrunc + dd if=dummy.bin of=${bootImg} bs=512 seek=1 conv=notrunc + ''; + installPhase = '' + mkdir -p $out/bin + cp boot.bin $out/bin + cp dummy.bin $out/bin + cp ${bootImg} $out/bin + + # create emulation binary + cat<<EOF>$out/bin/bootler + #!/usr/bin/env bash + + # create temp dir + mkdir -p ./.bootler + cp $(echo $out)/bin/${bootImg} ./.bootler/${bootImg} + chmod a+w ./.bootler/${bootImg} + + # run image + ${qemu}/bin/qemu-system-x86_64 \ + -nographic \ + -drive file=./.bootler/${bootImg},format=raw,index=0,media=disk + + # clean up + rm ./.bootler -r + + EOF + + chmod +x $out/bin/${bootImg} + chmod +x $out/bin/bootler + ''; + } |
