toolchain/
-boot
boot.img
**/*.o
**/*.bin
all: boot.img
-boot.img: boot kernel/kernel.bin
- cat boot kernel/kernel.bin > $@
+boot.img: boot/x86/boot kernel/kernel.bin
+ cat boot/x86/boot kernel/kernel.bin > $@
truncate -s1440K $@
-boot: boot.s
- $(AS) -f bin boot.s -o $@
+boot/x86/boot: boot/x86/boot.s boot/x86/a20.s boot/x86/protected.s
+ $(AS) -f bin boot/x86/boot.s -o $@
kernel/kernel.bin: ${KERNEL_OBJ}
$(LD) -o $@ -T kernel/linker.ld ${KERNEL_OBJ} --oformat=binary
$(CC) -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/kernel.c
clean:
- rm boot kernel/kernel.bin ${KERNEL_OBJ} boot.img
+ rm boot/x86/boot kernel/kernel.bin ${KERNEL_OBJ} boot.img
.PHONY: all clean
--- /dev/null
+; Some routines for managing the A20 line
+
+; Check if A20 is enabled
+; Based on <https://wiki.osdev.org/A20_Line#Testing_the_A20_line>, slightly modified
+check_a20
+ pushf ; save EFLAGS
+ ; save segment and index registers
+ push ds
+ push es
+ push di
+ push si
+
+ cli ; disable interrupts
+ xor ax, ax ; ax = 0
+ mov es, ax ; es = 0
+
+ not ax ; ax = FFFFh
+ mov ds, ax ; ds = FFFFh
+
+ mov di, 500h
+ mov si, 510h
+
+ mov al, [es:di] ; preserve contents of 0:500h
+ mov ah, [ds:si] ; preserve contents of FFFFh:510h
+ push ax ; push all that to stack
+
+ mov byte [es:di], 0 ; set 0:500h to 0
+ mov byte [ds:si], 0FFh ; set FFFFh:510h to FFh
+
+ cmp byte [es:di], 0FFh ; compare 0:500h with FFh
+ ; if A20 is enabled, this will not be equal, but if A20 is disabled it will be
+
+ pop ax ; pop old contents of the 2 addresses
+ mov [ds:si], ah ; restore contents of FFFFh:510h
+ mov [es:di], al ; restore contents of 0:500h
+
+ xor ax, ax ; clear ax
+ je .exit ; if A20 is disabled, return 0
+ mov ax, 1 ; otherwise return 1
+
+.exit
+ pop si
+ pop di
+ pop es
+ pop ds
+ popf
+ sti
+ ret
+
--- /dev/null
+ bits 16 ; boot sectors run in real mode
+ org 7C00h ; BIOS loads us at 0x7c00
+
+KERNEL_OFFSET equ 1000h ; where we will load our kernel
+
+ mov [BOOT_DRIVE], dl ; BIOS puts the number of our boot drive in dl, so we will want to remember this
+
+ mov bp, 9000h ; initialize stack at a spot sufficiently distanced from this code
+ mov sp, bp
+
+ mov di, 0
+
+ xor ax, ax ; clear accumulator
+ int 13h ; BIOS disk services, with a cleared accumulator this will reset the disk controller
+ jc reset_error ; halt if controller reset fails
+
+ mov ah, 2 ; instruct BIOS's interrupt 13h to read sectors
+ mov al, 10 ; load 10 sectors, might be excessive (for now) but it's still good to do so
+ xor ch, ch ; read from 1st cylinder
+ mov cl, 2 ; start reading from 2nd sector, right after the boot sector
+ xor dh, dh ; read from 1st head
+ xor bx, bx ; clear B register
+ mov es, bx ; clear extended segment
+ mov bx, KERNEL_OFFSET ; put the sectors in our desired offset
+ ; dl holds the number of the drive to read from, but BIOS already filled this in
+ int 13h ; do the read
+ jc read_error ; halt if read fails
+ cmp al, 10 ; make sure we actually read 10 sectors
+ jl read_error ; halt if we didn't
+ jmp switch_to_pm ; if all is good, begin the switch to 32-bit protected mode
+
+reset_error
+ mov bx, 0B800h
+ mov es, bx
+ mov byte [es:di], '1'
+ jmp halt
+
+read_error
+ mov bx, 0B800h
+ mov es, bx
+ mov byte [es:di], '2'
+ jmp halt
+
+halt
+ jmp $
+
+%include "a20.s"
+%include "protected.s"
+
+BOOT_DRIVE db 0 ; reserve a spot in RAM for our boot drive variable
+
+ times 510-($-$$) db 0
+ dw 0AA55h ; MBR signature
- bits 16 ; boot sectors run in real mode
- org 7C00h ; BIOS loads us at 0x7c00
+; Everything between real mode and the C kernel
-KERNEL_OFFSET equ 1000h ; where we will load our kernel
-
- mov [BOOT_DRIVE], dl ; BIOS puts the number of our boot drive in dl, so we will want to remember this
-
- mov bp, 9000h ; initialize stack at a spot sufficiently distanced from this code
- mov sp, bp
-
- mov di, 0
-
- xor ax, ax ; clear accumulator
- int 13h ; BIOS disk services, with a cleared accumulator this will reset the disk controller
- jc reset_error ; halt if controller reset fails
-
- mov ah, 2 ; instruct BIOS's interrupt 13h to read sectors
- mov al, 10 ; load 10 sectors, might be excessive (for now) but it's still good to do so
- xor ch, ch ; read from 1st cylinder
- mov cl, 2 ; start reading from 2nd sector, right after the boot sector
- xor dh, dh ; read from 1st head
- xor bx, bx ; clear B register
- mov es, bx ; clear extended segment
- mov bx, KERNEL_OFFSET ; put the sectors in our desired offset
- ; dl holds the number of the drive to read from, but BIOS already filled this in
- int 13h ; do the read
- jc read_error ; halt if read fails
- cmp al, 10 ; make sure we actually read 10 sectors
- jl read_error ; halt if we didn't
- jmp switch_to_pm ; if all is good, begin the switch to 32-bit protected mode
-
-reset_error
- mov bx, 0B800h
- mov es, bx
- mov byte [es:di], '1'
- jmp halt
-
-read_error
- mov bx, 0B800h
- mov es, bx
- mov byte [es:di], '2'
- jmp halt
-
- call switch_to_pm
-
-halt
- jmp $
+ bits 16
switch_to_pm
mov bx, 0B800h
CODE_SEG equ code_seg - gdt_start
DATA_SEG equ data_seg - gdt_start
-BOOT_DRIVE db 0 ; reserve a spot in RAM for our boot drive variable
-
- times 510-($-$$) db 0
- dw 0AA55h ; MBR signature