redbear: migrate bootloader patches into local fork

Apply the full Red Bear bootloader patch set:
- P0-gpt-partition-offset
- fix-uefi-alloc-panic
- redox.patch (Makefile/mk and misc fixes)
- P1-bootloader-timeout-and-default-resolution
- P2-live-preload-guard
- P3-uefi-live-image-safe-read
- P4-live-large-iso-boot
- P5-live-preload-cap-1gib

Also switch redoxfs dependency to the local fork path.
This commit is contained in:
2026-07-06 08:03:08 +03:00
parent 6c3c312819
commit ea5b2418e6
22 changed files with 724 additions and 116 deletions
+1 -1
View File
@@ -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;
+121 -35
View File
@@ -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<OsVideoMode> {
const DEFAULT_WIDTH: u32 = 1280;
const DEFAULT_HEIGHT: u32 = 720;
const AUTOBOOT_SECONDS: usize = 5;
let mut modes = Vec::new();
for mode in os.video_modes(output_i) {
let mut aspect_w = mode.width;
@@ -141,15 +146,26 @@ fn select_mode(
// Sort modes by pixel area, reversed
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
// Set selected based on best resolution
// Set selected based on Red Bear default resolution first, then best resolution fallback
print!("Output {}", output_i);
let mut selected = modes.first().map_or(0, |x| x.0.id);
if let Some((best_width, best_height)) = os.best_resolution(output_i) {
print!(", best resolution: {}x{}", best_width, best_height);
for (mode, _text) in modes.iter() {
if mode.width == best_width && mode.height == best_height {
selected = mode.id;
break;
let mut selected_from_default = false;
for (mode, _text) in modes.iter() {
if mode.width == DEFAULT_WIDTH && mode.height == DEFAULT_HEIGHT {
selected = mode.id;
selected_from_default = true;
print!(", default resolution: {}x{}", DEFAULT_WIDTH, DEFAULT_HEIGHT);
break;
}
}
if !selected_from_default {
if let Some((best_width, best_height)) = os.best_resolution(output_i) {
print!(", best resolution: {}x{}", best_width, best_height);
for (mode, _text) in modes.iter() {
if mode.width == best_width && mode.height == best_height {
selected = mode.id;
break;
}
}
}
}
@@ -163,12 +179,19 @@ fn select_mode(
println!("Press l to enable live mode");
}
println!("Press e to edit boot environment");
println!(
"Autobooting default mode in {} seconds (press any key to cancel countdown)",
AUTOBOOT_SECONDS
);
println!();
print!(" ");
let (off_x, off_y) = os.get_text_position();
let rows = 12;
let mut mode_opt = None;
let countdown_y = off_y.saturating_sub(2);
let mut countdown = AUTOBOOT_SECONDS;
let mut countdown_active = true;
while !modes.is_empty() {
let mut row = 0;
let mut col = 0;
@@ -186,9 +209,38 @@ fn select_mode(
row += 1;
}
os.set_text_position(0, countdown_y);
os.set_text_highlight(false);
if countdown_active {
println!(
"Autobooting default mode in {} seconds (press any key to cancel countdown)",
countdown
);
} else {
println!("Manual mode selection active. Press Enter to boot selected mode. ");
}
// Read keypress
match os.get_key() {
match if countdown_active {
os.get_key_timeout(1000)
} else {
os.get_key()
} {
OsKey::Timeout => {
if countdown_active {
if countdown == 0 {
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
mode_opt = Some(*mode);
}
}
break;
}
countdown = countdown.saturating_sub(1);
}
}
OsKey::Left => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
if mode_i < rows {
while mode_i < modes.len() {
@@ -202,6 +254,7 @@ fn select_mode(
}
}
OsKey::Right => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
mode_i += rows;
if mode_i >= modes.len() {
@@ -213,6 +266,7 @@ fn select_mode(
}
}
OsKey::Up => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
if mode_i % rows == 0 {
mode_i += rows;
@@ -227,6 +281,7 @@ fn select_mode(
}
}
OsKey::Down => {
countdown_active = false;
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
mode_i += 1;
if mode_i % rows == 0 {
@@ -241,6 +296,7 @@ fn select_mode(
}
}
OsKey::Enter => {
countdown_active = false;
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
mode_opt = Some(*mode);
@@ -249,6 +305,7 @@ fn select_mode(
break;
}
OsKey::Char('l') => {
countdown_active = false;
*live = !*live;
os.set_text_position(live_mode.0, live_mode.1);
if *live {
@@ -258,6 +315,7 @@ fn select_mode(
}
}
OsKey::Char('e') => {
countdown_active = false;
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
*edit_env = true;
@@ -266,7 +324,9 @@ fn select_mode(
}
break;
}
_ => (),
OsKey::Other | OsKey::Backspace | OsKey::Delete | OsKey::Char(_) => {
countdown_active = false;
}
}
}
@@ -498,36 +558,62 @@ fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
print!("live: 0/{} MiB", size / MIBI as u64);
let ptr = os.alloc_zeroed_page_aligned(size as usize);
if ptr.is_null() {
panic!("Failed to allocate memory for live");
let live_size = match usize::try_from(size) {
Ok(live_size) => live_size,
Err(_) => {
println!("\rlive: disabled (image too large for bootloader address space)");
live = false;
0
}
};
let ptr = if live {
os.alloc_zeroed_page_aligned(live_size)
} else {
ptr::null_mut()
};
if live && ptr.is_null() {
println!(
"\rlive: disabled (unable to allocate {} MiB upfront)",
size / MIBI as u64
);
live = false;
}
let live = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
let live = if live {
Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) })
} else {
println!("Continuing without live preload");
None
};
let mut i = 0;
for chunk in live.chunks_mut(MIBI) {
print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
i += unsafe {
fs.disk
.read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
.expect("Failed to read live disk") as u64
};
if let Some(live) = live {
let mut i = 0;
for chunk in live.chunks_mut(MIBI) {
print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
i += unsafe {
fs.disk
.read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
.expect("Failed to read live disk") as u64
};
}
println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
println!("Switching to live disk");
unsafe {
LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, live_size)));
}
area_add(OsMemoryEntry {
base: live.as_ptr() as u64,
size: live.len() as u64,
kind: OsMemoryKind::Reserved,
});
Some(live)
} else {
None
}
println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
println!("Switching to live disk");
unsafe {
LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, size as usize)));
}
area_add(OsMemoryEntry {
base: live.as_ptr() as u64,
size: live.len() as u64,
kind: OsMemoryKind::Reserved,
});
Some(live)
} else {
None
};
+3 -3
View File
@@ -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;
+1 -1
View File
@@ -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 {
+9 -4
View File
@@ -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
+4
View File
@@ -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);
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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,
+154 -16
View File
@@ -1,17 +1,157 @@
use alloc::{string::String, vec, vec::Vec};
use core::{fmt::Write, mem, ptr, slice};
use uefi::{
Handle,
device::{
DevicePath, DevicePathAcpiType, DevicePathBbsType, DevicePathEndType,
DevicePathHardwareType, DevicePathMediaType, DevicePathMessagingType, DevicePathType,
},
guid::Guid,
status::Status,
Handle,
};
use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol};
use super::disk::{DiskEfi, DiskOrFileEfi};
use redoxfs::Disk;
const LINUX_FS_GUID: [u8; 16] = [
0xAF, 0x3D, 0xC6, 0x0F, 0x83, 0x84, 0x72, 0x47,
0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4,
];
fn gpt_find_redoxfs_offset(disk: &mut DiskEfi) -> u64 {
let block_size = disk.0.Media.BlockSize as u64;
if block_size == 0 {
return 2 * crate::MIBI as u64;
}
let mut header_buf = [0u8; 512];
let lba1_block = block_size / redoxfs::BLOCK_SIZE;
let header_read_len = if 512 <= redoxfs::BLOCK_SIZE as usize {
redoxfs::BLOCK_SIZE as usize
} else {
512
};
let mut read_buf = vec![0u8; header_read_len];
if unsafe { disk.read_at(lba1_block, &mut read_buf) }.is_err() {
log::warn!("GPT: failed to read LBA 1");
return 2 * crate::MIBI as u64;
}
let copy_len = 512.min(read_buf.len());
header_buf[..copy_len].copy_from_slice(&read_buf[..copy_len]);
if &header_buf[0..8] != b"EFI PART" {
log::warn!("GPT: no valid signature at LBA 1");
return 2 * crate::MIBI as u64;
}
let read_le_u32 = |off: usize| -> u32 {
u32::from_le_bytes(header_buf[off..off + 4].try_into().unwrap_or([0; 4]))
};
let read_le_u64 = |off: usize| -> u64 {
u64::from_le_bytes(header_buf[off..off + 8].try_into().unwrap_or([0; 8]))
};
let _revision = read_le_u32(8);
let _header_size = read_le_u32(12);
let partition_entry_start_lba = read_le_u64(72);
let num_entries = read_le_u32(80);
let entry_size = read_le_u32(84);
if num_entries == 0 || entry_size == 0 || num_entries > 128 {
log::warn!("GPT: invalid entry count {} or size {}", num_entries, entry_size);
return 2 * crate::MIBI as u64;
}
let entries_byte_offset = partition_entry_start_lba * block_size;
let entries_start_block = entries_byte_offset / redoxfs::BLOCK_SIZE;
let total_entries_bytes = num_entries as usize * entry_size as usize;
let read_size = total_entries_bytes.min(4096);
let mut entries_buf = vec![0u8; read_size + redoxfs::BLOCK_SIZE as usize];
let entries_read_blocks = (read_size as u64).div_ceil(redoxfs::BLOCK_SIZE) as usize;
if unsafe { disk.read_at(entries_start_block, &mut entries_buf[..entries_read_blocks * redoxfs::BLOCK_SIZE as usize]) }.is_err() {
log::warn!("GPT: failed to read partition entries");
return 2 * crate::MIBI as u64;
}
let entries_per_chunk = read_size / entry_size as usize;
for i in 0..entries_per_chunk.min(num_entries as usize) {
let off = i * entry_size as usize;
if off + entry_size as usize > entries_buf.len() {
break;
}
let entry = &entries_buf[off..off + entry_size as usize];
let type_guid = &entry[0..16];
if type_guid == [0u8; 16] {
continue;
}
if type_guid == LINUX_FS_GUID {
let first_lba = u64::from_le_bytes(
entry[32..40].try_into().unwrap_or([0; 8]),
);
let offset_bytes = first_lba * block_size;
log::debug!("GPT: found REDOXFS/Linux partition at LBA {} (offset {} bytes)", first_lba, offset_bytes);
return offset_bytes;
}
}
log::warn!("GPT: no Linux filesystem partition found, falling back to 2 MiB");
2 * crate::MIBI as u64
}
fn gpt_find_redoxfs_offset_from_slice(data: &[u8]) -> u64 {
if data.len() < 520 {
return 2 * crate::MIBI as u64;
}
let header = &data[512..];
if &header[0..8] != b"EFI PART" {
return 2 * crate::MIBI as u64;
}
let read_le_u32 = |off: usize| -> u32 {
u32::from_le_bytes(header[off..off + 4].try_into().unwrap_or([0; 4]))
};
let read_le_u64 = |off: usize| -> u64 {
u64::from_le_bytes(header[off..off + 8].try_into().unwrap_or([0; 8]))
};
let partition_entry_start_lba = read_le_u64(72);
let num_entries = read_le_u32(80);
let entry_size = read_le_u32(84);
if num_entries == 0 || entry_size == 0 || num_entries > 128 {
return 2 * crate::MIBI as u64;
}
let gpt_lba_size: u64 = 512;
let entries_byte_offset = partition_entry_start_lba * gpt_lba_size;
for i in 0..num_entries as usize {
let off = entries_byte_offset as usize + i * entry_size as usize;
if off + entry_size as usize > data.len() {
break;
}
let entry = &data[off..off + entry_size as usize];
let type_guid = &entry[0..16];
if type_guid == [0u8; 16] {
continue;
}
if type_guid == LINUX_FS_GUID {
let first_lba = u64::from_le_bytes(
entry[32..40].try_into().unwrap_or([0; 8]),
);
return first_lba * gpt_lba_size;
}
}
2 * crate::MIBI as u64
}
#[derive(Debug)]
enum DevicePathRelation {
@@ -126,17 +266,15 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
};
if cfg!(feature = "live") {
// First try to get a live image from redox-live.iso. This is required to support netbooting.
if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) {
let partition_offset = if buffer.len() > 520 && &buffer[512..520] == b"EFI PART" {
gpt_find_redoxfs_offset_from_slice(&buffer)
} else {
0
};
return vec![DiskDevice {
handle: esp_handle,
// Support both a copy of livedisk.iso and a standalone redoxfs partition
partition_offset: if &buffer[512..520] == b"EFI PART" {
//TODO: get block from partition table
2 * crate::MIBI as u64
} else {
0
},
partition_offset,
disk: DiskOrFileEfi::File(buffer),
device_path: esp_device_path,
file_path: Some("redox-live.iso"),
@@ -154,7 +292,7 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
};
let mut devices = Vec::with_capacity(handles.len());
for handle in handles {
let disk = match DiskEfi::handle_protocol(handle) {
let mut disk = match DiskEfi::handle_protocol(handle) {
Ok(ok) => ok,
Err(err) => {
log::warn!(
@@ -182,14 +320,14 @@ pub fn disk_device_priority() -> Vec<DiskDevice> {
}
};
let partition_offset = if disk.0.Media.LogicalPartition {
0
} else {
gpt_find_redoxfs_offset(&mut disk)
};
devices.push(DiskDevice {
handle,
partition_offset: if disk.0.Media.LogicalPartition {
0
} else {
//TODO: get block from partition table
2 * crate::MIBI as u64
},
partition_offset,
disk: DiskOrFileEfi::Disk(disk),
device_path,
file_path: None,
+3 -3
View File
@@ -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),
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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;
+54 -7
View File
@@ -2,13 +2,13 @@ use alloc::vec::Vec;
use core::{cell::RefCell, mem, ptr, slice};
use std::proto::Protocol;
use uefi::{
Handle,
boot::LocateSearchType,
memory::MemoryType,
reset::ResetType,
status::{Result, Status},
system::SystemTable,
text::TextInputKey,
Handle,
};
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
@@ -53,12 +53,16 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
pages,
&mut ptr,
))
.unwrap();
ptr as *mut u8
.unwrap_or_else(|_| {
ptr = 0;
0
});
if ptr == 0 { ptr::null_mut() } else { ptr as *mut u8 }
};
assert!(!ptr.is_null());
unsafe { ptr::write_bytes(ptr, 0, pages * page_size) };
if !ptr.is_null() {
unsafe { ptr::write_bytes(ptr, 0, pages * page_size) };
}
ptr
}
@@ -103,7 +107,7 @@ impl OsEfi {
for other_output in outputs.iter() {
if output.0.Mode.FrameBufferBase
== other_output.0.0.Mode.FrameBufferBase
== other_output.0 .0.Mode.FrameBufferBase
{
log::debug!(
"Skipping output with frame buffer base matching another output"
@@ -180,6 +184,8 @@ impl Os for OsEfi {
&self,
password_opt: Option<&[u8]>,
) -> syscall::Result<redoxfs::FileSystem<DiskOrFileEfi>> {
let mut seen_enokey = false;
// Search for RedoxFS on disks in prioritized order
println!("Looking for RedoxFS:");
for device in disk_device_priority() {
@@ -200,6 +206,8 @@ impl Os for OsEfi {
Err(err) => match err.errno {
// Ignore header not found error
syscall::ENOENT => (),
// Bubble ENOKEY up later if we can't find another partition to return
syscall::ENOKEY => seen_enokey = true,
// Print any other errors
_ => {
log::warn!("BlockIo error: {:?}", err);
@@ -208,6 +216,11 @@ impl Os for OsEfi {
}
}
// Let the caller prompt for a password
if seen_enokey {
return Err(syscall::Error::new(syscall::ENOKEY));
}
log::warn!("No RedoxFS partitions found");
Err(syscall::Error::new(syscall::ENOENT))
}
@@ -236,7 +249,7 @@ impl Os for OsEfi {
let output_opt = match self.outputs.borrow_mut().get_mut(output_i) {
Some(output) => unsafe {
// Hack to enable clone
let ptr = output.0.0 as *mut _;
let ptr = output.0 .0 as *mut _;
Some(Output::new(&mut *ptr))
},
None => None,
@@ -320,6 +333,40 @@ impl Os for OsEfi {
}
}
fn get_key_timeout(&self, milliseconds: usize) -> OsKey {
let slices = milliseconds.div_ceil(100);
for _ in 0..slices {
let mut key = TextInputKey {
ScanCode: 0,
UnicodeChar: 0,
};
let status = (self.st.ConsoleIn.ReadKeyStroke)(self.st.ConsoleIn, &mut key);
if status.is_success() {
return match key.ScanCode {
0 => match key.UnicodeChar {
8 => OsKey::Backspace,
13 => OsKey::Enter,
w => match char::from_u32(w as u32) {
Some(c) => OsKey::Char(c),
None => OsKey::Other,
},
},
1 => OsKey::Up,
2 => OsKey::Down,
3 => OsKey::Right,
4 => OsKey::Left,
8 => OsKey::Delete,
_ => OsKey::Other,
};
}
let _ = status_to_result((self.st.BootServices.Stall)(100_000));
}
OsKey::Timeout
}
fn clear_text(&self) {
//TODO: why does this sometimes return InvalidParameter, but otherwise appear to work?
let _ = status_to_result((self.st.ConsoleOut.ClearScreen)(self.st.ConsoleOut));
+1 -1
View File
@@ -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<Output>,