bootloader: clean per-phase screens + Red Bear branding
Comprehensive fix for overlapping bootloader text. The loader ran three phases
(filesystem, resolution menu, loading) that all wrote to one never-fully-cleared
screen at absolute cursor positions, so they overprinted each other. Most
visibly, the loading phase began printing "live: .../..." exactly where the menu
had left the cursor (the "Autobooting" row), so "live" landed on top of the
countdown. clear_text() also only ran for the 2nd+ video output, so on a single
display the menu was drawn under the header.
Introduce draw_header(os): clear the screen and draw a consistent, branded
"Red Bear OS" title + version/platform + separator. Call it at the start of each
phase so every screen is clean and no phase overprints another:
- Phase 1 (filesystem / password prompt)
- Phase 2 (resolution menu, per video output)
- Phase 3 (kernel/initfs load + optional live preload)
Also:
- Rebrand user-facing strings: "Redox OS Bootloader" -> "Red Bear OS", and
the env editor title -> "Red Bear OS Boot Environment Editor". (RedoxFS /
the RedoxFtw initfs magic are left as-is: those are real format names.)
- Drop the raw "Hardware descriptor: {:x?}" Debug dump from the UI.
- Indent all loading-phase progress lines (RedoxFS/live/kernel/initfs) to
match the header for a consistent layout.
Verified: cargo check passes for x86_64-unknown-uefi (--bin) and
x86-unknown-none (--lib).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ use crate::os::{Os, OsKey};
|
||||
|
||||
fn edit_banner(os: &impl Os) {
|
||||
os.clear_text();
|
||||
println!("--- Redox Bootloader Environment Editor ---");
|
||||
println!("--- Red Bear OS Boot Environment Editor ---");
|
||||
println!("ENTER twice to boot. UP/DOWN to edit lines.");
|
||||
println!("-------------------------------------------");
|
||||
}
|
||||
|
||||
+51
-30
@@ -509,7 +509,7 @@ fn load_to_memory<O: Os>(
|
||||
|
||||
let size = node.data().size();
|
||||
|
||||
print!("{}: 0/{} MiB", path, size / MIBI as u64);
|
||||
print!(" {}: 0/{} MiB", path, size / MIBI as u64);
|
||||
|
||||
let ptr = os.alloc_zeroed_page_aligned(size as usize);
|
||||
if ptr.is_null() {
|
||||
@@ -520,13 +520,13 @@ fn load_to_memory<O: Os>(
|
||||
|
||||
let mut i = 0;
|
||||
for chunk in slice.chunks_mut(MIBI) {
|
||||
print!("\r{}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
|
||||
print!("\r {}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
|
||||
i += tx
|
||||
.read_node_inner(&node, i, chunk)
|
||||
.unwrap_or_else(|err| panic!("Failed to read `{}` file: {}", path, err))
|
||||
as u64;
|
||||
}
|
||||
println!("\r{}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
|
||||
println!("\r {}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
|
||||
|
||||
if filetype == Filetype::Elf {
|
||||
let magic = &slice[..4];
|
||||
@@ -586,15 +586,43 @@ fn elf_entry(data: &[u8]) -> (u64, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the screen and draw the Red Bear OS bootloader header.
|
||||
///
|
||||
/// Called at the start of each phase (filesystem, resolution menu, loading) so
|
||||
/// every phase renders on a clean, consistently-branded screen and no phase
|
||||
/// overprints another. Previously only the second-and-later video outputs were
|
||||
/// cleared, so on a single display the menu was drawn under the header and the
|
||||
/// loading progress was drawn on top of the still-visible menu (e.g. "live:"
|
||||
/// landing on the "Autobooting" line).
|
||||
fn draw_header(os: &impl Os) {
|
||||
os.clear_text();
|
||||
os.set_text_position(0, 0);
|
||||
os.set_text_color(TextColor::Red);
|
||||
println!(" Red Bear OS");
|
||||
os.set_text_color(TextColor::Default);
|
||||
println!(" Bootloader {} ({})", env!("CARGO_PKG_VERSION"), os.name());
|
||||
println!(" --------------------------------------------");
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Print the RedoxFS identity line (uuid + size) below the current cursor.
|
||||
fn print_fs_line<D: Disk>(fs: &redoxfs::FileSystem<D>) {
|
||||
print!(" RedoxFS ");
|
||||
for i in 0..fs.header.uuid().len() {
|
||||
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||
print!("-");
|
||||
}
|
||||
print!("{:>02x}", fs.header.uuid()[i]);
|
||||
}
|
||||
println!(": {} MiB", fs.header.size() / MIBI as u64);
|
||||
}
|
||||
|
||||
fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
println!(
|
||||
"Redox OS Bootloader {} on {}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
os.name()
|
||||
);
|
||||
// Phase 1: open the filesystem. redoxfs() prompts for a password on this
|
||||
// header screen if the disk is encrypted.
|
||||
draw_header(os);
|
||||
|
||||
let hwdesc = os.hwdesc();
|
||||
println!("Hardware descriptor: {:x?}", hwdesc);
|
||||
let (acpi_rsdp_base, acpi_rsdp_size) = match hwdesc {
|
||||
OsHwDesc::Acpi(base, size) => (base, size),
|
||||
OsHwDesc::DeviceTree(base, size) => (base, size),
|
||||
@@ -603,24 +631,12 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
|
||||
let (mut fs, password_opt) = redoxfs(os);
|
||||
|
||||
print!("RedoxFS ");
|
||||
for i in 0..fs.header.uuid().len() {
|
||||
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||
print!("-");
|
||||
}
|
||||
|
||||
print!("{:>02x}", fs.header.uuid()[i]);
|
||||
}
|
||||
println!(": {} MiB", fs.header.size() / MIBI as u64);
|
||||
println!();
|
||||
|
||||
// Phase 2: resolution selection — a clean, titled screen per video output.
|
||||
let mut mode_opts = Vec::new();
|
||||
let mut live = cfg!(feature = "live");
|
||||
let mut edit_env = false;
|
||||
for output_i in 0..os.video_outputs() {
|
||||
if output_i > 0 {
|
||||
os.clear_text();
|
||||
}
|
||||
draw_header(os);
|
||||
mode_opts.push(select_mode(os, output_i, &mut live, &mut edit_env));
|
||||
}
|
||||
|
||||
@@ -630,15 +646,20 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
panic!("Failed to allocate memory for stack");
|
||||
}
|
||||
|
||||
// Phase 3: load kernel + initfs (and optionally preload the live image) on a
|
||||
// fresh titled screen, so loading progress never overprints the menu.
|
||||
draw_header(os);
|
||||
print_fs_line(&fs);
|
||||
|
||||
let live_opt = if live {
|
||||
let size = fs.header.size();
|
||||
|
||||
print!("live: 0/{} MiB", size / MIBI as u64);
|
||||
print!(" live: 0/{} MiB", size / MIBI as u64);
|
||||
|
||||
let live_size = match usize::try_from(size) {
|
||||
Ok(live_size) => live_size,
|
||||
Err(_) => {
|
||||
println!("\rlive: disabled (image too large for bootloader address space)");
|
||||
println!("\r live: disabled (image too large for bootloader address space)");
|
||||
live = false;
|
||||
0
|
||||
}
|
||||
@@ -651,7 +672,7 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
};
|
||||
if live && ptr.is_null() {
|
||||
println!(
|
||||
"\rlive: disabled (unable to allocate {} MiB upfront)",
|
||||
"\r live: disabled (unable to allocate {} MiB upfront)",
|
||||
size / MIBI as u64
|
||||
);
|
||||
live = false;
|
||||
@@ -660,23 +681,23 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||
let live = if live {
|
||||
Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) })
|
||||
} else {
|
||||
println!("Continuing without live preload");
|
||||
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);
|
||||
print!("\r live: {}/{} 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!("\r live: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||
|
||||
println!("Switching to live disk");
|
||||
println!(" Switching to live disk");
|
||||
unsafe {
|
||||
LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, live_size)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user