Also did a fair share of refactoring in the makefiles.
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)
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
-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
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
; 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
push di
.loop:
call read_cluster
+ jc .done
cmp eax, 0xffffff7
jl .loop
+.done:
pop di
pop es
pop eax
; 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
movzx cx, BPB_SecPerClus
call read_sectors
pop cx
+ cmp dl, 0
+ je .error
; increment the load offset (and segment if needed)
push eax
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
; 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
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
+++ /dev/null
-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
--- /dev/null
+%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
+
--- /dev/null
+; 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:
+
--- /dev/null
+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
--- /dev/null
+; 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
--- /dev/null
+; 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:
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
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
popa
ret
-%include "fat32.s"
+%include "fat32/fat32.s"
stage3_missing: db "LOADER.BIN is missing", 0
STAGE3_NAME: db "LOADER BIN"
SECTIONS
{
- . = 0x1000;
+ . = 0x100000;
.text : { *(.text) }
.data : { *(.data) }