From bb60e1c8eddc547c8938e0066014d7d091b64881 Mon Sep 17 00:00:00 2001 From: pantonshire Date: Mon, 21 Apr 2025 10:53:34 +0100 Subject: [PATCH] load and jump to stage 3 separately from stage 2 --- include/{s1_vars.s => globals.s} | 14 +- justfile | 4 +- stage_1/main.s | 2 +- stage_2/load_s3.s | 69 ++++++++++ stage_2/main.s | 13 +- stage_2/prelude.s | 11 +- stage_3/build.rs | 213 +------------------------------ stage_3/link.ld | 5 + stage_3/src/asm/trampoline.s | 7 + stage_3/src/main.rs | 6 +- stage_3/src/x86.rs | 11 -- 11 files changed, 117 insertions(+), 238 deletions(-) rename include/{s1_vars.s => globals.s} (71%) create mode 100644 stage_2/load_s3.s create mode 100644 stage_3/src/asm/trampoline.s delete mode 100644 stage_3/src/x86.rs diff --git a/include/s1_vars.s b/include/globals.s similarity index 71% rename from include/s1_vars.s rename to include/globals.s index e9f080d..84e0202 100644 --- a/include/s1_vars.s +++ b/include/globals.s @@ -1,5 +1,5 @@ -%ifndef BOOT_S1_VARS_H -%define BOOT_S1_VARS_H +%ifndef BOOT_GLOBALS_H +%define BOOT_GLOBALS_H ; Stage 1 base stack frame variable offsets / globals ; (we use the same offsets once we copy the variables to the globals section) @@ -23,6 +23,16 @@ %define GPT_SECTOR_ENTRY_IDX 0x14 %define GPT_SECTORS_LOADED 0x16 %define GPT_CURRENT_LBA 0x18 +; 2-byte address of the partition GPT entry loaded into memory. %define STAGE_2_GPT_ENTRY_ADDR 0x1a +; Stage 2 globals +; ------------------------------------------------------------------------------------------------- +; 4-byte partition end LBA. +%define LOADER_PART_END_LBA 0x1c +; 2-byte stage 2 start LBA. +%define STAGE_2_START_LBA 0x20 +; 2-byte stage 2 start LBA (inclusive). +%define STAGE_2_END_LBA 0x22 + %endif diff --git a/justfile b/justfile index efc9e8a..a0bc69f 100644 --- a/justfile +++ b/justfile @@ -24,8 +24,8 @@ build_stage_2: mkimg: dd if=/dev/zero of=disk.bin bs=440 count=1 conv=notrunc - dd if=stage_1/stage_1.bin of=disk.bin conv=notrunc - dd if=stage_2/stage_2.bin of=disk.bin bs=512 seek=70 conv=notrunc + dd if=build/stage_1/s1.bin of=disk.bin conv=notrunc + dd if=build/s23.bin of=disk.bin bs=512 seek=70 conv=notrunc # build: # nasm -f bin -Iinclude -o boot0.bin boot0.s diff --git a/stage_1/main.s b/stage_1/main.s index 83f6596..37ec4e9 100644 --- a/stage_1/main.s +++ b/stage_1/main.s @@ -1,5 +1,5 @@ %include "layout.s" -%include "s1_vars.s" +%include "globals.s" [org S1_ADDR] [bits 16] diff --git a/stage_2/load_s3.s b/stage_2/load_s3.s new file mode 100644 index 0000000..d4507bc --- /dev/null +++ b/stage_2/load_s3.s @@ -0,0 +1,69 @@ +[bits 16] + +%include "fn.s" +%include "layout.s" +%include "globals.s" +%include "s2_fns.s" + +extern s2_data.s3_bin_offset_sectors +extern s2_data.s3_bin_len_sectors + +load_s3: + fnstart + push ebx + + ; Calculate the stage 3 start LBA. + mov ax, [REAL_GLOBALS + STAGE_2_START_LBA] + add ax, [s2_data.s3_bin_offset_sectors] + jc .fail + + ; Stage 3 should not overlap with stage 2. + cmp word [REAL_GLOBALS + STAGE_2_END_LBA], ax + jae .fail + + xor ebx, ebx + ; There must be at least one sector to load. + mov bx, [s2_data.s3_bin_len_sectors] + or bx, bx + jz .fail + + ; Calculate the end LBA (inclusive). + dec bx + add bx, ax + jc .fail + + ; Check stage 3 is entirely inside the partition. + cmp dword [REAL_GLOBALS + LOADER_PART_END_LBA], ebx + jb .fail + + push ax ; Current LBA + push bx ; s3 end LBA + mov ebx, S3_LOAD_ADDR ; Current load address + +.load_loop: + mov ax, [bp - 0x06] ; Load current LBA + cmp word [bp - 0x08], ax ; Compare to s3 end LBA + jb .load_done + + mov ecx, ebx + call read_sector + jc .fail + + add ebx, 512 + inc word [bp - 0x06] + jmp .load_loop + +.load_done: + add sp, 4 + + pop ebx + clc + fnret + +.fail: + pop ebx + stc + fnret + +global load_s3 + diff --git a/stage_2/main.s b/stage_2/main.s index 2cbfa8d..91461bb 100644 --- a/stage_2/main.s +++ b/stage_2/main.s @@ -6,6 +6,7 @@ extern test_a20 extern enable_a20_intel_8042 +extern load_s3 s2_main: call test_a20 @@ -24,6 +25,9 @@ s2_main: jmp panic_simple .a20_enabled: + call load_s3 + jc panic_simple + mov ax, 0x0003 int 0x10 @@ -63,19 +67,12 @@ s2_main: mov ebp, REAL_STACK_BASE mov esp, ebp - ;jmp _start - mov eax, 0xcafebabe + jmp S3_LOAD_ADDR .halt: hlt jmp .halt -; panic_simple_32: -; mov word [0xb8000], 0x4f21 -; .halt: -; hlt -; jmp .halt - global s2_main diff --git a/stage_2/prelude.s b/stage_2/prelude.s index 7582d82..ca57718 100644 --- a/stage_2/prelude.s +++ b/stage_2/prelude.s @@ -2,7 +2,7 @@ %include "fn.s" %include "layout.s" -%include "s1_vars.s" +%include "globals.s" extern s2_bin_len extern s2_bin_sectors @@ -25,6 +25,10 @@ s2_data: .padding: times (S2_DATA_LEN - 4) db 0xf4 +global s2_data +global s2_data.s3_bin_offset_sectors +global s2_data.s3_bin_len_sectors + %if ($ - $$) != S2_DATA_LEN %error "incorrect prelude data size" %endif @@ -84,6 +88,11 @@ prelude: cmp ebx, ecx ja panic_simple + ; Save partition / s2 extents for later. + mov [REAL_GLOBALS + LOADER_PART_END_LBA], ecx + mov [REAL_GLOBALS + STAGE_2_START_LBA], ax + mov [REAL_GLOBALS + STAGE_2_END_LBA], bx + .end_lba_ok: ; The first sector has already been loaded (we're running it right now!) so increment the diff --git a/stage_3/build.rs b/stage_3/build.rs index e81e422..cece707 100644 --- a/stage_3/build.rs +++ b/stage_3/build.rs @@ -1,213 +1,6 @@ -use std::{env, ffi::{OsStr, OsString}, fmt, fs, io, path::{Path, PathBuf}, process::{self, Command}}; - fn main() { - let out_dir = env::var_os("OUT_DIR") - .expect("OUT_DIR not set"); - let out_dir = PathBuf::from(out_dir); - - let search_path = SearchPath::load(); + const LINKER_SCRIPT_PATH: &str = "link.ld"; - let nasm_path = search_path.search("nasm") - .next() - .expect("failed to find nasm in PATH"); - let nasm = Nasm::new(nasm_path); - - build_asm(&nasm, &out_dir); - - emit_link_args(); - - rerun_if_changed("build.rs".as_ref()); -} - -fn emit_link_args() { - let linker_script_path = "link.ld"; - println!("cargo::rustc-link-arg=-T{}", linker_script_path); - rerun_if_changed(linker_script_path.as_ref()); -} - -fn rerun_if_changed(path: &Path) { - let path_str = path.to_str() - .expect("expected path to be valid utf8"); - println!("cargo::rerun-if-changed={}", path_str); -} - -fn link_obj(path: &Path) { - let path_str = path.to_str() - .expect("expected path to be valid utf8"); - println!("cargo::rustc-link-arg={}", path_str); -} - -fn build_asm(nasm: &Nasm, out_dir: &Path) { - let asm_srcs = fs::read_dir("src/asm") - .expect("failed to get asm sources"); - - for asm_src in asm_srcs { - let asm_src = asm_src.expect("failed to get asm source"); - - let ty = asm_src.file_type().expect("failed to get file type"); - if !ty.is_file() { - continue; - } - - let src_path = asm_src.path(); - - let is_asm = src_path - .extension() - .map(|ext| ext == "s") - .unwrap_or(false); - if !is_asm { - continue; - } - - let out_filename = src_path - .file_name() - .unwrap() - .apply(PathBuf::from) - .with_extension("o"); - let out_path = out_dir.join(out_filename); - - nasm.assemble( - &out_path, - &[&src_path], - &["../include".as_ref()] - ).expect("failed to assemble"); - - link_obj(&out_path); - } -} - -struct Nasm { - bin_path: PathBuf, -} - -impl Nasm { - fn new(bin_path: PathBuf) -> Self { - Self { bin_path } - } - - fn assemble(&self, output: &Path, sources: &[&Path], includes: &[&Path]) -> Result<(), CmdError> - { - for source in sources { - rerun_if_changed(source); - } - - let mut cmd = Command::new(&self.bin_path); - cmd - .arg("-Werror") - .arg("-f") - .arg("elf"); - - for include in includes { - let mut buf = OsString::new(); - buf.push("-I"); - buf.push(include); - cmd.arg(buf); - } - - cmd - .arg("-o") - .arg(output) - .args(sources); - - run_cmd(&mut cmd)?; - - Ok(()) - } -} - -struct SearchPath { - paths: Vec -} - -impl SearchPath { - fn load() -> Self { - let path_var = env::var_os("PATH").unwrap_or_default(); - let paths = env::split_paths(&path_var).collect(); - Self { paths } - } - - fn search<'a, 'b, 'c, T>(&'a self, bin: &'b T) -> impl Iterator + 'c - where - 'a: 'c, - 'b: 'c, - T: AsRef + ?Sized, - { - let bin = bin.as_ref(); - self.paths - .iter() - .filter_map(move |path| { - let path = path.join(bin); - fs::metadata(&path) - .ok() - .and_then(|meta| if meta.is_file() || meta.is_symlink() { - Some(path) - } else { - None - }) - }) - } -} - -fn run_cmd(cmd: &mut Command) -> Result { - use fmt::Write; - - cmd - .output() - .map_err(CmdErrorKind::Io) - .and_then(|out| if out.status.success() { - Ok(out) - } else { - Err(CmdErrorKind::Status(out)) - }) - .map_err(|err| { - let mut cmd_buf = String::new(); - write!(&mut cmd_buf, "{:?}", cmd).ok(); - CmdError::new(cmd_buf, err) - }) -} - -#[derive(Debug)] -struct CmdError { - cmd: String, - kind: CmdErrorKind, -} - -impl CmdError { - fn new(cmd: String, kind: CmdErrorKind) -> Self { - Self { cmd, kind } - } -} - -impl fmt::Display for CmdError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "`{}` failed: ", self.cmd)?; - match &self.kind { - CmdErrorKind::Io(err) => err.fmt(f), - CmdErrorKind::Status(out) => write!(f, "exited with status {}", out.status), - } - } -} - -#[derive(Debug)] -enum CmdErrorKind { - Io(io::Error), - Status(process::Output), -} - -trait Apply: Sized { - fn apply(self, f: F) -> T - where - F: FnOnce(Self) -> T; -} - -impl Apply for U -where - U: Sized, -{ - fn apply(self, f: F) -> T - where - F: FnOnce(Self) -> T - { - f(self) - } + println!("cargo::rerun-if-changed={}", LINKER_SCRIPT_PATH); + println!("cargo::rustc-link-arg=-T{}", LINKER_SCRIPT_PATH); } diff --git a/stage_3/link.ld b/stage_3/link.ld index a8338cb..a5a5e85 100644 --- a/stage_3/link.ld +++ b/stage_3/link.ld @@ -3,6 +3,11 @@ OUTPUT_FORMAT("binary") . = 0x10000; SECTIONS { + .trampoline : { + KEEP(*(.trampoline)) + KEEP(*(.trampoline.*)) + } + .text : { *(.text) *(.text.*) diff --git a/stage_3/src/asm/trampoline.s b/stage_3/src/asm/trampoline.s new file mode 100644 index 0000000..f139a1d --- /dev/null +++ b/stage_3/src/asm/trampoline.s @@ -0,0 +1,7 @@ +.code32 +.section ".trampoline", "ax", @progbits + +trampoline: + jmp _start + +.globl trampoline diff --git a/stage_3/src/main.rs b/stage_3/src/main.rs index 1f31a91..3924d8c 100644 --- a/stage_3/src/main.rs +++ b/stage_3/src/main.rs @@ -5,9 +5,10 @@ mod spin; mod vga; -mod x86; -use core::{arch::asm, panic::PanicInfo, fmt::Write}; +use core::{arch::{asm, global_asm}, panic::PanicInfo, fmt::Write}; + +global_asm!(include_str!("asm/trampoline.s")); #[panic_handler] fn panic(info: &PanicInfo) -> ! { @@ -36,4 +37,3 @@ fn hlt() -> ! { } } } - diff --git a/stage_3/src/x86.rs b/stage_3/src/x86.rs deleted file mode 100644 index 2c9e59f..0000000 --- a/stage_3/src/x86.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod flags { - pub const CF: u32 = 1 << 0; - pub const PF: u32 = 1 << 2; - pub const AF: u32 = 1 << 4; - pub const ZF: u32 = 1 << 6; - pub const SF: u32 = 1 << 7; - pub const TF: u32 = 1 << 8; - pub const IF: u32 = 1 << 9; - pub const DF: u32 = 1 << 10; - pub const OF: u32 = 1 << 11; -}