feat(bootload): load next stage and preload kernel from FAT

This commit is contained in:
d0p1 🏳️‍⚧️ 2024-02-08 14:58:18 +01:00
parent d94d9fb5bb
commit 7f88017ed7
10 changed files with 590 additions and 363 deletions

View file

@ -1,39 +1,43 @@
TOPDIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
SYSROOTDIR := $(TOPDIR)/sysroot
TOOLSDIR := $(TOPDIR)/tools
RM = echo
SUBDIRS := boot kernel lib bin
TARGET = stupid.tar.gz floppy_boot.img
ifneq ($(OS),Windows_NT)
TARGET += stupid.iso
endif
.PHONY: all
all: $(TARGET)
GOAL:=install
clean: GOAL:=clean
.PHONY: $(SUBDIRS)
$(SUBDIRS):
@echo "📁 $@"
DESTDIR=$(SYSROOTDIR) $(MAKE) -C $@ $(GOAL)
.PHONY: stupid.iso
stupid.iso: $(SUBDIRS)
$(TOPDIR)/tools/create-iso $@ sysroot
.PHONY: stupid.tar.gz
stupid.tar.gz: $(SUBDIRS)
tar -czvf $@ sysroot
.PHONY: floppy_boot.img
floppy_boot.img:
@ echo
.PHONY: clean
clean: $(SUBDIRS)
TOPDIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
SYSROOTDIR := $(TOPDIR)/sysroot
TOOLSDIR := $(TOPDIR)/tools
RM = echo
SUBDIRS := boot kernel lib bin
TARGET = stupid.tar.gz floppy_boot.img
ifneq ($(OS),Windows_NT)
TARGET += stupid.iso
endif
.PHONY: all
all: $(TARGET)
GOAL:=install
clean: GOAL:=clean
.PHONY: $(SUBDIRS)
$(SUBDIRS):
@echo "📁 $@"
DESTDIR=$(SYSROOTDIR) $(MAKE) -C $@ $(GOAL)
.PHONY: stupid.iso
stupid.iso: $(SUBDIRS)
$(TOPDIR)/tools/create-iso $@ sysroot
.PHONY: stupid.tar.gz
stupid.tar.gz: $(SUBDIRS)
tar -czvf $@ sysroot
.PHONY: floppy_boot.img
floppy_boot.img:
dd if=/dev/zero of=$@ bs=512 count=1440
mformat -C -f 1440 -i $@
dd if=boot/bootsector.bin of=$@ conv=notrunc
mcopy -i $@ boot/stpdboot.sys ::/STPDBOOT.SYS
mcopy -i $@ kernel/vmstupid ::/VMSTUPID.SYS
.PHONY: clean
clean: $(SUBDIRS)
$(RM) $(TARGET) $(SYSROOTDIR)

View file

@ -1,22 +1,29 @@
AS = fasm
RM = rm
TARGET = bootcode.bin
SRCS = boot.asm \
const.inc \
a20.inc
.PHONY: all
all: $(TARGET)
$(TARGET): $(SRCS)
$(AS) boot.asm $@
.PHONY: clean
clean:
$(RM) $(TARGET)
.PHONY: install
install: $(TARGET)
@ mkdir -p $(DESTDIR)
AS = fasm
RM = rm
TARGET = bootsector.bin stpdboot.sys
STAGE0_SRCS = boot0.asm \
const.inc \
fat12.inc
STAGE1_SRCS = boot1.asm \
const.inc \
a20.inc
.PHONY: all
all: $(TARGET)
bootsector.bin: $(STAGE0_SRCS)
$(AS) boot0.asm $@
stpdboot.sys: $(STAGE1_SRCS)
$(AS) boot1.asm $@
.PHONY: clean
clean:
$(RM) $(TARGET)
.PHONY: install
install: $(TARGET)
@ mkdir -p $(DESTDIR)

2
boot/README.org Normal file
View file

@ -0,0 +1,2 @@
#+TITLE: StupidOS Bootloader

View file

