]> git.dujemihanovic.xyz Git - nameless-os.git/commitdiff
FAT32 bootloader boots the kernel
authorDuje Mihanović <duje.mihanovic@skole.hr>
Fri, 6 May 2022 16:46:48 +0000 (18:46 +0200)
committerDuje Mihanović <duje.mihanovic@skole.hr>
Fri, 6 May 2022 17:08:59 +0000 (19:08 +0200)
Also did a fair share of refactoring in the makefiles.

13 files changed:
Makefile
boot/x86/Makefile
boot/x86/disk.dump
boot/x86/fat32/fat32-structs.s [moved from boot/x86/fat32-structs.s with 100% similarity]
boot/x86/fat32/fat32.s [moved from boot/x86/fat32.s with 91% similarity]
boot/x86/loader.s [deleted file]
boot/x86/stage3/a20.s [new file with mode: 0644]
boot/x86/stage3/gdt.s [new file with mode: 0644]
boot/x86/stage3/loader.s [new file with mode: 0644]
boot/x86/stage3/print.s [new file with mode: 0644]
boot/x86/stage3/unreal.s [new file with mode: 0644]
boot/x86/vbr-fat32.s
kernel/linker.ld

index d3f2ee4be49cbe23808b4d7813da6965cb51ed2e..738fb0c89192daa87e3ca2a3c2e65e1b6a43fcd7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,6 @@
 export AS = yasm
-export LD = i686-elf-ld
 export CC = i686-elf-gcc
-QEMU = qemu-system-i386
+QEMU = qemu-system-i386 -monitor stdio
 
 GIT_REV = $(shell git rev-parse --short HEAD)
 
@@ -9,38 +8,45 @@ CFLAGS = -std=gnu89 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_RE
 
 KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/kernel.o
 
-default: kernel/kernel.elf
+default: kernel/kernel.elf bootloader
 
 all: default boot/x86/disk.img
 
-run: boot/x86/disk.img
+bootloader: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN
+
+run: all
        $(QEMU) boot/x86/disk.img
 
