diff --git a/local/patches/bootloader/P1-bootloader-timeout-and-default-resolution.patch b/local/patches/bootloader/P1-bootloader-timeout-and-default-resolution.patch new file mode 100644 index 00000000..0b40c545 --- /dev/null +++ b/local/patches/bootloader/P1-bootloader-timeout-and-default-resolution.patch @@ -0,0 +1,564 @@ +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/redox.patch b/local/patches/bootloader/redox.patch new file mode 100644 index 00000000..0b40c545 --- /dev/null +++ b/local/patches/bootloader/redox.patch @@ -0,0 +1,564 @@ +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/recipes/core/bootloader/redox.patch b/recipes/core/bootloader/redox.patch new file mode 120000 index 00000000..e87c26fa --- /dev/null +++ b/recipes/core/bootloader/redox.patch @@ -0,0 +1 @@ +../../../local/patches/bootloader/redox.patch \ No newline at end of file