diff --git a/src/main.rs b/src/main.rs index e10b9d52fb0573e00fb12f1e2d8650789c4e9e61..70e8e6b249d74ca59a8e84d785b9d8b2ea55172f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -103,9 +103,9 @@ pub struct KernelArgs { fn select_mode< D: Disk, V: Iterator<Item=OsVideoMode> ->(os: &mut dyn Os<D, V>) -> Option<OsVideoMode> { +>(os: &mut dyn Os<D, V>, output: usize) -> Option<OsVideoMode> { let mut modes = Vec::new(); - for mode in os.video_modes() { + for mode in os.video_modes(output) { let mut aspect_w = mode.width; let mut aspect_h = mode.height; for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) { @@ -130,7 +130,7 @@ fn select_mode< // Set selected based on best resolution let mut selected = modes.get(0).map_or(0, |x| x.0.id); - if let Some((best_width, best_height)) = os.best_resolution() { + if let Some((best_width, best_height)) = os.best_resolution(output) { println!("Best resolution: {}x{}", best_width, best_height); for (mode, _text) in modes.iter() { if mode.width == best_width && mode.height == best_height { @@ -375,6 +375,19 @@ fn main< >(os: &mut dyn Os<D, V>) -> (usize, u64, KernelArgs) { println!("Redox OS Bootloader {} on {}", env!("CARGO_PKG_VERSION"), os.name()); + /*TODO: support multiple outputs + for output_i in 0..os.video_outputs() { + print!("{}:", output_i); + if let Some(best) = os.best_resolution(output_i) { + print!(" *{}x{}*", best.0, best.1); + } + for mode in os.video_modes(output_i) { + print!(" {}x{}", mode.width, mode.height); + } + println!(); + } + */ + let (mut fs, password_opt) = redoxfs(os); print!("RedoxFS "); @@ -387,7 +400,7 @@ fn main< } println!(": {} MiB", fs.header.size() / MIBI as u64); - let mode_opt = select_mode(os); + let mode_opt = select_mode(os, 0); let stack_size = 128 * KIBI; let stack_base = os.alloc_zeroed_page_aligned(stack_size); @@ -503,7 +516,7 @@ fn main< if let Some(mut mode) = mode_opt { // Set mode to get updated values - os.set_video_mode(&mut mode); + os.set_video_mode(0, &mut mode); let virt = unsafe { paging_framebuffer( diff --git a/src/os/bios/mod.rs b/src/os/bios/mod.rs index 9c98e94613f32e1fa5d56e4dc3eb59c2b11904eb..3e9f1a33f4576fd0e5fa8fbae1598ce5493d25d1 100644 --- a/src/os/bios/mod.rs +++ b/src/os/bios/mod.rs @@ -90,11 +90,16 @@ impl Os< redoxfs::FileSystem::open(disk, password_opt, Some(block), false) } - fn video_modes(&self) -> VideoModeIter { + fn video_outputs(&self) -> usize { + //TODO: return 1 only if vbe supported? + 1 + } + + fn video_modes(&self, _output_i: usize) -> VideoModeIter { VideoModeIter::new(self.thunk10) } - fn set_video_mode(&self, mode: &mut OsVideoMode) { + fn set_video_mode(&self, _output_i: usize, mode: &mut OsVideoMode) { // Set video mode let mut data = ThunkData::new(); data.eax = 0x4F02; @@ -103,7 +108,7 @@ impl Os< //TODO: check result } - fn best_resolution(&self) -> Option<(u32, u32)> { + fn best_resolution(&self, _output_i: usize) -> Option<(u32, u32)> { let mut data = ThunkData::new(); data.eax = 0x4F15; data.ebx = 0x01; diff --git a/src/os/mod.rs b/src/os/mod.rs index a88e2d8c96096210ab850ca379224f8f5c67fcc4..a528a1c32c15ad66c8a44155474d73f05f627c6f 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -67,9 +67,10 @@ pub trait Os< fn filesystem(&self, password_opt: Option<&[u8]>) -> syscall::Result<redoxfs::FileSystem<D>>; - fn video_modes(&self) -> V; - fn set_video_mode(&self, mode: &mut OsVideoMode); - fn best_resolution(&self) -> Option<(u32, u32)>; + fn video_outputs(&self) -> usize; + fn video_modes(&self, output_i: usize) -> V; + fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode); + fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)>; fn get_key(&self) -> OsKey; diff --git a/src/os/uefi/arch/aarch64.rs b/src/os/uefi/arch/aarch64.rs index 0f4c54ae4a90ecd55fc906c70f5073d4c2d85541..361122bc1ddae0b41cfb7d8fb53955d31acacd20 100644 --- a/src/os/uefi/arch/aarch64.rs +++ b/src/os/uefi/arch/aarch64.rs @@ -99,9 +99,7 @@ pub fn main() -> Result<()> { //TODO: support this in addition to ACPI? // let dtb = find_dtb()?; - let mut os = OsEfi { - st: std::system_table(), - }; + let mut os = OsEfi::new(); // Disable cursor let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false); diff --git a/src/os/uefi/arch/x86_64.rs b/src/os/uefi/arch/x86_64.rs index d39aeef6f757296d2060ed65b993212a3320fa14..8b616fa5e0e400e0836b95c10bf08f8f4b5472d2 100644 --- a/src/os/uefi/arch/x86_64.rs +++ b/src/os/uefi/arch/x86_64.rs @@ -76,9 +76,7 @@ unsafe extern "C" fn kernel_entry( pub fn main() -> Result<()> { LOGGER.init(); - let mut os = OsEfi { - st: std::system_table(), - }; + let mut os = OsEfi::new(); // Disable cursor let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false); diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs index 06529acd7cd29f93a7f5c7900baef92a65105028..3461862bd3285aec4b42fd16b73d9b28761a4311 100644 --- a/src/os/uefi/mod.rs +++ b/src/os/uefi/mod.rs @@ -1,4 +1,7 @@ +use alloc::vec::Vec; use core::{ + cell::RefCell, + mem, ops::{ControlFlow, Try}, ptr, slice @@ -7,6 +10,8 @@ use std::{ proto::Protocol, }; use uefi::{ + Handle, + boot::LocateSearchType, reset::ResetType, memory::MemoryType, status::{Result, Status}, @@ -66,6 +71,53 @@ pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 { pub struct OsEfi { st: &'static SystemTable, + outputs: RefCell<Vec<(Output, Option<EdidActive>)>>, +} + +impl OsEfi { + pub fn new() -> Self { + let st = std::system_table(); + let mut outputs = Vec::new(); + { + let guid = Output::guid(); + let mut handles = Vec::with_capacity(256); + let mut len = handles.capacity() * mem::size_of::<Handle>(); + status_to_result( + (st.BootServices.LocateHandle)( + LocateSearchType::ByProtocol, + &guid, + 0, + &mut len, + handles.as_mut_ptr() + ) + ).unwrap(); + unsafe { handles.set_len(len / mem::size_of::<Handle>()); } + for handle in handles { + //TODO: do we have to query all modes to get good edid? + match Output::handle_protocol(handle) { + Ok(output) => { + outputs.push(( + output, + match EdidActive::handle_protocol(handle) { + Ok(efi_edid) => Some(efi_edid), + Err(err) => { + log::warn!("Failed to get EFI EDID from handle {:?}: {:?}", handle, err); + None + } + } + )); + }, + Err(err) => { + log::warn!("Failed to get Output from handle {:?}: {:?}", handle, err); + } + } + } + } + Self { + st, + outputs: RefCell::new(outputs), + } + } } impl Os< @@ -127,12 +179,26 @@ impl Os< Err(syscall::Error::new(syscall::ENOENT)) } - fn video_modes(&self) -> VideoModeIter { - VideoModeIter::new() + fn video_outputs(&self) -> usize { + self.outputs.borrow().len() } - fn set_video_mode(&self, mode: &mut OsVideoMode) { - let output = Output::one().unwrap(); + fn video_modes(&self, output_i: usize) -> VideoModeIter { + 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 _; + Some(Output::new(&mut *ptr)) + }, + None => None, + }; + VideoModeIter::new(output_opt) + } + + fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode) { + //TODO: return error? + let mut outputs = self.outputs.borrow_mut(); + let (output, _efi_edid_opt) = &mut outputs[output_i]; status_to_result( (output.0.SetMode)(output.0, mode.id) ).unwrap(); @@ -143,42 +209,30 @@ impl Os< mode.base = output.0.Mode.FrameBufferBase as u64; } - fn best_resolution(&self) -> Option<(u32, u32)> { - //TODO: get this per output - match EdidActive::one() { - Ok(efi_edid) => { - let edid = unsafe { - slice::from_raw_parts(efi_edid.0.Edid, efi_edid.0.SizeOfEdid as usize) - }; - - if edid.len() > 0x3D { - Some(( - (edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4), - (edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4), - )) - } else { - log::warn!("EFI EDID too small: {}", edid.len()); - None - } - }, - Err(err) => { - log::warn!("Failed to get EFI EDID: {:?}", err); + fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)> { + let mut outputs = self.outputs.borrow_mut(); + let (output, efi_edid_opt) = outputs.get_mut(output_i)?; - // Fallback to the current output resolution - match Output::one() { - Ok(output) => { - Some(( - output.0.Mode.Info.HorizontalResolution, - output.0.Mode.Info.VerticalResolution, - )) - }, - Err(err) => { - log::error!("Failed to get output: {:?}", err); - None - } - } + if let Some(efi_edid) = efi_edid_opt { + let edid = unsafe { + slice::from_raw_parts(efi_edid.0.Edid, efi_edid.0.SizeOfEdid as usize) + }; + + if edid.len() > 0x3D { + return Some(( + (edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4), + (edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4), + )); + } else { + log::warn!("EFI EDID too small: {}", edid.len()); } } + + // Fallback to the current output resolution + Some(( + output.0.Mode.Info.HorizontalResolution, + output.0.Mode.Info.VerticalResolution, + )) } fn get_key(&self) -> OsKey { diff --git a/src/os/uefi/video_mode.rs b/src/os/uefi/video_mode.rs index be8dc4ea518a7b569c5bccaa4d921e0963247419..d1c7ee6d479275753d6ccff7513669ce96e818eb 100644 --- a/src/os/uefi/video_mode.rs +++ b/src/os/uefi/video_mode.rs @@ -11,9 +11,9 @@ pub struct VideoModeIter { } impl VideoModeIter { - pub fn new() -> Self { + pub fn new(output_opt: Option<Output>) -> Self { Self { - output_opt: Output::one().ok(), + output_opt, i: 0, } }