@ -1,171 +1,171 @@
; copy/pasted from https://wiki.osdev.org/A20_Line
; .... sorry I was lazy :x
a20_get_state:
pushf
push si
push di
push ds
push es
cli
xor ax, ax
mov ds, ax
mov si, 0x500
not ax
mov es, ax
mov di, 0x0510
mov al, [ds:si] ; save old values
mov byte [.BufferBelowMB], al
mov al, [es:di]
mov byte [.BufferOverMB], al
mov ah, 1 ; check byte [0x00100500] == byte [0x0500]
mov byte [ds:si], 0
mov byte [es:di], 1
mov al, [ds:si]
cmp al, [es:di]
jne .exit
dec ah
.exit:
mov al, [.BufferBelowMB]
mov [ds:si], al
mov al, [.BufferOverMB]
mov [es:di], al
shr ax, 8
sti
pop es
pop ds
pop di
pop si
popf
ret
.BufferBelowMB: db 0
.BufferOverMB db 0
a20_query_support:
push bx
clc
mov ax, 0x2403
int 0x15
jc .error
test ah, ah
jnz .error
mov ax, bx
pop bx
ret
.error:
stc
pop bx
ret
a20_enable_keyboard_controller:
cli
call .wait_io1
mov al, 0xad
out 0x64, al
call .wait_io1
mov al, 0xd0
out 0x64, al
call .wait_io2
in al, 0x60
push eax
call .wait_io1
mov al, 0xd1
out 0x64, al
call .wait_io1
pop eax
or al, 2
out 0x60, al
call .wait_io1
mov al, 0xae
out 0x64, al
call .wait_io1
sti
ret
.wait_io1:
in al, 0x64
test al, 2
jnz .wait_io1
ret
.wait_io2:
in al, 0x64
test al, 1
jz .wait_io2
ret
; out:
; cf - set on error
a20_enable:
clc ; clear cf
pusha
mov bh, 0 ; clear bh
call a20_get_state
jc .fast_gate
test ax, ax
jnz .done
call a20_query_support
mov bl, al
test bl, 1 ; enable A20 using keyboard controller
jnz .keybord_controller
test bl, 2 ; enable A20 using fast A20 gate
jnz .fast_gate
.bios_int:
mov ax, 0x2401
int 0x15
jc .fast_gate
test ah, ah
jnz .failed
call a20_get_state
test ax, ax
jnz .done
.fast_gate:
in al, 0x92
test al, 2
jnz .done
or al, 2
and al, 0xfe
out 0x92, al
call a20_get_state
test ax, ax
jnz .done
test bh, bh ; test if there was an attempt using the keyboard controller
jnz .failed
.keybord_controller:
call a20_enable_keyboard_controller
call a20_get_state
test ax, ax
jnz .done
mov bh, 1 ; flag enable attempt with keyboard controller
test bl, 2
jnz .fast_gate
jmp .failed
.failed:
stc
.done:
popa
; copy/pasted from https://wiki.osdev.org/A20_Line
; .... sorry I was lazy :x
a20_get_state:
pushf
push si
push di
push ds
push es
cli
xor ax, ax
mov ds, ax
mov si, 0x500
not ax
mov es, ax
mov di, 0x0510
mov al, [ds:si] ; save old values
mov byte [.BufferBelowMB], al
mov al, [es:di]
mov byte [.BufferOverMB], al
mov ah, 1 ; check byte [0x00100500] == byte [0x0500]
mov byte [ds:si], 0
mov byte [es:di], 1
mov al, [ds:si]
cmp al, [es:di]
jne .exit
dec ah
.exit:
mov al, [.BufferBelowMB]
mov [ds:si], al
mov al, [.BufferOverMB]
mov [es:di], al
shr ax, 8
sti
pop es
pop ds
pop di
pop si
popf
ret
.BufferBelowMB: db 0
.BufferOverMB db 0
a20_query_support:
push bx
clc
mov ax, 0x2403
int 0x15
jc .error
test ah, ah
jnz .error
mov ax, bx
pop bx
ret
.error:
stc
pop bx
ret
a20_enable_keyboard_controller:
cli
call .wait_io1
mov al, 0xad
out 0x64, al
call .wait_io1
mov al, 0xd0
out 0x64, al
call .wait_io2
in al, 0x60
push eax
call .wait_io1
mov al, 0xd1
out 0x64, al
call .wait_io1
pop eax
or al, 2
out 0x60, al
call .wait_io1
mov al, 0xae
out 0x64, al
call .wait_io1
sti
ret
.wait_io1:
in al, 0x64
test al, 2
jnz .wait_io1
ret
.wait_io2:
in al, 0x64
test al, 1
jz .wait_io2
ret
; out:
; cf - set on error
a20_enable:
clc ; clear cf
pusha
mov bh, 0 ; clear bh
call a20_get_state
jc .fast_gate
test ax, ax
jnz .done
call a20_query_support
mov bl, al
test bl, 1 ; enable A20 using keyboard controller
jnz .keybord_controller
test bl, 2 ; enable A20 using fast A20 gate
jnz .fast_gate
.bios_int:
mov ax, 0x2401
int 0x15
jc .fast_gate
test ah, ah
jnz .failed
call a20_get_state
test ax, ax
jnz .done
.fast_gate:
in al, 0x92
test al, 2
jnz .done
or al, 2
and al, 0xfe
out 0x92, al
call a20_get_state
test ax, ax
jnz .done
test bh, bh ; test if there was an attempt using the keyboard controller
jnz .failed
.keybord_controller:
call a20_enable_keyboard_controller
call a20_get_state
test ax, ax
jnz .done
mov bh, 1 ; flag enable attempt with keyboard controller
test bl, 2
jnz .fast_gate
jmp .failed
.failed:
stc
.done:
popa
ret

