diff --git a/local/patches/bootloader/P1-bootloader-timeout-and-default-resolution.patch b/local/patches/bootloader/P1-bootloader-timeout-and-default-resolution.patch deleted file mode 100644 index 0b40c545a3..0000000000 --- a/local/patches/bootloader/P1-bootloader-timeout-and-default-resolution.patch +++ /dev/null @@ -1,564 +0,0 @@ -diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs -index 55c68f5..765ae41 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 542b059..78dabb0 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 -- }; -- } -- println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI 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, size as usize))); -- } -+ 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, -- }); -+ area_add(OsMemoryEntry { -+ base: live.as_ptr() as u64, -+ size: live.len() as u64, -+ kind: OsMemoryKind::Reserved, -+ }); - -- Some(live) -+ Some(live) -+ } else { -+ None -+ } - } else { - None - }; -diff --git a/src/os/bios/disk.rs b/src/os/bios/disk.rs -index 4570292..aeee85d 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 d47f6b2..78677ec 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 a9873d7..c016688 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 92c00c9..f01c222 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 3e36beb..bede418 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/riscv64/mod.rs b/src/os/uefi/arch/riscv64/mod.rs -index ac3bc49..92dc280 100644 ---- a/src/os/uefi/arch/riscv64/mod.rs -+++ b/src/os/uefi/arch/riscv64/mod.rs -@@ -1,8 +1,8 @@ --use crate::KernelArgs; - use crate::arch::PHYS_OFFSET; - use crate::logger::LOGGER; --use crate::os::OsEfi; - use crate::os::uefi::memory_map::memory_map; -+use crate::os::OsEfi; -+use crate::KernelArgs; - use core::arch::asm; - use core::mem; - use uefi::status::Result; -diff --git a/src/os/uefi/arch/x86_64.rs b/src/os/uefi/arch/x86_64.rs -index e1ecaa5..52cef13 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 36b48dc..0b7991f 100644 ---- a/src/os/uefi/device.rs -+++ b/src/os/uefi/device.rs -@@ -1,13 +1,13 @@ - 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}; - -diff --git a/src/os/uefi/disk.rs b/src/os/uefi/disk.rs -index 31e02db..3f920bb 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 95b7c2f..8bbe6e3 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 812c752..975c983 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 bbd034b..c79266e 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}; -@@ -103,7 +103,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" -@@ -236,7 +236,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 +320,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 b6e020a..364dfbc 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, diff --git a/local/patches/bootloader/P2-live-preload-guard.patch b/local/patches/bootloader/P2-live-preload-guard.patch deleted file mode 100644 index 41108b5514..0000000000 --- a/local/patches/bootloader/P2-live-preload-guard.patch +++ /dev/null @@ -1,97 +0,0 @@ -diff --git a/src/main.rs b/src/main.rs -index b2e2736..a6a9474 100644 ---- a/src/main.rs -+++ b/src/main.rs -@@ -500,36 +500,63 @@ pub extern "C" fn main() -> ! { - - 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 = unsafe { slice::from_raw_parts_mut(ptr, size as usize) }; -- -- 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, size as usize))); -- } -+ 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 -+ } -+ }; - -- area_add(OsMemoryEntry { -- base: live.as_ptr() as u64, -- size: live.len() as u64, -- kind: OsMemoryKind::Reserved, -- }); -+ 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 = if live { -+ Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) }) -+ } else { -+ println!("Continuing without live preload"); -+ None -+ }; -+ -+ 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 -+ } -- -- Some(live) - } else { - None - }; diff --git a/local/patches/bootloader/P4-live-large-iso-boot.patch b/local/patches/bootloader/P4-live-large-iso-boot.patch deleted file mode 100644 index a816e5dc66..0000000000 --- a/local/patches/bootloader/P4-live-large-iso-boot.patch +++ /dev/null @@ -1,392 +0,0 @@ -diff --git a/src/arch/x86/mod.rs b/src/arch/x86/mod.rs -index bda3f5d..55889df 100644 ---- a/src/arch/x86/mod.rs -+++ b/src/arch/x86/mod.rs -@@ -3,10 +3,15 @@ use crate::os::Os; - pub(crate) mod x32; - pub(crate) mod x64; - --pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option { -+pub unsafe fn paging_create( -+ os: &impl Os, -+ kernel_phys: u64, -+ kernel_size: u64, -+ identity_map_end: u64, -+) -> Option { - unsafe { - if crate::KERNEL_64BIT { -- x64::paging_create(os, kernel_phys, kernel_size) -+ x64::paging_create(os, kernel_phys, kernel_size, identity_map_end) - } else { - x32::paging_create(os, kernel_phys, kernel_size) - } -diff --git a/src/arch/x86/x64.rs b/src/arch/x86/x64.rs -index a0a275a..fcf309d 100644 ---- a/src/arch/x86/x64.rs -+++ b/src/arch/x86/x64.rs -@@ -29,7 +29,12 @@ const PRESENT: u64 = 1; - const WRITABLE: u64 = 1 << 1; - const LARGE: u64 = 1 << 7; - --pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option { -+pub unsafe fn paging_create( -+ os: &impl Os, -+ kernel_phys: u64, -+ kernel_size: u64, -+ identity_map_end: u64, -+) -> Option { - unsafe { - // Create PML4 - let pml4 = paging_allocate(os)?; -@@ -42,8 +47,14 @@ pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) - - pml4[0] = pdp.as_ptr() as u64 | WRITABLE | PRESENT; - pml4[256] = pdp.as_ptr() as u64 | WRITABLE | PRESENT; - -- // Identity map 8 GiB using 2 MiB pages -- for pdp_i in 0..8 { -+ let mut needed_pdp = identity_map_end.div_ceil(0x4000_0000); -+ if needed_pdp == 0 { -+ needed_pdp = 1; -+ } -+ assert!(needed_pdp <= pdp.len() as u64, "identity map end exceeds paging span"); -+ -+ // Identity map required physical range using 2 MiB pages -+ for pdp_i in 0..needed_pdp as usize { - let pd = paging_allocate(os)?; - pdp[pdp_i] = pd.as_ptr() as u64 | WRITABLE | PRESENT; - for pd_i in 0..pd.len() { -diff --git a/src/main.rs b/src/main.rs -index 78dabb0..fd8eb81 100644 ---- a/src/main.rs -+++ b/src/main.rs -@@ -62,6 +62,10 @@ pub static mut KERNEL_64BIT: bool = false; - - pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None; - -+fn region_end(base: u64, size: u64) -> u64 { -+ base.saturating_add(size).next_multiple_of(0x1000) -+} -+ - struct SliceWriter<'a> { - slice: &'a mut [u8], - i: usize, -@@ -645,9 +649,6 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) { - (memory.len() as u64, memory.as_mut_ptr() as u64) - }; - -- let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) } -- .expect("Failed to set up paging"); -- - let max_env_size = 64 * KIBI; - let mut env_size = max_env_size; - let env_base = os.alloc_zeroed_page_aligned(env_size); -@@ -655,6 +656,28 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) { - panic!("Failed to allocate memory for stack"); - } - -+ let mut identity_map_end = region_end(kernel.as_ptr() as u64, kernel.len() as u64) -+ .max(region_end(stack_base as u64, stack_size as u64)) -+ .max(region_end(bootstrap_base, bootstrap_size)) -+ .max(region_end(env_base as u64, max_env_size as u64)); -+ -+ if let Some(ref live) = live_opt { -+ identity_map_end = identity_map_end.max(region_end( -+ live.as_ptr() as u64, -+ live.len() as u64, -+ )); -+ } -+ -+ let page_phys = unsafe { -+ paging_create( -+ os, -+ kernel.as_ptr() as u64, -+ kernel.len() as u64, -+ identity_map_end, -+ ) -+ } -+ .expect("Failed to set up paging"); -+ - { - let mut w = SliceWriter { - slice: unsafe { slice::from_raw_parts_mut(env_base, max_env_size) }, -diff --git a/src/os/uefi/device.rs b/src/os/uefi/device.rs -index 0b7991f..554d88e 100644 ---- a/src/os/uefi/device.rs -+++ b/src/os/uefi/device.rs -@@ -13,6 +13,154 @@ use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol}; - - use super::disk::{DiskEfi, DiskOrFileEfi}; - -+#[derive(Clone, Copy)] -+struct GptPartitionInfo { -+ first_lba: u64, -+ last_lba: u64, -+} -+ -+fn read_u32_le(bytes: &[u8]) -> Option { -+ Some(u32::from_le_bytes(bytes.get(..4)?.try_into().ok()?)) -+} -+ -+fn read_u64_le(bytes: &[u8]) -> Option { -+ Some(u64::from_le_bytes(bytes.get(..8)?.try_into().ok()?)) -+} -+ -+fn decode_utf16_name(bytes: &[u8]) -> Option { -+ let mut units = Vec::new(); -+ for chunk in bytes.chunks_exact(2) { -+ let unit = u16::from_le_bytes([chunk[0], chunk[1]]); -+ if unit == 0 { -+ break; -+ } -+ units.push(unit); -+ } -+ String::from_utf16(&units).ok() -+} -+ -+fn select_partition(best: &mut Option, candidate: GptPartitionInfo) { -+ match best { -+ Some(current) if current.last_lba.saturating_sub(current.first_lba) >= candidate.last_lba.saturating_sub(candidate.first_lba) => {} -+ _ => *best = Some(candidate), -+ } -+} -+ -+fn parse_gpt_partition_offset_from_bytes(data: &[u8], block_size: usize) -> Option { -+ let header_offset = block_size; -+ let header = data.get(header_offset..header_offset + 92)?; -+ if header.get(..8)? != b"EFI PART" { -+ return None; -+ } -+ -+ let entries_lba = read_u64_le(header.get(72..80)?)?; -+ let entry_count = read_u32_le(header.get(80..84)?)? as usize; -+ let entry_size = read_u32_le(header.get(84..88)?)? as usize; -+ if entry_size < 128 { -+ return None; -+ } -+ -+ let entries_offset = entries_lba.checked_mul(block_size as u64)? as usize; -+ let mut redox_partition = None; -+ let mut fallback_partition = None; -+ -+ for index in 0..entry_count { -+ let entry_offset = entries_offset.checked_add(index.checked_mul(entry_size)?)?; -+ let entry = data.get(entry_offset..entry_offset + entry_size)?; -+ if entry.get(..16)?.iter().all(|byte| *byte == 0) { -+ continue; -+ } -+ -+ let first_lba = read_u64_le(entry.get(32..40)?)?; -+ let last_lba = read_u64_le(entry.get(40..48)?)?; -+ if first_lba == 0 || last_lba < first_lba { -+ continue; -+ } -+ -+ let partition = GptPartitionInfo { first_lba, last_lba }; -+ let name = decode_utf16_name(entry.get(56..128)?).unwrap_or_default(); -+ if name == "REDOX" { -+ redox_partition = Some(partition); -+ break; -+ } -+ -+ select_partition(&mut fallback_partition, partition); -+ } -+ -+ redox_partition -+ .or(fallback_partition) -+ .map(|partition| partition.first_lba * block_size as u64) -+} -+ -+fn parse_gpt_partition_offset_from_parts( -+ entries: &[u8], -+ entry_count: usize, -+ entry_size: usize, -+ block_size: usize, -+) -> Option { -+ let mut redox_partition = None; -+ let mut fallback_partition = None; -+ -+ for index in 0..entry_count { -+ let entry_offset = index.checked_mul(entry_size)?; -+ let entry = entries.get(entry_offset..entry_offset + entry_size)?; -+ if entry.get(..16)?.iter().all(|byte| *byte == 0) { -+ continue; -+ } -+ -+ let first_lba = read_u64_le(entry.get(32..40)?)?; -+ let last_lba = read_u64_le(entry.get(40..48)?)?; -+ if first_lba == 0 || last_lba < first_lba { -+ continue; -+ } -+ -+ let partition = GptPartitionInfo { first_lba, last_lba }; -+ let name = decode_utf16_name(entry.get(56..128)?).unwrap_or_default(); -+ if name == "REDOX" { -+ redox_partition = Some(partition); -+ break; -+ } -+ -+ select_partition(&mut fallback_partition, partition); -+ } -+ redox_partition -+ .or(fallback_partition) -+ .map(|partition| partition.first_lba * block_size as u64) -+} -+ -+fn gpt_partition_offset_from_buffer(data: &[u8]) -> Option { -+ parse_gpt_partition_offset_from_bytes(data, 512) -+} -+ -+fn gpt_partition_offset_from_disk(disk: &mut DiskEfi) -> Option { -+ const GPT_SECTOR_SIZE: usize = 512; -+ -+ if disk.media_block_size() == 0 { -+ return None; -+ } -+ -+ let mut boot_region = vec![0_u8; 2048]; -+ disk.read_bytes(0, &mut boot_region).ok()?; -+ let header = boot_region.get(GPT_SECTOR_SIZE..GPT_SECTOR_SIZE + 92)?; -+ if header.get(..8)? != b"EFI PART" { -+ return None; -+ } -+ -+ let entries_lba = read_u64_le(header.get(72..80)?)?; -+ let entry_count = read_u32_le(header.get(80..84)?)? as usize; -+ let entry_size = read_u32_le(header.get(84..88)?)? as usize; -+ if entry_size < 128 { -+ return None; -+ } -+ -+ let entries_bytes = entry_count.checked_mul(entry_size)?; -+ let entries_offset = entries_lba.checked_mul(GPT_SECTOR_SIZE as u64)?; -+ let mut entries = vec![0_u8; entries_bytes]; -+ disk.read_bytes(entries_offset, &mut entries).ok()?; -+ -+ parse_gpt_partition_offset_from_parts(&entries, entry_count, entry_size, GPT_SECTOR_SIZE) -+} -+ - #[derive(Debug)] - enum DevicePathRelation { - This, -@@ -131,12 +285,7 @@ pub fn disk_device_priority() -> Vec { - 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: gpt_partition_offset_from_buffer(&buffer).unwrap_or(0), - disk: DiskOrFileEfi::File(buffer), - device_path: esp_device_path, - file_path: Some("redox-live.iso"), -@@ -154,7 +303,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 +331,15 @@ pub fn disk_device_priority() -> Vec { - } - }; - -+ let partition_offset = if disk.0.Media.LogicalPartition { -+ 0 -+ } else { -+ gpt_partition_offset_from_disk(&mut disk).unwrap_or(2 * crate::MIBI as u64) -+ }; -+ - 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 3f920bb..4d109f8 100644 ---- a/src/os/uefi/disk.rs -+++ b/src/os/uefi/disk.rs -@@ -117,3 +117,43 @@ impl Disk for DiskEfi { - Err(Error::new(EIO)) - } - } -+ -+impl DiskEfi { -+ pub fn media_block_size(&self) -> usize { -+ self.0.Media.BlockSize as usize -+ } -+ -+ pub fn read_bytes(&mut self, offset: u64, buffer: &mut [u8]) -> Result<()> { -+ let block_size = self.media_block_size(); -+ if block_size == 0 || block_size > self.1.len() { -+ return Err(Error::new(EINVAL)); -+ } -+ -+ let scratch = &mut self.1[..block_size]; -+ let mut copied = 0usize; -+ -+ while copied < buffer.len() { -+ let absolute = offset as usize + copied; -+ let lba = (absolute / block_size) as u64; -+ let in_block = absolute % block_size; -+ -+ match (self.0.ReadBlocks)( -+ self.0, -+ self.0.Media.MediaId, -+ lba, -+ block_size, -+ scratch.as_mut_ptr(), -+ ) { -+ status if status.is_success() => { -+ let chunk_len = core::cmp::min(block_size - in_block, buffer.len() - copied); -+ buffer[copied..copied + chunk_len] -+ .copy_from_slice(&scratch[in_block..in_block + chunk_len]); -+ copied += chunk_len; -+ } -+ _ => return Err(Error::new(EIO)), -+ } -+ } -+ -+ Ok(()) -+ } -+} -diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs -index c79266e..86235a4 100644 ---- a/src/os/uefi/mod.rs -+++ b/src/os/uefi/mod.rs -@@ -47,17 +47,19 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 { - let ptr = { - // Max address mapped by src/arch paging code (8 GiB) - let mut ptr = 0x2_0000_0000; -- status_to_result((std::system_table().BootServices.AllocatePages)( -- 1, // AllocateMaxAddress -- MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list -+ if status_to_result((std::system_table().BootServices.AllocatePages)( -+ 0, // AllocateAnyPages -+ MemoryType::EfiLoaderData, - pages, - &mut ptr, - )) -- .unwrap(); -+ .is_err() -+ { -+ return ptr::null_mut(); -+ } - ptr as *mut u8 - }; - -- assert!(!ptr.is_null()); - unsafe { ptr::write_bytes(ptr, 0, pages * page_size) }; - ptr - } diff --git a/local/patches/bootloader/P5-live-preload-cap-1gib.patch b/local/patches/bootloader/P5-live-preload-cap-1gib.patch index 8b29b6301b..b0b762349b 100644 --- a/local/patches/bootloader/P5-live-preload-cap-1gib.patch +++ b/local/patches/bootloader/P5-live-preload-cap-1gib.patch @@ -1,196 +1,13 @@ -diff --git a/src/main.rs b/src/main.rs -index 542b059..adc8da3 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, - }; -@@ -62,6 +63,10 @@ pub static mut KERNEL_64BIT: bool = false; - - pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None; - -+fn region_end(base: u64, size: u64) -> u64 { -+ base.saturating_add(size).next_multiple_of(0x1000) -+} -+ - struct SliceWriter<'a> { - slice: &'a mut [u8], - i: usize, -@@ -114,6 +119,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 +150,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 +183,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 +213,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 +258,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 +270,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 +285,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 +300,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 +309,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 +319,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 +328,9 @@ fn select_mode( - } - break; - } -- _ => (), -+ OsKey::Other | OsKey::Backspace | OsKey::Delete | OsKey::Char(_) => { -+ countdown_active = false; -+ } - } - } - -@@ -496,38 +560,84 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) { +@@ -556,9 +556,21 @@ let live_opt = if live { let size = fs.header.size(); - print!("live: 0/{} MiB", size / MIBI as u64); -+ let max_preload: u64 = 1024 * MIBI as u64; // Cap live preload at 1 GiB +- +- let live_size = match usize::try_from(size) { ++ let max_preload: u64 = 1024 * MIBI as u64; + let preload_size = if size > max_preload { + println!( + "live: filesystem is {} MiB, capping preload at {} MiB", @@ -201,71 +18,26 @@ index 542b059..adc8da3 100644 + } else { + size + }; - -- let ptr = os.alloc_zeroed_page_aligned(size as usize); -- if ptr.is_null() { -- panic!("Failed to allocate memory for live"); -- } ++ + print!("live: 0/{} MiB", preload_size / MIBI as u64); - -- let live = unsafe { slice::from_raw_parts_mut(ptr, size as usize) }; ++ + let live_size = match usize::try_from(preload_size) { -+ Ok(live_size) => live_size, -+ Err(_) => { -+ println!("\rlive: disabled (image too large for bootloader address space)"); -+ live = false; -+ 0 -+ } -+ }; - -- 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 -- }; -+ 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; - } -- 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))); -- } -+ let live = if live { -+ Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) }) -+ } else { -+ println!("Continuing without live preload"); -+ None -+ }; - -- area_add(OsMemoryEntry { -- base: live.as_ptr() as u64, -- size: live.len() as u64, -- kind: OsMemoryKind::Reserved, -- }); -+ if let Some(live) = live { -+ let mut i = 0; -+ for chunk in live.chunks_mut(MIBI) { + Ok(live_size) => live_size, + Err(_) => { + println!("\rlive: disabled (image too large for bootloader address space)"); +@@ -590,14 +602,23 @@ + 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); + print!("\rlive: {}/{} MiB", i / MIBI as u64, preload_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 -+ }; -+ } + 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!("\rlive: {}/{} MiB", i / MIBI as u64, preload_size / MIBI as u64); + + if preload_size < size { @@ -276,61 +48,6 @@ index 542b059..adc8da3 100644 + (size - preload_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) -+ Some(live) -+ } else { -+ None -+ } - } else { - None - }; -@@ -555,9 +665,6 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) { - (memory.len() as u64, memory.as_mut_ptr() as u64) - }; - -- let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) } -- .expect("Failed to set up paging"); -- - let max_env_size = 64 * KIBI; - let mut env_size = max_env_size; - let env_base = os.alloc_zeroed_page_aligned(env_size); -@@ -565,6 +672,28 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) { - panic!("Failed to allocate memory for stack"); - } - -+ let mut identity_map_end = region_end(kernel.as_ptr() as u64, kernel.len() as u64) -+ .max(region_end(stack_base as u64, stack_size as u64)) -+ .max(region_end(bootstrap_base, bootstrap_size)) -+ .max(region_end(env_base as u64, max_env_size as u64)); -+ -+ if let Some(ref live) = live_opt { -+ identity_map_end = identity_map_end.max(region_end( -+ live.as_ptr() as u64, -+ live.len() as u64, -+ )); -+ } -+ -+ let page_phys = unsafe { -+ paging_create( -+ os, -+ kernel.as_ptr() as u64, -+ kernel.len() as u64, -+ identity_map_end, -+ ) -+ } -+ .expect("Failed to set up paging"); -+ - { - let mut w = SliceWriter { - slice: unsafe { slice::from_raw_parts_mut(env_base, max_env_size) }, + println!("Switching to live disk"); + unsafe { diff --git a/local/patches/bootloader/P3-uefi-live-image-safe-read.patch b/local/patches/bootloader/absorbed/P3-uefi-live-image-safe-read.patch similarity index 53% rename from local/patches/bootloader/P3-uefi-live-image-safe-read.patch rename to local/patches/bootloader/absorbed/P3-uefi-live-image-safe-read.patch index 9051018cf1..43640d93e7 100644 --- a/local/patches/bootloader/P3-uefi-live-image-safe-read.patch +++ b/local/patches/bootloader/absorbed/P3-uefi-live-image-safe-read.patch @@ -1,22 +1,15 @@ -diff --git a/src/os/uefi/device.rs b/src/os/uefi/device.rs -index 4b0bf31..90a97b8 100644 --- a/src/os/uefi/device.rs +++ b/src/os/uefi/device.rs -@@ -46,6 +46,8 @@ fn device_path_relation(a_path: &DevicePath, b_path: &DevicePath) -> DevicePath - } - - fn esp_live_image(esp_handle: Handle, esp_device_path: &DevicePath) -> Option> { -+ const MAX_LIVE_IMAGE_PRELOAD: usize = 128 * 1024 * 1024; -+ - let mut esp_fs = match FileSystem::handle_protocol(esp_handle) { - Ok(esp_fs) => esp_fs, - Err(err) => { -@@ -87,8 +89,36 @@ fn esp_live_image(esp_handle: Handle, esp_device_path: &DevicePath) -> Option read, @@ -33,8 +26,7 @@ index 4b0bf31..90a97b8 100644 + if read == 0 { + break; + } - -- live_image.read_to_end(&mut buffer).unwrap(); ++ + if buffer.len().saturating_add(read) > MAX_LIVE_IMAGE_PRELOAD { + log::warn!( + "Skipping {}\\redox-live.iso preload: file exceeds {} MiB safety limit", @@ -49,12 +41,12 @@ index 4b0bf31..90a97b8 100644 Some(buffer) } -@@ -130,7 +160,7 @@ pub fn disk_device_priority() -> Vec { - 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" { -+ partition_offset: if buffer.len() >= 520 && &buffer[512..520] == b"EFI PART" { - //TODO: get block from partition table - 2 * crate::MIBI as u64 - } else { +@@ -267,7 +297,7 @@ + + if cfg!(feature = "live") { + 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" { ++ let partition_offset = if buffer.len() >= 520 && &buffer[512..520] == b"EFI PART" { + gpt_find_redoxfs_offset_from_slice(&buffer) + } else { + 0 diff --git a/local/patches/bootloader/absorbed/P4-live-large-iso-boot.patch b/local/patches/bootloader/absorbed/P4-live-large-iso-boot.patch new file mode 100644 index 0000000000..c11abba9a6 --- /dev/null +++ b/local/patches/bootloader/absorbed/P4-live-large-iso-boot.patch @@ -0,0 +1,215 @@ +--- a/src/arch/x86/mod.rs ++++ b/src/arch/x86/mod.rs +@@ -3,10 +3,15 @@ + pub(crate) mod x32; + pub(crate) mod x64; + +-pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option { ++pub unsafe fn paging_create( ++ os: &impl Os, ++ kernel_phys: u64, ++ kernel_size: u64, ++ identity_map_end: u64, ++) -> Option { + unsafe { + if crate::KERNEL_64BIT { +- x64::paging_create(os, kernel_phys, kernel_size) ++ x64::paging_create(os, kernel_phys, kernel_size, identity_map_end) + } else { + x32::paging_create(os, kernel_phys, kernel_size) + } +--- a/src/arch/x86/x64.rs ++++ b/src/arch/x86/x64.rs +@@ -29,7 +29,12 @@ + const WRITABLE: u64 = 1 << 1; + const LARGE: u64 = 1 << 7; + +-pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option { ++pub unsafe fn paging_create( ++ os: &impl Os, ++ kernel_phys: u64, ++ kernel_size: u64, ++ identity_map_end: u64, ++) -> Option { + unsafe { + // Create PML4 + let pml4 = paging_allocate(os)?; +@@ -42,8 +47,14 @@ + pml4[0] = pdp.as_ptr() as u64 | WRITABLE | PRESENT; + pml4[256] = pdp.as_ptr() as u64 | WRITABLE | PRESENT; + +- // Identity map 8 GiB using 2 MiB pages +- for pdp_i in 0..8 { ++ let mut needed_pdp = identity_map_end.div_ceil(0x4000_0000); ++ if needed_pdp == 0 { ++ needed_pdp = 1; ++ } ++ assert!(needed_pdp <= pdp.len() as u64, "identity map end exceeds paging span"); ++ ++ // Identity map required physical range using 2 MiB pages ++ for pdp_i in 0..needed_pdp as usize { + let pd = paging_allocate(os)?; + pdp[pdp_i] = pd.as_ptr() as u64 | WRITABLE | PRESENT; + for pd_i in 0..pd.len() { +--- a/src/main.rs ++++ b/src/main.rs +@@ -641,15 +641,34 @@ + (memory.len() as u64, memory.as_mut_ptr() as u64) + }; + +- let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) } +- .expect("Failed to set up paging"); +- + let max_env_size = 64 * KIBI; + let mut env_size = max_env_size; + let env_base = os.alloc_zeroed_page_aligned(env_size); + if env_base.is_null() { + panic!("Failed to allocate memory for stack"); + } ++ ++ let mut identity_map_end = region_end(kernel.as_ptr() as u64, kernel.len() as u64) ++ .max(region_end(stack_base as u64, stack_size as u64)) ++ .max(region_end(bootstrap_base, bootstrap_size)) ++ .max(region_end(env_base as u64, max_env_size as u64)); ++ ++ if let Some(ref live) = live_opt { ++ identity_map_end = identity_map_end.max(region_end( ++ live.as_ptr() as u64, ++ live.len() as u64, ++ )); ++ } ++ ++ let page_phys = unsafe { ++ paging_create( ++ os, ++ kernel.as_ptr() as u64, ++ kernel.len() as u64, ++ identity_map_end, ++ ) ++ } ++ .expect("Failed to set up paging"); + + { + let mut w = SliceWriter { +--- a/src/os/uefi/device.rs ++++ b/src/os/uefi/device.rs +@@ -26,19 +26,10 @@ + } + + 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() { ++ if disk.read_bytes(block_size, &mut header_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"); +@@ -64,19 +55,15 @@ + } + + 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() { ++ let mut entries_buf = vec![0u8; total_entries_bytes]; ++ if disk.read_bytes(entries_byte_offset, &mut entries_buf).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) { ++ for i in 0..num_entries as usize { + let off = i * entry_size as usize; + if off + entry_size as usize > entries_buf.len() { + break; +--- a/src/os/uefi/disk.rs ++++ b/src/os/uefi/disk.rs +@@ -117,3 +117,43 @@ + Err(Error::new(EIO)) + } + } ++ ++impl DiskEfi { ++ pub fn media_block_size(&self) -> usize { ++ self.0.Media.BlockSize as usize ++ } ++ ++ pub fn read_bytes(&mut self, offset: u64, buffer: &mut [u8]) -> Result<()> { ++ let block_size = self.media_block_size(); ++ if block_size == 0 || block_size > self.1.len() { ++ return Err(Error::new(EINVAL)); ++ } ++ ++ let scratch = &mut self.1[..block_size]; ++ let mut copied = 0usize; ++ ++ while copied < buffer.len() { ++ let absolute = offset as usize + copied; ++ let lba = (absolute / block_size) as u64; ++ let in_block = absolute % block_size; ++ ++ match (self.0.ReadBlocks)( ++ self.0, ++ self.0.Media.MediaId, ++ lba, ++ block_size, ++ scratch.as_mut_ptr(), ++ ) { ++ status if status.is_success() => { ++ let chunk_len = core::cmp::min(block_size - in_block, buffer.len() - copied); ++ buffer[copied..copied + chunk_len] ++ .copy_from_slice(&scratch[in_block..in_block + chunk_len]); ++ copied += chunk_len; ++ } ++ _ => return Err(Error::new(EIO)), ++ } ++ } ++ ++ Ok(()) ++ } ++} +--- a/src/os/uefi/mod.rs ++++ b/src/os/uefi/mod.rs +@@ -45,19 +45,18 @@ + let pages = size.div_ceil(page_size); + + let ptr = { +- // Max address mapped by src/arch paging code (8 GiB) + let mut ptr = 0x2_0000_0000; +- status_to_result((std::system_table().BootServices.AllocatePages)( +- 1, // AllocateMaxAddress +- MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list ++ if status_to_result((std::system_table().BootServices.AllocatePages)( ++ 0, ++ MemoryType::EfiLoaderData, + pages, + &mut ptr, + )) +- .unwrap_or_else(|_| { +- ptr = 0; +- 0 +- }); +- if ptr == 0 { ptr::null_mut() } else { ptr as *mut u8 } ++ .is_err() ++ { ++ return ptr::null_mut(); ++ } ++ ptr as *mut u8 + }; + + if !ptr.is_null() { diff --git a/recipes/core/bootloader/P3-uefi-live-image-safe-read.patch b/recipes/core/bootloader/P3-uefi-live-image-safe-read.patch index 815cca72fd..761c7b879a 120000 --- a/recipes/core/bootloader/P3-uefi-live-image-safe-read.patch +++ b/recipes/core/bootloader/P3-uefi-live-image-safe-read.patch @@ -1 +1 @@ -../../../local/patches/bootloader/P3-uefi-live-image-safe-read.patch \ No newline at end of file +../../../local/patches/bootloader/absorbed/P3-uefi-live-image-safe-read.patch \ No newline at end of file diff --git a/recipes/core/bootloader/P4-live-large-iso-boot.patch b/recipes/core/bootloader/P4-live-large-iso-boot.patch index 3adf823ef9..8a5f6bb5de 120000 --- a/recipes/core/bootloader/P4-live-large-iso-boot.patch +++ b/recipes/core/bootloader/P4-live-large-iso-boot.patch @@ -1 +1 @@ -../../../local/patches/bootloader/P4-live-large-iso-boot.patch \ No newline at end of file +../../../local/patches/bootloader/absorbed/P4-live-large-iso-boot.patch \ No newline at end of file diff --git a/recipes/core/bootloader/recipe.toml b/recipes/core/bootloader/recipe.toml index 32431b01fd..cc0fc74a9a 100644 --- a/recipes/core/bootloader/recipe.toml +++ b/recipes/core/bootloader/recipe.toml @@ -1,6 +1,6 @@ [source] git = "https://gitlab.redox-os.org/redox-os/bootloader.git" -patches = ["redox.patch", "fix-uefi-alloc-panic.patch", "P0-gpt-partition-offset.patch"] +patches = ["redox.patch", "fix-uefi-alloc-panic.patch", "P0-gpt-partition-offset.patch", "P5-live-preload-cap-1gib.patch"] [build] template = "custom"