117 Commits

Author SHA1 Message Date
4a944bc493 minor changes in fat32 2025-09-20 08:29:43 -04:00
137735431a move stack after bss padding 2025-09-18 19:16:27 -04:00
6caaf6aa6d some structures for okteta
put in $(qtpaths --paths GenericDataLocation)
2025-09-18 17:20:17 -04:00
1e9de446f0 a few fixes to complete the move 2025-09-18 14:25:27 -04:00
5f2ca55af7 move mbr/vbr code to FAT32 system 2025-09-18 14:19:39 -04:00
11656c2827 more error codes and checks in vbr 2025-09-18 14:07:45 -04:00
f87f88a0dc fix qemu launch 2025-09-18 14:06:52 -04:00
7baa53178a update gitignore 2025-09-18 14:06:32 -04:00
8002d1cb54 more fat fs type checks 2025-09-18 11:50:27 -04:00
d8a89cfd2b remove shortjmp and ident from bpb 2025-09-18 11:20:05 -04:00
6c402b5c4f update structures 2025-09-17 21:48:37 -04:00
a3c9897291 prototyping 2025-09-17 20:59:48 -04:00
831d45c33c rename ext_disk_read to BIOS_int13h_ext_read 2025-09-17 18:54:59 -04:00
918e94689f 2KiB stack 2025-09-17 18:44:55 -04:00
8d2cde0a31 added no planned fat32 error codes and added mbr/vbr reading error codes 2025-09-17 17:02:08 -04:00
8d3788a76e remove unused structure 2025-09-17 16:50:59 -04:00
4a27e09872 use partition entry size constant 2025-09-17 16:24:45 -04:00
59bc1afb7c stick mmap info in SteviaInfo 2025-09-17 16:24:27 -04:00
8b0b73d8a4 start partition table at entry 1 2025-09-17 16:22:47 -04:00
803be58c10 Merge branch 'trunk' into fat_v2 2025-09-16 08:36:46 -04:00
528e3d69fe update github workflow since github includes firefox & snaps in the default runner image, which causes the build to fail.
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 1m11s
going to need a custom runner I guess...might as well only do actions on gitea.
2025-09-16 08:34:22 -04:00
51bd250925 minor corrections 2025-09-13 15:30:11 -04:00
460165a8d1 re-naming 2025-09-13 15:07:36 -04:00
935cbd1089 naming changes to ReadMbr/Vbr 2025-09-13 14:56:26 -04:00
7f1b4fa632 more naming changes 2025-09-13 14:51:30 -04:00
14f6788a22 renaming things to a hungarian style and adding "types" 2025-09-13 14:41:09 -04:00
526c3e7ea5 remove define_info macro, improved cstr macro and renamed all the OK/Msg debug strings 2025-09-13 13:45:37 -04:00
721ff2e62d cap bss, clean up some padding issues and clean up sign section 2025-09-13 12:50:42 -04:00
a50af35abd cleaned up memory maps and added a map for stage2 2025-09-13 12:48:19 -04:00
7cd6baa74d added 2 proc's to read mbr/vbr to a buffer and setup some early ptr's to partition tables and the like 2025-09-12 23:16:04 -04:00
40fa4062ce 1KiB stack stage2 gets doesn't need extra information from the mbr/vbr now. 2025-09-12 23:13:57 -04:00
a8b24c7b01 add functions to get the mbr/vbr in stage2 2025-09-11 19:05:50 -04:00
58fc4a08dd more me just reviewing the code base and comments 2025-09-11 17:33:08 -04:00
3da2af2d2d add comments to args passed to vbr 2025-09-11 17:06:35 -04:00
75d77e92e1 🤔 2025-09-11 17:06:08 -04:00
8e6c823a00 modified: include/cdecl16.inc 2025-09-11 14:01:30 -04:00
fca8a5d573 update readme 2025-09-11 12:25:03 -04:00
f341a50b78 removed unused macro for calling read_disk_raw
more simplification of push's
2025-09-11 12:12:48 -04:00
ef22bc9cd3 quick todo 2025-09-10 21:14:34 -04:00
507a05949f account for .sign section in bytes_remaining calculation 2025-09-10 11:36:40 -04:00
caed66a243 don't relocate the MBR to the stage2 heap space
this will save us disk reads for patition table info
2025-09-10 11:36:10 -04:00
d5cd8d6c45 remove unused early_mem.inc 2025-09-09 18:54:28 -04:00
b0f118b139 remove unused 2025-09-09 18:54:15 -04:00
71d50cdbd8 move the bpb_offset_bx file to old fat32 2025-09-09 18:50:34 -04:00
3c654b1be0 more rtfm'ing with encoding, 25 bytes of free code space in vbr now 2025-09-09 18:37:39 -04:00
0d90b3efd6 I rtfm'ed and saw there was a push m/16 encoding
mbr now has 93 bytes free for more code
2025-09-09 18:25:24 -04:00
b08605f295 remove dep on fat32_structures 2025-09-09 17:47:17 -04:00
aa0efe5ff8 a bunch of planning for what the new FAT driver should probably look like. 2025-09-09 17:02:35 -04:00
700148a2f6 disable fat32 parts to keep the build...building 2025-09-09 17:01:44 -04:00
14ef0151ca move old fat32 system to fat32/old 2025-09-09 17:01:14 -04:00
1d696371d2 updated QEMU args to match as close as possible to bochs 2025-09-09 17:00:41 -04:00
d30abe7a27 don't need to keep these dir's 2025-09-09 17:00:12 -04:00
d6f6076d45 don't need the directory here 2025-09-09 12:09:31 -04:00
4e05efaa6b Big cleanup in Makefile to follow Makefile standards a bit better.
minor edits to create-disk.sh to work with some of the changes for now
2025-09-09 11:03:50 -04:00
7cc0b4a392 first round of refactoring for the Makefile 2025-09-09 10:06:35 -04:00
9a6fea658d bugs
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 51s
2025-09-07 18:43:03 -04:00
891bff9509 use __CDECL16 macros and clean up a few proc calls 2025-09-07 17:38:21 -04:00
1e181bc22c remove print_string macro 2025-09-07 16:25:08 -04:00
68c8200aa4 remove invoke macro for now 2025-09-07 16:21:19 -04:00
766c67c041 first calls to use the new cdecl16 macros 2025-09-07 16:11:18 -04:00
49d82be0e8 use rotate instead of stack context in macros 2025-09-07 16:10:59 -04:00
69bd7d8f56 small clean up and use configured segment for return to real mode 2025-09-07 14:50:40 -04:00
875527aa98 output to bochs magic debug port as well 2025-09-07 14:49:21 -04:00
c0d47d4db0 re-organize bochs config, added USB 1.0/1.1/2.0 and e1000 network 2025-09-07 14:48:35 -04:00
81a3442ac5 remove magic debug from unrealmode entry 2025-09-07 13:51:23 -04:00
3ec55f5a39 change some macro names to make them more clear 2025-09-07 13:33:26 -04:00
9493aefa68 rename __CDECL16_ENTRY and EXIT 2025-09-07 13:29:24 -04:00
6f2fc627be new fancy macros to make it nicer to call functions 2025-09-07 13:16:37 -04:00
20c715fadd update notes on unreal entry 2025-09-07 13:15:40 -04:00
920c623a93 update README.md
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 1m5s
2025-09-06 13:56:00 -04:00
af8e7d74d3 I don't plan on using mmx/sse, set the minimum cpu to a 686 2025-09-06 13:55:45 -04:00
fde7abc544 replace or opcode with test
test only updates flags
2025-09-06 08:38:16 -04:00
69223a1ad2 explaination on the power of two stuff
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 1m21s
2025-09-05 20:54:29 -04:00
130cd22ff1 remove unreal mode stuff from cdecl16.inc 2025-09-05 20:33:00 -04:00
4e5112b45b move unreal mode hackery back to stage2.nasm 2025-09-05 20:32:19 -04:00
66e8e3c7fc prototype allocate function 2025-09-05 19:55:08 -04:00
b0bd4b27b0 🎉 simple allocator init in the code 2025-09-05 16:48:24 -04:00
cd316afdd1 basic arena allocator implementation 2025-09-05 16:42:25 -04:00
9a478aa4d4 added another __REFLAT variant 2025-09-05 16:28:01 -04:00
31d05c35e5 move __TINY_DS_ES to unreal_mode function
also some cleanup
2025-09-05 16:18:34 -04:00
534e04ef34 fix a typo 2025-09-05 15:48:43 -04:00
f58bf93507 don't exit if we are running as root
just warn
2025-09-05 15:45:33 -04:00
b5ae11f850 set ds/es to a tiny memory model after entering Unreal mode
ss/fs/gs will all be huge/flat model after
2025-09-05 15:43:22 -04:00
b9b3e92632 actually set cs to 16-bit segment for a bit in-case we want to run some pm code 2025-09-05 15:26:24 -04:00
5fac10f02c use easier to read logic 2025-09-05 14:40:44 -04:00
fa4524aa59 add macros to go from real <-> unreal 2025-09-05 14:39:16 -04:00
01ec6da0cc make unreal mode unreal again 2025-09-05 11:45:46 -04:00
2e2b4f991d add cursor manipulation via bios or crtc 2025-09-05 11:45:20 -04:00
d7b29d9113 __CDECL16_ENTRY now takes 0-1 params, if you give it a size it sets up local varible redzone 2025-09-05 11:44:30 -04:00
69d82dc0c2 small comment update 2025-09-05 11:43:41 -04:00
a53534acd2 clear the carry flag before calling BIOS functions 2025-09-05 10:33:42 -04:00
c3871d2b7d move string functions to video.nasm
caught a bug where SetTextMode was setting mono text not 16-color mode
2025-09-05 10:33:03 -04:00
d824493ba2 small clarification on what "unreal" mode really is 2025-09-05 09:12:55 -04:00
cb089681cf make arena_align_up use 16-bit values 2025-09-05 09:12:26 -04:00
d868008726 another oopsie from when I changed the calling convention... 2025-09-05 08:43:41 -04:00
935427509a inital code for align_up function 2025-09-05 08:27:56 -04:00
f5e4927d70 add basic arena area init code
Some checks failed
Daily Build and trunk tester / debian_update (push) Failing after 1m14s
2025-09-04 20:04:25 -04:00
b106eae066 fix alignment macros 2025-09-04 20:04:03 -04:00
f2657fdc01 seems I found a /little/ oopsie in the fat32 system 2025-09-04 19:59:36 -04:00
86d966811b prototypes for displaying some memory maps
but now I need to write an arena allocator...so brb
2025-09-04 17:51:52 -04:00
c10394a0cd early allocator prototypes 2025-09-04 17:49:57 -04:00
28b23b9ce2 paragraph align the values in bss 2025-09-04 15:32:42 -04:00
105ceb8b8b make sure eax and edx are set each call to E820 2025-09-04 15:32:28 -04:00
7fd5b9b85d wx is still broken, also point to disk.img in build/ 2025-09-04 15:31:39 -04:00
a618a837d4 added root check back in, but check that we aren't root
changed how mtools accesses images, should functionally be the same.
2025-09-04 12:29:57 -04:00
930d49c353 Delete LICENSE.md
GPLv3 is in COPYING, remove LICENSE.md
2025-09-04 10:11:36 -04:00
06d00d02e2 Update .github/workflows/daily.yaml
oh boy, github actions as well
2025-09-04 09:19:23 -04:00
61379e8116 add a daily cron and general build action for github actions 2025-09-04 08:58:45 -04:00
828428e73b removed debug files (they were moved earlier)
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 1m19s
2025-08-24 10:47:54 -04:00
402bf9974d moved map file output to build
moved output of builds to build/output
updated gitea action
2025-08-24 10:47:18 -04:00
5dfc3533d9 ignore everything in build output manually track .keep_dir files 2025-08-24 10:19:10 -04:00
d9225718c2 moving some documentation around and a bit of build cleanup 2025-08-24 10:18:01 -04:00
8a6730bc9c Update README.md
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 1m27s
2025-08-23 09:32:44 -04:00
67523208b3 Update scripts/create-disk.sh
fix label-id, was a bit over the ballmer ridge
2025-08-23 08:35:26 -04:00
ffe887b6a9 Update .gitea/workflows/onpush.yaml
All checks were successful
Daily Build and trunk tester / debian_update (push) Successful in 1m34s
2025-08-22 22:45:59 -04:00
c45e62ecc2 Merge branch 'trunk' of chtm-lapp-02.lan.chtm.me:nivirx/stevia into trunk 2025-08-22 22:43:40 -04:00
d7c50575cf re-wrote crate-disk.sh to not need root access, now requires mtools
removed macOS support in disk creation script
2025-08-22 22:43:24 -04:00
40 changed files with 1826 additions and 1027 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- name: Ensure image is up to date and install build tools and deps
run: |
bash -c "apt update && apt upgrade -y"
apt install -y build-essential nasm mount fdisk dosfstools
apt install -y build-essential nasm mtools fdisk dosfstools
- name: Build stevia
run: make
@@ -33,8 +33,8 @@ jobs:
with:
name: stevia-${{ github.run_number }}
path: |
build/**
disk.img
disk.img.gz
build/output/artifacts.tar.gz
build/output/disk.img.gz
build/output/part.img.gz
if-no-files-found: error
retention-days: 30 # override server default if you like

35
.github/workflows/daily.yaml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Daily Build and trunk tester
on:
push:
branches: [ $default-branch ]
schedule:
- cron: "0 7 * * 1" # mondays at 07:00
workflow_dispatch: {}
jobs:
debian_update:
runs-on: ubuntu-latest
steps:
- name: Checkout (if needed)
uses: actions/checkout@v4
- name: Update and install build tools and deps
run: |
sudo bash -c "apt update && apt upgrade -y"
sudo apt install -y build-essential nasm mtools fdisk dosfstools
- name: Build stevia
run: make
- name: Upload build artifacts
if: always() # still keep logs/artifacts even if build fails
uses: actions/upload-artifact@v4
with:
name: stevia-${{ github.run_number }}
path: |
build/output/artifacts.tar.gz
build/output/disk.img.gz
build/output/part.img.gz
if-no-files-found: error
retention-days: 7 # override server default if you like

5
.gitignore vendored
View File

@@ -1,5 +1,5 @@
out/*
build/*
build/output/*
*.img
*.img.gz
*.img.gz
@@ -9,3 +9,6 @@ stevia-log
.vscode/
*.map
compose-dev.yaml
eth_null-tx.log
eth_null-txdump.txt
*.log

View File

@@ -1,14 +0,0 @@
Copyright (C) 2025 Elaina Claus
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

140
Makefile
View File

@@ -13,74 +13,108 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
include := './include'
mbr_source_files := $(wildcard src/mbr/*.nasm)
vbr_source_files := $(wildcard src/vbr/*.nasm)
stage2_source_files := $(wildcard src/stage2/*.nasm)
boottest_source_files := $(wildcard src/miniboot32/*.nasm)
mbr_binary_files := $(patsubst src/mbr/%.nasm, build/%.bin, $(mbr_source_files))
vbr_binary_files := $(patsubst src/vbr/%.nasm, build/%.bin, $(vbr_source_files))
stage2_binary_files := $(patsubst src/stage2/%.nasm, build/%.bin, $(stage2_source_files))
boottest_binary_files := $(patsubst src/miniboot32/%.nasm, build/%.bin, $(boottest_source_files))
build_dir := 'build'
INCDIR := include
BUILD_DIR := build
IMG := $(BUILD_DIR)/disk.img
IMGZ := $(BUILD_DIR)/disk.img.gz
DEP_DIR := $(BUILD_DIR)/deps
# Get current Git version (tag) and hash
GIT_VERSION := $(shell git describe --tags)
GIT_HASH := $(shell git rev-parse HEAD)
GIT_VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo unknown)
GIT_HASH := $(shell git rev-parse HEAD 2>/dev/null || echo unknown)
GIT_NASM_DEFINES := -D __GIT_VER__='"$(GIT_VERSION)"' -D __GIT_HASH__='"$(GIT_HASH)"'
iso := '/tmp/disk.img'
isoz := 'disk.img.gz'
NASMFLAGS := -Wall -f bin -i$(INCDIR)/ $(GIT_NASM_DEFINES)
qemu_args := -L ./bin/ -bios bios.bin -cpu pentium3 -m 128 -S -s -monitor stdio -nic none
.PHONY: all mbr vbr stage2 boottest clean run run_bochs iso isoz
QEMU ?= qemu-system-i386
QEMU_ARGS := \
-M pc-i440fx-8.2,accel=tcg \
-cpu pentium3 \
-m 64M \
-rtc base=localtime \
-boot c \
-vga std \
-display gtk,gl=off \
-device e1000,netdev=n0,mac=52:54:00:12:34:56 \
-netdev user,id=n0 \
-device piix3-usb-uhci \
-parallel null \
-serial null -serial none -serial none -serial none \
-chardev file,id=dbg,path=bochs-e9.log,append=on \
-device isa-debugcon,iobase=0xe9,chardev=dbg \
-msg timestamp=on
all: $(iso) $(isoz) $(mbr_binary_files) $(vbr_binary_files) $(stage2_binary_files)
mbr: $(mbr_binary_files)
vbr: $(vbr_binary_files)
stage2: $(stage2_binary_files)
boottest: $(boottest_binary_files)
MBR_SRCS := $(wildcard src/mbr/*.nasm)
VBR_SRCS := $(wildcard src/vbr/*.nasm)
STAGE2_SRCS := $(wildcard src/stage2/*.nasm)
BOOTTEST_SRCS := $(wildcard src/miniboot32/*.nasm)
clean:
@rm -rvf *.map
@rm -rvf $(build_dir)/*
@rm -rvf $(iso)
@rm -rvf $(isoz)
MBR_BINS := $(patsubst src/mbr/%.nasm, $(BUILD_DIR)/mbr/%.bin, $(MBR_SRCS))
VBR_BINS := $(patsubst src/vbr/%.nasm, $(BUILD_DIR)/vbr/%.bin, $(VBR_SRCS))
STAGE2_BINS := $(patsubst src/stage2/%.nasm, $(BUILD_DIR)/stage2/%.bin, $(STAGE2_SRCS))
BOOTTEST_BINS := $(patsubst src/miniboot32/%.nasm, $(BUILD_DIR)/miniboot32/%.bin, $(BOOTTEST_SRCS))
run: $(iso)
@sudo qemu-system-i386 $(qemu_args) -hda $(iso)
ALL_BINS := $(MBR_BINS) $(VBR_BINS) $(STAGE2_BINS) $(BOOTTEST_BINS)
run_bochs: $(iso)
@bochs -q
.DEFAULT_GOAL := all
.DELETE_ON_ERROR:
iso: $(iso)
@file $(iso)
.PHONY: all mbr vbr stage2 boottest clean run run_qemu run_bochs img imgz help
all: $(IMG) $(IMGZ) $(ALL_BINS)
mbr: $(MBR_BINS)
vbr: $(VBR_BINS)
stage2: $(STAGE2_BINS)
boottest: $(BOOTTEST_BINS)
isoz: $(isoz)
@file $(isoz)
# Build Rules
build/%.bin: src/mbr/%.nasm
@mkdir -p $(shell dirname $@)
@nasm -i$(include) -Wall -f bin $(GIT_NASM_DEFINES) $< -o $@
$(BUILD_DIR)/mbr/%.bin: src/mbr/%.nasm | $(DEP_DIR)
@mkdir -p $(@D)
@nasm $(NASMFLAGS) -MD $(DEP_DIR)/$*.d -MT $@ $< -o $@
build/%.bin: src/vbr/%.nasm
@mkdir -p $(shell dirname $@)
@nasm -i$(include) -Wall -f bin $(GIT_NASM_DEFINES) $< -o $@
$(BUILD_DIR)/vbr/%.bin: src/vbr/%.nasm | $(DEP_DIR)
@mkdir -p $(@D)
@nasm $(NASMFLAGS) -MD $(DEP_DIR)/$*.d -MT $@ $< -o $@
build/%.bin: src/stage2/%.nasm
@mkdir -p $(shell dirname $@)
@nasm -i$(include) -Wall -f bin $(GIT_NASM_DEFINES) $< -o $@
$(BUILD_DIR)/stage2/%.bin: src/stage2/%.nasm | $(DEP_DIR)
@mkdir -p $(@D)
@nasm $(NASMFLAGS) -MD $(DEP_DIR)/$*.d -MT $@ $< -o $@
build/%.bin: src/miniboot32/%.nasm
@mkdir -p $(shell dirname $@)
@nasm -i$(include) -Wall -f bin $(GIT_NASM_DEFINES) $< -o $@
$(BUILD_DIR)/miniboot32/%.bin: src/miniboot32/%.nasm | $(DEP_DIR)
@mkdir -p $(@D)
@nasm $(NASMFLAGS) -MD $(DEP_DIR)/$*.d -MT $@ $< -o $@
$(iso): $(mbr_binary_files) $(vbr_binary_files) $(stage2_binary_files) $(boottest_binary_files)
@echo root access needed to create disk image...
$(DEP_DIR):
@mkdir -p $@
# Disk image's
$(IMG): $(ALL_BINS) | scripts/create-disk.sh
@scripts/create-disk.sh
$(isoz): $(iso)
@gzip -9kc $(iso) > $(isoz)
$(IMGZ): $(IMG)
@gzip -9kc $(IMG) > $(IMGZ)
img: $(IMG)
@file $(IMG)
imgz: $(IMGZ)
@file $(IMGZ)
# Helpers
run: $(IMG)
@$(QEMU) $(QEMU_ARGS) -drive file=$(IMG),if=ide,index=0,media=disk,format=raw \
run_bochs: $(IMG)
@bochs -q
clean:
@$(RM) -rv $(BUILD_DIR)/
help:
@awk '/^[A-Za-z0-9_%-]+:/{print $$1}' $(MAKEFILE_LIST) | sed 's/:$$//' | sort -u
# Include header deps
-include $(wildcard $(DEP_DIR)/*.d)

View File

@@ -1,6 +1,8 @@
# Stevia Bootloader
Stevia is a lightweight, hobby bootloader written in Assembly and C, designed for educational purposes. It targets x86 (Pentium III era) and aims to be simple, approachable, and understandable, focusing on minimalism and core functionality.
Stevia is a lightweight, hobby bootloader written in Assembly (NASM) and is designed for educational purposes. It targets 686+ and aims to be simple, approachable, and understandable, focusing on minimalism and core functionality.
Please note: **Most development currently takes place in Bochs (and maybe is tested on QEMU), due to this, the code in this repo has not been tested throughly aginist real hardware. As with most projects of this nature, there is a risk of damage due to improper programing of hardware, while careful effort is taken to not break the computer...accidents and mistakes can/do happen.**
## Features
@@ -10,18 +12,13 @@ Stevia is a lightweight, hobby bootloader written in Assembly and C, designed fo
- **Stage 2 Bootloader**: Loads the kernel into memory and passes control.
- **Basic Filesystem Support**: Initial filesystem recognition (details depend on implemented filesystem support).
- **Memory Map Parsing**: Detects available memory regions via BIOS interrupts.
- **GDT Initialization**: Sets up the Global Descriptor Table for protected mode.
### In Progress
- **Interrupt Descriptor Table (IDT) Setup**: Setting up basic interrupt handling.
- **Basic Keyboard Input**: Initial support for capturing keystrokes.
- **GDT Initialization**: Sets up the Global Descriptor Table for protected/unreal mode.
### Planned Features
- **Task Scheduling**: Basic round-robin task switching.
- **Interrupt Descriptor Table (IDT) Setup**: Setting up basic interrupt handling.
- **Basic Keyboard Input**: Initial support for capturing keystrokes.
- **Filesystem Expansion**: Support for additional filesystems.
- **More Robust Driver Support**: Additional hardware drivers, such as for storage devices.
## Installation
@@ -29,42 +26,40 @@ Stevia is a lightweight, hobby bootloader written in Assembly and C, designed fo
To build and run Stevia, you will need the following tools installed on your system:
- **Assembler**: NASM for assembling bootloader components.
- **C Compiler**: GCC (cross-compiler recommended).
- **Emulator**: Bochs or QEMU for testing.
- **GNU Make**: For building the project.
- **Utilities**:
- **dd**: For creating raw disk images.
- **losetup** (Linux only): For associating loop devices with disk images.
- **sfdisk**: For creating DOS disk partitions.
- **mkfs.fat**: For formatting partitions as FAT32.
- **hdiutil** (macOS only): For attaching disk images.
- **newfs\_msdos** (macOS only): For formatting FAT32 partitions.
- **Assembler**: NASM for assembling bootloader components, you could use a compatible assembler but YMMV.
- **GNU make**: For building the project.
- **mtools**: To access the FAT partition
- **fdisk**: To create disk images and setup partition tables
- **dosfstools**: FAT32 creation
- **an x86 Emulator**: Bochs is the primary dev target, but stevia should run on any x86 emulator/hardware with at least a Pentium 3 and 640KiB of memory.
### Building and Running
1. **Clone the repository**:
```sh
git clone https://github.com/Nivirx/stevia.git
cd stevia
```
2. **Build the bootloader and create a bootable disk image**:
```sh
sudo make
make
```
3. **Run using Bochs**:
```sh
bochs -f bochsrc.txt
bochs -q -f bochsrc.txt
```
## Project Goals
Stevia is intended to be a learning tool for those interested in low-level programming, focusing on understanding the basics of how a computer starts up and manages early system resources. Contributions and forks are welcome, especially from those interested in expanding the functionality.
Stevia is intended to be a learning tool for those interested in low-level programming, focusing on understanding the basics of how a computer starts up and manages early system resources.
## Contributing
We welcome contributions! Feel free to open issues for bugs or feature requests, and submit pull requests for any improvements. Please ensure that your code follows the existing style and includes appropriate comments.
You are welcome to fork/review the code base but I am generally not accepting PR's/Contributions to the code at this time.
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
This project is licensed under the GPLv3 License. See the [LICENSE](LICENSE.md) or [COPYING](COPYING) file for more details.

View File

@@ -1,44 +1,44 @@
# configuration file generated by Bochs
plugin_ctrl: voodoo=false, unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, busmouse=false, e1000=false, es1370=false, gameport=true, ne2k=false, sb16=false, usb_uhci=false, usb_ohci=false, usb_ehci=false, usb_xhci=false
#
# Bochs config
#
config_interface: textconfig
display_library: wx
memory: guest=64, host=64, block_size=128
romimage: file="/usr/share/bochs/BIOS-bochs-legacy", address=0x00000000, options=none, flash_data=none
vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest"
boot: disk
floppy_bootsig_check: disabled=0
floppya: type=1_44
# no floppyb
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="./disk.img", mode=flat, cylinders=0, heads=0, spt=0, sect_size=512, model="Stevia Disk", biosdetect=auto, translation=lba
ata0-slave: type=none
ata1: enabled=false
ata1-master: type=none
ata1-slave: type=none
ata2: enabled=false
ata3: enabled=false
optromimage1: file=none
optromimage2: file=none
optromimage3: file=none
optromimage4: file=none
optramimage1: file=none
optramimage2: file=none
optramimage3: file=none
optramimage4: file=none
pci: enabled=1, chipset=i440fx, slot1=none, slot2=none, slot3=none, slot4=none, slot5=none
vga: extension=vbe, update_freq=10, realtime=1, ddc=builtin
cpu: count=1, ips=1000000, model=p3_katmai, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
print_timestamps: enabled=0
port_e9_hack: enabled=false, all_rings=false
display_library: x, options=gui_debug
plugin_ctrl: voodoo=false, unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, busmouse=false, e1000=true, es1370=false, gameport=true, ne2k=false, sb16=false, usb_uhci=true, usb_ohci=false, usb_ehci=false, usb_xhci=false
print_timestamps: enabled=1
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
log: -
logprefix: %t%e%d
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
magic_break: enabled=1
port_e9_hack: enabled=1, all_rings=false
#
# Machine Spec
# 1x Pentium 3 @ (1M IPS) w/ 64MiB of System memory on the i440fx
cpu: count=1, ips=1000000, model=p3_katmai, reset_on_triple_fault=0, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
memory: guest=64, host=64, block_size=128
romimage: file="/usr/share/bochs/BIOS-bochs-legacy", address=0x00000000, options=none, flash_data=none
vgaromimage: file="/usr/share/vgabios/vgabios-stdvga.bin"
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=10, realtime=1, ddc=builtin
clock: sync=none, time0=local, rtc_sync=0
#
# Disks
#
boot: disk
floppy_bootsig_check: disabled=0
floppya: type=1_44
floppyb: type=1_44
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="./build/disk.img", mode=flat, cylinders=0, heads=0, spt=0, sect_size=512, model="Stevia Disk", biosdetect=auto, translation=lba
ata0-slave: type=none
#
# Other hardware
#
usb_ehci: enabled=1, companion=ohci
e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=null
keyboard: type=mf, serial_delay=150, paste_delay=100000, user_shortcut=none
mouse: type=none, enabled=false, toggle=ctrl+mbutton
sound: waveoutdrv=dummy, waveout=none, waveindrv=dummy, wavein=none, midioutdrv=dummy, midiout=none
@@ -49,5 +49,3 @@ com1: enabled=true, mode=null
com2: enabled=false
com3: enabled=false
com4: enabled=false
magic_break: enabled=1
port_e9_hack: enabled=1, all_rings=false

View File

@@ -207,24 +207,45 @@ Each GDT entry is 8 bytes:
## **5. Memory Layout Example**
### **Low Memory (First MiB)**
### **Low Memory/Upper Memory map (<= 1MiB)**
| Start | End | Size | Type | Description |
|-------------|-------------|-----------------|-----------------------|----------------------------------|
| 0x00000000 | 0x000003FF | 1 KiB | RAM (partially unusable) | Real Mode IVT (Interrupt Vector Table) |
| 0x00000400 | 0x000004FF | 256 bytes | RAM (partially unusable) | BDA (BIOS data area) |
| 0x00000500 | 0x00007BFF | almost 30 KiB | RAM - free for use | Conventional memory |
| 0x00007C00 | 0x00007DFF | 512 bytes | RAM (partially unusable) | OS BootSector |
| 0x00007E00 | 0x0007FFFF | 480.5 KiB | RAM - free for use | Conventional memory |
| 0x00080000 | 0x0009FFFF | 128 KiB | RAM (partially unusable) | EBDA (Extended BIOS Data Area) |
| 0x000A0000 | 0x000FFFFF | 384 KiB | various (unusable) | Video memory, ROM Area |
|-------------|-------------|-----------------|----------------------|--------------------------------|
| 0x00000000 | 0x000003FF | 1 KiB | RAM reclaimable~1~ | Real Mode IVT |
| 0x00000400 | 0x000004FF | 256 bytes | RAM reclaimable~1~ | BDA |
| 0x00000500 | 0x00007BFF | 29 KiB + 767b | RAM - free | Conventional memory |
| 0x00007C00 | 0x00007DFF | 512 bytes | RAM reclaimable~2~ | OS BootSector |
| 0x00007E00 | 0x0007FFFF | 480.5 KiB | RAM - free | Conventional memory |
| 0x00080000 | 0x0009FFFF | 128 KiB | RAM (unusable)~1~ | EBDA (Extended BIOS Data Area) |
| 0x000A0000 | 0x000FFFFF | 384 KiB | Upper RAM (unusable) | Video memory, ROM Area |
### **Extended Memory (Above 1 MiB)**
1. Reclaimable in the event that you *never* need the BIOS again & once the CPU is in protected mode.
2. Reclaimable after you are done with with the MBR/VBR stages.
### **Extended Memory (> 1 MiB)**
This is only an example, you should always check the memory map.
| Start | End | Size | Description |
|-------------|-------------|-----------------|------------------------|
| 0x00100000 | 0x00EFFFFF | 14 MiB | RAM - free for use |
|-------------|-------------|-----------------|---------------------------------------|
| 0x00100000 | 0x00EFFFFF | 14 MiB | RAM(?) - free for use |
| 0x00F00000 | 0x00FFFFFF | 1 MiB | Possible memory-mapped hardware (ISA) |
| 0x01000000 | ? | ? | More extended memory |
| 0xC0000000 | 0xFFFFFFFF | 1 GiB | Memory mapped PCI devices, BIOS, etc. |
| 0x0000000100000000 | ? | ? | RAM - usable in PAE/64-bit mode |
| 0x0000000100000000 | ? | ? | RAM(?) - usable in PAE/64-bit mode |
## **6. Stage2 Memory Layout **
Overall, Tiny (64 KiB page) Code/Data, Flat 4GiB mapping in gs/fs after unreal switch
| Start | End | Size (Bytes) | Type | Description |
|-------------|-------------|--------------|--------------------|----------------------------|
| 0x00000000 | 0x000003FF | 0x400 | RAM - (BIOS) | Real Mode IVT |
| 0x00000400 | 0x000004FF | 0x100 | RAM - (BIOS) | BDA |
| 0x00000500 | 0x000042FF | 0x3E00 | RAM - .text/.data | Code and constants/strings |
| 0x00004300 | 0x000044FF | 0x100 | RAM - .sign | EOF marker/signature |
| 0x00004500 | 0x000074FF | 0x3000 | RAM - bss | Runtime data/stack |
| 0x00007500 | 0x00007FFF | 0xB00 | RAM - reserved | Reserved |
| 0x00008000 | 0x0000DFFF | 0x6000 | RAM - heap | Conventional memory |
| 0x0000F000 | 0x0000FFFF | 0x1000 | RAM - reserved | Reserved |

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<data>
<struct name="MBR">
<!-- 446 bytes: bootstrap code -->
<array name="bootCode" length="446">
<primitive type="UInt8"/>
</array>
<!-- 4 x 16-byte partition entries -->
<array name="partitions" length="4">
<struct name="PartitionEntry">
<primitive name="status" type="UInt8"/> <!-- 0x80=bootable -->
<array name="chsFirst" length="3"><primitive type="UInt8"/></array>
<primitive name="type" type="UInt8"/>
<array name="chsLast" length="3"><primitive type="UInt8"/></array>
<primitive name="lbaStart" type="UInt32"/>
<primitive name="lbaSectors" type="UInt32"/>
</struct>
</array>
<!-- Signature 0x55AA (little-endian UInt16 == 0xAA55 on disk) -->
<primitive name="signature" type="UInt16"/>
</struct>
</data>

View File

@@ -0,0 +1,12 @@
[Desktop Entry]
Icon=application-x-executable
Type=Service
ServiceTypes=KPluginInfo
Name=Stevia MBR Structure
X-KDE-PluginInfo-Author=Elaina Claus
X-KDE-PluginInfo-Name=mbr
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Website=https://chtm.me
X-KDE-PluginInfo-Category=structure
X-KDE-PluginInfo-License=MIT
X-KDE-PluginInfo-EnabledByDefault=false

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<data>
<struct name="VBR">
<!-- 3 bytes: jmp over bpb code -->
<array name="jmpcode" length="3">
<primitive name="opcode" type="UInt8"/>
</array>
<array name="bs_ident" length="8">
<primitive type="Char"/>
</array>
<struct name="FAT32_bpb">
<primitive name="BytesPerSector" type="UInt16"/>
<primitive name="SectorsPerCluster" type="UInt8"/>
<primitive name="ReservedSectors" type="UInt16"/>
<primitive name="FatCount" type="UInt8"/>
<primitive name="RootEntryCount16" type="UInt16"/>
<primitive name="TotalSectors16" type="UInt16"/>
<primitive name="MediaDesc" type="UInt8"/>
<primitive name="FATSize16" type="UInt16"/>
<primitive name="SectorsPerTrack" type="UInt16"/>
<primitive name="HeadCount" type="UInt16"/>
<primitive name="HiddenSectors" type="UInt32"/>
<primitive name="TotalSectors32" type="UInt32"/>
</struct>
<struct name="FAT32_ebpb">
<primitive name="FATSize32" type="UInt32"/>
<primitive name="ExtFlags" type="UInt16"/>
<primitive name="FSVersion" type="UInt16"/>
<primitive name="RootDirCluster" type="UInt32"/>
<primitive name="FSInfoSector" type="UInt16"/>
<primitive name="BkBootSector" type="UInt16"/>
<array name="reserved0" length="12">
<primitive type="uint8"/>
</array>
<primitive name="DriveNumber" type="UInt8"/>
<primitive name="NtFlags" type="UInt8"/>
<primitive name="Signature" type="UInt8"/>
<primitive name="VolumeId" type="UInt32"/>
<array name="VolumeLabel" length="11">
<primitive type="Char"/>
</array>
<array name="FilesystemIdent" length="8">
<primitive type="Char"/>
</array>
</struct>
<array name="BootCode" length="420">
<primitive type="UInt8"/>
</array>
<!-- Signature 0xAA55 -->
<primitive name="signature" type="UInt16"/>
</struct>
</data>

View File

@@ -0,0 +1,12 @@
[Desktop Entry]
Icon=application-x-executable
Type=Service
ServiceTypes=KPluginInfo
Name=Stevia FAT32 VBR Structure
X-KDE-PluginInfo-Author=Elaina Claus
X-KDE-PluginInfo-Name=vbr32
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Website=https://chtm.me
X-KDE-PluginInfo-Category=structure
X-KDE-PluginInfo-License=MIT
X-KDE-PluginInfo-EnabledByDefault=false

View File

@@ -33,30 +33,27 @@ struc AddressRangeDescStruct_t
.ExtType resd 1
endstruc
; !!! this procedure changes ES !!!
ALIGN 4, db 0x90
GetMemoryMap:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
push es ; save segment register
.func:
mov dword [SteviaInfo + SteviaInfoStruct_t.MemoryMapEntries], 0
mov word [SteviaInfo + SteviaInfoStruct_t.u16_E820MMapEntryCount], 0
mov eax, 0xE820 ; select 0xE820 function
xor ebx, ebx ; Continuation value, 0 for the first call
lea dx, [BIOSMemoryMap]
shr dx, 4
mov es, dx
xor di, di ; (BIOSMemoryMap >> 4):0 makes di an index into BIOSMemoryMap
mov ecx, AddressRangeDescStruct_t_size
mov edx, 0x534D4150 ; 'SMAP' magic
mov di, BIOSMemoryMap ; destination is es:di, es should == ds
mov ecx, AddressRangeDescStruct_t_size ; hard request ACPI 3.0 table versions (24 bytes)
.loop_L1:
int 0x15
mov eax, 0x0000E820 ; select 0xE820 function
mov edx, 0x534D4150 ; 'SMAP' magic
clc ; clear carry
int 0x15 ; data will be stored at es:di by e820 call
jc GetMemoryMap.error
cmp eax, 0x534D4150
jne GetMemoryMap.no_smap_returned
.no_error:
inc dword [SteviaInfo + SteviaInfoStruct_t.MemoryMapEntries]
inc word [SteviaInfo + SteviaInfoStruct_t.u16_E820MMapEntryCount]
cmp ecx, 20 ; TODO: maybe this could be handled better than just panicing
jb GetMemoryMap.nonstandard_e820 ; non-standard entry found
@@ -64,9 +61,11 @@ GetMemoryMap:
cmp ebx, 0
je GetMemoryMap.endp ; 0 in ebx means we have reached the end of memory ranges
add di, AddressRangeDescStruct_t_size ; increment di to next descriptor
mov edx, eax ; 'SMAP' to edx
mov eax, 0xE820 ; select E820 function
; setup for next loop, ebx is our next index and has already been set.
xor ecx, ecx
mov ecx, AddressRangeDescStruct_t_size ; set ecx (requested_size) to 24 bytes
add di, cx ; increment di to next descriptor so es:di points to the next free space
jmp GetMemoryMap.loop_L1
.error:
ERROR STAGE2_MM_E820_MISC_ERR
@@ -76,8 +75,40 @@ GetMemoryMap:
ERROR STAGE2_MM_E820_NO_SMAP
.endp:
pop es
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
PrintMemoryMap:
__CDECL16_PROC_ENTRY
.func:
mov eax, dword [SteviaInfo + SteviaInfoStruct_t.u16_E820MMapEntryCount]
cmp eax, 0
je PrintMemoryMap.endp ; if entries == 0, exit
mov ecx, eax ; store # of entries in counter
mov eax, BIOSMemoryMap ; address to start of e820 mmap
push dword ecx
push dword eax
call FormatMemoryMapEntry
add sp, 0x8
; ax contains segment offset to string after call
; TODO: going to need an allocator for strings and stuff...
; print_string strformat_buffer
.endp:
__CDECL16_PROC_EXIT
ret
FormatMemoryMapEntry:
__CDECL16_PROC_ENTRY
.func:
; create a string buffer somewhere and return address to result string in ax
.endp:
__CDECL16_PROC_EXIT
ret
%endif
%define __INC_E820MEMORY_MAP

View File

@@ -64,7 +64,7 @@
; Bits 6-7 - 00: HDD activity LED off; any other value is "on"
ALIGN 4, db 0x90
EnableA20:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
push ds
push es
.a20_check:
@@ -105,6 +105,7 @@ EnableA20:
je EnableA20.endp ; A20 is already enabled
mov ax, 0x2403
clc ; clear carry
int 0x15
jc EnableA20.do_fallback_a20 ; carry = error...not supported?
cmp ah, 0
@@ -121,6 +122,7 @@ EnableA20:
ERROR STAGE2_A20_FAILED
.do_bios_a20:
mov ax, 0x2401
clc ; clear carry
int 0x15
jmp EnableA20.a20_check
.do_fast_a20:
@@ -132,7 +134,7 @@ EnableA20:
.endp:
pop es
pop ds
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
%endif

View File

@@ -31,29 +31,6 @@ struc LBAPkt_t
.lower_lba resd 1
.upper_lba resd 1
endstruc
; call_read_disk_raw <word segment> <word offset> <dword lba> <word count> <word drive_num>
%macro call_read_disk_raw 5
movzx ax, %5
push ax ; drive_num
movzx ax, %4
push ax ; count
movzx dword eax, %3
push dword eax ; lba
movzx ax, %2
push ax ; offset
movzx ax, %1
push ax ; segment = 0
; uint8_t read_stage2_raw(uint16_t buf_segment, uint16_t buf_offset,
; uint32_t lba,
; uint16_t count, uint16_t drive_num)
call read_disk_raw
add sp, 0xC
%endmacro
; Wrapper for AH=0x42 INT13h (Extended Read)
;
@@ -70,21 +47,17 @@ endstruc
; disk address packet's block count field set to number of blocks
; successfully transferred
;
;
; uint8_t read_stage2_raw(uint16_t buf_segment, uint16_t buf_offset,
; uint32_t lba,
; uint16_t count, uint8_t drive_num)
ALIGN 4, db 0x90
read_disk_raw:
__CDECL16_ENTRY
BIOS_int13h_ext_read:
__CDECL16_PROC_ENTRY
.func:
mov ax, LBAPkt_t_size
push ax ; len
xor ax, ax
push ax ; val = 0
mov ax, lba_packet
push ax ; dest = lba_packet address
call kmemset
push LBAPkt_t_size ; len
push 0x00 ; val = 0
push lba_packet ; dest = lba_packet address
call kmemset ; kmemset(dst, val, len)
add sp, 0x06
mov bx, lba_packet
@@ -97,7 +70,8 @@ read_disk_raw:
mov dword [bx + LBAPkt_t.lower_lba], eax
; upper_lba is zero from kmemset
; TODO: possiblly support >32bit LBA addresses in the future, this limits us to 4GiB
; TODO: possiblly support >32bit LBA addresses in the future
; this will limit us to (4GiB * sector size) of readable lba's from the disk
mov ax, [bp + 6]
mov word [bx + LBAPkt_t.offset], ax
@@ -111,6 +85,7 @@ read_disk_raw:
mov ah, 0x42 ; call #
mov dl, byte [bp + 14] ; drive #
clc ; clear carry
int 0x13
jnc .endf
@@ -119,10 +94,10 @@ read_disk_raw:
%elifdef __STEVIA_VBR
ERROR VBR_ERROR_DISK_READ_ERR
%else
ERROR STAGE2_MBR_DISK_READ_ERROR
ERROR STAGE2_INT13_DISK_READ_ERR
%endif
.endf:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
%endif

View File

@@ -20,25 +20,27 @@
ALIGN 4, db 0x90
SetTextMode:
.prolog:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
pushf
.func:
xor ah, ah ; Set Video mode BIOS function
mov al, 0x02 ; 16 color 80x25 Text mode
mov al, 0x03 ; 16 color 80x25 Text mode
int 0x10 ; Call video interrupt
mov ah, 0x05 ; Select active display page BIOS function
xor al, al ; page 0
int 0x10 ; call video interrupt
.endp:
popf
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
; disables blinking text mode cursor
; disables blinking text mode cursor via crtc pokes
; 0x3D4/0x3D5 for color, mono at 0x3B4/0x3B5
ALIGN 4, db 0x90
disable_cursor:
__CDECL16_ENTRY
disable_cursor_crtc:
__CDECL16_PROC_ENTRY
.func:
mov dx, 0x3D4
mov al, 0xA ; low cursor shape register
@@ -48,8 +50,126 @@ disable_cursor:
mov al, 0x20 ; bits 6-7 unused, bit 5 disables the cursor, bits 0-4 control the cursor shape
out dx, al
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
; disables blinking text mode cursor via BIOS int 10h, ah = 01
ALIGN 4, db 0x90
disable_cursor_bios:
__CDECL16_PROC_ENTRY
.func:
push bx
mov ah, 03h ; read cursor pos & shape
xor bh, bh
int 10h ; CH=top scanline, CL=bottom
or ch, 20h ; set bit 5 = disable
mov ah, 01h ; set leaf 01h
int 10h
pop bx
.endp:
__CDECL16_PROC_EXIT
ret
; Prints a C-Style string (null terminated) using BIOS vga teletype call
; void PrintString(char* buf)
ALIGN 4, db 0x90
PrintString:
__CDECL16_PROC_ENTRY
mov di, [bp + 4] ; first arg is char[]
.str_len:
xor cx, cx ; ECX = 0
not cx ; ECX = -1 == 0xFFFF
xor ax, ax ; search for al = 0x0
cld
repne scasb ; deincrement cx while searching for al
not cx ; the inverse of a neg number = abs(x) - 1
dec cx ; CX contains the length of the string - nul byte at end
.print:
mov si, [bp + 4] ; source string
.print_L0:
movzx ax, byte [si]
%ifdef __STEVIA_DEV_DEBUG
out 0xe9, byte al ; bochs magic debug port
%endif
push ax
call PrintCharacter
add sp, 0x2
inc si
dec cx
jcxz PrintString.endp
jmp PrintString.print_L0 ; Fetch next character from string
.endp:
__CDECL16_PROC_EXIT
ret ; Return from procedure
; Prints a single character
; void PrintCharacter(char c);
ALIGN 4, db 0x90
PrintCharacter:
__CDECL16_PROC_ENTRY
.func:
mov al, byte [bp + 4] ; AL = character c
mov ah, 0x0E ; INT 0x10, AH=0x0E call
mov bx, 0x0007 ; BH = page no. BL =Text attribute
int 0x10 ; call video interrupt
; TODO: check for carry and clear carry before call
.endp:
__CDECL16_PROC_EXIT
ret
; TODO: fix the prolog, epilog and stack usage to confirm with cdecl16
; prints the hex representation of of val
; void PrintDWORD(uint32_t val);
ALIGN 4, db 0x90
PrintDWORD:
__CDECL16_PROC_ENTRY
.func:
mov si, IntToHex_table
mov ebx, 16 ; base-16
mov eax, dword [bp + 4] ;val
xor edx, edx
xor cx, cx
.next_digit:
div ebx ; dividend in edx:eax -> quotient in eax, remainder in edx
push dx ; save remainder
inc cx
xor dx, dx
test eax, eax
jnz PrintDWORD.next_digit
.zero_pad:
cmp cx, 0x0008
je PrintDWORD.print_stack
xor ax, ax
push ax
inc cx
jmp PrintDWORD.zero_pad
.print_stack:
pop bx
dec cx
push cx
movzx ax, byte [bx+si+0] ; bx = index into Hex lookup table
push ax
call PrintCharacter
add sp, 0x2
pop cx
jcxz PrintDWORD.endp
jmp PrintDWORD.print_stack
.endp:
__CDECL16_PROC_EXIT
ret
%endif
%define __INC_VIDEO

View File

@@ -13,20 +13,23 @@
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
%ifnmacro __CDECL16_ENTRY
%macro __CDECL16_ENTRY 0
%ifnmacro __CDECL16_PROC_ENTRY
%macro __CDECL16_PROC_ENTRY 0-1
push bp
mov bp, sp
sub sp, 0x10
push si
push di
push bx
%if %0 = 1
sub sp, %1 ; reserve locals only when needed
%endif
%endmacro
%endif
%ifnmacro __CDECL16_EXIT
%macro __CDECL16_EXIT 0
%ifnmacro __CDECL16_PROC_EXIT
%macro __CDECL16_PROC_EXIT 0
pop bx
pop di
pop si
@@ -36,29 +39,108 @@
%endmacro
%endif
%ifnmacro __CDECL16_CALLER_SAVE
%macro __CDECL16_CALLER_SAVE 0
push ax
push cx
push dx
; __CDECL16_PROC_ARGS nargs
; Creates %$arg1 .. %$argN as [bp+4], [bp+6], ...
; for use inside of function bodies
; BUG: still needs to be tested
%ifnmacro __CDECL16_PROC_ARGS
%macro __CDECL16_PROC_ARGS 1
%push __CDECL16_PROC_ARGS
%assign %$__i 1
%rep %1
%xdefine %$arg%$__i [bp + 4 + 2*(%$__i-1)]
%assign %$__i %$__i + 1
%endrep
%pop
%endmacro
%endif
%ifnmacro __CDECL16_CALLER_RESTORE
%macro __CDECL16_CALLER_RESTORE 0
pop dx
pop cx
pop ax
; __CDECL16_CALL_ARGS a,b,c ==> push c / push b / push a
; handles pushing values to the stack in the correct order
%ifnmacro __CDECL16_CALL_ARGS
%macro __CDECL16_CALL_ARGS 1-*
%rep %0
%rotate -1 ; move the last argument into %1
push %1 ; push last, then next-to-last, ...
%endrep
%endmacro
%endif
; __CDECL16_CALL func, nargs ; adds sp by nargs*2 after call
; adds up word pushes to restore stack frame
; WARNING: if you push a dword it will only count 2, use __CDECL16_CALL_ARGS_SIZED
%ifnmacro __CDECL16_CALL
%macro __CDECL16_CALL 2
call %1
add sp, %2*2
%endmacro
%endif
; __CDECL16_CALL_ARGS_SIZED func, size1[, size2 ...] ; sizes in BYTES
; if you *need* to pass dword sized args in 16-bit mode, use this to properly
; count the stack frame to restore later!
; BUG: still needs to be tested
%ifnmacro __CDECL16_CALL_ARGS_SIZED
%macro __CDECL16_CALL_ARGS_SIZED 2-*
%push __CDECL16_CALL_ARGS_SIZED
call %1
%assign %$__sum 0
%assign %$__i 2
%rep %0-1
%assign %$__sum %$__sum + %[%$__i] ; sizes may be expressions
%assign %$__i %$__i + 1
%endrep
%if %$__sum & 1
%error "__CDECL16_CALL_ARGS_SIZED: total size is odd; pushes must sum to whole bytes actually pushed (usually even)."
%endif
add sp, %$__sum
%pop
%endmacro
%endif
%ifnmacro __CDECL16_CLOB
; Save registers in the order listed. Supports "flags" as a pseudo-reg.
%macro __CDECL16_CLOB 1-*
%rep %0
%ifidni %1, flags
pushf
%else
push %1
%endif
%rotate 1
%endrep
%endmacro
%endif
%ifnmacro __CDECL16_UNCLOB
; Restore in reverse order of CLOB. Supports "flags".
%macro __CDECL16_UNCLOB 1-*
%rep %0
%rotate -1
%ifidni %1, flags
popf
%else
pop %1
%endif
%endrep
%endmacro
%endif
;
; Only setup the minimum stack frame
; unlike the above, everything is caller save
;
%ifnmacro __FASTCALL16_ENTRY
%macro __FASTCALL16_ENTRY 0
%macro __FASTCALL16_ENTRY 0-1
push bp
mov bp, sp
%if %0 = 1
sub sp, %1 ; reserve locals only when needed
%endif
%endmacro
%endif
; restore minimum stack frame, all regs are caller save
%ifnmacro __FASTCALL16_EXIT
%macro __FASTCALL16_EXIT 0
mov sp, bp

View File

@@ -15,7 +15,7 @@
%ifndef __INC_STEVIA_CONFIG
%define SECTOR_SIZE 512
%define SECTOR_SIZE 0x200
%define STAGE2_SECTOR_COUNT 0x20
; 16 KiB
%define MAX_STAGE2_BYTES (SECTOR_SIZE * STAGE2_SECTOR_COUNT)

View File

@@ -1,28 +0,0 @@
; Copyright (C) 2025 Elaina Claus
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
%ifndef __INC_EARLY_MEM
;PhysicalAddress = Segment * 16 + Offset
%define SEG_TO_LINEAR(s,o) ((s << 4) + o)
; Offset = physical / (Segment * 16)
%define LINEAR_TO_OFFSET(p,s) ((p / (s << 4)))
; Seg = (physical - offset) / 16
%define LINEAR_TO_SEGMENT(p,o) ((p - o) >> 4)
%endif
%define __INC_EARLY_MEM

View File

@@ -15,7 +15,7 @@
%ifndef __INC_ENTRY
%define MBR_ENTRY 0x8C00
%define MBR_ENTRY 0x6C00
%define VBR_ENTRY 0x7C00
%define STAGE2_ENTRY 0x0500

View File

@@ -15,8 +15,7 @@
%ifndef __INC_ERROR_CODES
; Errors
; 12 Errors, 5 in use
; MBR Error codes
%define MBR_ERROR_DISK_T_ERR 'a'
%define MBR_ERROR_NO_INT32E 'b'
%define MBR_ERROR_NO_NO_BOOT_PART 'c'
@@ -30,7 +29,7 @@
%define MBR_ERROR_RESERVED_k 'k'
%define MBR_ERROR_INT13h_EREAD_ERR 'l'
; 12 Error
; VBR Error codes
%define VBR_ERROR_WRONG_FAT_SIZE 'm'
%define VBR_ERROR_NO_SIGNATURE 'n'
%define VBR_ERROR_DISK_READ_ERR 'o'
@@ -44,31 +43,31 @@
%define VBR_ERROR_RESERVED_w 'w'
%define VBR_ERROR_RESERVED_x 'x'
; 22 errors, 8 in use
; Stage2 Error codes
%define STAGE2_A20_FAILED 'A'
%define STAGE2_SIGNATURE_MISSING 'B'
%define STAGE2_MM_E820_NO_SUPPORT 'C'
%define STAGE2_MM_E820_MISC_ERR 'D'
%define STAGE2_MM_E820_NONSTANDARD 'E'
%define STAGE2_MM_E820_NO_SMAP 'F'
%define STAGE2_MBR_DISK_READ_ERROR 'G'
%define STAGE2_FAT32_INIT_ERROR 'H'
%define STAGE2_FAT32_NO_FILE 'I'
%define STAGE2_FAT32_END_OF_CHAIN 'J'
%define STAGE2_FAT32_NCLUS_CFDIVZ 'K'
%define STAGE2_FAT32_CLS2LBA_CF 'L'
%define STAGE2_FAT32_INIT_CF 'M'
%define STAGE2_ERROR_INFOPRINTER 'N'
%define STAGE2_ERROR_RESERVED_O 'O'
%define STAGE2_ERROR_RESERVED_P 'P'
%define STAGE2_ERROR_RESERVED_Q 'Q'
%define STAGE2_ERROR_RESERVED_R 'R'
%define STAGE2_ERROR_RESERVED_S 'S'
%define STAGE2_ERROR_RESERVED_T 'T'
%define STAGE2_ERROR_RESERVED_U 'U'
%define STAGE2_ERROR_RESERVED_V 'V'
%define STAGE2_INT13_DISK_READ_ERR 'G'
%define STAGE2_FAT32_E_FMT 'H'
%define STAGE2_FAT32_E_RANGE 'I'
%define STAGE2_FAT32_E_NOSUCH 'J'
%define STAGE2_FAT32_E_ISDIR 'K'
%define STAGE2_FAT32_E_TOOLONG 'L'
%define STAGE2_FAT32_E_UNSUPPORTED 'M'
%define STAGE2_FAT32_E_UNIMPLEMENTED 'N'
%define STAGE2_ERROR_BAD_MBR 'O'
%define STAGE2_VBR_E_ACTIVE 'P'
%define STAGE2_VBR_E_SIGN 'Q'
%define STAGE2_VBR_E_TOT 'R'
%define STAGE2_VBR_E_FATSZ 'S'
%define STAGE2_VBR_E_DIRENT 'T'
%define STAGE2_VBR_E_PARTTYPE 'U'
%define STAGE2_RESERVED_E_V 'V'
; for development only, specific errors should be above.
; Debug error codes
%define STEVIA_DEBUG_OK 'W'
%define STEVIA_DEBUG_ERR 'X'
%define STEVIA_DEBUG_UNIMPLEMENTED 'Y'

View File

@@ -0,0 +1,290 @@
; Copyright (C) 2025 Elaina Claus
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
%ifndef __INC_FAT32_SYS
%include "partition_table.inc"
%include "fat32/fat32_structures.inc"
; int read_mbr(int boot_drive, void* dst)
; destination buffer needs 512 bytes of space
FAT32_load_mbr:
__CDECL16_PROC_ENTRY
.proc:
; read mbr on boot drive to memory (for the partition table)
movzx ax, byte [bp + 4]
push ax ; drive_num (2)
push 0x01 ; count (2)
push dword 0x0 ; lba (4)
push word [bp + 6] ; offset = dst
push __STAGE2_SEGMENT ; this segment
call BIOS_int13h_ext_read
add sp, 0xC
.check_sig:
mov bx, [bp + 6]
cmp word [bx + 0x1FE], 0xAA55 ; check for bytes at end
jne .error_nosign
; TODO: this needs more error checking, zero checking, check the sig a bunch of stuff...
.endp:
__CDECL16_PROC_EXIT
ret
.error_nosign:
ERROR STAGE2_ERROR_BAD_MBR
; int read_vbr(int boot_drive, void* buf)
; read vbr on boot partition to memory (for fat bpb/ebpb)
; TODO: seperate validation and loading the sector, just check the 0xAA55 at the end here...
FAT32_load_vbr:
__CDECL16_PROC_ENTRY
.proc:
mov bx, SteviaInfo
mov ax, word [bx + SteviaInfoStruct_t.p16_MbrPtr]
add ax, DISK_PARTITION_TABLE_OFFSET
mov bx, ax ; offset to part table
mov cx, 4 ; only checking 4 entries
.find_active_L0:
mov al, [bx + PartEntry_t.attributes]
cmp al, 0x80 ; 0x80 == 1000_0000b
je .check_fstype
add bx, PartEntry_t_size ; next part entry's attributes
loop .find_active_L0
jmp .error_noactive
.check_fstype:
; check for part_type = 0x0C (DOS 7.1+/W95 FAT32 w/ LBA) or 0x1C (Hidden 0x0C)
__BOCHS_MAGIC_DEBUG
mov al, [bx + PartEntry_t.part_type]
cmp al, 0x0C
je .active_ok
; *or*
cmp al, 0x1C
je .active_ok
jmp .error_badparttype ; error if part_type != 0x1C or 0x0C
.active_ok:
movzx ax, byte [bp + 4]
push ax ; drive_num (2)
push 0x01 ; count (2)
mov eax, dword [bx + PartEntry_t.lba_start]
push dword eax ; lba (4)
push word [bp + 6] ; offset = dst
push __STAGE2_SEGMENT ; this segment
call BIOS_int13h_ext_read
add sp, 0xC
; vbr (with fat bpb/ebpb) is at the buffer now
.check_sig:
mov bx, [bp + 6]
cmp word [bx + 0x1FE], 0xAA55 ; check for bytes at end
jne .error_nosign
.check_FAT_sanity:
; we only quickly validate that this is *probably* a FAT32 volume
add bx, 11 ; point bx at start of bpb (skip jmp code and ident)
cmp word [bx + FAT32_bpb_t.u16_TotalSectors16], 0 ; TotalSectors16 should be 0 (use TotalSectors32 in bpb)
jne .error_totsectors
cmp word [bx + FAT32_bpb_t.u16_FATSize16], 0 ; FatSize16 will be 0 if FAT32 (use FATSize32 in ebpb)
jne .error_fatsz
cmp word [bx + FAT32_bpb_t.u16_RootEntryCount16], 0 ; root dir info is in data clusters on fat32
jne .error_rootdir
.endp:
__CDECL16_PROC_EXIT
ret
.error_noactive:
ERROR STAGE2_VBR_E_ACTIVE
.error_nosign:
ERROR STAGE2_VBR_E_SIGN
.error_totsectors:
ERROR STAGE2_VBR_E_TOT
.error_fatsz:
ERROR STAGE2_VBR_E_FATSZ
.error_rootdir:
ERROR STAGE2_VBR_E_DIRENT
.error_badparttype:
ERROR STAGE2_VBR_E_PARTTYPE
; what a 'mount' should probably do at this point...
; - read VBR at partition_lba
; - validate FAT32 signatures
; - fill fat32_bpb_t and compute derived fields
; - read FSInfo (free count/next free)
; - ???
; int fat32_mount(FAT32_State_t* state, uint32_t partition_lba);
FAT32_mountfs:
__CDECL16_PROC_ENTRY
.proc:
; mount: parse BPB, derive fat0_lba, data_lba, cluster0_lba.
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; int fat32_read_fat(FAT32_State_t* state, uint32_t clus, uint32_t* out);
FAT32_read_fat:
__CDECL16_PROC_ENTRY
.proc:
; Read 32-bit FAT entry for cluster n
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; FAT32 entry is 32-bits; only low 28 bits are meaningful.
; EOC if (val & 0x0FFFFFFF) >= 0x0FFFFFF8.
; bad if == 0x0FFFFFF7.
; free if == 0x00000000.
; int fat32_next_clus(FAT32_State_t* state, uint32_t clus, uint32_t* out_next);
FAT32_next_cluster:
__CDECL16_PROC_ENTRY
.proc:
; Walk to next cluster in chain (validates EOC/bad)
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; e.g:
; uint64_t clus_to_lba(FAT32_State_t* state, uint32_t clus) {
; return v->data_lba + (uint64_t)(clus - 2) * v->secs_per_clus;
; }
FAT32_clus_to_lba:
__CDECL16_PROC_ENTRY
.proc:
; Convert cluster -> LBA of first sector of that cluster
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
; Prototyping for now...
; TODO:
; - bio_read_sectors (BIOS int13h LBA or CHS) + tiny sector cache.
; - mount: parse BPB, derive fat0_lba, data_lba, cluster0_lba.
; - FAT read: fat32_read_fat, fat32_next_clus.
; - dir iterator (SFN only) + path lookup.
; - file reader (fopen/fread) with cluster crossing.
; - contiguity probe for speed (optional).
; - (later) LFN support & code page handling.
; keep count <= 127
; int bio_read_sectors(uint64_t lba, uint16_t count, void* dst);
; will also need a bio_bios_int13h() (refactor ext_disk_read?)
; to abstract the backend 'sector getter'
; Error codes
; FS_OK 0
; FS_E_IO 1 (bio read failed)
; FS_E_FMT 2 (not FAT32 or bad BPB)
; FS_E_RANGE 3 (bad cluster or out of range)
; FS_E_END 4 (iterator end)
; FS_E_NOSUCH 5 (file/dir not found)
; FS_E_ISDIR 6 (opened dir as file)
; FS_E_TOOLONG 7 (path segment too long for 8.3)
; FS_E_UNSUPPORTED 8 (LFN present but disabled)
; FS_E_UNIMPLEMENTED 0xffff (procedure call unimplmented)
;
; Cache methods
;
; TODO: make a cache for ~4-8 disk sectors
; TODO: make a cache for FATs to avoid having to re-read the FAT
;
; // uses bio_read_sectors + cache
; int cache_read_sector(uint64_t lba, void* dst);
; int cache_invalidate_all(void);
;
; SFN directories
;
; typedef struct {
; char name83[12]; // "FILENAMEEXT" (no dot), NUL-terminated
; uint8_t attr; // ATTR_DIRECTORY=0x10, ATTR_VOLUME=0x08
; uint32_t first_clus; // (hi<<16)|lo
; uint32_t size; // bytes
; } fat32_dirent_sfn_t;
; typedef struct { // iterator
; fat32_bpb_t* v;
; uint32_t cur_clus;
; uint32_t offs_in_clus; // byte offset
; } fat32_dir_iter_t;
;int fat32_dir_iter_open_root(fat32_bpb_t* v, fat32_dir_iter_t* it);
;int fat32_dir_iter_open(fat32_bpb_t* v, uint32_t first_clus, fat32_dir_iter_t* it);
;int fat32_dir_iter_next(fat32_dir_iter_t* it, fat32_dirent_sfn_t* out); // FS_OK or FS_E_END
; typedef struct {
; uint32_t first_clus;
; uint32_t size;
; uint8_t attr; // dir or file
; } fat32_node_t;
; split by / or \.
; start at root (v->root_clus).
; for each component:
; iterate directory with dir_iter_* to find case-insensitive 8.3 match (fold to upper and space-pad during compare).
; if final component: return files cluster & size.
; if intermediate: ensure ATTR_DIRECTORY and descend.
;
; int fat32_path_lookup(fat32_bpb_t* v, const char* path, fat32_node_t* out);
; an example 'do the thing' proc to load a file to a location
;
; // loads file at path into dst, up to max_bytes; returns size read.
; int fat32_load_file(fat32_bpb_t* v, const char* path, void* dst,
; uint32_t max_bytes, uint32_t* out_size);
; streamed reads
;
; typedef struct {
; fat32_bpb_t* v;
; uint32_t first_clus;
; uint32_t cur_clus;
; uint32_t cur_clus_index; // which cluster within file were on
; uint32_t size; // total bytes
; uint32_t pos; // current byte position
; } fat32_file_t;
;
;
; int fat32_fopen(fat32_bpb_t* v, const char* path, fat32_file_t* f);
; int fat32_fread(fat32_file_t* f, void* dst, uint32_t nbytes, uint32_t* out_read);
; int fat32_fseek(fat32_file_t* f, uint32_t new_pos); // forward seeks only is fine
; int fat32_fclose(fat32_file_t* f);
; an example 'do the thing' proc to load a file to a location
;
; // loads file at path into dst, up to max_bytes; returns size read.
; int fat32_load_file(fat32_bpb_t* v, const char* path, void* dst,
; uint32_t max_bytes, uint32_t* out_size);
%endif
%define __INC_FAT32_SYS

View File

@@ -58,20 +58,18 @@
; resulting in a value which does not fit in the Number of Sectors entry at 0x13.
struc FAT32_bpb_t
.reserved_jmpboot resb 3
.ident resb 8
.bytes_per_sector_word resb 2
.sectors_per_cluster_byte resb 1
.reserved_sectors_word resb 2
.fat_count_byte resb 1
.unused1_ZERO_word resb 2 ; Root entry count field, 0 on fat32
.unused2_ZERO_word resb 2 ; total sectors size 16, 0 on fat32
.media_desc_byte resb 1
.unused3_ZERO_word resb 2 ; FAT size 16, 0 on fat32
.sectors_per_track_word resb 2
.head_count_word resb 2
.hidden_sectors_dword resb 4
.total_sectors_dword resb 4
.u16_BytesPerSector resw 1
.u8_SectorsPerCluster resb 1
.u16_ReservedSectors resw 1
.u8_FatCount resb 1
.u16_RootEntryCount16 resw 1 ; Root dir entry count field, 0 on fat32
.u16_TotalSectors16 resw 1 ; total number of sectors size, 0 on fat32
.u8_MediaDesc resb 1
.u16_FATSize16 resw 1 ; FAT size 16, 0 on fat32
.u16_SectorsPerTrack resw 1
.u16_HeadCount resw 1
.u32_HiddenSectors resd 1
.u32_TotalSectors32 resd 1
endstruc
; EBPB Information (FAT32)
@@ -91,19 +89,19 @@ endstruc
; 8 System identifier string. Always "FAT32 ". The spec says never to trust the contents of this string for any use.
struc FAT32_ebpb_t
.FATSz_dword resb 4
.extflags_word resb 2
.FSVersion_word resb 2
.root_clus_dword resb 4
.FSInfo_word resb 2
.BkBootSec_word resb 2
.reserved1 resb 12
.drive_number_byte resb 1
.nt_flags_byte resb 1
.signature_byte resb 1
.volume_id_dword resb 4
.volume_label_bytea resb 11
.system_ident_bytea resb 8
.u32_FATSize32 resd 1 ; size of *each* fat in sectors, total = FATSz * (# of FATs)
.u16_ExtFlags resw 1
.u16_FSVersion resw 1
.u32_RootDirCluster resd 1
.u16_FSInfoSector resw 1
.u16_BkBootSector resw 1
.reserved0 resb 12
.u8_DriveNumber resb 1
.u8_NtFlags resb 1
.u8_Signature resb 1
.u32_VolumeId resd 1
.volume_label resb 11
.system_ident resb 8 ; Should always be 'FAT32 '
endstruc
; ## Standard 8.3 structure ###
@@ -197,20 +195,11 @@ endstruc
; 32 bytes
struc FAT32_State_t
.first_data_sector_32 resd 1
.first_fat_sector_32 resd 1
.fat_size_32 resd 1
.curr_FAT_cluster_32 resd 1
.curr_dir_cluster_32 resd 1
.curr_drive_lba_32 resd 1
endstruc
; 16 bytes
struc FAT32_NextClusterData_t
.fat_offset resd 1
.fat_sector resd 1
.entry_offset resd 1
.reserved_1 resd 1
.u32_TotalClusters resd 1
.u32_DataAreaStartSector resd 1
.u32_FATAreaStartSector resd 1
.p16_FATBuffer resw 1
.p16_ClustBuffer resw 1
endstruc
; FAT32 Attributes

View File

@@ -23,16 +23,11 @@
; returns: none
ALIGN 4, db 0x90
InitFATDriver:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.func:
mov ax, FAT32_State_t_size
push ax ; length of fat32_state structure
xor ax, ax
push ax ; init fat32_state with zero
mov ax, fat32_state
push ax ; address of structure
call kmemset
sub sp, 0x6
__CDECL16_CALL_ARGS fat32_state, 0x0000, FAT32_State_t_size
__CDECL16_CALL kmemset, 3
.calc_active_part:
mov ax, word [partition_offset]
@@ -70,14 +65,14 @@ InitFATDriver:
mov eax, dword [bx + FAT32_ebpb_t.root_clus_dword]
mov dword [di + FAT32_State_t.curr_dir_cluster_32], eax
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
.error:
ERROR STAGE2_FAT32_INIT_CF
ALIGN 4, db 0x90
FSInfoPrinter:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.func:
;info we want to print to validate we are loading stuff from the disk correctly
; boot_drive # (i.e 0x80)
@@ -88,7 +83,7 @@ FSInfoPrinter:
; print entire FAT32 state
;
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
.error:
ERROR STAGE2_ERROR_INFOPRINTER
@@ -104,18 +99,19 @@ FSInfoPrinter:
; uint32_t SearchFATDIR(uint8_t* SFN);
ALIGN 4, db 0x90
SearchFATDIR:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.file_lookup:
print_string SearchFATDIR_info
__CDECL16_CALL_ARGS SearchFATDIR_info
__CDECL16_CALL PrintString, 1
mov bx, fat32_state
.load_first_dir:
mov eax, dword [bx + FAT32_State_t.curr_dir_cluster_32]
push dword eax ; cluster
mov ax, dir_buffer
push ax ; offset
xor ax, ax
push ax ; segment
call ReadFATCluster ; uint8_t ReadFATCluster(uint16_t seg, uint16_t offset, uint32_t cluster)
; uint8_t ReadFATCluster(uint16_t seg, uint16_t offset, uint32_t cluster)
push dword [bx + FAT32_State_t.curr_dir_cluster_32] ; cluster
push dir_buffer ; offset
push 0x0000 ; segment
call ReadFATCluster
add sp, 0x8
mov si, dir_buffer
@@ -125,9 +121,9 @@ SearchFATDIR:
; if eax >= 0x0FFFFFF8 then there are no more clusters (end of chain)
; if eax == 0x0FFFFFF7 then this is a cluster that is marked as bad
mov eax, dword [bx + FAT32_State_t.curr_dir_cluster_32]
push dword eax
call NextCluster ; uint32_t NextCluster(uint32_t active_cluster);
; uint32_t NextCluster(uint32_t active_cluster);
push dword [bx + FAT32_State_t.curr_dir_cluster_32]
call NextCluster
add sp, 0x4
cmp eax, 0x0fff_fff7
@@ -137,16 +133,14 @@ SearchFATDIR:
.load_next_dir_next_OK:
; load 512 bytes of directory entries from data sector
mov eax, [bx + FAT32_State_t.curr_dir_cluster_32]
push dword eax ; cluster
mov ax, dir_buffer
push ax ; offset
; uint8_t ReadFATCluster(uint16_t seg, uint16_t offset, uint32_t cluster)
push dword [bx + FAT32_State_t.curr_dir_cluster_32] ; cluster
push dir_buffer ; offset
push 0x0000 ; segment
call ReadFATCluster
add sp, 0x8
xor ax, ax
push ax ; segment
call ReadFATCluster ; uint8_t ReadFATCluster(uint16_t seg, uint16_t offset, uint32_t cluster)
sub sp, 0x8
.empty_dir_entry:
; check for 0x0 in first byte, if true then there are no more files
; if true we did not find the file, we should error here
@@ -176,7 +170,9 @@ SearchFATDIR:
; TODO: move this to a seperate string search function
.parse_dir:
print_string MaybeFound_Boot_info
__CDECL16_CALL_ARGS MaybeFound_Boot_info
__CDECL16_CALL PrintString, 1
.lfn_check:
; check for ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID (0x0F) in offset 11
; TODO: going to skip LFN for now, since all valid volumes will have SFN's
@@ -201,7 +197,7 @@ SearchFATDIR:
mov ax, [si + FAT32_SFN_t.cluster_16_low]
; eax == first cluster of file
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
; BUG: this function needs review
@@ -210,20 +206,15 @@ SearchFATDIR:
; if eax == 0x0FFFFFF7 then this is a cluster that is marked as bad
ALIGN 4, db 0x90
NextCluster:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.func:
print_string NextFATCluster_info
__CDECL16_CALL_ARGS NextFATCluster_info
__CDECL16_CALL PrintString, 1
mov ax, FAT32_NextClusterData_t_size
push ax ; length
xor ax, ax
push ax ; init with zero
mov ax, fat32_nc_data
push ax ; address of structure
call kmemset
sub sp, 0x6
__CDECL16_CALL_ARGS fat32_nc_data, 0x0000, FAT32_NextClusterData_t_size
__CDECL16_CALL kmemset, 3
mov edx, dword [bp + 4]
mov edx, dword [bp + 4] ; active_cluster
mov si, fat32_nc_data ; instead of push/pop and moving the data back
mov di, fat32_bpb ; load si & di then use xchg
mov bx, fat32_state
@@ -270,12 +261,13 @@ NextCluster:
; uint32_t lba,
; uint16_t count, uint16_t drive_num)
.read_cluster:
; next_cluster = fat_buffer[entry_offset]
; next_cluster = fat_buffer[entry_offset]
mov ebx, dword [si + FAT32_NextClusterData_t.entry_offset]
mov si, fat_buffer
mov eax, dword [bx+si+0]
; BUG: ???
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
.error_cfdivz:
ERROR STAGE2_FAT32_NCLUS_CFDIVZ
@@ -283,9 +275,10 @@ NextCluster:
; uint8_t ReadFATCluster(uint16_t seg, uint16_t offset, uint32_t cluster)
ALIGN 4, db 0x90
ReadFATCluster:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.func:
print_string ReadFATCluster_info
__CDECL16_CALL_ARGS ReadFATCluster_info
__CDECL16_CALL PrintString, 1
mov bx, fat32_bpb
mov si, fat32_ebpb
@@ -318,7 +311,7 @@ ReadFATCluster:
call read_disk_raw
add sp, 0xC
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
.error:
ERROR STAGE2_FAT32_CLS2LBA_CF

View File

@@ -0,0 +1,228 @@
; Copyright (C) 2025 Elaina Claus
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <https://www.gnu.org/licenses/>.
%ifndef __INC_FAT32_STRUCT
; ## FAT32 Info ##
; total_sectors = bsSectorsHuge
; fat_size = bsSectorsPerFat
;
; first_data_sector = bsResSectors + (bsFATs * bsSectPerFAT);
; total_data_sectors = bsSectorsHuge - (bsResSectors + (bsFATs * bsSectPerFAT));
; total_clusters = total_data_sectors / bsSectsPerClust
; first_fat_sector = bsResSectors
;
; ## FAT32 Table information ##
;
; fat_offset = active_cluster * 4
; fat_sector = first_fat_sector + (fat_offset / sector_size)
; entry_offset = fat_offset % sector_size
;
; table_value = fat_table[entry_offset] & 0x0FFF_FFFF
;
; ## FAT32 Directory Entries ##
; root_dir_cluster = bsRootDirCluster
; root_dir_sectors = ? (this is varible on FAT32 since the root dir is a cluster chain)
; first_sector_of_cluster = ((cluster - 2) * bsSectPerClust) + first_data_sector;
; BPB Information
; Size Description
; 8 OEM identifier.
; 2 The number of Bytes per sector (remember, all numbers are in the little-endian format).
; 1 Number of sectors per cluster.
; 2 Number of reserved sectors. The boot record sectors are included in this value.
; 1 Number of File Allocation Tables (FAT's) on the storage media. Often this value is 2.
; 2 Number of directory entries (must be set so that the root directory occupies entire sectors).
; 2 The total sectors in the logical volume.
; If this value is 0, it means there are more than 65535 sectors in the volume,
; and the actual count is stored in the Large Sector Count entry at 0x20.
;
; 1 This Byte indicates the media descriptor type.
; 2 Number of sectors per FAT. FAT12/FAT16 only.
; 2 Number of sectors per track.
; 2 Number of heads or sides on the storage media.
; 4 Number of hidden sectors. (i.e. the LBA of the beginning of the partition.)
; 4 Large sector count. This field is set if there are more than 65535 sectors in the volume,
; resulting in a value which does not fit in the Number of Sectors entry at 0x13.
struc FAT32_bpb_t
.reserved_jmpboot resb 3
.ident resb 8
.bytes_per_sector_word resb 2
.sectors_per_cluster_byte resb 1
.reserved_sectors_word resb 2
.fat_count_byte resb 1
.unused1_ZERO_word resb 2 ; Root entry count field, 0 on fat32
.unused2_ZERO_word resb 2 ; total sectors size 16, 0 on fat32
.media_desc_byte resb 1
.unused3_ZERO_word resb 2 ; FAT size 16, 0 on fat32
.sectors_per_track_word resb 2
.head_count_word resb 2
.hidden_sectors_dword resb 4
.total_sectors_dword resb 4
endstruc
; EBPB Information (FAT32)
; Size Description
; 4 Sectors per FAT. The size of the FAT in sectors.
; 2 Flags.
; 2 FAT version number. The high byte is the major version and the low byte is the minor version. FAT drivers should respect this field.
; 4 The cluster number of the root directory. Often this field is set to 2.
; 2 The sector number of the FSInfo structure.
; 2 The sector number of the backup boot sector.
; 12 Reserved. When the volume is formated these bytes should be zero.
; 1 Drive number. The values here are identical to the values returned by the BIOS interrupt 0x13. 0x00 for a floppy disk and 0x80 for hard disks.
; 1 Flags in Windows NT. Reserved otherwise.
; 1 Signature (must be 0x28 or 0x29).
; 4 Volume ID 'Serial' number. Used for tracking volumes between computers. You can ignore this if you want.
; 11 Volume label string. This field is padded with spaces.
; 8 System identifier string. Always "FAT32 ". The spec says never to trust the contents of this string for any use.
struc FAT32_ebpb_t
.FATSz_dword resb 4
.extflags_word resb 2
.FSVersion_word resb 2
.root_clus_dword resb 4
.FSInfo_word resb 2
.BkBootSec_word resb 2
.reserved1 resb 12
.drive_number_byte resb 1
.nt_flags_byte resb 1
.signature_byte resb 1
.volume_id_dword resb 4
.volume_label_bytea resb 11
.system_ident_bytea resb 8
endstruc
; ## Standard 8.3 structure ###
; Offset Length (Bytes) Description
; 0 11 8.3 file name. The first 8 characters are the name and the last 3 are the extension.
; 11 1 File Atrributes
; READ_ONLY=0x01
; HIDDEN=0x02
; SYSTEM=0x04
; VOLUME_ID=0x08
; DIRECTORY=0x10
; ARCHIVE=0x20
; LFN=READ_ONLY|HIDDEN|SYSTEM|VOLUME_ID == 0x0F
;
; 12 1 NT Reserved
; 13 1 Creation time in tenths of a second.
; 14 2 File creation time, Hour 5 bits, Minutes 6 bits, Seconds 5 Bits, multiply seconds by 2
; 16 2 File creation date, Year 7 bits, month 4 bits, day 5 bits
; 18 2 Last Accessed date. same format at creation date
; 20 2 High 16 bits of entry's first cluster
; 22 2 Last modification time. same format at creation time
; 24 2 Last modification date. same format as creation date
; 26 2 Low 16 bits of entry's first cluster
; 28 4 File size in bytes
; FSInfo
; 0 0x0 4 Lead signature (must be 0x41615252 to indicate a valid FSInfo structure)
; 4 0x4 480 Reserved, these bytes should never be used
; 484 0x1E4 4 Another signature (must be 0x61417272)
; 488 0x1E8 4 Contains the last known free cluster count on the volume. If the value is 0xFFFFFFFF, then the free count is unknown and must be computed.
; However, this value might be incorrect and should at least be range checked (<= volume cluster count)
;
; 492 0x1EC 4 Indicates the cluster number at which the filesystem driver should start looking for available clusters.
; If the value is 0xFFFFFFFF, then there is no hint and the driver should start searching at 2.
; Typically this value is set to the last allocated cluster number. As the previous field, this value should be range checked.
;
; 496 0x1F0 12 Reserved
; 508 0x1FC 4 Trail signature (0xAA550000)
struc FAT32_FSInfo_t
.head_sig_dword resd 1 ; 0x41615252
.reserved1 resb 480 ; fill zero
.body_sig_dword resd 1 ; 0x61417272
.free_count_dword resd 1
.next_free_dword resd 1
.reserved2 resb 12 ; fill zero
.tail_sig_dword resd 1 ; 0xAA550000
endstruc
struc FAT32_SFN_t
.label_83 resb 11
.attributes_byte resb 1
.nt_res_byte resb 1
.csec_byte resb 1
.ctime_word resb 2
.cdate_word resb 2
.adate_word resb 2
.cluster_16_high resb 2
.mtime_word resb 2
.mdate_word resb 2
.cluster_16_low resb 2
.size_dword resb 4
endstruc
; ## Long file name (LFN) structure format ##
;
; 0 1 The order of this entry in the sequence of long file name entries. This value helps you to know where in the file's name the characters from this entry should be placed.
; 1 10 The first 5, 2-byte characters of this entry.
; 11 1 Attribute. Always equals 0x0F. (the long file name attribute)
; 12 1 Long entry type. Zero for name entries.
; 13 1 Checksum generated of the short file name when the file was created. The short filename can change without changing the long filename in cases where the partition is mounted on a system which does not support long filenames.
; 14 12 The next 6, 2-byte characters of this entry.
; 26 2 Always zero.
; 28 4 The final 2, 2-byte characters of this entry.
;
; LFN entries are always placed immediately before their respective 8.3 entry
;
; LAST_LFN_ENTRY == 0x40
; Max of 20 in sequence == 0x14
struc FAT32_LFN_t
.order resb 1
.lfn_first5 resb 10
.attributes_8 resb 1
.zero1 resb 1
.checksum resb 1
.lfn_next6 resb 12
.zero2 resb 2
.lfn_last2 resb 4
endstruc
; 32 bytes
struc FAT32_State_t
.first_data_sector_32 resd 1
.first_fat_sector_32 resd 1
.fat_size_32 resd 1
.curr_FAT_cluster_32 resd 1
.curr_dir_cluster_32 resd 1
.curr_drive_lba_32 resd 1
endstruc
; 16 bytes
struc FAT32_NextClusterData_t
.fat_offset resd 1
.fat_sector resd 1
.entry_offset resd 1
.reserved_1 resd 1
endstruc
; FAT32 Attributes
%define FAT32_ATTR_RO 0x01
%define FAT32_ATTR_HIDDEN 0x02
%define FAT32_ATTR_SYSTEM 0x04
%define FAT32_ATTR_VOLID 0x08
%define FAT32_ATTR_DIR 0x10
%define FAT32_ATTR_ARC 0x20
; LFN == RO | HIDDEN | SYSTEM | VOLID == 0x0F
%define FAT32_ATTR_LFN 0x0F
%endif
%define __INC_FAT32_STRUCT

View File

@@ -15,6 +15,9 @@
%ifndef __INC_PART_TABLE
%define DISK_SIGNATURE_OFFSET 0x1B8
%define DISK_PARTITION_TABLE_OFFSET 0x1BE
; Partition table entry format
; Off. Size. Description
;0x00 1 Drive attributes (bit 7 set = active or bootable)
@@ -34,8 +37,6 @@ struc PartEntry_t
endstruc
struc PartTable_t
.signature resb 4
.reserved resb 2
.partition1 resb PartEntry_t_size
.partition2 resb PartEntry_t_size
.partition3 resb PartEntry_t_size

View File

@@ -0,0 +1,164 @@
%ifndef __INC_ARENA_ALLOC_FUNC
%define __ARENA_HEAP_START 0x8000
%define __ARENA_HEAP_SIZE 0x6000 ; 0x8000 -> 0xE000 = 24KiB of Heap
struc ArenaStateStruc_t
.start resw 1
.end resw 1
.mark resw 1
endstruc
; void arena_init(ArenaState *a)
;
ArenaInit:
__CDECL16_PROC_ENTRY
.proc:
mov ax, word [bp + 4] ; ptr to state structure
mov di, ax
xor ax, ax
mov word [di + ArenaStateStruc_t.mark], ax
mov word [di + ArenaStateStruc_t.end], word (__ARENA_HEAP_START + __ARENA_HEAP_SIZE)
mov word [di + ArenaStateStruc_t.start], __ARENA_HEAP_START
; zero out heap area on init
; void* kmemset_byte(void* dst, uint8_t val, uint16_t len);
; TODO: use word or qword spacing at least to speed this up
push __ARENA_HEAP_SIZE ; len
push 0x0 ; val = 0
push __ARENA_HEAP_START ; dst
call kmemset
add sp, 0x6
.endp:
__CDECL16_PROC_EXIT
ret
; size_t align_up(size_t x, size_t a)
; ax, bx. cx are all clobbered
; align x up to the nearest specified alignment (a), a should be a power of 2
; (x + (a-1)) & ~(a-1)
; return value in ax
ArenaAlignUp:
__CDECL16_PROC_ENTRY
.func:
; if a == 0 return x
mov ax, [bp + 4] ; x
mov bx, [bp + 6] ; a
test bx, bx
jz .endf
; enforce power-of-two for alignment, return x
; for example...
; alignment = 0x0010,
; 0x0010 & (0x0010 - 0x0001)
; 0x0010 & 0x000F -> 1_0000000b & 0_11111111b => 0b == zero
;
; alignment = 0x0006
; 0x0006 & (0x0006 - 0x0001)
; 0x0006 & 0x0005 -> 00000110b & 00000101b => 00000100b != 0
;
; i.e any power of 2 has only 1 bit set in binary (2 = 0010b, 4 = 0100b, 8 = 1000b and so on)
; subtracting 1 from a power of 2 'flips' lower bits ( 7 = 0111b, 15 = 1111b ...)
; so 'and'ing a power of two with (itself - 1) will result in none of the bit's being set
mov cx, bx
dec cx
test bx, cx
jnz .endf
dec bx ; a - 1
mov cx, ax
add cx, bx ; x + (a-1)
not bx ; ~(a-1)
and cx, bx ; and with the inverse
mov ax, cx ; move to ax and return
.endf:
__CDECL16_PROC_EXIT
ret
; void* arena_alloc(size_t bytes, size_t align)
; bp-2 - current used arena (i.e 'highmark')
; bp-4 - aligned_ptr
; bp-6 - new_end
ArenaAlloc:
__CDECL16_PROC_ENTRY 0x10
.proc:
; remove bytes from pool and increment mark to the new cursor location
; return a pointer to the begining of allocated segment
mov bx, early_heap_state
mov ax, word [bx + ArenaStateStruc_t.start]
mov dx, word [bx + ArenaStateStruc_t.mark]
add ax, dx ; i.e the total used arena
mov word [bp - 2], ax ; save as a local
push bx ; save heap_state pointer
push word [bp + 6] ; requested next allocation alignment
push word [bp - 2] ; current arena 'highmark'
call ArenaAlignUp
add sp, 0x4
mov word [bp - 4], ax ; save return value
pop bx ; restore heap_state pointer
; new_end = aligned_ptr + bytes
add ax, word [bp + 4] ; add to total (so current aligned mark + bytes)
mov word [bp - 6], ax ; save as local
mov dx, word [bx + ArenaStateStruc_t.end]
cmp ax, dx
ja .error ; if our heap end is < the requested throw an error, heap is full
; else update the mark to the new value & return the aligned pointer
; mark_delta = new_end - curr_used
mov dx, word [bp - 6]
sub dx, word [bp - 2]
; mark += mark_delta
mov ax, word [bx + ArenaStateStruc_t.mark]
add ax, dx
mov word [bx + ArenaStateStruc_t.mark], ax
; return aligned_ptr
mov ax, word [bp - 4]
.endp:
__CDECL16_PROC_EXIT
ret
.error:
ERROR STEVIA_DEBUG_ERR
arena_mark:
__CDECL16_PROC_ENTRY
.proc:
; return the current location of the 'cursor' in the allocator
ERROR STEVIA_DEBUG_UNIMPLEMENTED
.endp:
__CDECL16_PROC_EXIT
ret
arena_reset_to:
__CDECL16_PROC_ENTRY
.proc:
; rewind the arena to a previously marked point
ERROR STEVIA_DEBUG_UNIMPLEMENTED
.endp:
__CDECL16_PROC_EXIT
ret
arena_reset:
__CDECL16_PROC_ENTRY
.proc:
; reset the entire heap to a fresh state
ERROR STEVIA_DEBUG_UNIMPLEMENTED
.endp:
__CDECL16_PROC_EXIT
ret
%endif
%define __INC_ARENA_ALLOC_FUNC

View File

@@ -19,7 +19,7 @@
; void* kmemset_byte(void* dst, uint8_t val, uint16_t len);
ALIGN 4, db 0x90
kmemset:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.func:
mov cx, [bp + 8] ; uint16_t len
mov al, byte [bp + 6] ; uint8_t val
@@ -29,14 +29,14 @@ kmemset:
rep stosb
mov ax, di ; return pointer to dest + len (last elem of dest)
.endp:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
; uint8_t* kmemset(uint16_t* dest, uint16_t* src, uint16_t len);
; not overlap safe
ALIGN 4, db 0x90
kmemcpy:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.func:
mov cx, [bp + 8] ; len
mov si, [bp + 6] ; src
@@ -46,7 +46,7 @@ kmemcpy:
rep movsb
mov ax, di ; return pointer to dest
.endf:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
%endif

View File

@@ -20,7 +20,7 @@
; not overlap safe, only for
ALIGN 4, db 0x90
kmemcpy5:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
push ds
push es
.setup_segments:
@@ -41,7 +41,7 @@ kmemcpy5:
pop es
pop ds
.endf:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
%define __INC_KMEMCPY5_FUNC

View File

@@ -19,7 +19,7 @@
; word kmemset_byte(word segment, word dst, byte val, word len);
ALIGN 4, db 0x90
kmemset4:
__CDECL16_ENTRY
__CDECL16_PROC_ENTRY
.setup_segment:
push es
mov ax, [bp + 4]
@@ -35,7 +35,7 @@ kmemset4:
.restore_segments:
pop es
.endf:
__CDECL16_EXIT
__CDECL16_PROC_EXIT
ret
%endif

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
# Copyright (C) 2025 Elaina Claus
#
@@ -15,246 +16,161 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
if ! [ $(id -u) = 0 ]; then
echo "Script must be run as root!" >&2
exit 1
if [ $(id -u) = 0 ]; then
echo "Script should not be run as root, it could break something!" >&2
fi
# paths to bootcode
mbr_file=build/mbr.bin
vbr_file=build/vbr.bin
stage2_file=build/stage2.bin
boottest_file=build/BOOTi686.bin
mbr_file=build/mbr/mbr.bin
vbr_file=build/vbr/vbr.bin
stage2_file=build/stage2/stage2.bin
boottest_file=build/miniboot32/BOOTi686.bin
# Disk creation options
mount_point=/tmp/stevia_disk
disk_tmp_file=/tmp/disk.img
disk_file_final=./disk.img.gz
disk_img=build/disk.img
disk_img_final=build/output/disk.img.gz
part_img=build/part.img
part_img_final=build/output/part.img.gz
artifacts_archive=build/output/artifacts.tar.gz
# $disk_sector_size * $disk_size = total bytes, default is 256MiB
disk_size=(524288 * 2)
disk_sectors=(524288 * 2)
disk_sector_size=512
part_start=2048
part_sectors=$((disk_sectors - part_start))
if ! [ -e $disk_tmp_file ]; then
#
# Build sanity section
#
echo "*** Performing pre-build sanity checks ***"
# Tools needed:
# sfdisk dosfstools (mkfs.fat), mtools (mcopy,mmd), gzip, dd, truncate, awk
for t in mcopy mmd gzip dd truncate awk; do
command -v "$t" >/dev/null || { echo "Missing tool: $t" >&2; exit 1; }
done
# these are normally in the sbin paths so I've had issues detecting them with command -v
SF=$(command -v sfdisk || echo /usr/sbin/sfdisk)
MKFS=$(command -v mkfs.fat || echo /usr/sbin/mkfs.fat)
[[ -x "$SF" ]] || { echo "sfdisk not found"; exit 1; }
[[ -x "$MKFS" ]] || { echo "mkfs.fat not found"; exit 1; }
# check that required build files exist
for f in "$mbr_file" "$vbr_file" "$stage2_file" "$boottest_file"; do
[[ -f "$f" ]] || { echo "missing $f" >&2; exit 1; }
done
cat > /tmp/pt.sfdisk <<EOF
label: dos
unit: sectors
sector-size: $disk_sector_size
label-id: 0xa0b0c0d0
start=$part_start, size=$((disk_sectors - part_start)), type=c, bootable
EOF
mtool_src=/tmp/mtools.conf
cat > $mtool_src <<EOF
drive c: file="$part_img"
EOF
#
# Create disk images
#
echo "[1/7] Create disk.img and part.img"
if ! [ -e "$disk_img" ]; then
# create raw disk image
if ! dd if=/dev/zero of=$disk_tmp_file bs=$disk_sector_size count=$disk_size; then
if ! truncate -s $((disk_sectors * disk_sector_size)) "$disk_img"; then
echo "Failed creating blank disk image." >&2
exit 1
fi
sync
else
echo "Removing old disk image..."
rm -rfv $disk_tmp_file
if ! dd if=/dev/zero of=$disk_tmp_file bs=$disk_sector_size count=$disk_size; then
rm -rfv "$disk_img"
if ! truncate -s $((disk_sectors * disk_sector_size)) "$disk_img"; then
echo "Failed creating blank disk image." >&2
exit 1
fi
sync
fi
if ! [ -e "$part_img" ]; then
# create raw partition disk image
if ! truncate -s $((part_sectors * disk_sector_size)) "$part_img"; then
echo "Failed creating blank partition image." >&2
exit 1
fi
sync
else
echo "Removing old (partition) disk image..."
rm -rfv "$part_img"
if ! truncate -s $((part_sectors * disk_sector_size)) "$part_img"; then
echo "Failed creating blank partition image." >&2
exit 1
fi
sync
fi
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
if [ -e $mbr_file ] && [ -e $vbr_file ]; then
# get next loop device and mount it
ld=$(losetup -f)
losetup -P -b 512 $ld $disk_tmp_file
echo "[2/7] Write DOS partition table (single FAT32 LBA @ 2048)"
"$SF" --no-reread "$disk_img" < /tmp/pt.sfdisk
# create a DOS disk, with 1 FAT32 partition that is bootable, part1 starts at sector 2048
sfdisk $ld < scripts/loop_setup.sfdisk
# BUG: the default disk img is 256MiB which mkfs.fat wants to create
# a FAT16 FS by default. it needs to be at least 2GiB to to 'lock out'
# FAT16 as an option. Force FAT32 here, might(?) break some things.
echo "[3/7] Make FAT filesystem in partition image"
"$MKFS" -F32 -v -n 'STEVIAFS' "$part_img"
# get first partition, this is sloppy might need to review this...
firstpart=$(lsblk -ilp -o NAME $ld | tr '\n' ' ' | awk '{print $3}')
mkfs.fat -v -F32 -s 1 -n 'STEVIAFS' $firstpart
echo "[4/7] Patch VBR inside partition image (preserve BPB)"
#
# MBR setup
#
# copy MBR while preserving partition table
if ! dd if=$mbr_file of=$ld bs=1 count=440; then
echo "Failed to write MBR to disk. (part 1)" >&2
exit 1
fi
# copy MBR 0xAA55
if ! dd if=$mbr_file of=$ld bs=1 seek=510 skip=510 count=2; then
echo "Failed to write MBR to disk. (part 2)" >&2
exit 1
fi
#
# VBR Setup & backup VBR
#
# copy VBR to partition 1 while preserving partition information
# copy jmp short entry; nop
if ! dd if=$vbr_file of=$firstpart bs=1 count=3; then
echo "Failed to write VBR to disk. (part 1)" >&2
exit 1
fi
dd if=$vbr_file of="$part_img" bs=1 count=3 conv=notrunc
# copy bootcode
if ! dd if=$vbr_file of=$firstpart bs=1 seek=90 skip=90 count=420; then
echo "Failed to write VBR to disk. (part 2)" >&2
exit 1
fi
# copy 0xAA55
if ! dd if=$vbr_file of=$firstpart bs=1 seek=510 skip=510 count=2; then
echo "Failed to write VBR to disk. (part 3)" >&2
exit 1
fi
# write backup VBR
if ! dd if=$firstpart of=$firstpart bs=$disk_sector_size count=1 seek=6; then
echo "Failed to copy VBR (sector 1) to backup VBR." >&2
exit 1
fi
#
# Stage2 Setup
#
#stage2 to sectors 1-64
if ! dd if=$stage2_file of=$ld bs=$disk_sector_size seek=1; then
echo "Failed to write Stage2 to disk." >&2
exit 1
fi
# copy boot32 boot test file to disk image
if ! [ -e $mount_point ]; then
mkdir $mount_point
fi
mount $firstpart $mount_point
# ensure mountpoint is actually a mountpoint
if ! mountpoint -q $mount_point; then
echo "Failed to mount partition at $mount_point." >&2
exit 1
fi
# copy kernel to filesystem
if [ -e $boottest_file ]; then
cp -v $boottest_file $mount_point/BOOT.BIN
else
echo "Failed to write $boottest_file to disk image" >&2
exit 1
fi
#
# Final Cleanup
#
# detach loop device
umount $mount_point
sync
sleep 1
losetup -d $ld
# chown to the real user to prevent issues with reading/writing the file later
# BUG: ${logname}:$(id $(logname -g)) doesn't work right on WSL because of runlevel hacks in WSL
# BUG: https://github.com/microsoft/WSL/issues/1761
# as a work around I'll just reference LICENSE.md...WHICH SHOULD ALWAYS BE THERE 👀
chown --from=root:root --reference=LICENSE.md $disk_tmp_file
else
echo "unable to find MBR/VBR binaries!" >&2
exit 1
fi
# requires util-linux from homebrew
elif [[ "$OSTYPE" == "darwin"* ]]; then
sfdisk_path="/usr/local/opt/util-linux/sbin/sfdisk"
if [ -e $mbr_file ] && [ -e $vbr_file ]; then
# use hdiutil to attach our empty disk image
ld_path_raw=$(hdiutil attach -readwrite -imagekey diskimage-class=CRawDiskImage -nomount -blocksize 512 -noverify /tmp/disk.img)
ld=$(echo $ld_path_raw | sed s:/dev/::)
ld_path="/dev/$ld"
# create a DOS disk, with 1 FAT32 partition
if ! [ -e $sfdisk_path ]; then
echo "sfdisk utility was not found...We cannot use diskutil to make disks due to the fact that it only makes Hybrid MBR's & GPT disks...blame Apple"
exit 4
else
$sfdisk_path $ld_path < scripts/loop_setup.sfdisk
fi
# give stuff a chance to settle, macOS has problems here
sync
sleep .5
# reattch the raw image file since macOS doesn't have a partprobe...
# this is the only way I know to get macOS to reprobe the disk
hdiutil eject $ld
unset ld_path
unset ld
unset ld_path_raw
ld_path_raw=$(hdiutil attach -readwrite -imagekey diskimage-class=CRawDiskImage -nomount -blocksize 512 -noverify /tmp/disk.img)
ld=$(echo $ld_path_raw | grep "FDisk_partition_scheme" | awk '{print $1}' | sed s:/dev/::)
ld_path="/dev/$ld"
if ! [ -b /dev/$ld ]; then
echo "Unable to remount disk! exitting before I do some damage!"
exit 5
fi
# get first partition and format as FAT32
firstpart=$(diskutil list $ld | grep 1: | awk '{print $6}')
firstpart_direct="r$firstpart"
newfs_msdos -F 32 /dev/$firstpart_direct
sync
# copy MBR while preserving partition table
dd if=$mbr_file of=$ld_path bs=1 count=440 conv=sync
# copy MBR 0xAA55
dd if=$mbr_file of=$ld_path bs=1 seek=510 skip=510 count=2 conv=sync
# copy VBR to partition 1 while preserving partition information
# copy jmp short entry; nop
dd if=$vbr_file of=/dev/$firstpart bs=1 count=3 conv=sync
# copy bootcode
dd if=$vbr_file of=/dev/$firstpart bs=1 seek=90 skip=90 count=420 conv=sync
# copy 0xAA55
dd if=$vbr_file of=/dev/$firstpart bs=1 seek=510 skip=510 count=2 conv=sync
#stage2 to sectors 1-64
dd if=$stage2_file of=$ld_path bs=$disk_sector_size seek=1 conv=sync
#sync pending dd stuff
sync
# copy boot32 boot test file to disk image
if ! [ -e $mount_point ]; then
mkdir $mount_point
else
echo "$mount_point exists! clearing contents..."
rm -rfv $mount_point
mkdir $mount_point
fi
mount -t msdos /dev/$firstpart $mount_point
if [ -e $boottest_file ]; then
cp -v $boottest_file $mount_point
else
echo "unable to find boot32.bin!"
exit 3
fi
# detach loop device
sync
umount /dev/$firstpart
hdiutil eject $ld
# chown to the real user to prevent issues with reading/writing the file later
SUDOUSER=$(logname)
chown ${SUDOUSER}:staff disk.img
gzip -9kc $disk_tmp_file > $disk_file_final
else
echo "unable to find MBR/VBR binaries!"
exit 2
dd if=$vbr_file of="$part_img" bs=1 seek=90 skip=90 count=420 conv=notrunc
# copy signature (should be 0xAA55)
dd if=$vbr_file of="$part_img" bs=1 seek=510 skip=510 count=2 conv=notrunc
# copy backup VBR within the created partition image
# Linux dosfstools will complain (read: not work) unless this is done it seems
# HACK: sector 6 is the **default** location of the BPB_BkBootSec, it **can** be different.
dd if="$part_img" of="$part_img" bs=$disk_sector_size count=1 seek=6 conv=notrunc
echo "[5/7] Copy boot payload to FAT32 filesystem using mtools as BOOT.BIN"
MTOOLSRC="$mtool_src" mcopy "$boottest_file" C:BOOT.BIN
MTOOLSRC="$mtool_src" mdir C:
echo "[6/7] Patch MBR and install stage2 loader to disk image"
# patch MBR+signature while preserving partition table
dd if="$mbr_file" of="$disk_img" bs=1 count=440 conv=notrunc
dd if="$mbr_file" of="$disk_img" bs=1 seek=510 skip=510 count=2 conv=notrunc
# copy stage2 to absolute LBA 1
dd if="$stage2_file" of="$disk_img" bs=$disk_sector_size seek=1 conv=notrunc
echo "[7/7] Assembling final disk image"
# place partition at it's place in the disk image
dd if="$part_img" of="$disk_img" bs=$disk_sector_size seek=$part_start conv=notrunc
echo " *** Outputing disk images will be in ./build/output/* *** "
if [ ! -d ./build/output ]; then
echo "./build/output does not exist. creating"
mkdir -p ./build/output
fi
gzip -9c "$disk_img" > "$disk_img_final"
gzip -9c "$part_img" > "$part_img_final"
tar caf "$artifacts_archive" --exclude ./build/output ./build/
else
# Unknown.
echo "Unknown OS type! Supported build hosts systems are GNU/Linux (WSL) and macOS."
echo "Unknown OS type! Supported build hosts systems are GNU/Linux (& WSL)"
exit 1
fi

View File

@@ -1,5 +0,0 @@
label: dos
unit: sectors
sector-size: 512
start= 2048, type=c, bootable

View File

@@ -14,11 +14,11 @@
; along with this program. If not, see <https://www.gnu.org/licenses/>.
[BITS 16]
[ORG 0x8C00]
[ORG 0x6C00]
[CPU KATMAI]
[WARNING -reloc-abs-byte]
[WARNING -reloc-abs-word] ; Yes, we use absolute addresses. surpress these warnings.
[map all mbr.map]
[map all build/mbr.map]
%define __STEVIA_MBR
jmp short (init - $$)
nop
@@ -33,10 +33,10 @@ nop
%include "config.inc"
%include "error_codes.inc"
%include "partition_table.inc"
%include "fat32/fat32_structures.inc"
%undef __STEVIA_DEV_DEBUG
; BIOS will load 1st sector of boot drive to 0x7c00, init and relocate
ALIGN 4
init:
cli ; We do not want to be interrupted
@@ -58,14 +58,14 @@ init:
cld
rep stosb ; zero bss section
sub sp, 0x20 ; local varible space
sub sp, 0x10 ; local varible space (32 bytes)
push bp ; setup top of stack frame
xor cx, cx
mov ch, 0x02 ; 0x0200 in cx
mov si, 0x7C00 ; Current MBR Address (loaded here by BIOS)
mov di, MBR_ENTRY ; New MBR Address (our new relocation address)
rep movsb ; copy 512 bytes from 0x0000:7c00 to 0x0000:MBR_ENTRY (7A00 as of writing)
rep movsb ; copy 512 bytes from 0x0000:7c00 to 0x0000:MBR_ENTRY (6C00 as of writing)
sti
@@ -96,6 +96,8 @@ main:
mov ah, 0x41
mov bx, 0x55AA
mov dl, 0x80
clc
int 0x13
jnc main.find_active
ERROR MBR_ERROR_NO_INT32E ; no extended function support
@@ -107,7 +109,7 @@ main:
mov al, byte [bx + PartEntry_t.attributes]
test al, 0x80 ; 0x80 == 1000_0000b
jnz main.active_found
add bx, 0x10 ; add 16 bytes to offset
add bx, PartEntry_t_size ; add 16 bytes to offset
loop main.find_active_L0
ERROR MBR_ERROR_NO_NO_BOOT_PART
@@ -119,42 +121,26 @@ main:
mov word [bp - 4], ax ; update part_offset
.read_data:
movzx ax, byte [bp - 2]
push ax ; drive_num
mov ax, 0x1
push ax ; count
push ax ; drive_num (2)
push 0x01 ; count (2)
mov dword eax, dword [bx + PartEntry_t.lba_start]
push dword eax ; lba
push dword eax ; lba (4)
mov ax, VBR_ENTRY
push ax ; offset = 0x7c00
xor ax, ax
push ax ; segment = 0
push word 0x7C00 ; offset = 0x7c00 (2)
push 0x00 ; segment = 0 (2)
; uint8_t read_stage2_raw(uint16_t buf_segment, uint16_t buf_offset,
; uint32_t lba,
; uint16_t count, uint16_t drive_num)
call read_disk_raw
call BIOS_int13h_ext_read
add sp, 0xC
.goto_vbr:
cmp word [VBR_ENTRY + 0x1FE], 0xAA55
je main.sig_ok
ERROR MBR_ERROR_NO_VBR_SIG ; no signature present
.sig_ok:
mov ax, PartTable_t_size
push ax
mov ax, DiskSig ; start of partition table
push ax
mov ax, partition_table
push ax
call kmemcpy ; copy partition table to bss
add sp, 0x6
mov si, word [bp - 4]
mov dl, byte [bp - 2]
mov bx, partition_table
mov dl, byte [bp - 2] ; pass drive # from BIOS to VBR in dl
jmp word 0x0000:VBR_ENTRY
; ###############
@@ -183,18 +169,15 @@ PartEntry4:
times 16 db 0x00
BootSig:
dw 0xAA55 ; Add boot signature at the end of bootloader
; !!! end of MBR !!!
section .bss follows=.text
begin_bss:
align 16, resb 1
partition_table resb PartTable_t_size
align 16, resb 1
lba_packet resb LBAPkt_t_size
align 512, resb 1
stack_bottom resb 512 ; 512 byte stack early on
stack_bottom resb 1024 - 16 ; 512 byte stack early on
stack_top:
mbr_redzone resb 32
mbr_redzone resb 16
end_bss:

View File

@@ -15,8 +15,8 @@
[BITS 16]
[ORG 0x0500] ; IF YOU CHANGE ORG CHANGE THE SIGN OFFSET AT THE END
[CPU KATMAI]
[map all stage2.map]
[CPU 686]
[map all build/stage2.map]
[WARNING -reloc-abs-byte]
[WARNING -reloc-abs-word]
[WARNING -reloc-abs-dword] ; Yes, we use absolute addresses. surpress these warnings.
@@ -31,31 +31,15 @@
%include "cdecl16.inc"
%include "entry.inc"
%include "config.inc"
%include "early_mem.inc"
%include "error_codes.inc"
%macro print_string 1
mov ax, %1
push ax
call PrintString
add sp, 0x2
%endmacro
section .text
begin_text:
; dl = byte boot_drive
; ax = word part_offset (active partition offset)
; si = ptr PartTable_t partition_table
; di = ptr FAT32_bpb_t fat32_bpb
ALIGN 4, db 0x90
ALIGN 16, db 0x90
init:
cli ; We do not want to be interrupted
; these 4 are stored in the .data section and are effectivly const types
mov [vbr_part_table_ptr], si ; pointer to partition_table
mov [vbr_fat32_bpb_ptr], di ; pointer to fat32_bpb
mov [boot_drive], dl ; copy boot_drive to globals
mov [partition_offset], ax ; copy partition_offset to globals
mov [u8_BootDrive], dl ; copy boot_drive to globals
mov ax, __STAGE2_SEGMENT ; set all our segments to the configured segment, except es
mov ds, ax ; *
@@ -88,7 +72,7 @@ init:
jmp word __STAGE2_SEGMENT:main
; ###############
; Functions
; Core Functions
; ###############
%include "util/kmem_func.nasm"
@@ -96,11 +80,18 @@ init:
%include "util/kmemset4_func.nasm"
%include "util/error_func.nasm"
; ###############
; Functions
; ###############
%include "util/arena_alloc.nasm"
; ###############
; FAT32 Driver
; ###############
%include 'fat32/FAT32_SYS.inc'
%include "partition_table.inc"
%include "fat32/fat32_structures.inc"
%include 'fat32/FAT32_SYS.nasm'
; ###############
; BIOS functions
@@ -111,14 +102,10 @@ init:
; structures
struc SteviaInfoStruct_t
.MemoryMapPtr resd 1
.MemoryMapEntries resd 1
endstruc
struc EarlyBootStruct_t
.partition_table resb PartTable_t_size
.fat32_bpb resb FAT32_bpb_t_size
.fat32_ebpb resb FAT32_ebpb_t_size
.p16_E820MemoryMapPtr resw 1
.u16_E820MMapEntryCount resw 1
.p16_MbrPtr resw 1
.p16_VbrPtr resw 1
endstruc
ALIGN 4, db 0x90
@@ -130,55 +117,71 @@ main:
je main.stage2_main
ERROR STAGE2_SIGNATURE_MISSING
.stage2_main:
mov ax, PartTable_t_size
push ax ; len = PartTable_t_size
mov ax, word [vbr_part_table_ptr] ; src = ptr to vbr partition_table
push ax
mov ax, partition_table ; dst
push ax
call kmemcpy ; copy partition table data to .data section in stage2
add sp, 0x6
mov ax, (FAT32_bpb_t_size + FAT32_ebpb_t_size) ; len
push ax
mov ax, word [vbr_fat32_bpb_ptr] ; src
push ax
mov ax, fat32_bpb ; dst
push ax
call kmemcpy ; copy bpb & ebpb to memory
add sp, 0x6
call SetTextMode
call disable_cursor
print_string HelloPrompt_info
call disable_cursor_bios
; setup the early heap
__CDECL16_CALL_ARGS early_heap_state
__CDECL16_CALL ArenaInit, 1
__CDECL16_CALL_ARGS pszHelloPrompt
__CDECL16_CALL PrintString, 1
; setup heap space for mbr data
__CDECL16_CALL_ARGS 0x200, 0x10
__CDECL16_CALL ArenaAlloc, 2
mov word [SteviaInfo + SteviaInfoStruct_t.p16_MbrPtr], ax
push ax ; dst
movzx ax, byte [u8_BootDrive]
push ax ; boot_drive
__CDECL16_CALL FAT32_load_mbr, 2 ; fill/validate mbr buffer
; setup heap space for vbr data
__CDECL16_CALL_ARGS 0x200, 0x10
__CDECL16_CALL ArenaAlloc, 2
mov word [SteviaInfo + SteviaInfoStruct_t.p16_VbrPtr], ax
push ax ; dst
movzx ax, byte [u8_BootDrive]
push ax ; boot_drive
__CDECL16_CALL FAT32_load_vbr, 2 ; fill/validate vbr buffer
; enable A20 gate
call EnableA20
print_string A20_Enabled_OK_info
__CDECL16_CALL_ARGS pszA20EnabledOk
__CDECL16_CALL PrintString, 1
; get system memory map
call GetMemoryMap
print_string MemoryMap_OK_info
__CDECL16_CALL_ARGS pszMemoryMapOk
__CDECL16_CALL PrintString, 1
; enter unreal mode
; enter unreal mode (enter PM w/ 16 bit code, 32 bit flat memory model & return to real)
; ds, es will be set to the 64KiB STAGE2_SEGMENT, fs/gs will be flat/huge memory (4GiB)
; use __REFLAT macros to re-flat ds/es for easy transfers to >1MiB
; NOTE: if you modify a segment register you will need to re-unreal it
call EnterUnrealMode
print_string UnrealMode_OK_info
__CDECL16_CALL_ARGS pszUnrealModeOk
__CDECL16_CALL PrintString, 1
; FAT Driver setup
call InitFATDriver
print_string InitFATSYS_OK_info
;call InitFATDriver
;__CDECL16_CALL_ARGS pszInitFAT32Ok
;__CDECL16_CALL PrintString, 1
;
; Find first cluster of bootable file
call SearchFATDIR
push dword eax ; save first cluster of bootable file
;call SearchFATDIR
;push dword eax ; save first cluster of bootable file
print_string FileFound_OK_info
;__CDECL16_CALL_ARGS pszFileFoundMsg
;__CDECL16_CALL PrintString, 1
pop dword eax
push dword eax ; print Cluster of boot file
call PrintDWORD ; void PrintDWORD(uint32_t dword)
add sp, 0x4
;pop dword eax
;push dword eax ; print Cluster of boot file
;call PrintDWORD ; void PrintDWORD(uint32_t dword)
;add sp, 0x4
; TODO: using first cluster information, start loading the kernel to memory
; TODO: going to need an elf parser, some unreal mode file buffer functions to move the data
@@ -187,171 +190,109 @@ hcf:
; ##############################
;
; SYSTEM CONFIGURATION FUNCTIONS
; SYSTEM CONFIGURATION FUNCTIONS & MACROS
;
; ##############################
; Prints a C-Style string (null terminated) using BIOS vga teletype call
; void PrintString(char* buf)
ALIGN 4, db 0x90
PrintString:
__CDECL16_ENTRY
mov di, [bp + 4] ; first arg is char[]
.str_len:
xor cx, cx ; ECX = 0
not cx ; ECX = -1 == 0xFFFF
xor ax, ax ; search for al = 0x0
; set ds and es segments back to the base of the loader
%ifnmacro __TINY_DS_ES
%macro __TINY_DS_ES 0
mov ax, __STAGE2_SEGMENT
mov ds, ax
mov es, ax
%endmacro
%endif
cld
repne scasb ; deincrement cx while searching for al
not cx ; the inverse of a neg number = abs(x) - 1
dec cx ; CX contains the length of the string - nul byte at end
.print:
mov si, [bp + 4] ; source string
.print_L0:
movzx ax, byte [si]
push ax
call PrintCharacter
add sp, 0x2
inc si
dec cx
jcxz PrintString.endp
jmp PrintString.print_L0 ; Fetch next character from string
.endp:
__CDECL16_EXIT
ret ; Return from procedure
; Prints a single character
; void PrintCharacter(char c);
ALIGN 4, db 0x90
PrintCharacter:
__CDECL16_ENTRY
.func:
movzx ax, byte [bp + 4] ; AL = character c
mov ah, 0x0E ; INT 0x10, AH=0x0E call
mov bx, 0x0007 ; BH = page no. BL =Text attribute 0x07 is lightgrey font on black background
int 0x10 ; call video interrupt
.endp:
__CDECL16_EXIT
ret
; TODO: fix the prolog, epilog and stack usage to confirm with cdecl16
; prints the hex representation of of val
; void PrintDWORD(uint32_t val);
ALIGN 4, db 0x90
PrintDWORD:
__CDECL16_ENTRY
.func:
mov si, IntToHex_table
mov ebx, 16 ; base-16
mov eax, dword [bp + 4] ;val
xor edx, edx
xor cx, cx
.next_digit:
div ebx ; dividend in edx:eax -> quotient in eax, remainder in edx
push dx ; save remainder
inc cx
xor dx, dx
test eax, eax
jnz PrintDWORD.next_digit
.zero_pad:
cmp cx, 0x0008
je PrintDWORD.print_stack
xor ax, ax
push ax
inc cx
jmp PrintDWORD.zero_pad
.print_stack:
pop bx
dec cx
push cx
movzx ax, byte [bx+si+0] ; bx = index into Hex lookup table
push ax
call PrintCharacter
add sp, 0x2
pop cx
jcxz PrintDWORD.endp
jmp PrintDWORD.print_stack
.endp:
__CDECL16_EXIT
ret
; ##############################
;
; SYSTEM CONFIGURATION FUNCTIONS
;
; ##############################
ALIGN 4, db 0x90
EnterUnrealMode:
__CDECL16_ENTRY
.func:
; for copying between locations in high memory
%ifnmacro __REFLAT_DS_ES
%macro __REFLAT_DS_ES 0
cli ; no interrupts
push ds ; save real mode data/stack selectors
push es
push fs
push gs
push ss
push cs ; save real mode code selector
xor ax, ax ;
pop ax ; save cs to ax to setup far jump
mov word [__UNREAL_SEGMENT], ax
lgdt [((__STAGE2_SEGMENT << 4) + unreal_gdt_info)] ; load unreal gdt
lgdt [((__STAGE2_SEGMENT << 4) + UnrealGdtInfo)] ; load unreal gdt
mov eax, cr0
or al,1 ; set pmode bit
or eax, 1 ; set pmode bit
mov cr0, eax ; switch to pmode
jmp short $+2
jmp short $+2 ; i-cache flush
;jmp far 0x0008:EnterUnrealMode.load_cs
db 0xEA ; jmp far imm16:imm16
dw EnterUnrealMode.load_cs ; error_far_ptr
dw 0x0008 ; error_far_seg
.load_cs:
mov bx, 0x10 ; select descriptor 2
mov ds, bx ; 10h = 0001_0000b
mov ax, 0x10 ; select descriptor 2
mov ds, ax
mov es, ax
mov ss, bx
mov es, bx
mov fs, bx
mov gs, bx ; other data/stack to index 2 (off 0x10)
and al,0xFE ; toggle bit 1 of cr0
mov eax, cr0
and eax, ~1 ; toggle bit 1 of cr0
mov cr0, eax ; back to realmode
jmp short $+2
;jmp far 0x0008:EnterUnrealMode.unload_cs
db 0xEA ; jmp far imm16:imm16
dw EnterUnrealMode.unload_cs ; error_far_ptr
__UNREAL_SEGMENT:
dw 0x0000 ; error_far_seg
EnterUnrealMode.unload_cs:
pop ss
pop gs
pop fs
pop es
pop ds ; get back old segments
jmp short $+2 ; i-cache flush
sti
%endmacro
%endif
; for copying from low memory to high memory (ds on a real segment, es in flat mode)
%ifnmacro __REFLAT_ES
%macro __REFLAT_ES 0
cli ; no interrupts
lgdt [((__STAGE2_SEGMENT << 4) + UnrealGdtInfo)] ; load unreal gdt
mov eax, cr0
or eax, 1 ; set pmode bit
mov cr0, eax ; switch to pmode
jmp short $+2 ; i-cache flush
mov ax, 0x10 ; select descriptor 2
mov es, ax
mov eax, cr0
and eax, ~1 ; toggle bit 1 of cr0
mov cr0, eax ; back to realmode
jmp short $+2 ; i-cache flush
sti
%endmacro
%endif
ALIGN 4, db 0x90
EnterUnrealMode:
__CDECL16_PROC_ENTRY
cli ; no interrupts
.proc:
lgdt [((__STAGE2_SEGMENT << 4) + UnrealGdtInfo)] ; load unreal gdt
mov eax, cr0
or eax, 1 ; set pmode bit
mov cr0, eax ; switch to pmode
; set cs to a pm code segment (0x8) w/ the following
jmp 0x0008:EnterUnrealMode.set_segs
.set_segs:
mov ax, 0x10 ; select descriptor 2
mov ds, ax ; 10h = 0001_0000b
mov es, ax ; es to big data
mov fs, ax
mov gs, ax ; extra segments to big data as well
.pm_start:
; code here is running in protected mode w/ descriptor 0x8
; insert any PM test code needed here
.pm_end:
mov eax, cr0
and eax, ~1 ; toggle bit 1 of cr0
mov cr0, eax ; back to realmode
jmp __STAGE2_SEGMENT:EnterUnrealMode.endp
.endp:
__CDECL16_EXIT
sti ; re-enable interupts
; set ds, es to the STAGE2_SEGMENT, for our model (generally) ds == es == cs
; fs, gs & ss are all still huge data model, and the macro(s) "__REFLAT_xxx" exists
; to easily access data outside of 64KiB boundries using ds/es addressing
__TINY_DS_ES
__CDECL16_PROC_EXIT
ret
end_text:
section .data follows=.text
align 512
align 16, db 0
begin_data:
; #############
;
@@ -360,90 +301,70 @@ begin_data:
; #############
%define CRLF 0Dh, 0Ah
%macro define_cstr 2
ALIGN 16
%1_cstr:
%macro define_cstr 2-*
%if %0 > 2
align 16
%1:
db %{2:-1}, 00h
%else
align 16
%1:
db %2, 00h
%endif
%endmacro
%macro define_info 2
ALIGN 16
%1_info:
db %2, CRLF, 00h
%endmacro
define_cstr pszHelloPrompt, "Hello from Stevia Stage2!", CRLF
define_cstr pszA20EnabledOk, "A20 Enabled OK", CRLF
define_cstr pszMemoryMapOk, "Memory map OK", CRLF
define_cstr pszUnrealModeOk, "Unreal mode OK", CRLF
define_cstr pszInitFAT32Ok, "FAT32 Driver Init OK", CRLF
define_info HelloPrompt, "Hello from Stevia Stage2!"
define_info A20_Enabled_OK, "A20 Enabled OK"
define_info MemoryMap_OK, "Memory map OK"
define_info UnrealMode_OK, "Unreal mode OK"
define_info FileFound_OK, "Found SFN entry for bootable binary, first cluster -> "
define_info InitFATSYS_OK, "FAT32 Driver Init..."
define_info SearchFATDIR, "Searching FAT DIR for bootable file..."
define_info NextFATCluster, "Attempting to find next FAT cluster..."
define_info ReadFATCluster, "Attempting to load next FAT"
define_info MaybeFound_Boot, "Maybe found a file...checking..."
define_cstr pszFileFoundMsg, "Found SFN entry for bootable binary, first cluster -> ", CRLF
define_cstr pszSearchFAT32DirMsg, "Searching FAT fs for bootable file...", CRLF
define_cstr pszReadFAT32ClusterMsg, "Attempting to load next FAT", CRLF
define_cstr pszMaybeFoundBootMsg, "Maybe found a file...checking...", CRLF
define_cstr BootTarget, "BOOT BIN"
ALIGN 16, db 0
BootTarget:
db 'BOOT BIN'
;
; pre-bss init globals (generally const...but there are exceptions)
;
align 8, db 0x00
boot_drive:
; set to boot_drive passed from BIOS almost first thing in init
align 4, db 0
u8_BootDrive:
db 0x00
align 8, db 0x00
partition_offset:
dw 0x0000
align 8, db 0x00
vbr_fat32_bpb_ptr:
dw 0x0000
align 8, db 0x00
vbr_part_table_ptr:
dw 0x0000
ALIGN 16
align 16, db 0
IntToHex_table:
db '0123456789ABCDEF'
; see docs/gdt.txt for a quick refresher on GDT
ALIGN 16, db 0
unreal_gdt_info:
unreal_gdt_size: dw (unreal_gdt_end - unreal_gdt_start) - 1
unreal_gdt_ptr: dd ((__STAGE2_SEGMENT << 4) + unreal_gdt_start)
unreal_gdt_start:
align 16, db 0
UnrealGdtInfo:
u16_UnrealGdt_size: dw (UnrealGdtEnd - UnrealGdtStart) - 1
pF_UnrealGdtPtr: dd ((__STAGE2_SEGMENT << 4) + UnrealGdtStart)
UnrealGdtStart:
; entry 0 (null descriptor)
dq 0 ; first entry is null
; entry 1 (16bit code 64KiB limit)
; entry 1 (0x8) (16bit code 64KiB limit)
dd 0x0000FFFF ; Base Address(15:0) 31:16, Segment Limit(15:0) 15:0
db 0x00 ; Base Address 23:16
db 1001_1010b ; Access Byte: Present, ring0, S = 1, executable (1), non-conforming, readable, Accessed
db 0000_0000b ; Flags: GR = 4KiB, attr = <DB/L/Avl>, Granularity = 4KiB & 16:19 of limit
db 0x00 ; Base Address 31:24
; entry 2 (16bit data segment with 4 GiB flat mapping)
; entry 2 (0x10) (16bit data segment with 4 GiB flat mapping)
dd 0x0000FFFF ; Base Address(15:0) 31:16, Segment Limit(15:0) 15:0
db 0x00 ; Base Address(23:16)
db 1001_0010b ; Access Byte: Present, ring0, S = 1, data (0), non-confirming, writable, present
db 1000_1111b ; Flags: GR = 4KiB, attr = <16-bit/?/?>, Granularity = 4KiB & 16:19 of limit
db 0x00 ; Base Address(31:24)
unreal_gdt_end:
UnrealGdtEnd:
ALIGN 16, db 0
gdt32_info:
gdt32_size: dw (gdt32_end - gdt32_start) - 1
gdt32_ptr: dd ((__STAGE2_SEGMENT << 4) + gdt32_start)
gdt32_start:
dq 0
align 16, db 0
Gdt32Info:
gdt32_size: dw (Gdt32End - Gdt32Start) - 1
gdt32_ptr: dd ((__STAGE2_SEGMENT << 4) + Gdt32Start)
Gdt32Start:
dq 0 ; null segment
.gdt32_code:
dw 0xFFFF ; code segment (RX)
dw 0x0000
@@ -472,82 +393,79 @@ gdt32_start:
db 1001_0000b ; Access: readable, not writable
db 1100_1111b ; 4KB granularity, 32-bit
db 0x00
gdt32_end:
Gdt32End:
ALIGN 8,db 0x00
BUILD_NASM_VER:
align 16,db 0
BuildNasmVer:
db "Stevia Stage2 built with NASM - ", __NASM_VER__, 00h
ALIGN 8,db 0x00
BUILD_DATETIME:
align 16,db 0
BuildDateTime:
db 'Assembled - ', __DATE__, ' ', __TIME__, 00h
ALIGN 8,db 0x00
BUILD_GIT_VER:
align 16,db 0
BuildGitVer:
db __GIT_VER__, 00h
ALIGN 8,db 0x00
BUILD_GIT_HASH:
align 16,db 0
BuildGitHash:
db __GIT_HASH__, 00h
end_data:
%assign bytes_remaining ((MAX_STAGE2_BYTES - 4) - (($ - $$) + (end_text - begin_text)))
%warning STAGE2 has bytes_remaining bytes remaining for code/data (MAX: MAX_STAGE2_BYTES)
%define SIG_BYTES 0x4
; Optional: fail fast if we overflowed
%if ((MAX_STAGE2_BYTES - 512) - (($ - $$) + (end_text - begin_text))) < 0
%error "Stage2 overflow: code+data exceed MAX_STAGE2_BYTES - SIG_BYTES"
%endif
; section start location needs to be a 'critical expression'
; i.e resolvable at build time, we are setting 0x7E00 as the offset since
; i.e resolvable at build time, we are setting 0x0500 as the offset since
section .sign start=((MAX_STAGE2_BYTES - 512) + 0x0500)
times ((512 - 4) - ($ -$$) ) db 0x90 ; nop
STAGE2_SIG: dd 0xDEADBEEF ; Signature to mark the end of the stage2
; !!! END Stage2 .text/.data !!!
align 16, resb 1
section .bss follows=.sign
begin_bss:
; structures
align 8, resb 1
partition_table resb PartTable_t_size
align 8, resb 1
fat32_bpb resb FAT32_bpb_t_size
fat32_ebpb resb FAT32_ebpb_t_size
align 8, resb 1
fat32_nc_data resb 16
align 8, resb 1
lba_packet resb LBAPkt_t_size
align 8, resb 1
fat32_state:
resb FAT32_State_t_size
align 8, resb 1
align 16, resb 1
SteviaInfo:
resd 4
;
; post-bss init globals
;
resd SteviaInfoStruct_t_size
align 16, resb 1
early_heap_state:
resb ArenaStateStruc_t_size
align 16, resb 1
lba_packet:
resb LBAPkt_t_size
;
; large continuous allocations
;
align 16, resb 1
disk_buffer:
resb 512
fat_buffer:
resb 512
dir_buffer:
resb 512
fat_fsinfo:
resb 512
; TODO: this will hold 42 entries from the map function
; the e820 function needs to check that it doesn't overflow
; but realisticly 42 entries is enough for dev work
align 16, resb 1
%define BIOSMemoryMap_SIZE 2048
%define BIOSMemoryMap_SIZE 1024
BIOSMemoryMap:
resb 2048
align 512, resb 1
stack_bottom:
resb 1024
stack_top:
resb BIOSMemoryMap_SIZE
end_bss:
; !!! End bss data !!!
%define STACK_SIZE 0x1000 ; 4 KiB
; Pad to the cap (emits nothing in the file; only increases .bss virtual size).
; If BSS_SIZE > BSS_MAX_BYTES, this becomes negative and NASM errors out.
%define BSS_MAX_BYTES (0x2000 - STACK_SIZE)
resb (BSS_MAX_BYTES - (end_bss - begin_bss))
align 16, resb 1
stack_bottom:
resb STACK_SIZE
stack_top:
; Optional: keep a label for later math:
bss_cap_end:

View File

@@ -17,18 +17,25 @@
[CPU KATMAI]
[WARNING -reloc-abs-byte]
[WARNING -reloc-abs-word]
[map all vbr.map] ; Yes, we use absolute addresses. surpress these warnings.
[map all build/vbr.map] ; Yes, we use absolute addresses. surpress these warnings.
%define __STEVIA_VBR
section .text
__ENTRY:
; try to force encode a short jmp nop: i.e 0xEB 0x5A 0x90
; will jump right 0x5A (90 bytes) after the end of the ebpb
; this could also be encoded as 0xE9 0x?? 0x?? for a near jump
; if init ends up being >= 128 bytes from entry.
jmp short (init - $$)
nop
; fill BPB area with 0x00 since we skip writing this part to disk
; but we need it for the 'jmp short entry; nop' above
times 33 db 0x00
; 8 ascii bytes, "MSWIN 4.1", ""
phy_bs_ident:
times 8 db 0x00
phy_bpb_start:
times 25 db 0x00
phy_ebpb_start:
; fill eBPB area with 0x00 since we skip writing this part to disk
times 54 db 0x00
; ###############
@@ -41,7 +48,6 @@ times 54 db 0x00
%include "cdecl16.inc"
%include "entry.inc"
%include "config.inc"
%include "early_mem.inc"
%include "error_codes.inc"
%include "partition_table.inc"
%include "fat32/fat32_structures.inc"
@@ -72,7 +78,7 @@ init:
cld
rep stosb
sub sp, 0x20 ; local varible space
sub sp, 0x10 ; local varible space (32 bytes)
push bp
sti ; all done with inital setup and relocation, reenable interupts
@@ -86,10 +92,6 @@ init:
%include "util/kmem_func.nasm"
%include "util/error_func.nasm"
; ###############
; End Section
; ###############
;
; byte boot_drive @ bp - 2
; word part_offset @ bp - 4
@@ -97,31 +99,9 @@ init:
ALIGN 4, db 0x90
main:
mov byte [bp - 2], dl ; boot_drive
mov word [bp - 4], si ; part_offset
mov word [bp - 6], bx ; partition_table
.load_fs_data:
mov ax, PartTable_t_size ; count=
push ax
mov ax, [bp - 6] ; src= ptr partition_table
push ax
mov ax, partition_table ; dst=
push ax
call kmemcpy ; copy partition table data
add sp, 0x6
mov ax, (FAT32_bpb_t_size + FAT32_ebpb_t_size) ; size in byte, should be 90 bytes
push ax
mov ax, __ENTRY
push ax
mov ax, fat32_bpb ;
push ax
call kmemcpy ; copy bpb & ebpb to memory
add sp, 0x6
.check_FAT_size: ; we only support a very specific setup of FAT32
mov bx, fat32_bpb
test word [bx + FAT32_bpb_t.unused2_ZERO_word], 0 ; TotSectors16 will not be set if FAT32
mov bx, phy_bpb_start
test word [bx + FAT32_bpb_t.u16_TotalSectors16], 0 ; TotSectors16 will not be set if FAT32
jz main.load_stage2
ERROR VBR_ERROR_WRONG_FAT_SIZE
.load_stage2:
@@ -129,28 +109,21 @@ main:
movzx ax, byte [bp - 2]
push ax ; drive_num
mov ax, STAGE2_SECTOR_COUNT
push ax ; count
push STAGE2_SECTOR_COUNT ; count
mov dword eax, 0x1
push dword eax ; lba
mov ax, STAGE2_ENTRY
push ax ; offset
xor ax, ax
push ax ; segment = 0
push STAGE2_ENTRY ; offset
push 0x00 ; segment = 0
; uint8_t read_stage2_raw(uint16_t buf_segment, uint16_t buf_offset,
; uint32_t lba,
; uint16_t count, uint16_t drive_num)
call read_disk_raw
call BIOS_int13h_ext_read
add sp, 0xC
.enter_stage2:
mov dl, byte [bp - 2] ; byte boot_drive
mov ax, word [bp - 4] ; word part_offset
mov si, partition_table ; ptr partition_table
mov di, fat32_bpb ; ptr fat32_bpb
jmp word 0x0000:STAGE2_ENTRY
; ###############
@@ -166,25 +139,16 @@ times (510 - ($ - $$)) nop ; Fill the rest of sector with nop
BootSig:
dw 0xAA55 ; Add boot signature at the end of bootloader
; !!! END VBR !!!
section .bss follows=.text
begin_bss:
align 16, resb 1
partition_table resb PartTable_t_size
align 16, resb 1
fat32_bpb resb FAT32_bpb_t_size
fat32_ebpb resb FAT32_ebpb_t_size
align 16, resb 1
fat32_nc_data resb 16
align 16, resb 1
lba_packet resb LBAPkt_t_size
align 512, resb 1
stack_bottom resb 512 ; 512b stack early on
stack_bottom resb (1024 - 16) ; 512b stack early on
stack_top:
vbr_redzone resb 32
vbr_redzone resb 16
end_bss: