more refactoring

This commit is contained in:
pantonshire committed 2025-04-06 14:27:55 +01:00
1 parent 909875ec5b
commit 2219fac4e5
30 files changed
+438 -1456

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
*.o
*.bin *.bin
.pc .pc
target/ target/
-270
View File
@@ -1,270 +0,0 @@
; MEMORY LAYOUT
; R = reserved, U = usable
; --------------------------------------------------------------------
; R | 0x000000 - 0x000400: real-mode interrupt vector table
; R | 0x000400 - 0x000500: bios data area
; U | 0x000500 - 0x004000: main stack
; U | 0x004000 - 0x006a00: globals
; U | 0x006a00 - 0x007c00: memory map
; U | 0x007c00 - 0x007e00: boot sector
; U | 0x007e00 - 0x080000: conventional usable memory
; R | 0x080000 - 0x0a0000: extended bios data area (maximum possible size)
; R | 0x0a0000 - 0x0c0000: video memory
; R | 0x0c0000 - 0x0c8000: video bios
; R | 0x0c8000 - 0x0f0000: bios expansions
; R | 0x0f0000 - 0x100000: motherboard bios
%include "defines.s"
; BIOS puts our boot sector at 0000:7c00
[org BOOT0_LOADPOINT]
; We're (probably) in real mode
[bits 16]
main:
; Disable interrupts
cli
xor ax, ax
mov ds, ax
mov es, ax
; Put the stack base at 0x4000.
; Stack grows high->low, so we'll grow away from our globals and program text.
mov ss, ax
mov bp, STACK_BASE
mov sp, bp
; Segment for VGA (0xb800 * 16 = 0xb8000)
mov ax, 0xb800
mov fs, ax
; Set VGA mode
; https://mendelson.org/wpdos/videomodes.txt
mov ax, 0x0003
int 0x10
; Store boot drive number
xor dh, dh
push dx
; Get drive geometry
mov di, 0x00
mov ah, 0x08
int 0x13
jc panic
; Load sectors per track into cx & spill
and cl, 0x3f
xor ch, ch
push cx
; Load number of heads into bx & spill
movzx bx, dh
inc bx
push bx
; Load LBA 1.
mov ax, 1
mov bx, 0x7e00
call read_lba
; Check the GPT header magic "EFI PART"
mov cx, GPT_MAGIC_LEN
mov si, gpt_magic
mov di, 0x7e00
repe cmpsb
jne panic
; Ensure the 8-byte GPT starting LBA fits in 16 bits
mov di, 0x7e00 ; The rep increments di so we need to reset it
mov eax, [di + 0x4c]
mov bx, [di + 0x4a]
or ax, bx
or eax, eax
jnz panic
; Store the first 16 bits of the GPT starting LBA (we have made sure the remaining bits are 0)
push word [di + 0x48]
; Load number of partitions
mov ax, [di + 0x50]
mov bx, [di + 0x52]
or bx, bx
jz .gpt_n_partitions_loaded
; Number of partitions overflows 16 bits, so we just concern ourselves with the first 65535.
; That's an awful lot of partitions anyway.
mov ax, 0xffff
.gpt_n_partitions_loaded:
push ax
; Load GPT entry size
mov eax, [di + 0x54] ; Operand size override otherwise this is going to be painful
mov ebx, eax
; Assert that the entry size is 128 * 2^n for some integer n>=0. This is required for a valid GPT
; and has the nice properties that:
; - If each entry is larger than a sector (512 bytes), they'll be sector-aligned.
; - If each entry is smaller than a sector, an integer number of them will fit into a sector.
or eax, eax ; Test size != 0 because 128 * 2^n != 0
jz panic
test eax, 127 ; Test size is a multiple of 128
jnz panic
; Use the (n & (n - 1)) == 0 trick to test if the entry size is a power of 2. Since we already
; know it's a nonzero multiple of 128, if size is a power of 2 then size = 128 * 2^n holds.
; Therefore we don't need to bother dividing by 128 first (shr 7), which saves a couple of bytes.
mov ecx, ebx
dec ecx
and ecx, eax
jnz panic
; Find the "sector stride", which is the number of sectors we increment by each time we want to
; load a new entry.
shr eax, 9 ; Divide by sector size to get sectors per entry
cmp eax, 0xffff ; Make sure sectors per entry fits in 16 bits
ja panic
or ax, ax
jnz .gpt_sector_stride_loaded
; Sector stride must be at least one or we'll load the same sector each time!
inc ax
.gpt_sector_stride_loaded:
push ax
; Find the "byte stride", which is the number of bytes we increment by each time we want to load
; the next entry in the same sector.
cmp ebx, 512
jb .gpt_find_entries_per_sector
push word 0 ; Arbitrary byte stride since there's only one entry per sector
push word 1 ; 1 entry per sector, since an entry is larger than a sector
jmp .gpt_found_entries_per_sector
.gpt_find_entries_per_sector:
push bx ; Store byte stride = entry length in this case
xor dx, dx
mov ax, 512
div bx ; Find entries per sector
push ax
.gpt_found_entries_per_sector:
; Set up stack variables for our second stage search loop.
xor ax, ax
push ax ; Current entry
push ax ; Current entry within the current sector
push ax ; Number of sectors loaded
push word [bp - GPT_ENTRIES_START_LBA] ; Current LBA
; Search for the partition storing our second stage.
.loop_find_stage2:
mov dx, [bp - GPT_CURRENT_ENTRY_IDX]
cmp [bp - GPT_N_ENTRIES_16], dx
; Panic if we've run out of partitions and haven't found the second stage yet.
jbe panic
; If we haven't loaded any sectors yet, load the first one.
cmp word [bp - GPT_SECTORS_LOADED], 0
je .load_first_lba
; If there's still more entries in the current sector, skip loading a new sector
mov ax, [bp - GPT_SECTOR_ENTRY_IDX] ; Load current entry index within the current sector
cmp [bp - GPT_ENTRIES_PER_SECTOR], ax ; Compare to entries per sector
ja .process_current_entry
mov ax, [bp - GPT_SECTOR_STRIDE] ; Load sector stride
add word [bp - GPT_CURRENT_LBA], ax ; Increment current LBA by sector stride
mov word [bp - GPT_SECTOR_ENTRY_IDX], 0 ; Reset the current entry index within the current sector
.load_first_lba:
; Read the current LBA to 0x8000 (just past the end of the GPT header)
mov ax, [bp - GPT_CURRENT_LBA]
mov bx, 0x8000
call read_lba
; Increment number of sectors loaded
inc word [bp - GPT_SECTORS_LOADED]
.process_current_entry:
; Calculate the address of the current GPT entry.
mov ax, [bp - GPT_SECTOR_ENTRY_IDX] ; Load current entry index within current sector
xor dx, dx
mul word [bp - GPT_BYTE_STRIDE] ; Get the byte offset in the current sector of the current entry
add ax, 0x8000 ; Convert offset to address (we loaded the sector at 0x8000)
; Compare entry GUID to our stage 2 partition GUID.
mov cx, GUID_LEN
mov si, guid_stage2
mov di, ax
repe cmpsb
je .found_stage2
; Next iteration
inc word [bp - GPT_CURRENT_ENTRY_IDX] ; Increment current entry index
inc word [bp - GPT_SECTOR_ENTRY_IDX] ; Increment current entry index within the current sector
jmp .loop_find_stage2
.found_stage2:
push ax ; Address of the GPT entry for stage 2
mov si, ax
; Load partition LBA start.
mov eax, [si + 0x20]
mov ebx, [si + 0x24]
; Ensure it fits in 16 bits.
or ebx, ebx
jnz panic
cmp ebx, 0xffff
ja panic
; Load partition LBA end.
mov ecx, [si + 0x28]
mov edx, [si + 0x2c]
; Assert that the end LBA is greater than or equal to the start LBA, so we have at least one
; sector to load (end LBA is inclusive).
or edx, edx
jnz .stage2_end_lba_ok
cmp eax, ecx
ja panic
.stage2_end_lba_ok:
mov bx, BOOT1_LOADPOINT
call read_lba
jmp bx
; Load a single boot disk sector. Panic on failure.
; Inputs:
; - ax: LBA to load
; - bx: address to read sector to
; Clobber: ax, cx, dx
read_lba:
; sector - 1 = LBA % sectors_per_track
; temp = LBA / sectors_per_track
; head = temp % n_heads
; cylinder = temp / n_heads
xor dx, dx
; Divide by sectors per track. dx = mod (sector - 1), ax = div (temp)
div word [bp - SECTORS_PER_TRACK]
; Put the sector into cx (the bios call will use cl)
mov cx, dx
inc cx
xor dx, dx
; Divide by number of heads. dx = mod (head), ax = div (cylinder)
div word [bp - N_HEADS]
mov dh, dl
mov ch, al
mov dl, byte [bp - BOOT_DRIVE]
mov ah, 0x02
mov al, 1
; Read sector
int 0x13
jc panic
ret
panic:
mov ax, 0x0003
int 0x10
mov word fs:[0x0000], 0x4f21
hlt
gpt_magic db "EFI PART"
GPT_MAGIC_LEN equ $ - gpt_magic
; Our stage2 guid: fdffea69-3651-442f-a11d-88a09bf372dd
guid_stage2 db 0x69, 0xea, 0xff, 0xfd, 0x51, 0x36, 0x2f, 0x44, \
0xa1, 0x1d, 0x88, 0xa0, 0x9b, 0xf3, 0x72, 0xdd
GUID_LEN equ $ - guid_stage2
; MBR bootstrap field is 440 bytes long
%if ($ - $$) > 440
%error "exceeded mbr bootstrap field size"
%endif
-1101
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -17,6 +17,7 @@
| Reserved | 0x0c8000 - 0x0f0000 | bios expansions | | Reserved | 0x0c8000 - 0x0f0000 | bios expansions |
| Reserved | 0x0f0000 - 0x100000 | motherboard bios | | Reserved | 0x0f0000 - 0x100000 | motherboard bios |
TODO: ensure that we don't exceed 0x10000: stage 3
TODO: once we're in real mode, repurpose s2 and s3 for a stack TODO: once we're in real mode, repurpose s2 and s3 for a stack
TODO: load s4 into a separate memory region TODO: load s4 into a separate memory region
+15 -16
View File
@@ -1,25 +1,31 @@
include_flags := "-Iinclude" include_flags := "-Iinclude"
common_flags := "-werror " + include_flags common_flags := "-werror " + include_flags
ld32 := "ld -m elf_i386"
run: run:
qemu-system-x86_64 \ qemu-system-x86_64 \
-monitor stdio \ -monitor stdio \
-no-reboot \ -no-reboot \
-bios seabios/out/bios.bin \
-m 512M \ -m 512M \
-drive format=raw,file=disk.bin -drive format=raw,file=disk.bin
build: #-bios seabios/out/bios.bin
nasm -f bin {{common_flags}} -o s1.bin stages/s1/s1.s
nasm -f elf {{common_flags}} -o stages/s2/s2.o stages/s2/s2.s build: build_stage_1 build_stage_2
nasm -f elf {{common_flags}} -o stages/s3/s3.o stages/s3/s3.s
nasm -f elf {{common_flags}} -o stages/s3/a20.o stages/s3/a20.s build_stage_1:
ld.lld -T s2.ld -o s234.bin stages/s2/*.o stages/s3/*.o stages/s4/target/protected32/release/libs4.a nasm -f bin {{common_flags}} -o stage_1/stage_1.bin stage_1/main.s
build_stage_2:
nasm -f elf {{common_flags}} -o stage_2/prelude.o stage_2/prelude.s
nasm -f elf {{common_flags}} -o stage_2/main.o stage_2/main.s
nasm -f elf {{common_flags}} -o stage_2/a20.o stage_2/a20.s
{{ld32}} -m elf_i386 -T stage_2/link.ld -o stage_2/stage_2.bin stage_2/*.o
mkimg: mkimg:
dd if=/dev/zero of=disk.bin bs=440 count=1 conv=notrunc dd if=/dev/zero of=disk.bin bs=440 count=1 conv=notrunc
dd if=s1.bin of=disk.bin conv=notrunc dd if=stage_1/stage_1.bin of=disk.bin conv=notrunc
dd if=s234.bin of=disk.bin bs=512 seek=70 conv=notrunc dd if=stage_2/stage_2.bin of=disk.bin bs=512 seek=70 conv=notrunc
# build: # build:
# nasm -f bin -Iinclude -o boot0.bin boot0.s # nasm -f bin -Iinclude -o boot0.bin boot0.s
@@ -45,10 +51,3 @@ partition_disk:
parted --script disk.bin mkpart stage2 70s 900s parted --script disk.bin mkpart stage2 70s 900s
parted --script disk.bin type 6 fdffea69-3651-442f-a11d-88a09bf372dd parted --script disk.bin type 6 fdffea69-3651-442f-a11d-88a09bf372dd
# write_stage1:
# dd if=/dev/zero of=disk.bin bs=440 count=1 conv=notrunc
# dd if=boot0.bin of=disk.bin conv=notrunc
# write_stage2:
# # dd if=boot1.bin of=disk.bin bs=512 seek=70 conv=notrunc
# dd if=boot1/target/target_protected/release/boot1 of=disk.bin bs=512 seek=70 conv=notrunc
+2 -1
View File
@@ -199,8 +199,9 @@ main:
ja panic ja panic
.stage2_end_lba_ok: .stage2_end_lba_ok:
mov bx, S2_ADDR mov bx, S2_LOAD_ADDR
call read_lba call read_lba
add bx, S2_TEXT_OFFSET
jmp bx jmp bx
; Load a single boot disk sector. Panic on failure. ; Load a single boot disk sector. Panic on failure.
-2
View File
@@ -8,8 +8,6 @@
out %2, %1 out %2, %1
%endmacro %endmacro
section .s3_text
; Check whether the A20 line is enabled. Writes to the boot sector identifier. ; Check whether the A20 line is enabled. Writes to the boot sector identifier.
; Arguments: none ; Arguments: none
; Return: ; Return:
+6 -20
View File
@@ -3,25 +3,11 @@ OUTPUT_FORMAT("binary")
. = 0x8200; . = 0x8200;
SECTIONS { SECTIONS {
/* Stage 2 must come first so it's in the single sector loaded by stage 1. */ /* Prelude must come first so it's in the single sector loaded by stage 1. */
.s2_text : { .prelude : {
KEEP(*(.s2_text)) *(.prelude)
*(.s2_text)
} }
.s3_text : {
KEEP(*(.s3_text))
*(.s3_text)
}
.s3_data : {
KEEP(*(.s3_data))
*(.s3_data)
}
/* TODO: set current address for s4 loadpoint */
/* TODO: move magic & length */
.text : { .text : {
*(.text) *(.text)
*(.text.*) *(.text.*)
@@ -47,11 +33,11 @@ SECTIONS {
LONG(0x544e4150) LONG(0x544e4150)
} }
s234_magic = ADDR(.magic); s2_magic = ADDR(.magic);
/* Define a symbol for the total length of the binary, so the prelude knows how many blocks to /* Define a symbol for the total length of the binary, so the prelude knows how many blocks to
* load from disk. * load from disk.
*/ */
s234_bin_len = . - 0x8200; s2_bin_len = . - 0x8200;
s234_bin_sectors = (s234_bin_len + 511) / 512; s2_bin_sectors = (s2_bin_len + 511) / 512;
} }
+4 -6
View File
@@ -6,11 +6,8 @@
extern test_a20 extern test_a20
extern enable_a20_intel_8042 extern enable_a20_intel_8042
extern _start
section .s3_text s2_main:
s3_main:
call test_a20 call test_a20
test al, al test al, al
jnz .a20_enabled jnz .a20_enabled
@@ -66,7 +63,8 @@ s3_main:
mov ebp, REAL_STACK_BASE mov ebp, REAL_STACK_BASE
mov esp, ebp mov esp, ebp
jmp _start ;jmp _start
mov eax, 0xcafebabe
.halt: .halt:
hlt hlt
@@ -78,7 +76,7 @@ s3_main:
; hlt ; hlt
; jmp .halt ; jmp .halt
global s3_main global s2_main
section .s3_data section .s3_data
+31 -23
View File
@@ -4,13 +4,10 @@
%include "layout.s" %include "layout.s"
%include "s1_vars.s" %include "s1_vars.s"
extern s234_bin_len extern s2_bin_len
extern s234_bin_sectors extern s2_bin_sectors
extern s234_magic extern s2_magic
extern s3_main extern s2_main
extern s234_bin_len
extern s234_bin_sectors
extern s234_magic
%macro copy_stack_var_to_globals 2 %macro copy_stack_var_to_globals 2
mov %1, [bp - %2] mov %1, [bp - %2]
@@ -18,11 +15,22 @@ extern s234_magic
%endmacro %endmacro
section .s2_text section .prelude
s2_data:
.s3_bin_offset_sectors:
dw 0
.s3_bin_len_sectors:
dw 0
.padding:
times (S2_DATA_LEN - 4) db 0
; Load stages 3 and 4 into memory. %if ($ - $$) != S2_DATA_LEN
load_s234: %error "incorrect prelude data size"
%endif
; Load the rest of stage 2 into memory.
prelude:
; Now that we're not doing instruction byte golf like we were in stage 1, we can afford to move ; Now that we're not doing instruction byte golf like we were in stage 1, we can afford to move
; the various stage 1 stack variables to the globals section. ; the various stage 1 stack variables to the globals section.
copy_stack_var_to_globals ax, BOOT_DRIVE copy_stack_var_to_globals ax, BOOT_DRIVE
@@ -56,18 +64,18 @@ load_s234:
jnz panic_simple jnz panic_simple
ror eax, 16 ror eax, 16
; Calculate the s234 end LBA and panic if it overflows 16 bits. ; Calculate the s2 end LBA and panic if it overflows 16 bits.
; n.b. ebx is zero before this so both bx and ebx can be used as the s234 end LBA. ; n.b. ebx is zero before this so both bx and ebx can be used as the s2 end LBA.
mov bx, ax mov bx, ax
add bx, s234_bin_sectors add bx, s2_bin_sectors
jc panic_simple jc panic_simple
; Panic if the s234 end LBA is after the partition end LBA. ; Panic if the s2 end LBA is after the partition end LBA.
; If the upper 32 bits of the partition end LBA are nonzero, then it must be greater than our ; If the upper 32 bits of the partition end LBA are nonzero, then it must be greater than our
; 16-bit s234 end LBA. ; 16-bit s2 end LBA.
or edx, edx or edx, edx
jnz .end_lba_ok jnz .end_lba_ok
; Compare the s234 end LBA to the lower 32 bits of the partition end LBA. ; Compare the s2 end LBA to the lower 32 bits of the partition end LBA.
cmp ebx, ecx cmp ebx, ecx
ja panic_simple ja panic_simple
@@ -77,12 +85,12 @@ load_s234:
; current LBA. ; current LBA.
inc ax inc ax
push ax ; Current LBA push ax ; Current LBA
push bx ; s234 end LBA push bx ; s2 end LBA
mov ebx, S2_ADDR + 512 ; Current sector load address mov ebx, S2_LOAD_ADDR + 512 ; Current sector load address
.load_loop: .load_loop:
mov ax, [bp - 0x02] ; Load current LBA mov ax, [bp - 0x02] ; Load current LBA
cmp word [bp - 0x04], ax ; Compare to s234 end LBA cmp word [bp - 0x04], ax ; Compare to s2 end LBA
jb .load_done jb .load_done
mov ecx, ebx mov ecx, ebx
@@ -95,15 +103,15 @@ load_s234:
.load_done: .load_done:
; Check the magic bytes at the end of s234. ; Check the magic bytes at the end of s2.
push es push es
mov ebx, s234_magic mov ebx, s2_magic
call addr32_to_addr16 call addr32_to_addr16
cmp dword es:[bx], S234_MAGIC cmp dword es:[bx], S2_MAGIC
pop es pop es
jne panic_simple jne panic_simple
jmp s3_main jmp s2_main
; Converts a 32-bit address to a 16-bit sector and offset. ; Converts a 32-bit address to a 16-bit sector and offset.
File renamed without changes.
+1
View File
@@ -0,0 +1 @@
/target
+1 -1
View File
@@ -3,5 +3,5 @@
version = 4 version = 4
[[package]] [[package]]
name = "s4" name = "stage_3"
version = "0.1.0" version = "0.1.0"
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "stage_3"
version = "0.1.0"
edition = "2024"
[dependencies]
+213
View File
@@ -0,0 +1,213 @@
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();
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<PathBuf>
}
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<Item = PathBuf> + 'c
where
'a: 'c,
'b: 'c,
T: AsRef<OsStr> + ?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<process::Output, CmdError> {
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<T, F>(self, f: F) -> T
where
F: FnOnce(Self) -> T;
}
impl<U> Apply for U
where
U: Sized,
{
fn apply<T, F>(self, f: F) -> T
where
F: FnOnce(Self) -> T
{
f(self)
}
}
+25
View File
@@ -0,0 +1,25 @@
OUTPUT_FORMAT("binary")
. = 0x10000;
SECTIONS {
.text : {
*(.text)
*(.text.*)
}
.data : {
*(.data)
*(.data.*)
}
.bss : {
*(.bss)
*(.bss.*)
}
.rodata : {
*(.rodata)
*(.rodata.*)
}
}
File renamed without changes.
+8
View File
@@ -0,0 +1,8 @@
section .text
[bits 32]
; TODO: reference sysv abi spec to see what we can mangle and what we must preserve
load_sector:
ret
+1 -1
View File
@@ -19,7 +19,7 @@ fn panic(info: &PanicInfo) -> ! {
hlt() hlt()
} }
#[no_mangle] #[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {
vga::vga_init(); vga::vga_init();
File renamed without changes.
File renamed without changes.
File renamed without changes.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-14
View File
@@ -1,14 +0,0 @@
[package]
name = "s4"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib"]
[profile.release]
opt-level = "s"
debug = 0
[dependencies]
+1
View File
@@ -0,0 +1 @@
/target
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "xtask"
version = "0.1.0"
edition = "2024"
[dependencies]
+83
View File
@@ -0,0 +1,83 @@
use std::{env, error::Error, fmt, path::{Path, PathBuf}, process::ExitCode};
mod mkimg;
fn main() -> ExitCode {
match xtask() {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("{}", err);
ExitCode::FAILURE
}
}
}
fn xtask() -> Result<(), XtaskError> {
let mut args = env::args_os().skip(1);
let task = args
.next()
.ok_or_else(|| XtaskError::with_message("no task provided".to_owned()))?;
let task = task.to_str().ok_or_else(|| {
XtaskError::with_message(format!("invalid utf-8 \"{}\"", task.to_string_lossy()))
})?;
let manifest_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").ok_or_else(|| {
XtaskError::with_message("CARGO_MANIFEST_DIR not set".to_owned())
})?);
let workspace_dir = manifest_dir.parent().ok_or_else(|| {
XtaskError::with_message("invalid CARGO_MANIFEST_DIR".to_owned())
})?;
let ctx = Context {
workspace: workspace_dir,
};
match task {
"mkimg" => mkimg::mkimg_bios_gpt(ctx),
_ => Err(XtaskError::with_message(format!("unknown task \"{}\"", task))),
}
}
struct Context<'a> {
workspace: &'a Path,
}
#[derive(Debug)]
struct XtaskError {
message: String,
parent: Option<Box<dyn Error>>,
}
impl XtaskError {
fn with_message(message: String) -> Self {
Self {
message,
parent: None,
}
}
fn wrap_with_message<E>(err: E, message: String) -> Self
where
E: Error + 'static,
{
Self {
message,
parent: Some(Box::new(err)),
}
}
}
impl fmt::Display for XtaskError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if let Some(parent) = &self.parent {
write!(f, ": {}", parent)?;
}
Ok(())
}
}
impl Error for XtaskError {}
+32
View File
@@ -0,0 +1,32 @@
use crate::{Context, XtaskError};
const BLOCK_SIZE: u64 = 512;
const PARTITION_ENTRY_SIZE: u64 = 128;
const ATTR_REQUIRED: u64 = 1 >> 0;
const ATTR_NO_BLOCK_IO_PROTO: u64 = 1 >> 1;
const ATTR_LEGACY_BIOS_BOOTABLE: u64 = 1 >> 2;
// LBA 0: MBR
// LBA 1: partition header
// LBA 2..33: partition table entries
// LBA 34..n: usable blocks
// LBA -33..-2: partition table entries (dup)
// LBA -1: partition header (dup)
struct Partition {
type_guid: [u8; 16],
part_guid: [u8; 16],
lba_start: u64,
lba_end: u64,
attr: u64,
name: String,
}
pub fn mkimg_bios_gpt(ctx: Context) -> Result<(), XtaskError> {
println!("dir={}", ctx.workspace.to_string_lossy());
Ok(())
}