-boot/x86/disk.img: boot/x86/mbr.s boot/x86/vbr-fat32.s boot/x86/loader.s boot/x86/disk.dump boot/x86/fat32.s boot/x86/fat32-structs.s
-       cd boot/x86 && $(MAKE) all
+boot/x86/mbr: boot/x86/mbr.s
+       $(MAKE) -C boot/x86
+boot/x86/vbr-fat32: boot/x86/vbr-fat32.s boot/x86/fat32/*.s 
+       $(MAKE) -C boot/x86
+boot/x86/stage3/LOADER.BIN: boot/x86/stage3/*.s boot/x86/fat32/*.s
+       $(MAKE) -C boot/x86
+
+boot/x86/disk.img: boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN boot/x86/disk.dump 
+       truncate -s1G boot/x86/disk.img
+       sfdisk boot/x86/disk.img < boot/x86/disk.dump
+       mkfs.fat -F 32 --offset 2048 boot/x86/disk.img
+       dd if=boot/x86/mbr of=boot/x86/disk.img bs=440 count=1 conv=notrunc
+       dd if=boot/x86/vbr-fat32 of=boot/x86/disk.img bs=1 skip=90 seek=1048666 conv=notrunc
+       mcopy -i boot/x86/disk.img@@1M boot/x86/stage3/LOADER.BIN ::.
+       mcopy -i boot/x86/disk.img@@1M kernel/kernel.bin ::./KERNEL.BIN
 
 kernel/kernel.bin: ${KERNEL_OBJ}
-       $(LD) -o $@ -T kernel/linker.ld ${KERNEL_OBJ}
+       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ}
 
 kernel/entry.o: kernel/entry.s
        $(AS) -f elf kernel/entry.s -o $@
 
-kernel/arch/x86/tty/tty.o: kernel/arch/x86/tty/tty.c
-
-kernel/drivers/irq/i8259a.o: kernel/drivers/irq/i8259a.c
-
-kernel/arch/x86/irq/idt.o: kernel/arch/x86/irq/idt.c
-
 kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c
        $(CC) $(CFLAGS) -mgeneral-regs-only -c kernel/arch/x86/irq/sample_handler.c -o $@
 
-kernel/kernel.o: kernel/kernel.c
-
 kernel/kernel.elf: kernel/kernel.bin
-       $(LD) -o $@ -T kernel/linker.ld ${KERNEL_OBJ} --oformat=elf32-i386
+       $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -Wl,--oformat=elf32-i386
 
 clean:
        -rm kernel/kernel.bin kernel/kernel.elf ${KERNEL_OBJ}
        cd boot/x86 && $(MAKE) clean
 
-.PHONY: default all clean run
+.PHONY: default all clean run bootloader
index 0349d2ae8b9d5d1cacb1786f5543b4fcd9544ec3..015780113b39cf299577587119689da30d821d1c 100644 (file)
@@ -1,25 +1,15 @@
-default: mbr vbr-fat32 LOADER.BIN
-
-all: mbr vbr-fat32 LOADER.BIN disk.img
+default: mbr vbr-fat32 stage3/LOADER.BIN
 
 mbr: mbr.s
        $(AS) $(ASFLAGS) -w-zeroing -o $@ $<
 
-vbr-fat32: vbr-fat32.s fat32.s fat32-structs.s
+vbr-fat32: vbr-fat32.s fat32/*.s
        $(AS) $(ASFLAGS) -o $@ $<
 
-LOADER.BIN: loader.s
+stage3/LOADER.BIN: stage3/loader.s stage3/*.s
        $(AS) $(ASFLAGS) -o $@ $<
 
-disk.img: mbr vbr-fat32 LOADER.BIN disk.dump
-       truncate -s100M disk.img
-       sfdisk disk.img < disk.dump
-       mkfs.fat -F 32 --offset 2048 disk.img
-       dd if=mbr of=disk.img bs=440 count=1 conv=notrunc
-       dd if=vbr-fat32 of=disk.img bs=1 skip=90 seek=1048666 conv=notrunc
-       mcopy -i disk.img@@1M LOADER.BIN ::.
-
 clean:
-       -rm mbr vbr-fat32 LOADER.BIN disk.img
+       -rm mbr vbr-fat32 stage3/LOADER.BIN disk.img
 
-.PHONY : default all clean
+.PHONY : default clean
index 2210fd2da2a6fe24685cdc872876e1f5d0c05bbb..5a0329bef74940990720cf0486191443aa1cdf8e 100644 (file)
@@ -1,7 +1,7 @@
 label: dos
-label-id: 0xc0c03566
+label-id: 0x8bb0766f
 device: disk.img
 unit: sectors
 sector-size: 512
 
-disk.img1 : start=        2048, size=      202752, type=b, bootable
+disk.img1 : start=        2048, size=     2095104, type=b, bootable
similarity index 91%
rename from boot/x86/fat32.s
rename to boot/x86/fat32/fat32.s
index 75e7904ae5b94a2116529542e0342fb2f1791e0b..df5dd89260a3fff08e70e6729ee54f170def9559 100644 (file)
@@ -7,6 +7,7 @@
 
 ; eax - start cluster number
 ; es:di - where to load the cluster chain
+; RETURN: CF set = error
 ; NOTE: Cluster chain might be the root directory or a file.
 read_cluster_chain:
        push eax
@@ -14,8 +15,10 @@ read_cluster_chain:
        push di
        .loop:
                call read_cluster
+               jc .done
                cmp eax, 0xffffff7
                jl .loop
+.done:
        pop di
        pop es
        pop eax
@@ -24,6 +27,7 @@ read_cluster_chain:
 ; eax - cluster number
 ; es:di - where to load the cluster (incremented automatically)
 ; RETURN: eax - next cluster number
+;        CF set = error
 read_cluster:
        push ebx
        push ecx
@@ -44,6 +48,8 @@ read_cluster:
        movzx cx, BPB_SecPerClus
        call read_sectors
        pop cx
+       cmp dl, 0
+       je .error
 
        ; increment the load offset (and segment if needed)
        push eax
@@ -78,16 +84,20 @@ read_cluster:
        xor ax, ax
        mov es, ax
        pop ebx ; pop LBA into EBX
-       mov di, 0x1000
+       mov di, 0x6000
        mov cx, 1
        call read_sectors
-       
+       cmp dl, 0
        ; find the cluster we're looking for
        pop es ; restore STAGE3_SEGMENT
        pop ebx ; pop FAT offset back into EBX for cmp
        mov eax, [di+bx]
        pop di
+       jne .end
        ; cleanup and return
+.error:
+       stc
+.end:
        pop edx
        pop ecx
        pop ebx
@@ -96,8 +106,11 @@ read_cluster:
 ; es:di - where to load the sector(s)
 ; ebx - start LBA address
 ; cx - how many sectors to read
+; RETURN: DL=0 means error
 read_sectors:
-       pusha
+       clc
+       push ax
+       push si
        mov ah, 0x42
        push dword 0
        push dword ebx
@@ -108,15 +121,14 @@ read_sectors:
        mov si, sp
        mov dl, [BOOT_DRIVE]
        int 0x13
-       jc .error
+       mov dl, 1
+       jnc .done
+       mov dl, 0
+.done:
        add sp, 16
-       popa
+       pop si
+       pop ax
        ret
-       .error:
-               mov si, read_error
-               call print
-               hlt
-               jmp $-1
 
 ; ds:si - pointer to partition table entry
 ; RETURN: ecx - first data sector
diff --git a/boot/x86/loader.s b/boot/x86/loader.s
deleted file mode 100644 (file)
index e18b87e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-bits 16
-cpu 686
-org 0x0
-
-%macro print 1
-
-       mov si, %1
-       call print_str
-       
-%endmacro
-
-_start:
-       print string
-       hlt
-       jmp $-1
-       
-print_str:
-       pusha
-       mov ah, 0xe
-       xor bh, bh
-.loop:
-       lodsb
-       cmp al, 0
-       je .done
-       int 0x10
-       jmp .loop
-.done:
-       popa
-       ret
-
-string: db "Hello from LOADER.BIN!", 0xd, 0xa, 0
diff --git a/boot/x86/stage3/a20.s b/boot/x86/stage3/a20.s
new file mode 100644 (file)
index 0000000..8748dde
--- /dev/null
@@ -0,0 +1,74 @@
+%macro in_wait 0
+       push ax
+%%loop:
+       in al, 0x64
+       test al, 2
+       jnz %%loop
+       pop ax
+%endmacro
+
+%macro out_wait 0
+       push ax
+%%loop:
+       in al, 0x64
+       test al, 1
+       jz %%loop
+       pop ax
+%endmacro
+
+; CF set = A20 enabled
+check_a20:
+       push esi
+       push edi
+       push ax
+       mov esi, 0x500
+       mov edi, 0x100500
+
+       mov al, [esi]
+       mov ah, [edi]
+
+       mov [esi], byte 0
+       mov [edi], byte 0xff
+       cmp [esi], byte 0xff
+       
+       mov [esi], al
+       mov [edi], ah
+
+       pop ax
+       pop edi
+       pop esi
+       ret
+
+enable_a20:
+       push ax
+       mov ax, 0x2401
+       int 0x15
+       jc .i8042_method
+       call check_a20
+       jnc .i8042_method
+       pop ax
+       ret
+       .i8042_method:
+               in_wait
+               mov al, 0xad
+               out 0x64, al
+               in_wait
+               mov al, 0xd0
+               out 0x64, al
+               out_wait
+               in al, 0x60
+               or al, 2
+               push ax
+               in_wait
+               mov al, 0xd1
+               out 0x64, al
+               in_wait
+               pop ax
+               out 0x60, al
+               in_wait
+               mov al, 0xae
+               out 0x60, al
+               call check_a20
+               pop ax
+               ret
+
diff --git a/boot/x86/stage3/gdt.s b/boot/x86/stage3/gdt.s
new file mode 100644 (file)
index 0000000..d81bb64
--- /dev/null
@@ -0,0 +1,21 @@
+; Initial GDT for kernel, which wants a flat code and data descriptor
+gdt:
+       dw .end-.start-1
+       dd .start
+.start: dq 0 ; null descriptor
+.code:
+       dw 0xffff
+       dw 0
+       db 0
+       db 10011010b
+       db 11001111b
+       db 0
+.data:
+       dw 0xffff
+       dw 0
+       db 0
+       db 10010010b
+       db 11001111b
+       db 0
+.end:
+
diff --git a/boot/x86/stage3/loader.s b/boot/x86/stage3/loader.s
new file mode 100644 (file)
index 0000000..bd703ba
--- /dev/null
@@ -0,0 +1,184 @@
+bits 16
+cpu 686
+org 0x8000
+
+%include "../fat32/fat32-structs.s"
+
+%macro print 1
+       push si
+       mov si, %1
+       call print_str
+       pop si
+%endmacro
+
+_start:
+       mov [BOOT_DRIVE], dl
+       call enable_unreal
+       print string
+       call check_a20
+       jc .a20_enabled
+       call enable_a20
+       jnc .a20_enable_fail
+.a20_enabled:
+       print a20_enabled
+       call get_1st_data_sec
+       mov ax, 0x2000
+       mov es, ax
+       mov eax, BPB_RootClus
+       xor di, di
+       call read_cluster_chain
+       jc critical_error
+
+       push cx
+       push si
+.find_kernel:
+       mov si, kernel_name
+       cmp byte [es:di], 0 ; end of root directory
+       je .kernel_missing
+       cmp byte [es:di], 0xe5 ; unused entry
+       je .increment
+       mov cx, 11
+       push si
+       push di
+       repe cmpsb
+       pop di
+       pop si
+       je .kernel_found
+.increment:
+       add di, 32
+       jno .find_kernel
+       mov bx, es
+       add bx, 0x1000
+       mov es, bx
+       jmp .find_kernel
+.kernel_found:
+       pop si
+       pop cx
+       print kernel_found
+       mov ax, [es:di+dir_entry.firstclushi]
+       shl eax, 16
+       mov ax, [es:di+(dir_entry.firstcluslo)]
+       call print_dword
+       mov edi, 0x100000
+       call read_clus_chain_unreal ; load kernel
+       
+       cli
+       lgdt [gdt]
+       mov eax, cr0
+       or al, 1
+       mov cr0, eax
+       jmp 0x8:in_protected
+.kernel_missing:
+       print missing_kernel
+       jmp .halt
+
+.a20_enable_fail:
+       print a20_fail
+.halt:
+       hlt
+       jmp $-1
+
+; eax - start cluster
+; edi - address to load to
+read_clus_chain_unreal:
+       push eax
+       push edi
+.loop:
+       push di
+       push es
+       push ax
+       xor di, di
+       mov ax, 0x100
+       mov es, ax
+       pop ax
+       call read_cluster
+       pop es
+       pop di
+       jc .done
+       push esi
+       push eax
+       push ebx
+       push ecx
+       mov esi, 0x1000
+       xor ebx, ebx
+       movzx eax, word BPB_BytsPerSec
+       movzx bx, byte BPB_SecPerClus
+       mul ebx
+       mov ecx, eax
+       call memcpy
+       add edi, ecx
+       pop ecx
+       pop ebx
+       pop eax
+       pop esi
+       cmp eax, 0xffffff7
+       jl .loop
+.done:
+       pop edi
+       pop eax
+       ret
+
+; esi - copy from
+; edi - copy to
+; ecx - bytes to copy
+memcpy:
+       push esi
+       push edi
+       push ecx
+       push dx
+
+.loop:
+       mov dl, [esi]
+       mov [edi], dl
+       dec ecx
+       cmp ecx, 0
+       je .done
+       inc esi
+       inc edi
+       jmp .loop
+.done:
+       pop dx
+       pop ecx
+       pop edi
+       pop esi
+       ret
+
+
+%include "unreal.s"
+%include "a20.s"
+%include "../fat32/fat32.s"
+%include "gdt.s"
+%include "print.s"
+
+in_protected:
+bits 32
+       mov ax, 0x10
+       mov ds, ax
+       mov es, ax
+       mov ss, ax
+       mov fs, ax
+       mov gs, ax
+       call 0x100000
+       hlt
+       jmp $-1
+
+kernel_name: db "KERNEL  BIN"
+string: db "Hello from LOADER.BIN!", 0xd, 0xa, 0
+a20_enabled: db "A20 is enabled", 0xd, 0xa, 0
+a20_fail: db "Failed to enable A20, giving up!", 0xd, 0xa, 0
+crit_err: db "A critical error occurred, dumping registers now: ", 0xd, 0xa, 0
+kernel_found: db "Found kernel at cluster ", 0
+missing_kernel: db "Could not find KERNEL.BIN", 0xd, 0xa, 0
+eax_s: db "EAX: ", 0
+ebx_s: db "EBX: ", 0
+ecx_s: db "ECX: ", 0
+edx_s: db "EDX: ", 0
+esi_s: db "ESI: ", 0
+edi_s: db "EDI: ", 0
+cs_s: db "CS: ", 0
+ds_s: db "DS: ", 0
+es_s: db "ES: ", 0
+ss_s: db "SS: ", 0
+space: db " ", 0
+hex_delm: db "0x", 0
+newline: db 0xd, 0xa, 0
diff --git a/boot/x86/stage3/print.s b/boot/x86/stage3/print.s
new file mode 100644 (file)
index 0000000..73b34d5
--- /dev/null
@@ -0,0 +1,126 @@
+; Routines for printing
+
+print_str:
+       push ax
+       push bx
+       push si
+       mov ah, 0xe
+       xor bh, bh
+       mov bl, 0xf
+.loop:
+       lodsb
+       cmp al, 0
+       je .done
+       int 0x10
+       jmp .loop
+.done:
+       pop si
+       pop bx
+       pop ax
+       ret
+
+; AL - nibble to print
+; CF - clear prints low nibble, set prints high nibble
+print_nibble:
+       print hex_delm
+.no_delm:
+       pushf
+       push ax
+       push bx
+       pushf
+       xor bh, bh
+       mov bl, 0xf
+       mov ah, 0xe
+       popf
+       jnc .no_shift
+       shr al, 4
+.no_shift:
+       and al, 0xf
+       cmp al, 0xa
+       jge .letter
+       .numeric:
+               add al, '0'
+               int 0x10
+               jmp .done
+       .letter:
+               add al, 'a'-10
+               int 0x10
+.done:
+       pop bx
+       pop ax
+       popf
+       ret
+
+; AL - byte to print
+print_byte:
+       print hex_delm
+.no_delm:
+       pushf
+       stc
+       call print_nibble.no_delm
+       clc
+       call print_nibble.no_delm
+       popf
+       ret
+       
+print_word:
+       print hex_delm
+.no_delm:
+       ror ax, 8
+       call print_byte.no_delm
+       ror ax, 8
+       call print_byte.no_delm
+       ret
+
+print_dword:
+       print hex_delm
+.no_delm:
+       ror eax, 16
+       call print_word.no_delm
+       ror eax, 16
+       call print_word.no_delm
+       ret
+
+; Do a dump of all general purpose regs
+critical_error:
+       print crit_err
+       print eax_s
+       call print_dword
+       print space
+       print ebx_s
+       mov eax, ebx
+       call print_dword
+       print space
+       print ecx_s
+       mov eax, ecx
+       call print_dword
+       print space
+       print edx_s
+       mov eax, edx
+       call print_dword
+       print newline
+       print esi_s
+       mov eax, esi
+       call print_dword
+       print space
+       print edi_s
+       mov eax, edi
+       call print_dword
+       print newline
+       print cs_s
+       mov ax, cs
+       call print_word
+       print space
+       print ds_s
+       mov ax, ds
+       call print_word
+       print space
+       print es_s
+       mov ax, es
+       call print_word
+       print space
+       print ss_s
+       mov ax, ss
+       call print_word
+       hlt
+       jmp $-1
diff --git a/boot/x86/stage3/unreal.s b/boot/x86/stage3/unreal.s
new file mode 100644 (file)
index 0000000..cdd361b
--- /dev/null
@@ -0,0 +1,40 @@
+; Routine for enabling unreal mode, which allows using 32-bit offsets in real mode
+
+enable_unreal:
+       cli
+       push eax
+       push bx
+       push ds
+
+       lgdt [gdt_info]
+       mov eax, cr0
+       or al, 1
+       mov cr0, eax
+
+       jmp $+2
+
+       mov bx, 0x8
+       mov ds, bx
+
+       and al, 0xfe
+       mov cr0, eax
+
+       pop ds
+       pop bx
+       pop eax
+       sti
+       ret
+
+; GDT with 1 flat data segment descriptor
+gdt_info:
+       dw gdt_end-gdt_start-1
+       dd gdt_start
+gdt_start: dq 0 ; null entry
+gdt_flat:
+       dw 0xffff
+       dw 0
+       db 0
+       db 10010010b
+       db 11001111b
+       db 0
+gdt_end:
index b4c71489e5bf9d421ded07e145550c13e37cb716..7ec3739a8aa80ae7ddd650015a27ae99052c3616 100644 (file)
@@ -9,7 +9,12 @@ STAGE3_ADDRESS equ 0x8000
 STAGE3_SEGMENT equ STAGE3_ADDRESS >> 4
 STAGE3_OFFSET equ STAGE3_ADDRESS & 0xf
 
-%include "fat32-structs.s"
+%include "fat32/fat32-structs.s"
+
+%macro print 1
+       mov si, %1
+       call print_str
+%endmacro
 
 _start:
        jmp short real_start
@@ -66,19 +71,21 @@ real_start:
        mov es, bx
        mov di, STAGE3_OFFSET
        call read_cluster_chain ; read stage 3
-       mov ds, bx
-       call STAGE3_SEGMENT:STAGE3_OFFSET ; call stage 3
+       mov dl, [BOOT_DRIVE]
+       call STAGE3_ADDRESS ; call stage 3
        jmp .halt ; halt in case we return, which should never happen
 
 .stage3_missing:
-       mov si, stage3_missing
-       call print
+       print stage3_missing
+       jmp .halt
+.error:
+       print read_error
 .halt:
        hlt
        jmp short $-1
 
 ; ds:si - string
-print:
+print_str:
        pusha
        mov ah, 0xe
        xor bh, bh
@@ -96,7 +103,7 @@ print:
        popa
        ret
 
-%include "fat32.s"
+%include "fat32/fat32.s"
 
 stage3_missing: db "LOADER.BIN is missing", 0
 STAGE3_NAME: db "LOADER  BIN"
index d68e9f2c96af541f9042df444d10f7803ff1e7fb..90292e6c2ce2d99b9d3bfb4af9cbb0de4c8ebd5c 100644 (file)
@@ -3,7 +3,7 @@ OUTPUT_FORMAT(binary)
 
 SECTIONS
 {
-       . = 0x1000;
+       . = 0x100000;
 
        .text : { *(.text) }
        .data : { *(.data) }