From 8b3a1e658adbdd44faa54508f401248f51fc98b4 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 18 Dec 2024 19:23:12 -0800 Subject: [PATCH] Added builder --- bios/iso.asm | 161 --------- bios/main.asm | 31 -- bootloader/.gitignore | 1 + bootloader/Makefile | 48 +++ bootloader/bios.mk | 30 ++ {bios => bootloader/bios}/cpuid.asm | 0 bootloader/bios/defs.asm | 4 + {bios => bootloader/bios}/gdt.asm | 0 {bios => bootloader/bios}/long_mode.asm | 0 bootloader/bios/main.asm | 70 ++++ {bios => bootloader/bios}/print.asm | 0 {bios => bootloader/bios}/protected_mode.asm | 0 {bios => bootloader/bios}/stage1.asm | 93 +++--- {bios => bootloader/bios}/stage2.asm | 7 + {bios => bootloader/bios}/thunk.asm | 0 bootloader/{ => bootloader}/Cargo.lock | 0 bootloader/{ => bootloader}/Cargo.toml | 0 .../linkers/x86-unknown-none.ld | 0 .../{ => bootloader}/rust-toolchain.toml | 0 bootloader/{ => bootloader}/src/arch/mod.rs | 0 .../{ => bootloader}/src/arch/x86/mod.rs | 0 .../{ => bootloader}/src/arch/x86/x32.rs | 0 .../{ => bootloader}/src/arch/x86/x64.rs | 0 bootloader/{ => bootloader}/src/logger.rs | 0 bootloader/{ => bootloader}/src/main.rs | 0 .../{ => bootloader}/src/os/bios/disk.rs | 0 .../{ => bootloader}/src/os/bios/macros.rs | 0 .../src/os/bios/memory_map.rs | 0 .../{ => bootloader}/src/os/bios/mod.rs | 0 .../{ => bootloader}/src/os/bios/panic.rs | 0 .../{ => bootloader}/src/os/bios/serial.rs | 0 .../{ => bootloader}/src/os/bios/thunk.rs | 0 .../{ => bootloader}/src/os/bios/vbe.rs | 0 .../{ => bootloader}/src/os/bios/vga.rs | 0 bootloader/{ => bootloader}/src/os/mod.rs | 0 .../{ => bootloader}/src/serial_16550.rs | 0 .../targets/x86-unknown-none.json | 0 builder/.editorconfig | 9 + builder/.gitignore | 1 + builder/Cargo.lock | 316 ++++++++++++++++++ builder/Cargo.toml | 58 ++++ builder/rustfmt.toml | 1 + builder/src/cmd/disk.rs | 228 +++++++++++++ builder/src/cmd/mod.rs | 25 ++ builder/src/gpt.rs | 272 +++++++++++++++ builder/src/main.rs | 49 +++ builder/src/util.rs | 45 +++ make/bios | 57 ---- 48 files changed, 1214 insertions(+), 292 deletions(-) delete mode 100644 bios/iso.asm delete mode 100644 bios/main.asm create mode 100644 bootloader/.gitignore create mode 100644 bootloader/Makefile create mode 100644 bootloader/bios.mk rename {bios => bootloader/bios}/cpuid.asm (100%) create mode 100644 bootloader/bios/defs.asm rename {bios => bootloader/bios}/gdt.asm (100%) rename {bios => bootloader/bios}/long_mode.asm (100%) create mode 100644 bootloader/bios/main.asm rename {bios => bootloader/bios}/print.asm (100%) rename {bios => bootloader/bios}/protected_mode.asm (100%) rename {bios => bootloader/bios}/stage1.asm (88%) rename {bios => bootloader/bios}/stage2.asm (96%) rename {bios => bootloader/bios}/thunk.asm (100%) rename bootloader/{ => bootloader}/Cargo.lock (100%) rename bootloader/{ => bootloader}/Cargo.toml (100%) rename bootloader/{ => bootloader}/linkers/x86-unknown-none.ld (100%) rename bootloader/{ => bootloader}/rust-toolchain.toml (100%) rename bootloader/{ => bootloader}/src/arch/mod.rs (100%) rename bootloader/{ => bootloader}/src/arch/x86/mod.rs (100%) rename bootloader/{ => bootloader}/src/arch/x86/x32.rs (100%) rename bootloader/{ => bootloader}/src/arch/x86/x64.rs (100%) rename bootloader/{ => bootloader}/src/logger.rs (100%) rename bootloader/{ => bootloader}/src/main.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/disk.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/macros.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/memory_map.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/mod.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/panic.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/serial.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/thunk.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/vbe.rs (100%) rename bootloader/{ => bootloader}/src/os/bios/vga.rs (100%) rename bootloader/{ => bootloader}/src/os/mod.rs (100%) rename bootloader/{ => bootloader}/src/serial_16550.rs (100%) rename bootloader/{ => bootloader}/targets/x86-unknown-none.json (100%) create mode 100644 builder/.editorconfig create mode 100644 builder/.gitignore create mode 100644 builder/Cargo.lock create mode 100644 builder/Cargo.toml create mode 100644 builder/rustfmt.toml create mode 100644 builder/src/cmd/disk.rs create mode 100644 builder/src/cmd/mod.rs create mode 100644 builder/src/gpt.rs create mode 100644 builder/src/main.rs create mode 100644 builder/src/util.rs delete mode 100644 make/bios diff --git a/bios/iso.asm b/bios/iso.asm deleted file mode 100644 index d8fe2a8..0000000 --- a/bios/iso.asm +++ /dev/null @@ -1,161 +0,0 @@ -; Simple ISO emulation with el torito - -; Fill until CD sector 0x10 -times (0x10*2048)-($-$$) db 0 - -; Volume record -;TODO: fill in more fields -iso_volume_record: -db 1 ; Type volume record -db "CD001" ; Identifier -db 1 ; Version -db 0 ; Unused -times 32 db ' ' ; System identifier -.volume_id: ; Volume identifier -db 'Redox OS' -times 32-($-.volume_id) db ' ' -times 8 db 0 ; Unused -db 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 ; Volume space size (0x15) -times 32 db 0 ; Unused -db 0x01, 0x00, 0x00, 0x01 ; Volume set size -db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number -db 0x00, 0x08, 0x08, 0x00 ; Logical block size in little and big endian - -times 156-($-iso_volume_record) db 0 - -; Root directory entry -.root_directory: -db 0x22 ; Length of entry -db 0x00 ; Length of extended attributes -db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14) -db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent -db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time -db 0x02 ; File flags -db 0x00 ; Interleaved file unit size -db 0x00 ; Interleaved gap size -db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number -db 0x01 ; Length of file identifier -db 0x00 ; File identifier - -times 128 db ' ' ; Volume set identifier -times 128 db ' ' ; Publisher identifier -times 128 db ' ' ; Data preparer identifier -times 128 db ' ' ; Application identifier -times 37 db ' ' ; Copyright file ID -times 37 db ' ' ; Abstract file ID -times 37 db ' ' ; Bibliographic file ID - -times 881-($-iso_volume_record) db 0 - -db 1 ; File structure version - -; Fill until CD sector 0x11 -times (0x11*2048)-($-$$) db 0 - -; Boot record -iso_boot_record: -db 0 ; Type boot record -db "CD001" ; Identifier -db 1 ; Version -db "EL TORITO SPECIFICATION" ; Boot system identifier -times 0x47-($ - iso_boot_record) db 0 ; Padding -dd 0x13 ; Sector of boot catalog - -; Fill until CD sector 0x12 -times (0x12*2048)-($-$$) db 0 - -; Terminator -iso_terminator: -db 0xFF ; Type terminator -db "CD001" ; Identifier -db 1 ; Version - -; Fill until CD sector 0x13 -times (0x13*2048)-($-$$) db 0 - -; Boot catalog -iso_boot_catalog: - -; Validation entry -.validation: -db 1 ; Header ID -db 0 ; Platform ID (x86) -dw 0 ; Reserved -times 24 db 0 ; ID string -dw 0x55aa ; Checksum -dw 0xaa55 ; Key - -; Default entry -.default: -db 0x88 ; Bootable -db 4 ; Hard drive emulation -dw 0 ; Load segment (0 is platform default) -db 0xEE ; Partition type (0xEE is protective MBR) -db 0 ; Unused -dw 1 ; Sector count -dd 0 ; Start address for virtual disk -times 20 db 0 ; Padding - -; EFI section header entry -.efi_section_header: -db 0x91 ; Final header -db 0xEF ; Platform ID (EFI) -dw 1 ; Number of section header entries -times 28 db 0 ; ID string - -; EFI section entry -.efi_section_entry: -db 0x88 ; Bootable -db 0 ; No emulation -dw 0 ; Load segment (0 is platform default) -db 0 ; Partition type (not used) -db 0 ; Unused -dw 512 ; Sector count (1 MiB = 512 CD sectors) -dd 512 ; Start address for virtual disk (1 MiB = 512 CD sectors) -times 20 db 0 ; Padding - -; Fill until CD sector 0x14 -times (0x14*2048)-($-$$) db 0 - -iso_root_directory: -.self: -db 0x22 ; Length of entry -db 0x00 ; Length of extended attributes -db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14) -db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent -db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time -db 0x02 ; File flags -db 0x00 ; Interleaved file unit size -db 0x00 ; Interleaved gap size -db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number -db 0x01 ; Length of file identifier -db 0x00 ; File identifier - -.parent: -db 0x22 ; Length of entry -db 0x00 ; Length of extended attributes -db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14) -db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent -db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time -db 0x02 ; File flags -db 0x00 ; Interleaved file unit size -db 0x00 ; Interleaved gap size -db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number -db 0x01 ; Length of file identifier -db 0x01 ; File identifier - -.boot_cat: -db 0x2C ; Length of entry -db 0x00 ; Length of extended attributes -db 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13 ; Location of extent (0x13) -db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent -db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time -db 0x00 ; File flags -db 0x00 ; Interleaved file unit size -db 0x00 ; Interleaved gap size -db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number -db 0x0A ; Length of file identifier -db "BOOT.CAT;1",0 ; File identifier - -; Fill until CD sector 0x15 -times (0x15*2048)-($-$$) db 0 diff --git a/bios/main.asm b/bios/main.asm deleted file mode 100644 index 3911cd0..0000000 --- a/bios/main.asm +++ /dev/null @@ -1,31 +0,0 @@ -sectalign off - -; stage 1 is sector 0, loaded at 0x7C00 -%include "stage1.asm" - -; GPT area from sector 1 to 33, loaded at 0x7E00 -times (33*512) db 0 - -; stage 2, loaded at 0xC000 -stage2: - %include "stage2.asm" - align 512, db 0 -stage2.end: - -; the maximum size of stage2 is 4 KiB -times (4*1024)-($-stage2) db 0 - -; ISO compatibility, uses up space until 0x12400 -%include "iso.asm" - -times 3072 db 0 ; Pad to 0x13000 - -; stage3, loaded at 0x13000 -stage3: - %defstr STAGE3_STR %[STAGE3] - incbin STAGE3_STR - align 512, db 0 -.end: - -; the maximum size of the boot loader portion is 384 KiB -times (384*1024)-($-$$) db 0 diff --git a/bootloader/.gitignore b/bootloader/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/bootloader/.gitignore @@ -0,0 +1 @@ +build diff --git a/bootloader/Makefile b/bootloader/Makefile new file mode 100644 index 0000000..e3e610e --- /dev/null +++ b/bootloader/Makefile @@ -0,0 +1,48 @@ +# Build script for BIOS (legacy) boot. +# +# This compiles our bootloader as a static library, +# and wraps it in a multistage loader. + + +BUILD = ./build +BIOS_BUILD = $(BUILD)/bios + +STAGE2_SECTOR=34 + +.PHONY: all +all: $(BIOS_BUILD)/bios.mbr.bin $(BIOS_BUILD)/bios.stage2.bin + +.PHONY: clean +clean: + rm -drf $(BUILD) + +include bios.mk + +# Compile bootloader as library +LIB_SRC = ./bootloader/Cargo.toml ./bootloader/Cargo.lock $(shell find ./bootloader/src -type f) +$(BUILD)/bios.lib: $(LIB_SRC) + @mkdir -p $(BUILD) + env RUSTFLAGS="-C soft-float" \ + cargo rustc \ + --manifest-path="./bootloader/Cargo.toml" \ + -Z build-std=core,alloc \ + -Z build-std-features=compiler-builtins-mem \ + --target "./bootloader/targets/x86-unknown-none.json" \ + --lib \ + --release \ + -- \ + --emit link="$(CURDIR)/$@" + +# Link bootloader +BIOS_LD = ./bootloader/linkers/x86-unknown-none.ld +$(BUILD)/bios.elf: $(BUILD)/bios.lib $(BIOS_LD) + ld \ + -m elf_i386 \ + --gc-sections \ + -z max-page-size=0x1000 \ + -T "$(BIOS_LD)" \ + -o "$@" \ + "$<" + + objcopy --only-keep-debug "$@" "$@.sym" + objcopy --strip-debug "$@" diff --git a/bootloader/bios.mk b/bootloader/bios.mk new file mode 100644 index 0000000..1b927f9 --- /dev/null +++ b/bootloader/bios.mk @@ -0,0 +1,30 @@ +# Expects variables: +# STAGE3: path to linked stage 3 binary +# STAGE2_SECTOR: the index of the first sector +# of the stage 2 binary on the disk it will be written to. + +BIOS_SRC = ./bios + +# Wrap bootloader in three-stage BIOS loader +$(BIOS_BUILD)/bios.bin: $(wildcard $(BIOS_SRC)/*.asm) $(BUILD)/bios.elf + @mkdir -p "$(BIOS_BUILD)" + nasm \ + -f bin \ + -D STAGE3="$(BUILD)/bios.elf" \ + -D STAGE2_SECTOR=$(STAGE2_SECTOR) \ + -o "$@" \ + -l "$@.lst" \ + -i "$(BIOS_SRC)" \ + "$(BIOS_SRC)/main.asm" + +# Extract MBR (first 440 bytes) +$(BIOS_BUILD)/bios.mbr.bin: $(BIOS_BUILD)/bios.bin + @mkdir -p "$(BIOS_BUILD)" + @echo "" + dd if="$(BIOS_BUILD)/bios.bin" bs=440 count=1 of="$@" + +# Extract stage 2 (rest of file) +$(BIOS_BUILD)/bios.stage2.bin: $(BIOS_BUILD)/bios.bin + @mkdir -p "$(BIOS_BUILD)" + @echo "" + dd if="$(BIOS_BUILD)/bios.bin" bs=512 skip=1 of="$@" diff --git a/bios/cpuid.asm b/bootloader/bios/cpuid.asm similarity index 100% rename from bios/cpuid.asm rename to bootloader/bios/cpuid.asm diff --git a/bootloader/bios/defs.asm b/bootloader/bios/defs.asm new file mode 100644 index 0000000..404399b --- /dev/null +++ b/bootloader/bios/defs.asm @@ -0,0 +1,4 @@ +; sector = 512 bytes + +; first sector of stage 2, on disk. +%assign PARAM_STAGE2_SECTOR 34 diff --git a/bios/gdt.asm b/bootloader/bios/gdt.asm similarity index 100% rename from bios/gdt.asm rename to bootloader/bios/gdt.asm diff --git a/bios/long_mode.asm b/bootloader/bios/long_mode.asm similarity index 100% rename from bios/long_mode.asm rename to bootloader/bios/long_mode.asm diff --git a/bootloader/bios/main.asm b/bootloader/bios/main.asm new file mode 100644 index 0000000..d559cde --- /dev/null +++ b/bootloader/bios/main.asm @@ -0,0 +1,70 @@ +sectalign off + +; This program expects two external macros: +; STAGE3, a path to the stage3 binary +; STAGE2_SECTOR, the location of stage 2 +; on the disk, in 512-byte sectors. +; On a gpt disk, this is probably 34. + +; Stage 1 is MBR code, and should fit in LBA 0 +; (512 bytes). Layout is as follows: +; (Format is `offset, length: purpose`) +; 0, 424: x86 boot code +; 440, 4: Unique disk signature +; 444, 2: unknown +; 446, 16*4: Array of four legacy MBR records +; 510, 2: signature 0x55 0xAA +; 512 to end of logical block: reserved +; +; See https://uefi.org/specs/UEFI/2.10/05_GUID_Partition_Table_Format.html + +ORG 0x7C00 +SECTION .text + +; stage 1 is sector 0, loaded into memory at 0x7C00 +%include "stage1.asm" + +; Stage 1 is at most 440 bytes +times 440-($-$$) db 0 +db 0xee + +; Pad until 512 +times 510-($-$$) db 0 + +; MBR signature. +; This isn't loaded into memory, it's +; only here for debugging. +db 0x55 +db 0xaa + +; LEGACY +; GPT area from sector 1 to 33, loaded at 0x7E00 +;times (33*512) db 0 + +stage2: + %include "stage2.asm" + align 512, db 0 +stage2.end: + +; TODO: why? Stage 1 read limit? +; Can we make this smaller? +; The maximum size of stage2 is 4 KiB, +; This fill will throw an error if the subtraction +; is negative. +times (4*1024)-($-stage2) db 0 + +; LEGACY +; Pad to 0x13000 +; This needs to match the value configured +; in the stage3 linker script +times (0x13000 - 0x7c00)-($-$$) db 0 + +stage3: + %defstr STAGE3_STR %[STAGE3] + incbin STAGE3_STR + align 512, db 0 +.end: + +; TODO: why? Of the disk, or of memory? +; the maximum size of the boot loader portion is 384 KiB +times (384*1024)-($-$$) db 0 diff --git a/bios/print.asm b/bootloader/bios/print.asm similarity index 100% rename from bios/print.asm rename to bootloader/bios/print.asm diff --git a/bios/protected_mode.asm b/bootloader/bios/protected_mode.asm similarity index 100% rename from bios/protected_mode.asm rename to bootloader/bios/protected_mode.asm diff --git a/bios/stage1.asm b/bootloader/bios/stage1.asm similarity index 88% rename from bios/stage1.asm rename to bootloader/bios/stage1.asm index d2175e1..bef127e 100644 --- a/bios/stage1.asm +++ b/bootloader/bios/stage1.asm @@ -1,5 +1,3 @@ -ORG 0x7C00 -SECTION .text USE16 stage1: ; dl comes with disk @@ -18,7 +16,6 @@ stage1: ; dl comes with disk retf .set_cs: - ; save disk number mov [disk], dl @@ -48,8 +45,19 @@ stage1: ; dl comes with disk and cl, 0x3f mov [chs.s], cl - mov eax, (stage2 - stage1) / 512 + ; PARAM stage1 0x7C00 + ; PARAM stage2 0xC000 + ; PARAM stage3.end 0x4b600 + + ; disk address of stage 2 + ; (start sector) + mov eax, STAGE2_SECTOR + + ; where to load stage 2 mov bx, stage2 + + ; length of stage2 + stage3 + ; (on disk, in sectors) mov cx, (stage3.end - stage2) / 512 mov dx, 0 call load @@ -92,7 +100,40 @@ load: mov [DAPACK.count], cx mov [DAPACK.seg], dx - call print_dapack +; This should be a subroutine, +; but we don't call/ret to save a few bytes. +; (we only use this once) +; +;call print_dapack +;print_dapack: + mov bx, [DAPACK.addr + 2] + call print_hex + + mov bx, [DAPACK.addr] + call print_hex + + mov al, '#' + call print_char + + mov bx, [DAPACK.count] + call print_hex + + mov al, ' ' + call print_char + + mov bx, [DAPACK.seg] + call print_hex + + mov al, ':' + call print_char + + mov bx, [DAPACK.buf] + call print_hex + + call print_line + ;ret + ; End of print_dapack + cmp byte [chs.s], 0 jne .chs @@ -146,35 +187,6 @@ load: jc error ; carry flag set on error ret -print_dapack: - mov bx, [DAPACK.addr + 2] - call print_hex - - mov bx, [DAPACK.addr] - call print_hex - - mov al, '#' - call print_char - - mov bx, [DAPACK.count] - call print_hex - - mov al, ' ' - call print_char - - mov bx, [DAPACK.seg] - call print_hex - - mov al, ':' - call print_char - - mov bx, [DAPACK.buf] - call print_hex - - call print_line - - ret - error_chs: mov ah, 0 @@ -185,12 +197,10 @@ error: mov bl, ah call print_hex - mov al, ' ' - call print_char - - mov si, error_msg + mov si, stage1_error_msg call print call print_line + .halt: cli hlt @@ -199,7 +209,7 @@ error: %include "print.asm" stage_msg: db "Stage ",0 -error_msg: db "ERROR",0 +stage1_error_msg: db " ERROR",0 disk: db 0 @@ -216,7 +226,4 @@ DAPACK: .seg: dw 0 ; in memory page zero .addr: dq 0 ; put the lba to read in this spot -times 446-($-$$) db 0 -partitions: times 4 * 16 db 0 -db 0x55 -db 0xaa +db 0xff \ No newline at end of file diff --git a/bios/stage2.asm b/bootloader/bios/stage2.asm similarity index 96% rename from bios/stage2.asm rename to bootloader/bios/stage2.asm index 1ef0344..bd421c7 100644 --- a/bios/stage2.asm +++ b/bootloader/bios/stage2.asm @@ -2,6 +2,13 @@ SECTION .text USE16 stage2.entry: + mov si, stage_msg + call print + mov al, '3' + call print_char + call print_line + + ; check for required features call cpuid_check diff --git a/bios/thunk.asm b/bootloader/bios/thunk.asm similarity index 100% rename from bios/thunk.asm rename to bootloader/bios/thunk.asm diff --git a/bootloader/Cargo.lock b/bootloader/bootloader/Cargo.lock similarity index 100% rename from bootloader/Cargo.lock rename to bootloader/bootloader/Cargo.lock diff --git a/bootloader/Cargo.toml b/bootloader/bootloader/Cargo.toml similarity index 100% rename from bootloader/Cargo.toml rename to bootloader/bootloader/Cargo.toml diff --git a/bootloader/linkers/x86-unknown-none.ld b/bootloader/bootloader/linkers/x86-unknown-none.ld similarity index 100% rename from bootloader/linkers/x86-unknown-none.ld rename to bootloader/bootloader/linkers/x86-unknown-none.ld diff --git a/bootloader/rust-toolchain.toml b/bootloader/bootloader/rust-toolchain.toml similarity index 100% rename from bootloader/rust-toolchain.toml rename to bootloader/bootloader/rust-toolchain.toml diff --git a/bootloader/src/arch/mod.rs b/bootloader/bootloader/src/arch/mod.rs similarity index 100% rename from bootloader/src/arch/mod.rs rename to bootloader/bootloader/src/arch/mod.rs diff --git a/bootloader/src/arch/x86/mod.rs b/bootloader/bootloader/src/arch/x86/mod.rs similarity index 100% rename from bootloader/src/arch/x86/mod.rs rename to bootloader/bootloader/src/arch/x86/mod.rs diff --git a/bootloader/src/arch/x86/x32.rs b/bootloader/bootloader/src/arch/x86/x32.rs similarity index 100% rename from bootloader/src/arch/x86/x32.rs rename to bootloader/bootloader/src/arch/x86/x32.rs diff --git a/bootloader/src/arch/x86/x64.rs b/bootloader/bootloader/src/arch/x86/x64.rs similarity index 100% rename from bootloader/src/arch/x86/x64.rs rename to bootloader/bootloader/src/arch/x86/x64.rs diff --git a/bootloader/src/logger.rs b/bootloader/bootloader/src/logger.rs similarity index 100% rename from bootloader/src/logger.rs rename to bootloader/bootloader/src/logger.rs diff --git a/bootloader/src/main.rs b/bootloader/bootloader/src/main.rs similarity index 100% rename from bootloader/src/main.rs rename to bootloader/bootloader/src/main.rs diff --git a/bootloader/src/os/bios/disk.rs b/bootloader/bootloader/src/os/bios/disk.rs similarity index 100% rename from bootloader/src/os/bios/disk.rs rename to bootloader/bootloader/src/os/bios/disk.rs diff --git a/bootloader/src/os/bios/macros.rs b/bootloader/bootloader/src/os/bios/macros.rs similarity index 100% rename from bootloader/src/os/bios/macros.rs rename to bootloader/bootloader/src/os/bios/macros.rs diff --git a/bootloader/src/os/bios/memory_map.rs b/bootloader/bootloader/src/os/bios/memory_map.rs similarity index 100% rename from bootloader/src/os/bios/memory_map.rs rename to bootloader/bootloader/src/os/bios/memory_map.rs diff --git a/bootloader/src/os/bios/mod.rs b/bootloader/bootloader/src/os/bios/mod.rs similarity index 100% rename from bootloader/src/os/bios/mod.rs rename to bootloader/bootloader/src/os/bios/mod.rs diff --git a/bootloader/src/os/bios/panic.rs b/bootloader/bootloader/src/os/bios/panic.rs similarity index 100% rename from bootloader/src/os/bios/panic.rs rename to bootloader/bootloader/src/os/bios/panic.rs diff --git a/bootloader/src/os/bios/serial.rs b/bootloader/bootloader/src/os/bios/serial.rs similarity index 100% rename from bootloader/src/os/bios/serial.rs rename to bootloader/bootloader/src/os/bios/serial.rs diff --git a/bootloader/src/os/bios/thunk.rs b/bootloader/bootloader/src/os/bios/thunk.rs similarity index 100% rename from bootloader/src/os/bios/thunk.rs rename to bootloader/bootloader/src/os/bios/thunk.rs diff --git a/bootloader/src/os/bios/vbe.rs b/bootloader/bootloader/src/os/bios/vbe.rs similarity index 100% rename from bootloader/src/os/bios/vbe.rs rename to bootloader/bootloader/src/os/bios/vbe.rs diff --git a/bootloader/src/os/bios/vga.rs b/bootloader/bootloader/src/os/bios/vga.rs similarity index 100% rename from bootloader/src/os/bios/vga.rs rename to bootloader/bootloader/src/os/bios/vga.rs diff --git a/bootloader/src/os/mod.rs b/bootloader/bootloader/src/os/mod.rs similarity index 100% rename from bootloader/src/os/mod.rs rename to bootloader/bootloader/src/os/mod.rs diff --git a/bootloader/src/serial_16550.rs b/bootloader/bootloader/src/serial_16550.rs similarity index 100% rename from bootloader/src/serial_16550.rs rename to bootloader/bootloader/src/serial_16550.rs diff --git a/bootloader/targets/x86-unknown-none.json b/bootloader/bootloader/targets/x86-unknown-none.json similarity index 100% rename from bootloader/targets/x86-unknown-none.json rename to bootloader/bootloader/targets/x86-unknown-none.json diff --git a/builder/.editorconfig b/builder/.editorconfig new file mode 100644 index 0000000..941e639 --- /dev/null +++ b/builder/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/builder/.gitignore b/builder/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/builder/.gitignore @@ -0,0 +1 @@ +/target diff --git a/builder/Cargo.lock b/builder/Cargo.lock new file mode 100644 index 0000000..f3c0485 --- /dev/null +++ b/builder/Cargo.lock @@ -0,0 +1,316 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "builder" +version = "0.1.0" +dependencies = [ + "anstyle", + "clap", + "gpt", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gpt" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa5448a0d9d541f1840c0e1b5fe513360861ca83c4b920619f54efe277f9254" +dependencies = [ + "bitflags", + "crc", + "simple-bytes", + "uuid", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "simple-bytes" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11532d9d241904f095185f35dcdaf930b1427a94d5b01d7002d74ba19b44cc4" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/builder/Cargo.toml b/builder/Cargo.toml new file mode 100644 index 0000000..2b10c1c --- /dev/null +++ b/builder/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "builder" +version = "0.1.0" +edition = "2021" + +# +# MARK: lints +# + +[workspace.lints.rust] +unused_import_braces = "deny" +unit_bindings = "deny" +single_use_lifetimes = "deny" +non_ascii_idents = "deny" +macro_use_extern_crate = "deny" +elided_lifetimes_in_paths = "deny" +absolute_paths_not_starting_with_crate = "deny" +explicit_outlives_requirements = "warn" +unused_crate_dependencies = "warn" +redundant_lifetimes = "warn" +missing_docs = "allow" + +[workspace.lints.clippy] +needless_return = "allow" +new_without_default = "allow" +tabs_in_doc_comments = "allow" +expect_used = "deny" +dbg_macro = "deny" +allow_attributes = "deny" +create_dir = "deny" +filetype_is_file = "deny" +integer_division = "deny" +lossy_float_literal = "deny" +map_err_ignore = "deny" +mutex_atomic = "deny" +needless_raw_strings = "deny" +print_stderr = "deny" +print_stdout = "deny" +str_to_string = "deny" +string_add = "deny" +string_to_string = "deny" +unimplemented = "deny" +use_debug = "deny" +verbose_file_reads = "deny" +large_types_passed_by_value = "deny" +match_on_vec_items = "deny" +wildcard_dependencies = "deny" +negative_feature_names = "deny" +redundant_feature_names = "deny" + +# +# MARK: dependencies +# + +[dependencies] +anstyle = "1.0.10" +clap = { version = "4.5.23", features = ["derive"] } +gpt = "4.0.0" diff --git a/builder/rustfmt.toml b/builder/rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/builder/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/builder/src/cmd/disk.rs b/builder/src/cmd/disk.rs new file mode 100644 index 0000000..3ea8f3b --- /dev/null +++ b/builder/src/cmd/disk.rs @@ -0,0 +1,228 @@ +use clap::Subcommand; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; + +use crate::gpt::{GPTDisk, PartitionType}; +use crate::util::parse_bytes_postfix; + +#[derive(Subcommand, Debug)] +pub enum DiskCommand { + /// Create an empty file of the given size + Create { + /// The new file's size, in bytes. + /// May be followed by the usual SI prefixes: + /// - K, M, G, T for powers of 10 + /// - Kib,Mib,Gib,Tib for powers of 1024 + size: String, + }, + + /// Initialize partition table on the given disk + Init {}, + + /// Show GPT information. + /// + /// By default, this command dumps GPT headers & partitons. + /// Use the flags below to compute other values. + #[command(alias = "i")] + Inspect { + /// Show index of first byte of partition + #[clap(long, group = "inspect-mode", value_name = "PARTITION")] + part_start: Option, + + /// Show index of sector of partition + #[clap(long, group = "inspect-mode", value_name = "PARTITION")] + part_start_sector: Option, + }, + + /// Initialize partition table on the given disk + #[command(alias = "addpart")] + AddPartition { + /// The partition's name + name: String, + + /// The partition's size, in bytes. + /// May be followed by the usual SI prefixes: + /// - K, M, G, T for powers of 10 + /// - Kib,Mib,Gib,Tib for powers of 1024 + size: String, + + /// The type of this partition + partition_type: PartitionType, + }, + + /// Write a file to the given partition + #[command(alias = "writepart")] + WritePartition { + /// The index of the partition to write to + part_idx: u32, + + /// The data to write + source: PathBuf, + }, + + /// Write a BIOS bootloader to this disk + SetBootCode { + /// The data to write. + /// This file must contain exactly 440 bytes. + source: PathBuf, + }, +} + +impl DiskCommand { + pub fn run(&self, file: &Path) { + match self { + Self::Inspect { + part_start, + part_start_sector, + } => { + let file = OpenOptions::new() + .create(false) + .append(false) + .write(false) + .read(true) + .open(file) + .unwrap(); + + let mut dw = GPTDisk::new(file); + + if let Some(part) = part_start { + println!("{}", dw.get_partition(*part).unwrap().start()); + } else if let Some(part) = part_start_sector { + assert!(dw.get_partition(*part).unwrap().start() % 512 == 0); + println!("{}", dw.get_partition(*part).unwrap().start() / 512); + } else { + let disk = dw.disk(false); + println!("Disk header: {:#?}", disk.primary_header()); + println!("Partition layout: {:#?}", disk.partitions()); + } + } + + Self::Create { size } => { + let size = match parse_bytes_postfix(&size) { + Some(x) => x, + None => panic!("Bad byte string"), + }; + + if file.exists() { + panic!("file exists") + } + + let mut file = File::create(file).unwrap(); + + let mut buffer = [0; 1024]; + let buffer_len: u64 = buffer.len().try_into().unwrap(); + let mut remaining_size = size; + + while remaining_size > 0 { + let to_write = remaining_size.min(buffer_len); + let to_write_usize: usize = to_write.try_into().unwrap(); + + let buffer = &mut buffer[..to_write_usize]; + file.write(buffer).unwrap(); + + remaining_size -= to_write; + } + } + + Self::Init {} => { + let file = OpenOptions::new() + .create(false) + .append(false) + .write(true) + .read(true) + .open(file) + .unwrap(); + + let mut dw = GPTDisk::new(file); + + dw.init(); + } + + Self::AddPartition { + name, + size, + partition_type, + } => { + let file = OpenOptions::new() + .create(false) + .append(false) + .write(true) + .read(true) + .open(file) + .unwrap(); + + let size = match parse_bytes_postfix(&size) { + Some(x) => x, + None => panic!("Bad byte string"), + }; + + let mut dw = GPTDisk::new(file); + let mut disk = dw.disk(true); + + disk.add_partition(name, size, partition_type.into(), 0, None) + .expect("failed to write disk"); + + disk.write().unwrap(); + } + + Self::WritePartition { part_idx, source } => { + let file = OpenOptions::new() + .create(false) + .append(false) + .write(true) + .read(true) + .open(file) + .unwrap(); + + let mut dw = GPTDisk::new(file); + + let mut part = match dw.get_partition(*part_idx) { + Some(part) => part, + None => panic!("no such partition"), + }; + + let mut source = std::fs::File::open(source).unwrap(); + let src_size = source.metadata().unwrap().len(); + + if src_size > part.len() { + panic!("file too big"); + } + + let n = std::io::copy(&mut source, &mut part).unwrap(); + println!("wrote {n} bytes"); + } + + Self::SetBootCode { source } => { + let file = OpenOptions::new() + .create(false) + .append(false) + .write(true) + .read(true) + .open(file) + .unwrap(); + + let mut src = OpenOptions::new() + .create(false) + .append(false) + .write(false) + .read(true) + .open(source) + .unwrap(); + + if src.metadata().unwrap().len() != 440 { + panic!( + "Bad MBR size {}, expected 440.", + src.metadata().unwrap().len() + ) + } + + let mut mbr = [0u8; 440]; + src.read_exact(&mut mbr).unwrap(); + + let mut dw = GPTDisk::new(file); + dw.set_boot_code(mbr); + } + } + } +} diff --git a/builder/src/cmd/mod.rs b/builder/src/cmd/mod.rs new file mode 100644 index 0000000..397e466 --- /dev/null +++ b/builder/src/cmd/mod.rs @@ -0,0 +1,25 @@ +mod disk; + +use clap::Subcommand; +use std::path::PathBuf; + +#[derive(Subcommand, Debug)] +pub enum Command { + /// Disk utilities + #[command(alias = "d")] + Disk { + /// The disk image to work on + file: PathBuf, + + #[command(subcommand)] + command: disk::DiskCommand, + }, +} + +impl Command { + pub fn run(&self) { + match self { + Self::Disk { command, file } => command.run(file), + } + } +} diff --git a/builder/src/gpt.rs b/builder/src/gpt.rs new file mode 100644 index 0000000..8a51fc2 --- /dev/null +++ b/builder/src/gpt.rs @@ -0,0 +1,272 @@ +use gpt::{partition_types, DiskDevice, GptDisk}; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::str::FromStr; + +/// Convenience wrapper. +/// Represents a disk with a GPT partition table +pub struct GPTDisk { + device: D, + start_offset: u64, +} + +impl GPTDisk { + pub fn new(mut device: D) -> Self { + //TODO:error handling + let start_offset = device.stream_position().unwrap(); + + return Self { + device, + start_offset, + }; + } + + /// Re-initilaize this disk's GPT metadata. + /// This destroys data and creates a new, empty disk. + pub fn init(&mut self) { + let disk = gpt::GptConfig::new() + .writable(true) + .create_from_device(&mut self.device, None) + .expect("failed to open disk"); + + disk.write().unwrap(); + } + + /// Read this disk's partition table. + pub fn disk(&mut self, writable: bool) -> GptDisk<&mut D> { + self.device + .seek(SeekFrom::Start(self.start_offset)) + .unwrap(); + + let disk = gpt::GptConfig::new() + .writable(writable) + .readonly_backup(!writable) + .open_from_device(&mut self.device) + .expect("failed to open disk"); + + return disk; + } + + /// Get a handle to a partition on this disk + pub fn get_partition<'a>(&'a mut self, partition_idx: u32) -> Option> { + let disk = self.disk(false); + + let part = match disk.partitions().get(&partition_idx) { + Some(part) => part, + None => return None, + }; + + let lb_size = *disk.logical_block_size(); + let start_byte = part.bytes_start(lb_size).unwrap(); + let length = part.bytes_len(lb_size).unwrap(); + + return Some(GPTPartition { + disk: self, + start_byte, + length, + seek: 0, + }); + } + + /// Set this disk's boot code (first 440 bytes of MBR) + pub fn set_boot_code(&mut self, boot_code: [u8; 440]) { + let disk = self.disk(false); + let lbs = *disk.logical_block_size(); + drop(disk); + + self.device + .seek(SeekFrom::Start(self.start_offset)) + .unwrap(); + + // Get or make MBR + let mut mbr = gpt::mbr::ProtectiveMBR::from_disk(&mut self.device, lbs).unwrap_or( + gpt::mbr::ProtectiveMBR::with_lb_size(lbs.as_u64().try_into().unwrap_or(0xFF_FF_FF_FF)), + ); + + mbr.set_bootcode(boot_code); + + self.device.write_all(&mbr.to_bytes()).unwrap(); + } +} + +/// A view into a [`GPTDisk`]'s partition. +pub struct GPTPartition<'a, D: DiskDevice> { + /// The disk this partition is on + disk: &'a mut GPTDisk, + + /// The index of the first byte of this partition inside its GPTDisk + start_byte: u64, + + /// The length of this partition, in bytes. + length: u64, + + /// The position we are reading/writing at within this file + seek: u64, +} + +impl<'a, D: DiskDevice> Seek for GPTPartition<'a, D> { + fn seek(&mut self, pos: SeekFrom) -> std::io::Result { + match pos { + SeekFrom::Current(x) => { + if x.is_negative() { + let x: u64 = x.abs().try_into().unwrap(); + + if x > self.seek { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Seek past start", + )); + } + + self.seek -= x; + } else { + let x: u64 = x.try_into().unwrap(); + + if self.seek + x > self.length { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Seek past end", + )); + } + + self.seek += x; + } + } + + SeekFrom::Start(x) => { + let x: u64 = x.try_into().unwrap(); + if x > self.length { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Seek past end", + )); + } + + self.seek = x; + } + + SeekFrom::End(x) => { + let x: u64 = x.try_into().unwrap(); + if x > self.length { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Seek past start", + )); + } + self.seek = self.length - x; + } + } + + assert!(self.seek <= self.length); + + return Ok(self.seek); + } +} + +impl<'a, D: DiskDevice> Read for GPTPartition<'a, D> { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + // Save parent cursor & seek + // (Not necessary for now, we don't need to save the parent cursor) + // let initial_pos = self.disk.device.stream_position()?; + + self.disk.device.seek(SeekFrom::Start( + self.disk.start_offset + self.start_byte + self.seek, + ))?; + + // Make sure we stay within this partition + let from_back = self.length - self.seek; + let len: u64 = buf.len().try_into().unwrap(); + let len = len.min(from_back); + let len: usize = len.try_into().unwrap(); + + // Do the read + let n = self.disk.device.read(&mut buf[0..len])?; + + // Update cursor + let n_u64: u64 = n.try_into().unwrap(); + self.seek += n_u64; + assert!(n_u64 <= from_back); + assert!(self.seek <= self.length); + + // Fix parent cursor + // self.disk.device.seek(SeekFrom::Start(initial_pos))?; + + return Ok(n); + } +} + +impl<'a, D: DiskDevice> Write for GPTPartition<'a, D> { + fn flush(&mut self) -> std::io::Result<()> { + self.disk.device.flush() + } + + fn write(&mut self, buf: &[u8]) -> std::io::Result { + // Save parent cursor & seek + // (Not necessary for now, we don't need to save the parent cursor) + // let initial_pos = self.disk.device.stream_position()?; + + self.disk.device.seek(SeekFrom::Start( + self.disk.start_offset + self.start_byte + self.seek, + ))?; + + // Make sure we stay within this partition + let from_back = self.length - self.seek; + let len: u64 = buf.len().try_into().unwrap(); + let len = len.min(from_back); + let len: usize = len.try_into().unwrap(); + + // Do the write + let n = self.disk.device.write(&buf[0..len])?; + + // Update cursor + let n_u64: u64 = n.try_into().unwrap(); + self.seek += n_u64; + assert!(n_u64 <= from_back); + assert!(self.seek <= self.length); + + // Fix parent cursor + // self.disk.device.seek(SeekFrom::Start(initial_pos))?; + + return Ok(n); + } +} + +impl<'a, D: DiskDevice> GPTPartition<'a, D> { + /// The index of the first byte in this partition + pub fn start(&self) -> u64 { + self.start_byte + } + + /// The length of this partition, in bytes + pub fn len(&self) -> u64 { + self.length + } +} + +#[derive(Debug, Clone, Copy)] +pub enum PartitionType { + BIOS, + EFI, + Linux, +} + +impl FromStr for PartitionType { + type Err = String; + fn from_str(s: &str) -> Result { + Ok(match s { + "bios" => Self::BIOS, + "efi" => Self::EFI, + "linux" => Self::Linux, + _ => return Err("unknown partition type".into()), + }) + } +} + +impl From<&PartitionType> for partition_types::Type { + fn from(value: &PartitionType) -> Self { + match value { + PartitionType::BIOS => partition_types::BIOS, + PartitionType::EFI => partition_types::EFI, + PartitionType::Linux => partition_types::LINUX_FS, + } + } +} diff --git a/builder/src/main.rs b/builder/src/main.rs new file mode 100644 index 0000000..7be0589 --- /dev/null +++ b/builder/src/main.rs @@ -0,0 +1,49 @@ +use anstyle::{AnsiColor, Color, Style}; +use clap::Parser; + +mod cmd; +mod gpt; +mod util; + +fn get_styles() -> clap::builder::Styles { + clap::builder::Styles::styled() + .usage( + Style::new() + .bold() + .fg_color(Some(Color::Ansi(AnsiColor::BrightBlue))), + ) + .header( + Style::new() + .bold() + .fg_color(Some(Color::Ansi(AnsiColor::Green))), + ) + .literal(Style::new().fg_color(Some(Color::Ansi(AnsiColor::BrightBlack)))) + .invalid(Style::new().fg_color(Some(Color::Ansi(AnsiColor::Red)))) + .error( + Style::new() + .bold() + .fg_color(Some(Color::Ansi(AnsiColor::Red))), + ) + .valid( + Style::new() + .bold() + .underline() + .fg_color(Some(Color::Ansi(AnsiColor::Green))), + ) + .placeholder(Style::new().fg_color(Some(Color::Ansi(AnsiColor::White)))) +} + +/// Redox disk utility +#[derive(Parser, Debug)] +#[command(version, about, long_about = None, styles=get_styles())] +struct Cli { + #[command(subcommand)] + command: cmd::Command, +} + +/// Demonstrates how to create a new partition table without anything pre-existing +fn main() { + let cli = Cli::parse(); + + cli.command.run(); +} diff --git a/builder/src/util.rs b/builder/src/util.rs new file mode 100644 index 0000000..4186f6a --- /dev/null +++ b/builder/src/util.rs @@ -0,0 +1,45 @@ +// Parse a byte string like `5M` or `8 Tib` into a +// quantity of bytes. Returns `None` if the input +// could not be parsed. +pub fn parse_bytes_postfix(string: &str) -> Option { + let mut lower = string.trim().to_lowercase(); + // Turn Mb & Mib into M and Mi + if lower.ends_with("b") { + lower.pop(); + } + + let mut string = &lower[..]; + let mut multiplier = 1; + let len = string.len(); + + if string.ends_with("k") { + string = &string[0..len - 1]; + multiplier = 1000; + } else if lower.ends_with("m") { + string = &string[0..len - 1]; + multiplier = 1000 * 1000; + } else if lower.ends_with("g") { + string = &string[0..len - 1]; + multiplier = 1000 * 1000 * 1000; + } else if lower.ends_with("t") { + string = &string[0..len - 1]; + multiplier = 1000 * 1000 * 1000 * 1000; + } else if lower.ends_with("ki") { + string = &string[0..len - 2]; + multiplier = 1024; + } else if lower.ends_with("mi") { + string = &string[0..len - 2]; + multiplier = 1024 * 1024; + } else if lower.ends_with("gi") { + string = &string[0..len - 2]; + multiplier = 1024 * 1024 * 1024; + } else if lower.ends_with("ti") { + string = &string[0..len - 2]; + multiplier = 1024 * 1024 * 1024 * 1024; + } + + return match string.trim().parse::() { + Ok(x) => Some(multiplier * x), + Err(_) => None, + }; +} diff --git a/make/bios b/make/bios deleted file mode 100644 index 9bbd3f3..0000000 --- a/make/bios +++ /dev/null @@ -1,57 +0,0 @@ -# Build script for BIOS (legacy) boot. -# -# This compiles our bootloader as a static library, -# and wraps it in a multistage loader. - - -# Compile bootloader as library -LIB_SRC = ./bootloader/Cargo.toml ./bootloader/Cargo.lock $(shell find ./bootloader/src -type f) -$(BUILD_DIR)/bios.lib: $(LIB_SRC) - @mkdir -p $(BUILD_DIR) - env RUSTFLAGS="-C soft-float" \ - cargo rustc \ - --manifest-path="./bootloader/Cargo.toml" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target "./bootloader/targets/x86-unknown-none.json" \ - --lib \ - --release \ - -- \ - --emit link="$(CURDIR)/$@" - -# Link bootloader -BIOS_LD = ./bootloader/linkers/x86-unknown-none.ld -$(BUILD_DIR)/bios.elf: $(BUILD_DIR)/bios.lib $(BIOS_LD) - ld \ - -m elf_i386 \ - --gc-sections \ - -z max-page-size=0x1000 \ - -T "$(BIOS_LD)" \ - -o "$@" \ - "$<" - - objcopy --only-keep-debug "$@" "$@.sym" - objcopy --strip-debug "$@" - -# Wrap bootloader in three-stage BIOS loader -BIOS_SRC = ./bios -$(BUILD_DIR)/bios.bin: $(wildcard $(BIOS_SRC)/*.asm) $(BUILD_DIR)/bios.elf - @mkdir -p "$(BUILD_DIR)" - nasm \ - -f bin \ - -o "$@" \ - -l "$@.lst" \ - -D STAGE3="$(BUILD_DIR)/bios.elf" \ - -i "$(BIOS_SRC)" \ - "$(BIOS_SRC)/main.asm" - -# Build demo disk image -$(BUILD_DIR)/bios.img: $(BUILD_DIR)/bios.bin $(BUILD_DIR)/filesystem.img - rm -f "$@.partial" - fallocate -l 256MiB "$@.partial" - parted -s -a minimal "$@.partial" mklabel msdos - parted -s -a minimal "$@.partial" mkpart primary 2MiB 100% - dd if="$<" of="$@.partial" bs=1 count=512 conv=notrunc - dd if="$<" of="$@.partial" bs=512 skip=1 seek=1 conv=notrunc - dd if="$(BUILD_DIR)/filesystem.img" of="$@.partial" bs=1MiB seek=2 conv=notrunc - mv "$@.partial" "$@" \ No newline at end of file