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

View file

@ -1,22 +1,29 @@
AS = fasm AS = fasm
RM = rm RM = rm
TARGET = bootcode.bin TARGET = bootsector.bin stpdboot.sys
SRCS = boot.asm \ STAGE0_SRCS = boot0.asm \
const.inc \ const.inc \
a20.inc fat12.inc
.PHONY: all STAGE1_SRCS = boot1.asm \
all: $(TARGET) const.inc \
a20.inc
$(TARGET): $(SRCS)
$(AS) boot.asm $@ .PHONY: all
all: $(TARGET)
.PHONY: clean
clean: bootsector.bin: $(STAGE0_SRCS)
$(RM) $(TARGET) $(AS) boot0.asm $@
.PHONY: install stpdboot.sys: $(STAGE1_SRCS)
install: $(TARGET) $(AS) boot1.asm $@
@ mkdir -p $(DESTDIR)
.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 ; copy/pasted from https://wiki.osdev.org/A20_Line
; .... sorry I was lazy :x ; .... sorry I was lazy :x
a20_get_state: a20_get_state:
pushf pushf
push si push si
push di push di
push ds push ds
push es push es
cli cli
xor ax, ax xor ax, ax
mov ds, ax mov ds, ax
mov si, 0x500 mov si, 0x500
not ax not ax
mov es, ax mov es, ax
mov di, 0x0510 mov di, 0x0510
mov al, [ds:si] ; save old values mov al, [ds:si] ; save old values
mov byte [.BufferBelowMB], al mov byte [.BufferBelowMB], al
mov al, [es:di] mov al, [es:di]
mov byte [.BufferOverMB], al mov byte [.BufferOverMB], al
mov ah, 1 ; check byte [0x00100500] == byte [0x0500] mov ah, 1 ; check byte [0x00100500] == byte [0x0500]
mov byte [ds:si], 0 mov byte [ds:si], 0
mov byte [es:di], 1 mov byte [es:di], 1
mov al, [ds:si] mov al, [ds:si]
cmp al, [es:di] cmp al, [es:di]
jne .exit jne .exit
dec ah dec ah
.exit: .exit:
mov al, [.BufferBelowMB] mov al, [.BufferBelowMB]
mov [ds:si], al mov [ds:si], al
mov al, [.BufferOverMB] mov al, [.BufferOverMB]
mov [es:di], al mov [es:di], al
shr ax, 8 shr ax, 8
sti sti
pop es pop es
pop ds pop ds
pop di pop di
pop si pop si
popf popf
ret ret
.BufferBelowMB: db 0 .BufferBelowMB: db 0
.BufferOverMB db 0 .BufferOverMB db 0
a20_query_support: a20_query_support:
push bx push bx
clc clc
mov ax, 0x2403 mov ax, 0x2403
int 0x15 int 0x15
jc .error jc .error
test ah, ah test ah, ah
jnz .error jnz .error
mov ax, bx mov ax, bx
pop bx pop bx
ret ret
.error: .error:
stc stc
pop bx pop bx
ret ret
a20_enable_keyboard_controller: a20_enable_keyboard_controller:
cli cli
call .wait_io1 call .wait_io1
mov al, 0xad mov al, 0xad
out 0x64, al out 0x64, al
call .wait_io1 call .wait_io1
mov al, 0xd0 mov al, 0xd0
out 0x64, al out 0x64, al
call .wait_io2 call .wait_io2
in al, 0x60 in al, 0x60
push eax push eax
call .wait_io1 call .wait_io1
mov al, 0xd1 mov al, 0xd1
out 0x64, al out 0x64, al
call .wait_io1 call .wait_io1
pop eax pop eax
or al, 2 or al, 2
out 0x60, al out 0x60, al
call .wait_io1 call .wait_io1
mov al, 0xae mov al, 0xae
out 0x64, al out 0x64, al
call .wait_io1 call .wait_io1
sti sti
ret ret
.wait_io1: .wait_io1:
in al, 0x64 in al, 0x64
test al, 2 test al, 2
jnz .wait_io1 jnz .wait_io1
ret ret
.wait_io2: .wait_io2:
in al, 0x64 in al, 0x64
test al, 1 test al, 1
jz .wait_io2 jz .wait_io2
ret ret
; out: ; out:
; cf - set on error ; cf - set on error
a20_enable: a20_enable:
clc ; clear cf clc ; clear cf
pusha pusha
mov bh, 0 ; clear bh mov bh, 0 ; clear bh
call a20_get_state call a20_get_state
jc .fast_gate jc .fast_gate
test ax, ax test ax, ax
jnz .done jnz .done
call a20_query_support call a20_query_support
mov bl, al mov bl, al
test bl, 1 ; enable A20 using keyboard controller test bl, 1 ; enable A20 using keyboard controller
jnz .keybord_controller jnz .keybord_controller
test bl, 2 ; enable A20 using fast A20 gate test bl, 2 ; enable A20 using fast A20 gate
jnz .fast_gate jnz .fast_gate
.bios_int: .bios_int:
mov ax, 0x2401 mov ax, 0x2401
int 0x15 int 0x15
jc .fast_gate jc .fast_gate
test ah, ah test ah, ah
jnz .failed jnz .failed
call a20_get_state call a20_get_state
test ax, ax test ax, ax
jnz .done jnz .done
.fast_gate: .fast_gate:
in al, 0x92 in al, 0x92
test al, 2 test al, 2
jnz .done jnz .done
or al, 2 or al, 2
and al, 0xfe and al, 0xfe
out 0x92, al out 0x92, al
call a20_get_state call a20_get_state
test ax, ax test ax, ax
jnz .done jnz .done
test bh, bh ; test if there was an attempt using the keyboard controller test bh, bh ; test if there was an attempt using the keyboard controller
jnz .failed jnz .failed
.keybord_controller: .keybord_controller:
call a20_enable_keyboard_controller call a20_enable_keyboard_controller
call a20_get_state call a20_get_state
test ax, ax test ax, ax
jnz .done jnz .done
mov bh, 1 ; flag enable attempt with keyboard controller mov bh, 1 ; flag enable attempt with keyboard controller
test bl, 2 test bl, 2
jnz .fast_gate jnz .fast_gate
jmp .failed jmp .failed
.failed: .failed:
stc stc
.done: .done:
popa popa
ret 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 @@
CR = 0x0D
; -------- Address ---------- LF = 0x0A
STAGE0_BASE = 0x7C00 ; -------- Address ----------
STAGE1_BASE = 0x1000 STAGE0_BASE = 0x7C00
DISK_BUFFER = 0x8000 STAGE1_BASE = 0x1000
KERNEL_PRELOAD = 0xF000 DISK_BUFFER = 0x8000
STACK_TOP = 0x7000 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