View file

@ -1,125 +0,0 @@
ORG 0x7C00
USE16
jmp short _start
nop
; Boot Record
OEM_identifier db 'STUPID '
bytes_per_sector dw 512
sectors_per_cluster db 1
reserved_sectors dw 1
FAT_count db 2
root_dir_entries dw 224
total_sectors dw 2880
media_desc_type db 0xF0
sectors_per_FAT dw 9
sectors_per_track dw 18
heads_per_cylinder dw 2
hidden_sectors dd 0
large_sector_count dd 0
; Extended Boot Record
drive_number db 0x0
reserved db 0x0
signature db 0x29 ; 0x28 or 0x29
volume_id dd 0xB00B135 ; hope mine will grow :'(
volume_label db 'Stupid Boot'
system_id db 'FAT12 '
_start:
cli
cld
jmp 0x0:.canonicalize_cs
.canonicalize_cs:
xor ax, ax
mov ds, ax
mov ss, ax
mov es, ax
mov sp, 0x7c00
; clear screen
mov al, 0x03
mov ah, 0
int 0x10
mov si, msg_hello
call bios_print
; reset floppy disk
@@:
xor ah, ah
int 0x13
jc @b
mov si, msg_load_stage
call bios_print
; read sector into ES:BX (0100:0000)
mov ax, 0x100 ; 100 segment
mov es, ax ; 0100:0000 (0000:1000)
xor bx, bx
mov ah, 0x2 ; read
mov al, 0x1 ; one sector
mov ch, 0x0 ; cylinder 0
mov cl, 0x2 ; second sector
mov dh, 0x0 ; head 0
int 0x13
jc .error
jmp 0x0:0x1000
.error:
mov si, msg_error_sector
call bios_print
hlt
jmp $
bios_print:
lodsb
or al, al
jz @f
mov ah, 0x0E
int 0x10
jmp bios_print
@@:
ret
msg_hello db "StupidOS Bootloader", 0x0D, 0x0A, 0
msg_load_stage db "Loading next stage", 0x0D, 0x0A, 0
msg_error_sector db "ERROR: Can't read sector", 0x0D, 0x0A, 0
rb 0x7C00+512-2-$
db 0x55, 0xAA
; ---------- stage 1 --------------
ORG 0x1000
stage2:
push cs
pop ds
mov si, msg_stage2
call bios_print
call a20_enable
jc .error_a20
xchg bx, bx
jmp .hang
.error_a20:
mov si, msg_error_a20
call bios_print
.hang:
hlt
jmp $
INCLUDE 'a20.inc'
msg_stage2 db "StupidOS Bootloader (Stage 2)", 0x0D, 0x0A, 0
msg_error_a20 db "ERROR: can't enable a20 line", 0x0D, 0x0A, 0

