From ea5b2418e6ea2dbf48661818653408b0223ff50c Mon Sep 17 00:00:00 2001 From: vasilito Date: Mon, 6 Jul 2026 08:03:08 +0300 Subject: [PATCH] redbear: migrate bootloader patches into local fork Apply the full Red Bear bootloader patch set: - P0-gpt-partition-offset - fix-uefi-alloc-panic - redox.patch (Makefile/mk and misc fixes) - P1-bootloader-timeout-and-default-resolution - P2-live-preload-guard - P3-uefi-live-image-safe-read - P4-live-large-iso-boot - P5-live-preload-cap-1gib Also switch redoxfs dependency to the local fork path. --- Cargo.lock | 349 +++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +- Makefile | 4 +- mk/aarch64-unknown-uefi.mk | 14 +- mk/riscv64gc-unknown-uefi.mk | 14 +- mk/x86-unknown-none.mk | 10 +- mk/x86_64-unknown-uefi.mk | 10 +- rust-toolchain.toml | 2 +- src/arch/aarch64.rs | 2 +- src/main.rs | 156 ++++++++++++---- src/os/bios/disk.rs | 6 +- src/os/bios/memory_map.rs | 2 +- src/os/bios/mod.rs | 13 +- src/os/mod.rs | 4 + src/os/uefi/arch/aarch64.rs | 4 +- src/os/uefi/arch/x86_64.rs | 4 +- src/os/uefi/device.rs | 170 +++++++++++++++-- src/os/uefi/disk.rs | 6 +- src/os/uefi/display.rs | 2 +- src/os/uefi/dtb.rs | 2 +- src/os/uefi/mod.rs | 61 +++++- src/os/uefi/video_mode.rs | 2 +- 22 files changed, 724 insertions(+), 116 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..e1657a444a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,349 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "argon2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", +] + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dmidecode" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a070ca68f8ba202b05487d52b9ac56eaebb5b66cdd68d1a17e63174bb11e3b" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "endian-num" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f59926911ef34d1efb9ea1ee8ca78385df62ce700ccf2bcb149011bd226888" + +[[package]] +name = "fdt" +version = "0.2.0-alpha1" +source = "git+https://github.com/repnop/fdt.git?rev=2fb1409edd1877c714a0aa36b6a7c5351004be54#2fb1409edd1877c714a0aa36b6a7c5351004be54" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linked_list_allocator" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lz4_flex" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a" + +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox-path" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" + +[[package]] +name = "redox_bootloader" +version = "1.0.0+rb0.2.5" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "dmidecode", + "fdt", + "linked_list_allocator", + "log", + "redox_syscall", + "redox_uefi", + "redox_uefi_std", + "redoxfs", + "spin", + "x86", +] + +[[package]] +name = "redox_syscall" +version = "0.9.0+rb0.2.5" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "redox_uefi" +version = "0.1.14" +source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835" + +[[package]] +name = "redox_uefi_alloc" +version = "0.1.14" +source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835" +dependencies = [ + "redox_uefi", +] + +[[package]] +name = "redox_uefi_std" +version = "0.1.14" +source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835" +dependencies = [ + "redox_uefi", + "redox_uefi_alloc", +] + +[[package]] +name = "redoxfs" +version = "0.9.1+rb0.2.5" +dependencies = [ + "aes", + "argon2", + "base64ct", + "bitflags 2.13.0", + "endian-num", + "libc", + "log", + "lz4_flex", + "redox-path", + "redox_syscall", + "seahash", + "uuid", + "xts-mode", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uuid" +version = "1.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "x86" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" +dependencies = [ + "bit_field", + "bitflags 1.3.2", + "raw-cpuid", +] + +[[package]] +name = "xts-mode" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520" +dependencies = [ + "byteorder", + "cipher", +] diff --git a/Cargo.toml b/Cargo.toml index 3007e5732f..144bf4923b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ redox_syscall = { path = "../syscall" } spin = "0.9.5" [dependencies.redoxfs] -version = "0.8" +path = "../redoxfs" default-features = false features = ["log"] @@ -54,3 +54,4 @@ serial_debug = [] [patch.crates-io] redox_syscall = { path = "../syscall" } +redoxfs = { path = "../redoxfs" } diff --git a/Makefile b/Makefile index 0bfb445248..e276328505 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,9 @@ TARGET?=x86_64-unknown-uefi SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) BUILD:=$(CURDIR) export RUST_TARGET_PATH?=$(SOURCE)/targets - +CARGO_ARGS=--release --locked --target $(TARGET) \ + -Z build-std=core,alloc \ + -Z build-std-features=compiler-builtins-mem include $(SOURCE)/mk/$(TARGET).mk diff --git a/mk/aarch64-unknown-uefi.mk b/mk/aarch64-unknown-uefi.mk index 49f2e331d3..2dfca9d04e 100644 --- a/mk/aarch64-unknown-uefi.mk +++ b/mk/aarch64-unknown-uefi.mk @@ -5,27 +5,21 @@ all: $(BUILD)/bootloader.efi $(BUILD)/bootloader.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f) mkdir -p "$(BUILD)" - env RUSTFLAGS="--cfg aes_force_soft" \ + env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target $(TARGET) \ + $(CARGO_ARGS) \ --bin bootloader \ - --release \ -- \ --emit link="$@" $(BUILD)/bootloader-live.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f) mkdir -p "$(BUILD)" - env RUSTFLAGS="--cfg aes_force_soft" \ + env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target $(TARGET) \ + $(CARGO_ARGS) \ --bin bootloader \ - --release \ --features live \ -- \ --emit link="$@" diff --git a/mk/riscv64gc-unknown-uefi.mk b/mk/riscv64gc-unknown-uefi.mk index 639614a7e5..000cdd9bd8 100644 --- a/mk/riscv64gc-unknown-uefi.mk +++ b/mk/riscv64gc-unknown-uefi.mk @@ -20,27 +20,21 @@ $(BUILD)/%.efi.elf: $(BUILD)/%.a $(SCRIPT) $(BUILD)/bootloader.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f) mkdir -p "$(BUILD)" - env RUSTFLAGS="--cfg aes_force_soft" \ + env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target $(TARGET) \ + $(CARGO_ARGS) \ --lib \ - --release \ -- \ --emit link=$@ $(BUILD)/bootloader-live.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f) mkdir -p "$(BUILD)" - env RUSTFLAGS="--cfg aes_force_soft" \ + env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target $(TARGET) \ + $(CARGO_ARGS) \ --lib \ - --release \ --features live \ -- \ --emit link=$@ diff --git a/mk/x86-unknown-none.mk b/mk/x86-unknown-none.mk index 7071d8738b..3b4526bde0 100644 --- a/mk/x86-unknown-none.mk +++ b/mk/x86-unknown-none.mk @@ -10,11 +10,8 @@ $(BUILD)/libbootloader.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target "$(TARGET)" \ + $(CARGO_ARGS) \ --lib \ - --release \ -- \ --emit link="$@" @@ -23,11 +20,8 @@ $(BUILD)/libbootloader-live.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target "$(TARGET)" \ + $(CARGO_ARGS) \ --lib \ - --release \ --features live \ -- \ --emit link="$@" diff --git a/mk/x86_64-unknown-uefi.mk b/mk/x86_64-unknown-uefi.mk index 3e997a6458..8358e44976 100644 --- a/mk/x86_64-unknown-uefi.mk +++ b/mk/x86_64-unknown-uefi.mk @@ -8,11 +8,8 @@ $(BUILD)/bootloader.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find env RUSTFLAGS="--cfg aes_force_soft" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target $(TARGET) \ + $(CARGO_ARGS) \ --bin bootloader \ - --release \ -- \ --emit link="$@" @@ -22,11 +19,8 @@ $(BUILD)/bootloader-live.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell env RUSTFLAGS="--cfg aes_force_soft" \ cargo rustc \ --manifest-path="$<" \ - -Z build-std=core,alloc \ - -Z build-std-features=compiler-builtins-mem \ - --target $(TARGET) \ + $(CARGO_ARGS) \ --bin bootloader \ - --release \ --features live \ -- \ --emit link="$@" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 42f22f6190..d8c73f3941 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-10-03" +channel = "nightly-2026-05-24" components = ["rust-src"] diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs index 55c68f5537..765ae4161d 100644 --- a/src/arch/aarch64.rs +++ b/src/arch/aarch64.rs @@ -1,5 +1,5 @@ use crate::area_add; -use crate::os::{Os, OsMemoryEntry, OsMemoryKind, dtb::is_in_dev_mem_region}; +use crate::os::{dtb::is_in_dev_mem_region, Os, OsMemoryEntry, OsMemoryKind}; use core::slice; pub(crate) const PF_PRESENT: u64 = 1 << 0; diff --git a/src/main.rs b/src/main.rs index 542b059812..78dabb0377 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ extern crate uefi_std as std; use alloc::{format, string::String, vec::Vec}; use core::{ cmp, + convert::TryFrom, fmt::{self, Write}, mem, ptr, slice, str, }; @@ -114,6 +115,10 @@ fn select_mode( live: &mut bool, edit_env: &mut bool, ) -> Option { + const DEFAULT_WIDTH: u32 = 1280; + const DEFAULT_HEIGHT: u32 = 720; + const AUTOBOOT_SECONDS: usize = 5; + let mut modes = Vec::new(); for mode in os.video_modes(output_i) { let mut aspect_w = mode.width; @@ -141,15 +146,26 @@ fn select_mode( // Sort modes by pixel area, reversed modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height))); - // Set selected based on best resolution + // Set selected based on Red Bear default resolution first, then best resolution fallback print!("Output {}", output_i); let mut selected = modes.first().map_or(0, |x| x.0.id); - if let Some((best_width, best_height)) = os.best_resolution(output_i) { - print!(", best resolution: {}x{}", best_width, best_height); - for (mode, _text) in modes.iter() { - if mode.width == best_width && mode.height == best_height { - selected = mode.id; - break; + let mut selected_from_default = false; + for (mode, _text) in modes.iter() { + if mode.width == DEFAULT_WIDTH && mode.height == DEFAULT_HEIGHT { + selected = mode.id; + selected_from_default = true; + print!(", default resolution: {}x{}", DEFAULT_WIDTH, DEFAULT_HEIGHT); + break; + } + } + if !selected_from_default { + if let Some((best_width, best_height)) = os.best_resolution(output_i) { + print!(", best resolution: {}x{}", best_width, best_height); + for (mode, _text) in modes.iter() { + if mode.width == best_width && mode.height == best_height { + selected = mode.id; + break; + } } } } @@ -163,12 +179,19 @@ fn select_mode( println!("Press l to enable live mode"); } println!("Press e to edit boot environment"); + println!( + "Autobooting default mode in {} seconds (press any key to cancel countdown)", + AUTOBOOT_SECONDS + ); println!(); print!(" "); let (off_x, off_y) = os.get_text_position(); let rows = 12; let mut mode_opt = None; + let countdown_y = off_y.saturating_sub(2); + let mut countdown = AUTOBOOT_SECONDS; + let mut countdown_active = true; while !modes.is_empty() { let mut row = 0; let mut col = 0; @@ -186,9 +209,38 @@ fn select_mode( row += 1; } + os.set_text_position(0, countdown_y); + os.set_text_highlight(false); + if countdown_active { + println!( + "Autobooting default mode in {} seconds (press any key to cancel countdown)", + countdown + ); + } else { + println!("Manual mode selection active. Press Enter to boot selected mode. "); + } + // Read keypress - match os.get_key() { + match if countdown_active { + os.get_key_timeout(1000) + } else { + os.get_key() + } { + OsKey::Timeout => { + if countdown_active { + if countdown == 0 { + if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) { + if let Some((mode, _text)) = modes.get(mode_i) { + mode_opt = Some(*mode); + } + } + break; + } + countdown = countdown.saturating_sub(1); + } + } OsKey::Left => { + countdown_active = false; if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) { if mode_i < rows { while mode_i < modes.len() { @@ -202,6 +254,7 @@ fn select_mode( } } OsKey::Right => { + countdown_active = false; if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) { mode_i += rows; if mode_i >= modes.len() { @@ -213,6 +266,7 @@ fn select_mode( } } OsKey::Up => { + countdown_active = false; if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) { if mode_i % rows == 0 { mode_i += rows; @@ -227,6 +281,7 @@ fn select_mode( } } OsKey::Down => { + countdown_active = false; if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) { mode_i += 1; if mode_i % rows == 0 { @@ -241,6 +296,7 @@ fn select_mode( } } OsKey::Enter => { + countdown_active = false; if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) { if let Some((mode, _text)) = modes.get(mode_i) { mode_opt = Some(*mode); @@ -249,6 +305,7 @@ fn select_mode( break; } OsKey::Char('l') => { + countdown_active = false; *live = !*live; os.set_text_position(live_mode.0, live_mode.1); if *live { @@ -258,6 +315,7 @@ fn select_mode( } } OsKey::Char('e') => { + countdown_active = false; if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) { if let Some((mode, _text)) = modes.get(mode_i) { *edit_env = true; @@ -266,7 +324,9 @@ fn select_mode( } break; } - _ => (), + OsKey::Other | OsKey::Backspace | OsKey::Delete | OsKey::Char(_) => { + countdown_active = false; + } } } @@ -498,36 +558,62 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) { print!("live: 0/{} MiB", size / MIBI as u64); - let ptr = os.alloc_zeroed_page_aligned(size as usize); - if ptr.is_null() { - panic!("Failed to allocate memory for live"); + let live_size = match usize::try_from(size) { + Ok(live_size) => live_size, + Err(_) => { + println!("\rlive: disabled (image too large for bootloader address space)"); + live = false; + 0 + } + }; + + let ptr = if live { + os.alloc_zeroed_page_aligned(live_size) + } else { + ptr::null_mut() + }; + if live && ptr.is_null() { + println!( + "\rlive: disabled (unable to allocate {} MiB upfront)", + size / MIBI as u64 + ); + live = false; } - let live = unsafe { slice::from_raw_parts_mut(ptr, size as usize) }; + let live = if live { + Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) }) + } else { + println!("Continuing without live preload"); + None + }; - let mut i = 0; - for chunk in live.chunks_mut(MIBI) { - print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64); - i += unsafe { - fs.disk - .read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk) - .expect("Failed to read live disk") as u64 - }; + if let Some(live) = live { + let mut i = 0; + for chunk in live.chunks_mut(MIBI) { + print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64); + i += unsafe { + fs.disk + .read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk) + .expect("Failed to read live disk") as u64 + }; + } + println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64); + + println!("Switching to live disk"); + unsafe { + LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, live_size))); + } + + area_add(OsMemoryEntry { + base: live.as_ptr() as u64, + size: live.len() as u64, + kind: OsMemoryKind::Reserved, + }); + + Some(live) + } else { + None } - println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64); - - println!("Switching to live disk"); - unsafe { - LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, size as usize))); - } - - area_add(OsMemoryEntry { - base: live.as_ptr() as u64, - size: live.len() as u64, - kind: OsMemoryKind::Reserved, - }); - - Some(live) } else { None }; diff --git a/src/os/bios/disk.rs b/src/os/bios/disk.rs index 4570292339..aeee85d323 100644 --- a/src/os/bios/disk.rs +++ b/src/os/bios/disk.rs @@ -1,8 +1,8 @@ use core::{mem, ptr}; -use redoxfs::{BLOCK_SIZE, Disk}; -use syscall::error::{EIO, Error, Result}; +use redoxfs::{Disk, BLOCK_SIZE}; +use syscall::error::{Error, Result, EIO}; -use super::{DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR, ThunkData}; +use super::{ThunkData, DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR}; const SECTOR_SIZE: u64 = 512; const BLOCKS_PER_SECTOR: u64 = BLOCK_SIZE / SECTOR_SIZE; diff --git a/src/os/bios/memory_map.rs b/src/os/bios/memory_map.rs index d47f6b2844..78677ecc41 100644 --- a/src/os/bios/memory_map.rs +++ b/src/os/bios/memory_map.rs @@ -3,7 +3,7 @@ use core::{cmp, mem, ptr}; use crate::area_add; use crate::os::{OsMemoryEntry, OsMemoryKind}; -use super::{MEMORY_MAP_ADDR, thunk::ThunkData}; +use super::{thunk::ThunkData, MEMORY_MAP_ADDR}; #[repr(C, packed)] struct MemoryMapEntry { diff --git a/src/os/bios/mod.rs b/src/os/bios/mod.rs index a9873d769d..c016688639 100644 --- a/src/os/bios/mod.rs +++ b/src/os/bios/mod.rs @@ -1,11 +1,11 @@ -use alloc::alloc::{Layout, alloc_zeroed}; +use alloc::alloc::{alloc_zeroed, Layout}; use core::{convert::TryFrom, mem, ptr, slice}; use linked_list_allocator::LockedHeap; use spin::Mutex; -use crate::KernelArgs; use crate::logger::LOGGER; use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode}; +use crate::KernelArgs; use self::disk::DiskBios; use self::memory_map::memory_map; @@ -109,9 +109,14 @@ impl Os for OsBios { let page_size = self.page_size(); let pages = size.div_ceil(page_size); + let Some(total_size) = pages.checked_mul(page_size) else { + return ptr::null_mut(); + }; + let Ok(layout) = Layout::from_size_align(total_size, page_size) else { + return ptr::null_mut(); + }; - let ptr = - unsafe { alloc_zeroed(Layout::from_size_align(pages * page_size, page_size).unwrap()) }; + let ptr = unsafe { alloc_zeroed(layout) }; assert!(!ptr.is_null()); ptr diff --git a/src/os/mod.rs b/src/os/mod.rs index 92c00c90cc..f01c222aec 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -32,6 +32,7 @@ pub enum OsKey { Delete, Enter, Char(char), + Timeout, Other, } @@ -87,6 +88,9 @@ pub trait Os { fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)>; fn get_key(&self) -> OsKey; + fn get_key_timeout(&self, _milliseconds: usize) -> OsKey { + self.get_key() + } fn clear_text(&self); fn get_text_position(&self) -> (usize, usize); diff --git a/src/os/uefi/arch/aarch64.rs b/src/os/uefi/arch/aarch64.rs index 3e36bebcb5..bede4185b7 100644 --- a/src/os/uefi/arch/aarch64.rs +++ b/src/os/uefi/arch/aarch64.rs @@ -2,12 +2,12 @@ use core::{arch::asm, fmt::Write, mem, slice}; use uefi::status::Result; use crate::{ - KernelArgs, arch::{ENTRY_ADDRESS_MASK, PAGE_ENTRIES, PF_PRESENT, PF_TABLE, PHYS_OFFSET}, logger::LOGGER, + KernelArgs, }; -use super::super::{OsEfi, memory_map::memory_map}; +use super::super::{memory_map::memory_map, OsEfi}; unsafe fn dump_page_tables(table_phys: u64, table_virt: u64, table_level: u64) { unsafe { diff --git a/src/os/uefi/arch/x86_64.rs b/src/os/uefi/arch/x86_64.rs index e1ecaa5599..52cef13140 100644 --- a/src/os/uefi/arch/x86_64.rs +++ b/src/os/uefi/arch/x86_64.rs @@ -5,9 +5,9 @@ use x86::{ msr, }; -use crate::{KernelArgs, logger::LOGGER}; +use crate::{logger::LOGGER, KernelArgs}; -use super::super::{OsEfi, memory_map::memory_map}; +use super::super::{memory_map::memory_map, OsEfi}; unsafe extern "C" fn kernel_entry( page_phys: usize, diff --git a/src/os/uefi/device.rs b/src/os/uefi/device.rs index 36b48dc2d8..ba45bba3db 100644 --- a/src/os/uefi/device.rs +++ b/src/os/uefi/device.rs @@ -1,17 +1,157 @@ use alloc::{string::String, vec, vec::Vec}; use core::{fmt::Write, mem, ptr, slice}; use uefi::{ - Handle, device::{ DevicePath, DevicePathAcpiType, DevicePathBbsType, DevicePathEndType, DevicePathHardwareType, DevicePathMediaType, DevicePathMessagingType, DevicePathType, }, guid::Guid, status::Status, + Handle, }; use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol}; use super::disk::{DiskEfi, DiskOrFileEfi}; +use redoxfs::Disk; + +const LINUX_FS_GUID: [u8; 16] = [ + 0xAF, 0x3D, 0xC6, 0x0F, 0x83, 0x84, 0x72, 0x47, + 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4, +]; + +fn gpt_find_redoxfs_offset(disk: &mut DiskEfi) -> u64 { + let block_size = disk.0.Media.BlockSize as u64; + if block_size == 0 { + return 2 * crate::MIBI as u64; + } + + let mut header_buf = [0u8; 512]; + let lba1_block = block_size / redoxfs::BLOCK_SIZE; + let header_read_len = if 512 <= redoxfs::BLOCK_SIZE as usize { + redoxfs::BLOCK_SIZE as usize + } else { + 512 + }; + let mut read_buf = vec![0u8; header_read_len]; + if unsafe { disk.read_at(lba1_block, &mut read_buf) }.is_err() { + log::warn!("GPT: failed to read LBA 1"); + return 2 * crate::MIBI as u64; + } + let copy_len = 512.min(read_buf.len()); + header_buf[..copy_len].copy_from_slice(&read_buf[..copy_len]); + + if &header_buf[0..8] != b"EFI PART" { + log::warn!("GPT: no valid signature at LBA 1"); + return 2 * crate::MIBI as u64; + } + + let read_le_u32 = |off: usize| -> u32 { + u32::from_le_bytes(header_buf[off..off + 4].try_into().unwrap_or([0; 4])) + }; + let read_le_u64 = |off: usize| -> u64 { + u64::from_le_bytes(header_buf[off..off + 8].try_into().unwrap_or([0; 8])) + }; + + let _revision = read_le_u32(8); + let _header_size = read_le_u32(12); + let partition_entry_start_lba = read_le_u64(72); + let num_entries = read_le_u32(80); + let entry_size = read_le_u32(84); + + if num_entries == 0 || entry_size == 0 || num_entries > 128 { + log::warn!("GPT: invalid entry count {} or size {}", num_entries, entry_size); + return 2 * crate::MIBI as u64; + } + + let entries_byte_offset = partition_entry_start_lba * block_size; + let entries_start_block = entries_byte_offset / redoxfs::BLOCK_SIZE; + + let total_entries_bytes = num_entries as usize * entry_size as usize; + let read_size = total_entries_bytes.min(4096); + let mut entries_buf = vec![0u8; read_size + redoxfs::BLOCK_SIZE as usize]; + let entries_read_blocks = (read_size as u64).div_ceil(redoxfs::BLOCK_SIZE) as usize; + if unsafe { disk.read_at(entries_start_block, &mut entries_buf[..entries_read_blocks * redoxfs::BLOCK_SIZE as usize]) }.is_err() { + log::warn!("GPT: failed to read partition entries"); + return 2 * crate::MIBI as u64; + } + + let entries_per_chunk = read_size / entry_size as usize; + for i in 0..entries_per_chunk.min(num_entries as usize) { + let off = i * entry_size as usize; + if off + entry_size as usize > entries_buf.len() { + break; + } + let entry = &entries_buf[off..off + entry_size as usize]; + + let type_guid = &entry[0..16]; + if type_guid == [0u8; 16] { + continue; + } + + if type_guid == LINUX_FS_GUID { + let first_lba = u64::from_le_bytes( + entry[32..40].try_into().unwrap_or([0; 8]), + ); + let offset_bytes = first_lba * block_size; + log::debug!("GPT: found REDOXFS/Linux partition at LBA {} (offset {} bytes)", first_lba, offset_bytes); + return offset_bytes; + } + } + + log::warn!("GPT: no Linux filesystem partition found, falling back to 2 MiB"); + 2 * crate::MIBI as u64 +} + +fn gpt_find_redoxfs_offset_from_slice(data: &[u8]) -> u64 { + if data.len() < 520 { + return 2 * crate::MIBI as u64; + } + + let header = &data[512..]; + if &header[0..8] != b"EFI PART" { + return 2 * crate::MIBI as u64; + } + + let read_le_u32 = |off: usize| -> u32 { + u32::from_le_bytes(header[off..off + 4].try_into().unwrap_or([0; 4])) + }; + let read_le_u64 = |off: usize| -> u64 { + u64::from_le_bytes(header[off..off + 8].try_into().unwrap_or([0; 8])) + }; + + let partition_entry_start_lba = read_le_u64(72); + let num_entries = read_le_u32(80); + let entry_size = read_le_u32(84); + + if num_entries == 0 || entry_size == 0 || num_entries > 128 { + return 2 * crate::MIBI as u64; + } + + let gpt_lba_size: u64 = 512; + let entries_byte_offset = partition_entry_start_lba * gpt_lba_size; + + for i in 0..num_entries as usize { + let off = entries_byte_offset as usize + i * entry_size as usize; + if off + entry_size as usize > data.len() { + break; + } + let entry = &data[off..off + entry_size as usize]; + + let type_guid = &entry[0..16]; + if type_guid == [0u8; 16] { + continue; + } + + if type_guid == LINUX_FS_GUID { + let first_lba = u64::from_le_bytes( + entry[32..40].try_into().unwrap_or([0; 8]), + ); + return first_lba * gpt_lba_size; + } + } + + 2 * crate::MIBI as u64 +} #[derive(Debug)] enum DevicePathRelation { @@ -126,17 +266,15 @@ pub fn disk_device_priority() -> Vec { }; if cfg!(feature = "live") { - // First try to get a live image from redox-live.iso. This is required to support netbooting. if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) { + let partition_offset = if buffer.len() > 520 && &buffer[512..520] == b"EFI PART" { + gpt_find_redoxfs_offset_from_slice(&buffer) + } else { + 0 + }; return vec![DiskDevice { handle: esp_handle, - // Support both a copy of livedisk.iso and a standalone redoxfs partition - partition_offset: if &buffer[512..520] == b"EFI PART" { - //TODO: get block from partition table - 2 * crate::MIBI as u64 - } else { - 0 - }, + partition_offset, disk: DiskOrFileEfi::File(buffer), device_path: esp_device_path, file_path: Some("redox-live.iso"), @@ -154,7 +292,7 @@ pub fn disk_device_priority() -> Vec { }; let mut devices = Vec::with_capacity(handles.len()); for handle in handles { - let disk = match DiskEfi::handle_protocol(handle) { + let mut disk = match DiskEfi::handle_protocol(handle) { Ok(ok) => ok, Err(err) => { log::warn!( @@ -182,14 +320,14 @@ pub fn disk_device_priority() -> Vec { } }; + let partition_offset = if disk.0.Media.LogicalPartition { + 0 + } else { + gpt_find_redoxfs_offset(&mut disk) + }; devices.push(DiskDevice { handle, - partition_offset: if disk.0.Media.LogicalPartition { - 0 - } else { - //TODO: get block from partition table - 2 * crate::MIBI as u64 - }, + partition_offset, disk: DiskOrFileEfi::Disk(disk), device_path, file_path: None, diff --git a/src/os/uefi/disk.rs b/src/os/uefi/disk.rs index 31e02db813..3f920bb5f7 100644 --- a/src/os/uefi/disk.rs +++ b/src/os/uefi/disk.rs @@ -1,10 +1,10 @@ use alloc::vec::Vec; use core::slice; -use redoxfs::{BLOCK_SIZE, Disk, RECORD_SIZE}; +use redoxfs::{Disk, BLOCK_SIZE, RECORD_SIZE}; use std::proto::Protocol; -use syscall::{EINVAL, EIO, Error, Result}; +use syscall::{Error, Result, EINVAL, EIO}; use uefi::block_io::BlockIo as UefiBlockIo; -use uefi::guid::{BLOCK_IO_GUID, Guid}; +use uefi::guid::{Guid, BLOCK_IO_GUID}; pub enum DiskOrFileEfi { Disk(DiskEfi), diff --git a/src/os/uefi/display.rs b/src/os/uefi/display.rs index 95b7c2fd5e..8bbe6e3175 100644 --- a/src/os/uefi/display.rs +++ b/src/os/uefi/display.rs @@ -1,6 +1,6 @@ use std::proto::Protocol; use uefi::graphics::GraphicsOutput; -use uefi::guid::{GRAPHICS_OUTPUT_PROTOCOL_GUID, Guid}; +use uefi::guid::{Guid, GRAPHICS_OUTPUT_PROTOCOL_GUID}; pub struct Output(pub &'static mut GraphicsOutput); diff --git a/src/os/uefi/dtb.rs b/src/os/uefi/dtb.rs index 812c75249d..975c983dbf 100644 --- a/src/os/uefi/dtb.rs +++ b/src/os/uefi/dtb.rs @@ -1,7 +1,7 @@ use crate::Os; use alloc::vec::Vec; -use byteorder::BE; use byteorder::ByteOrder; +use byteorder::BE; use core::slice; use fdt::Fdt; use uefi::guid::DEVICE_TREE_GUID; diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs index bbd034b136..037ba121cb 100644 --- a/src/os/uefi/mod.rs +++ b/src/os/uefi/mod.rs @@ -2,13 +2,13 @@ use alloc::vec::Vec; use core::{cell::RefCell, mem, ptr, slice}; use std::proto::Protocol; use uefi::{ - Handle, boot::LocateSearchType, memory::MemoryType, reset::ResetType, status::{Result, Status}, system::SystemTable, text::TextInputKey, + Handle, }; use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode}; @@ -53,12 +53,16 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 { pages, &mut ptr, )) - .unwrap(); - ptr as *mut u8 + .unwrap_or_else(|_| { + ptr = 0; + 0 + }); + if ptr == 0 { ptr::null_mut() } else { ptr as *mut u8 } }; - assert!(!ptr.is_null()); - unsafe { ptr::write_bytes(ptr, 0, pages * page_size) }; + if !ptr.is_null() { + unsafe { ptr::write_bytes(ptr, 0, pages * page_size) }; + } ptr } @@ -103,7 +107,7 @@ impl OsEfi { for other_output in outputs.iter() { if output.0.Mode.FrameBufferBase - == other_output.0.0.Mode.FrameBufferBase + == other_output.0 .0.Mode.FrameBufferBase { log::debug!( "Skipping output with frame buffer base matching another output" @@ -180,6 +184,8 @@ impl Os for OsEfi { &self, password_opt: Option<&[u8]>, ) -> syscall::Result> { + let mut seen_enokey = false; + // Search for RedoxFS on disks in prioritized order println!("Looking for RedoxFS:"); for device in disk_device_priority() { @@ -200,6 +206,8 @@ impl Os for OsEfi { Err(err) => match err.errno { // Ignore header not found error syscall::ENOENT => (), + // Bubble ENOKEY up later if we can't find another partition to return + syscall::ENOKEY => seen_enokey = true, // Print any other errors _ => { log::warn!("BlockIo error: {:?}", err); @@ -208,6 +216,11 @@ impl Os for OsEfi { } } + // Let the caller prompt for a password + if seen_enokey { + return Err(syscall::Error::new(syscall::ENOKEY)); + } + log::warn!("No RedoxFS partitions found"); Err(syscall::Error::new(syscall::ENOENT)) } @@ -236,7 +249,7 @@ impl Os for OsEfi { let output_opt = match self.outputs.borrow_mut().get_mut(output_i) { Some(output) => unsafe { // Hack to enable clone - let ptr = output.0.0 as *mut _; + let ptr = output.0 .0 as *mut _; Some(Output::new(&mut *ptr)) }, None => None, @@ -320,6 +333,40 @@ impl Os for OsEfi { } } + fn get_key_timeout(&self, milliseconds: usize) -> OsKey { + let slices = milliseconds.div_ceil(100); + for _ in 0..slices { + let mut key = TextInputKey { + ScanCode: 0, + UnicodeChar: 0, + }; + + let status = (self.st.ConsoleIn.ReadKeyStroke)(self.st.ConsoleIn, &mut key); + if status.is_success() { + return match key.ScanCode { + 0 => match key.UnicodeChar { + 8 => OsKey::Backspace, + 13 => OsKey::Enter, + w => match char::from_u32(w as u32) { + Some(c) => OsKey::Char(c), + None => OsKey::Other, + }, + }, + 1 => OsKey::Up, + 2 => OsKey::Down, + 3 => OsKey::Right, + 4 => OsKey::Left, + 8 => OsKey::Delete, + _ => OsKey::Other, + }; + } + + let _ = status_to_result((self.st.BootServices.Stall)(100_000)); + } + + OsKey::Timeout + } + fn clear_text(&self) { //TODO: why does this sometimes return InvalidParameter, but otherwise appear to work? let _ = status_to_result((self.st.ConsoleOut.ClearScreen)(self.st.ConsoleOut)); diff --git a/src/os/uefi/video_mode.rs b/src/os/uefi/video_mode.rs index b6e020a69e..364dfbce1b 100644 --- a/src/os/uefi/video_mode.rs +++ b/src/os/uefi/video_mode.rs @@ -2,8 +2,8 @@ use core::ptr; use log::error; use uefi::status::Status; -use crate::os::OsVideoMode; use crate::os::uefi::display::Output; +use crate::os::OsVideoMode; pub struct VideoModeIter { output_opt: Option,