120
boot/boot0.asm Normal file
View file

@ -0,0 +1,120 @@
INCLUDE 'const.inc'
ORG STAGE0_BASE
USE16
jmp short _start
nop
; Boot Record
OEM_identifier db 'STUPID '
bytes_per_sector dw 512
sectors_per_cluster db 1
reserved_sectors dw 1
FAT_count db 2
root_dir_entries dw 224
total_sectors dw 2880
media_desc_type db 0xF0
sectors_per_FAT dw 9
sectors_per_track dw 18
heads_per_cylinder dw 2
hidden_sectors dd 0
large_sector_count dd 0
; Extended Boot Record
drive_number db 0x0
reserved db 0x0
signature db 0x29 ; 0x28 or 0x29
volume_id dd 0xB00B135 ; hope mine will grow :'(
volume_label db 'Stupid Boot'
system_id db 'FAT12 '
_start:
cli
cld
jmp 0x0:.canonicalize_cs
.canonicalize_cs:
xor ax, ax
mov ds, ax
mov ss, ax
mov es, ax
mov sp, 0x7c00
mov [drive_number], dl
; reset floppy disk
@@:
mov dl, [drive_number]
xor ah, ah
int 0x13
jc @b
call fat_load_root
; search in root directory
mov si, kernel_file
call fat_search_root
jc .error_not_found
mov [kernel_start], ax
mov si, stage1_file
call fat_search_root
jc .error_not_found
mov [stage1_start], ax
; load fat
xor ax, ax
mov al, [FAT_count]
mul word [sectors_per_FAT]
mov cx, ax
mov ax, [reserved_sectors]
xor bx, bx
call disk_read_sectors
; preload kernel
mov ax, KERNEL_PRELOAD/0x10
mov es, ax
mov ax, [kernel_start]
xor bx, bx
call fat_load_binary
; load stage 2
mov ax, STAGE1_BASE/0x10
mov es, ax
mov ax, [stage1_start]
xor bx, bx
call fat_load_binary
jmp 0x0:STAGE1_BASE
.error_not_found:
push si
mov si, msg_error
call bios_print
pop si
call bios_print
mov si, msg_not_found
call bios_print
hlt
jmp $
INCLUDE "utils.inc"
INCLUDE "fat12.inc"
msg_error db "ERROR: ", 0
msg_not_found db " not found", CR, LF, 0
kernel_file db "VMSTUPIDSYS", 0
stage1_file db "STPDBOOTSYS", 0
kernel_start dw 0x0
stage1_start dw 0x0
rb 0x7C00+512-2-$
db 0x55, 0xAA

28
boot/boot1.asm Normal file
View file

@ -0,0 +1,28 @@
INCLUDE 'const.inc'
ORG STAGE1_BASE
USE16
stage2:
push cs
pop ds
mov si, msg_stage2
call bios_print
call a20_enable
jc .error_a20
jmp .hang
.error_a20:
mov si, msg_error_a20
call bios_print
.hang:
hlt
jmp $
INCLUDE 'a20.inc'
INCLUDE 'utils.inc'
msg_stage2 db "StupidOS Bootloader (Stage 1)", 0x0D, 0x0A, 0
msg_error_a20 db "ERROR: can't enable a20 line", 0x0D, 0x0A, 0

View file

@ -1,8 +1,9 @@
; -------- Address ----------
STAGE0_BASE = 0x7C00
STAGE1_BASE = 0x1000
DISK_BUFFER = 0x8000
KERNEL_PRELOAD = 0xF000
STACK_TOP = 0x7000
CR = 0x0D
LF = 0x0A
; -------- Address ----------
STAGE0_BASE = 0x7C00
STAGE1_BASE = 0x1000
DISK_BUFFER = 0x8000
KERNEL_PRELOAD = 0xF000
STACK_TOP = 0x7000

181
boot/fat12.inc Normal file
View file

@ -0,0 +1,181 @@
ATTR_READ_ONLY = 0x01
ATTR_HIDDEN = 0x02
ATTR_SYSTEM = 0x04
ATTR_VOLUME_ID = 0x08
ATTR_DIRECTORY = 0x10
ATTR_ARCHIVE = 0x20
struc fat_entry
{
.name db 8 dup ?
.ext db 3 dup ?
.attrs db ?
.reserved dw ?
.creation_time dw ?
.creation_date dw ?
.access_date dw ?
.reserved2 dw ?
.mod_time dw ?
.mod_date dw ?
.start dw ?
.size dd ?
}
virtual at di
fat_entry fat_entry
end virtual
; CHS to LBA
; LBA = (C * HPC + H) * SPT + (S - 1)
;;; Read sectors from disk to buffer
;;;
;;; @param AX LBA starting sector
;;; @param CX sector count
;;; @param ES:BX buffer
;;;
disk_read_sectors:
; https://en.wikipedia.org/wiki/Logical_block_addressing
; convert LBA to CHS
; HPC = Head per Cluster
; SPT = Sector per Track
; S = (LBA % SPT) + 1
push ax
push bx
push cx
xor dx, dx
div word [sectors_per_track]
inc dx
mov [S], dx
; H = (LBA / SPT) % HPC
; C = LBA / (HPC * SPT)
xor dx, dx
div word [heads_per_cylinder]
mov [C], ax
mov [H], dx
; read sectors
mov ah, 0x2
mov al, 0x1
mov ch, byte [C]
mov cl, byte [S]
mov dh, byte [H]
mov dl, [drive_number]
int 0x13
jc @f
pop cx
pop bx
pop ax
add bx, word [bytes_per_sector]
inc ax
loop disk_read_sectors
ret
@@:
mov si, msg_error_sector
call bios_print
ret
C dw 0x00
H dw 0x00
S dw 0x00
fat_load_root:
mov ax, DISK_BUFFER/0x10
mov es, ax
; load root directory
mov ax, [sectors_per_FAT]
xor cx, cx
mov cl, [FAT_count]
mul cx
add ax, [reserved_sectors]
push ax
mov bx, [bytes_per_sector]
mov cl, 0x5
shr bx, cl
mov ax, [root_dir_entries]
xor dx, dx
div bx
mov cx, ax
pop ax
mov [data_start], ax
add [data_start], cx
xor bx, bx
call disk_read_sectors
ret
;; @param SI filename to search
fat_search_root:
mov cx, [root_dir_entries]
mov di, 0x0
@@:
push si
push cx
mov cx, 0xB ; name(8) + ext(3)
push di
rep cmpsb
pop di
pop cx
pop si
je .file_found
add di, 0x20
loop @b
; set carry if not found
stc
ret
.file_found:
mov ax, [es:fat_entry.start]
clc
ret
;;; @param AX cluster
;;; @param ES:BX buffer
fat_load_binary:
push ax
sub ax, 0x2
xor cx, cx
mov cl, [sectors_per_cluster]
mul cx
add ax, [data_start]
xor cx, cx
mov cl, [sectors_per_cluster]
call disk_read_sectors
pop ax
mov cx, ax
mov dx, ax
shr dx, 0x1
add cx, dx
push bx
mov bx, DISK_BUFFER
add bx, cx
mov dx, [bx]
pop bx
test ax, 0x1
jnz .odd_cluster
.even_cluster:
and dx, 0xFFF
jmp .end
.odd_cluster:
shr dx, 0x4
.end:
mov ax, dx
cmp dx, 0xFF0
jb fat_load_binary
ret
msg_error_sector db "ERROR: reading sector", CR, LF, 0
data_start dw 0x0

9
boot/utils.inc Normal file
View file

@ -0,0 +1,9 @@
bios_print:
lodsb
or al, al
jz @f
mov ah, 0x0E
int 0x10
jmp bios_print
@@:
ret