From 9b244a56a3b184b971317262ce1af92fe992885e Mon Sep 17 00:00:00 2001 From: Jeremy Soller <jackpot51@gmail.com> Date: Tue, 8 Feb 2022 21:40:26 -0700 Subject: [PATCH] Unify logic for BIOS and UEFI --- Cargo.lock | 6 - Cargo.toml | 5 - src/arch/aarch64/mod.rs | 3 + src/arch/aarch64/paging.rs | 29 ++ src/arch/mod.rs | 6 + src/arch/x86_64/paging.rs | 34 +- src/main.rs | 173 +++++++- src/os/bios/mod.rs | 174 ++------ src/os/bios/vga.rs | 4 +- src/os/mod.rs | 8 +- src/os/uefi/arch/aarch64/mod.rs | 5 +- src/os/uefi/arch/x86_64/memory_map.rs | 76 ++++ src/os/uefi/arch/x86_64/mod.rs | 550 ++++++++------------------ src/os/uefi/arch/x86_64/paging.rs | 73 ---- src/os/uefi/arch/x86_64/video_mode.rs | 63 +++ src/os/uefi/display.rs | 175 +------- src/os/uefi/key.rs | 95 ----- src/os/uefi/mod.rs | 5 +- src/os/uefi/text.rs | 252 ------------ 19 files changed, 574 insertions(+), 1162 deletions(-) create mode 100644 src/arch/aarch64/mod.rs create mode 100644 src/arch/aarch64/paging.rs create mode 100644 src/os/uefi/arch/x86_64/video_mode.rs delete mode 100644 src/os/uefi/key.rs delete mode 100644 src/os/uefi/text.rs diff --git a/Cargo.lock b/Cargo.lock index ba7d5f6..0650b03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,11 +53,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "orbclient" -version = "0.3.21" -source = "git+https://gitlab.redox-os.org/redox-os/orbclient.git?branch=no_std#0cf93f23b88fdeff6561a4f9b4b91798c3c3a4a1" - [[package]] name = "raw-cpuid" version = "10.2.0" @@ -73,7 +68,6 @@ version = "0.1.0" dependencies = [ "linked_list_allocator", "log", - "orbclient", "redox_syscall", "redox_uefi", "redox_uefi_std", diff --git a/Cargo.toml b/Cargo.toml index 74fbb55..801082e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,10 +25,5 @@ spin = "0.9.2" redox_uefi = "0.1.2" redox_uefi_std = "0.1.5" -[target.'cfg(target_os = "uefi")'.dependencies.orbclient] -git = "https://gitlab.redox-os.org/redox-os/orbclient.git" -branch = "no_std" -features = ["no_std"] - [target."x86_64-unknown-uefi".dependencies] x86 = "0.43.0" diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs new file mode 100644 index 0000000..d059901 --- /dev/null +++ b/src/arch/aarch64/mod.rs @@ -0,0 +1,3 @@ +pub use self::paging::paging_create; + +mod paging; diff --git a/src/arch/aarch64/paging.rs b/src/arch/aarch64/paging.rs new file mode 100644 index 0000000..8feb1fe --- /dev/null +++ b/src/arch/aarch64/paging.rs @@ -0,0 +1,29 @@ +use core::slice; +use redoxfs::Disk; + +use crate::os::{Os, OsMemoryEntry, OsVideoMode}; + +unsafe fn paging_allocate< + D: Disk, + M: Iterator<Item=OsMemoryEntry>, + V: Iterator<Item=OsVideoMode> +>(os: &mut dyn Os<D, M, V>) -> Option<&'static mut [u64]> { + let ptr = os.alloc_zeroed_page_aligned(4096); + if ! ptr.is_null() { + Some(slice::from_raw_parts_mut( + ptr as *mut u64, + 512 // page size divided by u64 size + )) + } else { + None + } +} + +pub unsafe fn paging_create< + D: Disk, + M: Iterator<Item=OsMemoryEntry>, + V: Iterator<Item=OsVideoMode> +>(os: &mut dyn Os<D, M, V>, kernel_phys: usize) -> Option<usize> { + log::error!("paging_create not implemented for aarch64"); + None +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 473564a..9cbc928 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -1,3 +1,9 @@ +#[cfg(target_arch = "aarch64")] +pub use self::aarch64::*; + +#[cfg(target_arch = "aarch64")] +mod aarch64; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use self::x86_64::*; diff --git a/src/arch/x86_64/paging.rs b/src/arch/x86_64/paging.rs index 6664430..5715cd0 100644 --- a/src/arch/x86_64/paging.rs +++ b/src/arch/x86_64/paging.rs @@ -1,10 +1,14 @@ -use alloc::alloc; use core::slice; +use redoxfs::Disk; -unsafe fn paging_allocate() -> Option<&'static mut [u64]> { - let ptr = alloc::alloc_zeroed( - alloc::Layout::from_size_align(4096, 4096).unwrap() - ); +use crate::os::{Os, OsMemoryEntry, OsVideoMode}; + +unsafe fn paging_allocate< + D: Disk, + M: Iterator<Item=OsMemoryEntry>, + V: Iterator<Item=OsVideoMode> +>(os: &mut dyn Os<D, M, V>) -> Option<&'static mut [u64]> { + let ptr = os.alloc_zeroed_page_aligned(4096); if ! ptr.is_null() { Some(slice::from_raw_parts_mut( ptr as *mut u64, @@ -15,16 +19,20 @@ unsafe fn paging_allocate() -> Option<&'static mut [u64]> { } } -pub unsafe fn paging_create(kernel_phys: usize) -> Option<usize> { +pub unsafe fn paging_create< + D: Disk, + M: Iterator<Item=OsMemoryEntry>, + V: Iterator<Item=OsVideoMode> +>(os: &mut dyn Os<D, M, V>, kernel_phys: usize) -> Option<usize> { // Create PML4 - let pml4 = paging_allocate()?; + let pml4 = paging_allocate(os)?; // Recursive mapping for compatibility pml4[511] = pml4.as_ptr() as u64 | 1 << 1 | 1; { // Create PDP for identity mapping - let pdp = paging_allocate()?; + let pdp = paging_allocate(os)?; // Link first user and first kernel PML4 entry to PDP pml4[0] = pdp.as_ptr() as u64 | 1 << 1 | 1; @@ -32,10 +40,10 @@ pub unsafe fn paging_create(kernel_phys: usize) -> Option<usize> { // Identity map 8 GiB pages for pdp_i in 0..8 { - let pd = paging_allocate()?; + let pd = paging_allocate(os)?; pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1; for pd_i in 0..pd.len() { - let pt = paging_allocate()?; + let pt = paging_allocate(os)?; pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1; for pt_i in 0..pt.len() { let addr = @@ -50,17 +58,17 @@ pub unsafe fn paging_create(kernel_phys: usize) -> Option<usize> { { // Create PDP for kernel mapping - let pdp = paging_allocate()?; + let pdp = paging_allocate(os)?; // Link second to last PML4 entry to PDP pml4[510] = pdp.as_ptr() as u64 | 1 << 1 | 1; // Map 1 GiB at kernel offset for pdp_i in 0..1 { - let pd = paging_allocate()?; + let pd = paging_allocate(os)?; pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1; for pd_i in 0..pd.len() { - let pt = paging_allocate()?; + let pt = paging_allocate(os)?; pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1; for pt_i in 0..pt.len() { let addr = diff --git a/src/main.rs b/src/main.rs index 66ba720..85015a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,10 +16,18 @@ extern crate alloc; #[macro_use] extern crate uefi_std as std; -use alloc::vec::Vec; -use core::cmp; +use alloc::{ + vec::Vec, +}; +use core::{ + cmp, + fmt::{self, Write}, + slice, + str, +}; use redoxfs::Disk; +use self::arch::paging_create; use self::os::{Os, OsKey, OsMemoryEntry, OsVideoMode}; #[macro_use] @@ -28,11 +36,68 @@ mod os; mod arch; mod logger; +const KIBI: usize = 1024; +const MIBI: usize = KIBI * KIBI; + +struct SliceWriter<'a> { + slice: &'a mut [u8], + i: usize, +} + +impl<'a> Write for SliceWriter<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for b in s.bytes() { + if let Some(slice_b) = self.slice.get_mut(self.i) { + *slice_b = b; + self.i += 1; + } else { + return Err(fmt::Error); + } + } + Ok(()) + } +} + +#[allow(dead_code)] +#[derive(Debug)] +#[repr(packed)] +pub struct KernelArgs { + kernel_base: u64, + kernel_size: u64, + stack_base: u64, + stack_size: u64, + env_base: u64, + env_size: u64, + + /// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly + /// userspace), to decide which RSDP to use. The buffer will be a linked list containing a + /// 32-bit relative (to this field) next, and the actual struct afterwards. + /// + /// This field can be NULL, and if so, the system has not booted with UEFI or in some other way + /// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS + /// memory instead. On UEFI systems, searching is not guaranteed to actually work though. + acpi_rsdps_base: u64, + /// The size of the RSDPs region. + acpi_rsdps_size: u64, +} + fn main< D: Disk, M: Iterator<Item=OsMemoryEntry>, V: Iterator<Item=OsVideoMode> ->(os: &mut dyn Os<D, M, V>) -> Option<OsVideoMode> { +>(os: &mut dyn Os<D, M, V>) -> (usize, KernelArgs) { + let mut fs = os.filesystem(); + + print!("RedoxFS "); + for i in 0..fs.header.1.uuid.len() { + if i == 4 || i == 6 || i == 8 || i == 10 { + print!("-"); + } + + print!("{:>02x}", fs.header.1.uuid[i]); + } + println!(": {} MiB", fs.header.1.size / MIBI as u64); + let mut modes = Vec::new(); for mode in os.video_modes() { let mut aspect_w = mode.width; @@ -62,6 +127,7 @@ fn main< let rows = 12; //TODO 0x4F03 VBE function to get current mode let mut selected = modes.get(0).map_or(0, |x| x.0.id); + let mut mode_opt = None; while ! modes.is_empty() { let mut row = 0; let mut col = 0; @@ -134,6 +200,11 @@ fn main< } }, OsKey::Enter => { + 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; }, _ => (), @@ -144,11 +215,99 @@ fn main< os.set_text_highlight(false); println!(); - if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) { - if let Some((mode, _text)) = modes.get(mode_i) { - return Some(*mode); + let kernel = { + let node = fs.find_node("kernel", fs.header.1.root) + .expect("Failed to find kernel file"); + + let size = fs.node_len(node.0) + .expect("Failed to read kernel size"); + + print!("Kernel: 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 kernel"); + } + + let kernel = unsafe { + slice::from_raw_parts_mut(ptr, size as usize) + }; + + let mut i = 0; + for chunk in kernel.chunks_mut(MIBI) { + print!("\rKernel: {}/{} MiB", i / MIBI as u64, size / MIBI as u64); + i += fs.read_node(node.0, i, chunk, 0, 0) + .expect("Failed to read kernel file") as u64; + } + println!("\rKernel: {}/{} MiB", i / MIBI as u64, size / MIBI as u64); + + let magic = &kernel[..4]; + if magic != b"\x7FELF" { + panic!("Kernel has invalid magic number {:#X?}", magic); + } + + kernel + }; + + let page_phys = unsafe { paging_create(os, kernel.as_ptr() as usize) } + .expect("Failed to set up paging"); + + //TODO: properly reserve page table allocations so kernel does not re-use them + + let stack_size = 128 * KIBI; + let stack_base = os.alloc_zeroed_page_aligned(stack_size); + if stack_base.is_null() { + panic!("Failed to allocate memory for stack"); + } + + let mut env_size = 4 * KIBI; + let env_base = os.alloc_zeroed_page_aligned(env_size); + if env_base.is_null() { + panic!("Failed to allocate memory for stack"); + } + + { + let mut w = SliceWriter { + slice: unsafe { + slice::from_raw_parts_mut(env_base, env_size) + }, + i: 0, + }; + + writeln!(w, "REDOXFS_BLOCK={:016x}", fs.block).unwrap(); + write!(w, "REDOXFS_UUID=").unwrap(); + for i in 0..fs.header.1.uuid.len() { + if i == 4 || i == 6 || i == 8 || i == 10 { + write!(w, "-").unwrap(); + } + + write!(w, "{:>02x}", fs.header.1.uuid[i]).unwrap(); } + writeln!(w).unwrap(); + + if let Some(mut mode) = mode_opt { + // Set mode to get updated values + os.set_video_mode(&mut mode); + + writeln!(w, "FRAMEBUFFER_ADDR={:016x}", mode.base).unwrap(); + writeln!(w, "FRAMEBUFFER_WIDTH={:016x}", mode.width).unwrap(); + writeln!(w, "FRAMEBUFFER_HEIGHT={:016x}", mode.height).unwrap(); + } + + env_size = w.i; } - None + ( + page_phys, + KernelArgs { + kernel_base: kernel.as_ptr() as u64, + kernel_size: kernel.len() as u64, + stack_base: stack_base as u64, + stack_size: stack_size as u64, + env_base: env_base as u64, + env_size: env_size as u64, + acpi_rsdps_base: 0, + acpi_rsdps_size: 0, + } + ) } diff --git a/src/os/bios/mod.rs b/src/os/bios/mod.rs index 9db10f2..8d8f276 100644 --- a/src/os/bios/mod.rs +++ b/src/os/bios/mod.rs @@ -1,17 +1,14 @@ -use alloc::{ - string::String, -}; +use alloc::alloc::{alloc_zeroed, Layout}; use core::{ - alloc::{GlobalAlloc, Layout}, convert::TryFrom, - slice, + ptr, }; use linked_list_allocator::LockedHeap; use spin::Mutex; -use crate::arch::paging_create; +use crate::KernelArgs; use crate::logger::LOGGER; -use crate::os::{Os, OsKey}; +use crate::os::{Os, OsKey, OsVideoMode}; use self::disk::DiskBios; use self::memory_map::{memory_map, MemoryMapIter}; @@ -48,28 +45,6 @@ pub(crate) static VGA: Mutex<Vga> = Mutex::new( unsafe { Vga::new(VGA_ADDR, 80, 25) } ); -#[allow(dead_code)] -#[repr(packed)] -pub struct KernelArgs { - kernel_base: u64, - kernel_size: u64, - stack_base: u64, - stack_size: u64, - env_base: u64, - env_size: u64, - - /// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly - /// userspace), to decide which RSDP to use. The buffer will be a linked list containing a - /// 32-bit relative (to this field) next, and the actual struct afterwards. - /// - /// This field can be NULL, and if so, the system has not booted with UEFI or in some other way - /// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS - /// memory instead. On UEFI systems, searching is not guaranteed to actually work though. - acpi_rsdps_base: u64, - /// The size of the RSDPs region. - acpi_rsdps_size: u64, -} - pub struct OsBios { boot_disk: usize, thunk10: extern "C" fn(), @@ -83,8 +58,34 @@ impl Os< MemoryMapIter, VideoModeIter > for OsBios { - fn disk(&self) -> DiskBios { - DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13) + fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 { + assert!(size != 0); + + let page_size = self.page_size(); + let pages = (size + page_size - 1) / page_size; + + let ptr = unsafe { + alloc_zeroed(Layout::from_size_align( + pages * page_size, + page_size + ).unwrap()) + }; + + assert!(!ptr.is_null()); + ptr + } + + fn page_size(&self) -> usize { + 4096 + } + + fn filesystem(&self) -> redoxfs::FileSystem<DiskBios> { + let disk = DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13); + + //TODO: get block from partition table + let block = crate::MIBI as u64 / redoxfs::BLOCK_SIZE; + redoxfs::FileSystem::open(disk, Some(block)) + .expect("Failed to open RedoxFS") } fn memory(&self) -> MemoryMapIter { @@ -95,11 +96,11 @@ impl Os< VideoModeIter::new(self.thunk10) } - fn set_video_mode(&self, id: u32) { + fn set_video_mode(&self, mode: &mut OsVideoMode) { // Set video mode let mut data = ThunkData::new(); data.eax = 0x4F02; - data.ebx = id; + data.ebx = mode.id; unsafe { data.with(self.thunk10); } //TODO: check result } @@ -133,11 +134,11 @@ impl Os< fn set_text_highlight(&self, highlight: bool) { let mut vga = VGA.lock(); if highlight { - vga.bg = VgaTextColor::White; + vga.bg = VgaTextColor::Gray; vga.fg = VgaTextColor::Black; } else { - vga.bg = VgaTextColor::DarkGray; - vga.fg = VgaTextColor::White; + vga.bg = VgaTextColor::Black; + vga.fg = VgaTextColor::Gray; } } } @@ -190,109 +191,12 @@ pub unsafe extern "C" fn start( thunk16, }; - // Locate kernel on RedoxFS - let disk = os.disk(); - - //TODO: get block from partition table - let block = 1024 * 1024 / redoxfs::BLOCK_SIZE; - let mut fs = redoxfs::FileSystem::open(disk, Some(block)) - .expect("Failed to open RedoxFS"); - - print!("RedoxFS "); - for i in 0..fs.header.1.uuid.len() { - if i == 4 || i == 6 || i == 8 || i == 10 { - print!("-"); - } - - print!("{:>02x}", fs.header.1.uuid[i]); - } - println!(": {} MiB", fs.header.1.size / 1024 / 1024); - - let mode_opt = crate::main(&mut os); - - let kernel = { - let node = fs.find_node("kernel", fs.header.1.root) - .expect("Failed to find kernel file"); - - let size = fs.node_len(node.0) - .expect("Failed to read kernel size"); - - print!("Kernel: 0/{} MiB", size / 1024 / 1024); - - let ptr = ALLOCATOR.alloc_zeroed( - Layout::from_size_align(size as usize, 4096).unwrap() - ); - if ptr.is_null() { - panic!("Failed to allocate memory for kernel"); - } - - let kernel = slice::from_raw_parts_mut( - ptr, - size as usize - ); - - let mut i = 0; - for chunk in kernel.chunks_mut(1024 * 1024) { - print!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024); - i += fs.read_node(node.0, i, chunk, 0, 0) - .expect("Failed to read kernel file") as u64; - } - println!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024); - - let magic = &kernel[..4]; - if magic != b"\x7FELF" { - panic!("Kernel has invalid magic number {:#X?}", magic); - } - - kernel - }; - - let page_phys = paging_create(kernel.as_ptr() as usize) - .expect("Failed to set up paging"); - - //TODO: properly reserve page table allocations so kernel does not re-use them - - let stack_size = 0x20000; - let stack_base = ALLOCATOR.alloc_zeroed( - Layout::from_size_align(stack_size, 4096).unwrap() - ); - if stack_base.is_null() { - panic!("Failed to allocate memory for stack"); - } - - let mut env = String::with_capacity(4096); - - if let Some(mode) = mode_opt { - env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", mode.base)); - env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", mode.width)); - env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", mode.height)); - os.set_video_mode(mode.id); - } - env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block)); - env.push_str("REDOXFS_UUID="); - for i in 0..fs.header.1.uuid.len() { - if i == 4 || i == 6 || i == 8 || i == 10 { - env.push('-'); - } - - env.push_str(&format!("{:>02x}", fs.header.1.uuid[i])); - } - - let args = KernelArgs { - kernel_base: kernel.as_ptr() as u64, - kernel_size: kernel.len() as u64, - stack_base: stack_base as u64, - stack_size: stack_size as u64, - env_base: env.as_ptr() as u64, - env_size: env.len() as u64, - acpi_rsdps_base: 0, - acpi_rsdps_size: 0, - }; + let (page_phys, args) = crate::main(&mut os); kernel_entry( page_phys, args.stack_base + args.stack_size + PHYS_OFFSET, - *(kernel.as_ptr().add(0x18) as *const u64), + ptr::read((args.kernel_base + 0x18) as *const u64), &args, ); } diff --git a/src/os/bios/vga.rs b/src/os/bios/vga.rs index 2a1b9d1..78451ce 100644 --- a/src/os/bios/vga.rs +++ b/src/os/bios/vga.rs @@ -47,8 +47,8 @@ impl Vga { height, x: 0, y: 0, - bg: VgaTextColor::DarkGray, - fg: VgaTextColor::White, + bg: VgaTextColor::Black, + fg: VgaTextColor::Gray, } } diff --git a/src/os/mod.rs b/src/os/mod.rs index 0e6ab87..6f14fe8 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -51,12 +51,16 @@ pub trait Os< M: Iterator<Item=OsMemoryEntry>, V: Iterator<Item=OsVideoMode> > { - fn disk(&self) -> D; + fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8; + + fn page_size(&self) -> usize; + + fn filesystem(&self) -> redoxfs::FileSystem<D>; fn memory(&self) -> M; fn video_modes(&self) -> V; - fn set_video_mode(&self, id: u32); + fn set_video_mode(&self, mode: &mut OsVideoMode); fn get_key(&self) -> OsKey; diff --git a/src/os/uefi/arch/aarch64/mod.rs b/src/os/uefi/arch/aarch64/mod.rs index 49c145b..fc888c6 100644 --- a/src/os/uefi/arch/aarch64/mod.rs +++ b/src/os/uefi/arch/aarch64/mod.rs @@ -2,7 +2,6 @@ use alloc::{ string::String, }; use core::{mem, ptr, slice}; -use orbclient::{Color, Renderer}; use std::fs::find; use std::proto::Protocol; use uefi::guid::Guid; @@ -11,9 +10,7 @@ use uefi::status::{Error, Result}; use super::super::{ disk::DiskEfi, - display::{Display, ScaledDisplay, Output}, - key::{key, Key}, - text::TextDisplay, + display::{Output}, }; use self::memory_map::memory_map; diff --git a/src/os/uefi/arch/x86_64/memory_map.rs b/src/os/uefi/arch/x86_64/memory_map.rs index 3b6e18f..4e16fe1 100644 --- a/src/os/uefi/arch/x86_64/memory_map.rs +++ b/src/os/uefi/arch/x86_64/memory_map.rs @@ -1,6 +1,82 @@ use core::{mem, ptr}; +use log::error; use uefi::memory::{MemoryDescriptor, MemoryType}; +use crate::os::{OsMemoryEntry, OsMemoryKind}; + +pub struct MemoryMapIter { + map: [u8; 4096], + map_size: usize, + descriptor_size: usize, + i: usize, +} + +impl MemoryMapIter { + pub fn new() -> Self { + let uefi = std::system_table(); + + let mut map: [u8; 4096] = [0; 4096]; + let mut map_size = map.len(); + let mut map_key = 0; + let mut descriptor_size = 0; + let mut descriptor_version = 0; + let _ = (uefi.BootServices.GetMemoryMap)( + &mut map_size, + map.as_mut_ptr() as *mut MemoryDescriptor, + &mut map_key, + &mut descriptor_size, + &mut descriptor_version + ); + + Self { + map, + map_size, + descriptor_size, + i: 0, + } + } +} + +impl Iterator for MemoryMapIter { + type Item=OsMemoryEntry; + fn next(&mut self) -> Option<Self::Item> { + if + self.descriptor_size >= mem::size_of::<MemoryDescriptor>() && + self.i < self.map_size/self.descriptor_size + { + let descriptor_ptr = unsafe { self.map.as_ptr().add(self.i * self.descriptor_size) }; + self.i += 1; + + let descriptor = unsafe { ptr::read(descriptor_ptr as *const MemoryDescriptor) }; + let descriptor_type: MemoryType = unsafe { mem::transmute(descriptor.Type) }; + + Some(OsMemoryEntry { + base: descriptor.PhysicalStart.0, + //TODO: do not hard code page size + size: descriptor.NumberOfPages * 4096, + kind: match descriptor_type { + MemoryType::EfiBootServicesCode | + MemoryType::EfiBootServicesData | + MemoryType::EfiConventionalMemory => { + OsMemoryKind::Free + }, + MemoryType::EfiLoaderCode | + MemoryType::EfiLoaderData | + MemoryType::EfiACPIReclaimMemory => { + OsMemoryKind::Reclaim + }, + _ => { + OsMemoryKind::Reserved + } + } + }) + } else { + error!("Unknown memory descriptor size: {}", self.descriptor_size); + None + } + } +} + static MM_BASE: u64 = 0x500; static MM_SIZE: u64 = 0x4B00; diff --git a/src/os/uefi/arch/x86_64/mod.rs b/src/os/uefi/arch/x86_64/mod.rs index 0bcc0c7..1e3ac79 100644 --- a/src/os/uefi/arch/x86_64/mod.rs +++ b/src/os/uefi/arch/x86_64/mod.rs @@ -1,69 +1,37 @@ -use core::{cmp, mem, ptr, slice}; -use orbclient::{Color, Renderer}; -use std::fs::find; +use core::{mem, ops::{ControlFlow, Try}, ptr}; use std::proto::Protocol; -use std::string::String; use std::vec::Vec; -use uefi::status::{Error, Result}; +use uefi::status::{Result, Status}; use uefi::guid::GuidKind; use uefi::memory::MemoryType; +use uefi::system::SystemTable; +use uefi::text::TextInputKey; + +use crate::{ + KernelArgs, + Os, + OsKey, + OsVideoMode, + logger::LOGGER, +}; use super::super::{ disk::DiskEfi, - display::{Display, ScaledDisplay, Output}, - key::{key, Key}, - text::TextDisplay, + display::Output, }; -use self::memory_map::memory_map; -use self::paging::{paging_create, paging_enter}; +use self::memory_map::{MemoryMapIter, memory_map}; +use self::paging::paging_enter; +use self::video_mode::VideoModeIter; mod memory_map; mod paging; +mod video_mode; static PHYS_OFFSET: u64 = 0xFFFF800000000000; -static mut KERNEL_PHYS: u64 = 0; -static mut KERNEL_SIZE: u64 = 0; -static mut KERNEL_ENTRY: u64 = 0; - -static mut STACK_PHYS: u64 = 0; -static STACK_SIZE: u64 = 0x20000; - -static mut ENV_PHYS: u64 = 0; -static mut ENV_SIZE: u64 = 0; - static mut RSDPS_AREA: Option<Vec<u8>> = None; -#[repr(packed)] -pub struct KernelArgs { - kernel_base: u64, - kernel_size: u64, - stack_base: u64, - stack_size: u64, - env_base: u64, - env_size: u64, - - acpi_rsdps_base: u64, - acpi_rsdps_size: u64, -} - -unsafe fn allocate_zero_pages(pages: usize) -> Result<usize> { - let uefi = std::system_table(); - - let mut ptr = 0; - (uefi.BootServices.AllocatePages)( - 0, // AllocateAnyPages - MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list - pages, - &mut ptr - )?; - - ptr::write_bytes(ptr as *mut u8, 0, 4096); - - Ok(ptr) -} - unsafe fn exit_boot_services(key: usize) { let handle = std::handle(); let uefi = std::system_table(); @@ -71,22 +39,6 @@ unsafe fn exit_boot_services(key: usize) { let _ = (uefi.BootServices.ExitBootServices)(handle, key); } -unsafe fn enter() -> ! { - let args = KernelArgs { - kernel_base: KERNEL_PHYS, - kernel_size: KERNEL_SIZE, - stack_base: STACK_PHYS, - stack_size: STACK_SIZE, - env_base: ENV_PHYS, - env_size: ENV_SIZE, - acpi_rsdps_base: RSDPS_AREA.as_ref().map(Vec::as_ptr).unwrap_or(core::ptr::null()) as usize as u64 + PHYS_OFFSET, - acpi_rsdps_size: RSDPS_AREA.as_ref().map(Vec::len).unwrap_or(0) as u64, - }; - - let entry_fn: extern "sysv64" fn(args_ptr: *const KernelArgs) -> ! = mem::transmute(KERNEL_ENTRY); - entry_fn(&args); -} - struct Invalid; fn validate_rsdp(address: usize, v2: bool) -> core::result::Result<usize, Invalid> { @@ -137,7 +89,7 @@ fn validate_rsdp(address: usize, v2: bool) -> core::result::Result<usize, Invali Ok(length) } -fn find_acpi_table_pointers() -> Result<()> { +fn find_acpi_table_pointers() { let rsdps_area = unsafe { RSDPS_AREA = Some(Vec::new()); RSDPS_AREA.as_mut().unwrap() @@ -145,7 +97,15 @@ fn find_acpi_table_pointers() -> Result<()> { let cfg_tables = std::system_table().config_tables(); - for (address, v2) in cfg_tables.iter().find_map(|cfg_table| if cfg_table.VendorGuid.kind() == GuidKind::Acpi { Some((cfg_table.VendorTable, false)) } else if cfg_table.VendorGuid.kind() == GuidKind::Acpi2 { Some((cfg_table.VendorTable, true)) } else { None }) { + for (address, v2) in cfg_tables.iter().find_map(|cfg_table| { + if cfg_table.VendorGuid.kind() == GuidKind::Acpi { + Some((cfg_table.VendorTable, false)) + } else if cfg_table.VendorGuid.kind() == GuidKind::Acpi2 { + Some((cfg_table.VendorTable, true)) + } else { + None + } + }) { match validate_rsdp(address, v2) { Ok(length) => { let align = 8; @@ -157,371 +117,181 @@ fn find_acpi_table_pointers() -> Result<()> { Err(_) => println!("Found RSDP that wasn't valid at {:p}", address as *const u8), } } - Ok(()) } -fn redoxfs() -> Result<redoxfs::FileSystem<DiskEfi>> { - for (i, block_io) in DiskEfi::all().into_iter().enumerate() { - if !block_io.0.Media.LogicalPartition { - continue; - } +pub struct OsEfi { + st: &'static SystemTable, +} - match redoxfs::FileSystem::open(block_io, Some(0)) { - Ok(ok) => return Ok(ok), - Err(err) => { - log::error!("Failed to open RedoxFS on block I/O {}: {}", i, err); - } - } +fn status_to_result(status: Status) -> Result<usize> { + match status.branch() { + ControlFlow::Continue(ok) => Ok(ok), + ControlFlow::Break(err) => Err(err), } - panic!("Failed to find RedoxFS"); } -const MB: usize = 1024 * 1024; - -fn inner() -> Result<()> { - //TODO: detect page size? - let page_size = 4096; - - { - let mut env = String::new(); - if let Ok(output) = Output::one() { - let mode = &output.0.Mode; - env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", mode.FrameBufferBase)); - env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", mode.Info.HorizontalResolution)); - env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", mode.Info.VerticalResolution)); - } - - println!("Loading Kernel..."); - let kernel = if let Ok((_i, mut kernel_file)) = find("\\redox_bootloader\\kernel") { - let info = kernel_file.info()?; - let len = info.FileSize; - - let kernel = unsafe { - let ptr = allocate_zero_pages((len as usize + page_size - 1) / page_size)?; - slice::from_raw_parts_mut( - ptr as *mut u8, - len as usize - ) - }; - - let mut i = 0; - for mut chunk in kernel.chunks_mut(4 * MB) { - print!("\r{}% - {} MB", i as u64 * 100 / len, i / MB); - - let count = kernel_file.read(&mut chunk)?; - if count == 0 { - break; - } - //TODO: return error instead of assert - assert_eq!(count, chunk.len()); - - i += count; - } - println!("\r{}% - {} MB", i as u64 * 100 / len, i / MB); - - kernel - } else { - let mut fs = redoxfs()?; - - let root = fs.header.1.root; - let node = fs.find_node("kernel", root).map_err(|_| Error::DeviceError)?; - - let len = fs.node_len(node.0).map_err(|_| Error::DeviceError)?; - - let kernel = unsafe { - let ptr = allocate_zero_pages((len as usize + page_size - 1) / page_size)?; - println!("{:X}", ptr); - - slice::from_raw_parts_mut( - ptr as *mut u8, - len as usize +impl Os< + DiskEfi, + MemoryMapIter, + VideoModeIter +> for OsEfi { + fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 { + assert!(size != 0); + + let page_size = self.page_size(); + let pages = (size + page_size - 1) / page_size; + + let ptr = { + let mut ptr = 0; + status_to_result( + (self.st.BootServices.AllocatePages)( + 0, // AllocateAnyPages + MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list + pages, + &mut ptr ) - }; + ).unwrap(); + ptr as *mut u8 + }; - let mut i = 0; - for mut chunk in kernel.chunks_mut(4 * MB) { - print!("\r{}% - {} MB", i as u64 * 100 / len, i / MB); + assert!(!ptr.is_null()); + unsafe { ptr::write_bytes(ptr, 0, pages * page_size) }; + ptr + } - let count = fs.read_node(node.0, i as u64, &mut chunk, 0, 0).map_err(|_| Error::DeviceError)?; - if count == 0 { - break; - } - //TODO: return error instead of assert - assert_eq!(count, chunk.len()); + fn page_size(&self) -> usize { + 4096 + } - i += count; + fn filesystem(&self) -> redoxfs::FileSystem<DiskEfi> { + for (i, block_io) in DiskEfi::all().into_iter().enumerate() { + if !block_io.0.Media.LogicalPartition { + continue; } - println!("\r{}% - {} MB", i as u64 * 100 / len, i / MB); - env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block)); - env.push_str("REDOXFS_UUID="); - for i in 0..fs.header.1.uuid.len() { - if i == 4 || i == 6 || i == 8 || i == 10 { - env.push('-'); + match redoxfs::FileSystem::open(block_io, Some(0)) { + Ok(ok) => return ok, + Err(err) => { + log::warn!("Failed to open RedoxFS on block I/O {}: {}", i, err); } - - env.push_str(&format!("{:>02x}", fs.header.1.uuid[i])); } - - kernel - }; - - unsafe { - KERNEL_PHYS = kernel.as_ptr() as u64; - KERNEL_SIZE = kernel.len() as u64; - KERNEL_ENTRY = *(kernel.as_ptr().offset(0x18) as *const u64); - println!("Kernel {:X}:{:X} entry {:X}", KERNEL_PHYS, KERNEL_SIZE, KERNEL_ENTRY); - } - - println!("Allocating stack {:X}", STACK_SIZE); - unsafe { - STACK_PHYS = allocate_zero_pages(STACK_SIZE as usize / page_size)? as u64; - println!("Stack {:X}:{:X}", STACK_PHYS, STACK_SIZE); - } - - println!("Allocating env {:X}", env.len()); - unsafe { - ENV_PHYS = allocate_zero_pages((env.len() + page_size - 1) / page_size)? as u64; - ENV_SIZE = env.len() as u64; - ptr::copy(env.as_ptr(), ENV_PHYS as *mut u8, env.len()); - println!("Env {:X}:{:X}", ENV_PHYS, ENV_SIZE); } - - println!("Parsing and writing ACPI RSDP structures."); - find_acpi_table_pointers(); - - println!("Done!"); + panic!("Failed to find RedoxFS"); } - println!("Creating page tables"); - let page_phys = unsafe { - paging_create(KERNEL_PHYS)? - }; - - println!("Entering kernel"); - unsafe { - let key = memory_map(); - exit_boot_services(key); + fn memory(&self) -> MemoryMapIter { + MemoryMapIter::new() } - unsafe { - llvm_asm!("cli" : : : "memory" : "intel", "volatile"); - paging_enter(page_phys); + fn video_modes(&self) -> VideoModeIter { + VideoModeIter::new() } - unsafe { - llvm_asm!("mov rsp, $0" : : "r"(STACK_PHYS + PHYS_OFFSET + STACK_SIZE) : "memory" : "intel", "volatile"); - enter(); - } -} + fn set_video_mode(&self, mode: &mut OsVideoMode) { + let output = Output::one().unwrap(); + status_to_result( + (output.0.SetMode)(output.0, mode.id) + ).unwrap(); -fn draw_text(display: &mut ScaledDisplay, mut x: i32, y: i32, text: &str, color: Color) { - for c in text.chars() { - display.char(x, y, c, color); - x += 8; + // Update frame buffer base + mode.base = output.0.Mode.FrameBufferBase as u64; } -} -fn draw_background(display: &mut ScaledDisplay) { - let bg = Color::rgb(0x4a, 0xa3, 0xfd); + fn get_key(&self) -> OsKey { + //TODO: do not unwrap - display.set(bg); + let mut index = 0; + status_to_result( + (self.st.BootServices.WaitForEvent)(1, &self.st.ConsoleIn.WaitForKey, &mut index) + ).unwrap(); - { - let prompt = format!( - "Redox Bootloader {} {}", - env!("CARGO_PKG_VERSION"), - env!("TARGET").split('-').next().unwrap_or("") - ); - let x = (display.width() as i32 - prompt.len() as i32 * 8)/2; - let y = display.height() as i32 - 32; - draw_text(display, x, y, &prompt, Color::rgb(0xff, 0xff, 0xff)); - } -} - -fn select_mode(output: &mut Output) -> Result<()> { - // Read all available modes - let mut modes = Vec::new(); - for i in 0..output.0.Mode.MaxMode { - let mut mode_ptr = ::core::ptr::null_mut(); - let mut mode_size = 0; - (output.0.QueryMode)(output.0, i, &mut mode_size, &mut mode_ptr)?; - - let mode = unsafe { &mut *mode_ptr }; - let w = mode.HorizontalResolution; - let h = mode.VerticalResolution; - - let mut aspect_w = w; - let mut aspect_h = h; - for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) { - while aspect_w % i == 0 && aspect_h % i == 0 { - aspect_w /= i; - aspect_h /= i; - } - } - - //TODO: support resolutions that are not perfect multiples of 4 - if w % 4 != 0 { - continue; + let mut key = TextInputKey { + ScanCode: 0, + UnicodeChar: 0 + }; + status_to_result( + (self.st.ConsoleIn.ReadKeyStroke)(self.st.ConsoleIn, &mut key) + ).unwrap(); + + match key.ScanCode { + 0 => match key.UnicodeChar { + 13 => OsKey::Enter, + _ => OsKey::Other, + }, + 1 => OsKey::Up, + 2 => OsKey::Down, + 3 => OsKey::Right, + 4 => OsKey::Left, + _ => OsKey::Other, } - - modes.push((i, w, h, format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h))); } - // Sort modes by pixel area, reversed - modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2))); - - // Find current mode index - let mut selected = output.0.Mode.Mode; - - // If there are no modes from querymode, don't change mode - if modes.is_empty() { - return Ok(()); + fn get_text_position(&self) -> (usize, usize) { + ( + self.st.ConsoleOut.Mode.CursorColumn as usize, + self.st.ConsoleOut.Mode.CursorRow as usize, + ) } - let white = Color::rgb(0xff, 0xff, 0xff); - let black = Color::rgb(0x00, 0x00, 0x00); - let rows = 12; - loop { - { - // Create a scaled display - let mut display = Display::new(output); - let mut display = ScaledDisplay::new(&mut display); - - draw_background(&mut display); - - let off_x = (display.width() as i32 - 60 * 8)/2; - let mut off_y = 16; - draw_text( - &mut display, - off_x, off_y, - "Arrow keys and enter select mode", - white - ); - off_y += 24; - - let mut row = 0; - let mut col = 0; - for (i, _w, _h, text) in modes.iter() { - if row >= rows as i32 { - col += 1; - row = 0; - } - - let x = off_x + col * 20 * 8; - let y = off_y + row * 16; - - let fg = if *i == selected { - display.rect(x - 8, y, text.len() as u32 * 8 + 16, 16, white); - black - } else { - white - }; - - draw_text(&mut display, x, y, text, fg); - - row += 1; - } - - display.sync(); - } + fn set_text_position(&self, x: usize, y: usize) { + status_to_result( + (self.st.ConsoleOut.SetCursorPosition)(self.st.ConsoleOut, x, y) + ).unwrap(); + } - match key(true)? { - Key::Left => { - if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { - if mode_i < rows { - while mode_i < modes.len() { - mode_i += rows; - } - } - mode_i -= rows; - if let Some(new) = modes.get(mode_i) { - selected = new.0; - } - } - }, - Key::Right => { - if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { - mode_i += rows; - if mode_i >= modes.len() { - mode_i = mode_i % rows; - } - if let Some(new) = modes.get(mode_i) { - selected = new.0; - } - } - }, - Key::Up => { - if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { - if mode_i % rows == 0 { - mode_i += rows; - if mode_i > modes.len() { - mode_i = modes.len(); - } - } - mode_i -= 1; - if let Some(new) = modes.get(mode_i) { - selected = new.0; - } - } - }, - Key::Down => { - if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { - mode_i += 1; - if mode_i % rows == 0 { - mode_i -= rows; - } - if mode_i >= modes.len() { - mode_i = mode_i - mode_i % rows; - } - if let Some(new) = modes.get(mode_i) { - selected = new.0; - } - } - }, - Key::Enter => { - (output.0.SetMode)(output.0, selected)?; - return Ok(()); - }, - _ => (), - } + fn set_text_highlight(&self, highlight: bool) { + let attr = if highlight { 0x70 } else { 0x07 }; + status_to_result( + (self.st.ConsoleOut.SetAttribute)(self.st.ConsoleOut, attr) + ).unwrap(); } } -fn pretty_pipe<T, F: FnMut() -> Result<T>>(output: &mut Output, f: F) -> Result<T> { - let mut display = Display::new(output); +unsafe extern "C" fn kernel_entry( + page_phys: usize, + stack: u64, + func: u64, + args: *const KernelArgs, +) -> ! { + // Read memory map and exit boot services + let key = memory_map(); + exit_boot_services(key); - let mut display = ScaledDisplay::new(&mut display); + // Disable interrupts + llvm_asm!("cli" : : : "memory" : "intel", "volatile"); - draw_background(&mut display); + // Enable paging + paging_enter(page_phys as u64); - display.sync(); + // Set stack + llvm_asm!("mov rsp, $0" : : "r"(stack) : "memory" : "intel", "volatile"); - { - let cols = 80; - let off_x = (display.width() as i32 - cols as i32 * 8)/2; - let off_y = 16; - let rows = (display.height() as i32 - 64 - off_y - 1) as usize/16; - display.rect(off_x, off_y, cols as u32 * 8, rows as u32 * 16, Color::rgb(0, 0, 0)); - display.sync(); - - let mut text = TextDisplay::new(display); - text.off_x = off_x; - text.off_y = off_y; - text.cols = cols; - text.rows = rows; - text.pipe(f) - } + // Call kernel entry + let entry_fn: extern "sysv64" fn(args_ptr: *const KernelArgs) -> ! = mem::transmute(func); + entry_fn(args); } pub fn main() -> Result<()> { - if let Ok(mut output) = Output::one() { - select_mode(&mut output)?; + LOGGER.init(); - pretty_pipe(&mut output, inner)?; - } else { - inner()?; - } + let mut os = OsEfi { + st: std::system_table(), + }; + + println!("Parsing and writing ACPI RSDP structures."); + find_acpi_table_pointers(); + + let (page_phys, mut args) = crate::main(&mut os); - Ok(()) + unsafe { + args.acpi_rsdps_base = RSDPS_AREA.as_ref().map(Vec::as_ptr).unwrap_or(core::ptr::null()) as usize as u64 + PHYS_OFFSET; + args.acpi_rsdps_size = RSDPS_AREA.as_ref().map(Vec::len).unwrap_or(0) as u64; + + kernel_entry( + page_phys, + args.stack_base + args.stack_size + PHYS_OFFSET, + ptr::read((args.kernel_base + 0x18) as *const u64), + &args, + ); + } } diff --git a/src/os/uefi/arch/x86_64/paging.rs b/src/os/uefi/arch/x86_64/paging.rs index 310efa7..cb73887 100644 --- a/src/os/uefi/arch/x86_64/paging.rs +++ b/src/os/uefi/arch/x86_64/paging.rs @@ -1,80 +1,7 @@ -use core::slice; use x86::{ controlregs::{self, Cr0, Cr4}, msr, }; -use uefi::status::Result; - -unsafe fn paging_allocate() -> Result<&'static mut [u64]> { - let ptr = super::allocate_zero_pages(1)?; - - Ok(slice::from_raw_parts_mut( - ptr as *mut u64, - 512 // page size divided by u64 size - )) -} - -pub unsafe fn paging_create(kernel_phys: u64) -> Result<u64> { - // Create PML4 - let pml4 = paging_allocate()?; - - // Recursive mapping for compatibility - pml4[511] = pml4.as_ptr() as u64 | 1 << 1 | 1; - - { - // Create PDP for identity mapping - let pdp = paging_allocate()?; - - // Link first user and first kernel PML4 entry to PDP - pml4[0] = pdp.as_ptr() as u64 | 1 << 1 | 1; - pml4[256] = pdp.as_ptr() as u64 | 1 << 1 | 1; - - // Identity map 8 GiB pages - for pdp_i in 0..8 { - let pd = paging_allocate()?; - pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1; - for pd_i in 0..pd.len() { - let pt = paging_allocate()?; - pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1; - for pt_i in 0..pt.len() { - let addr = - pdp_i as u64 * 0x4000_0000 + - pd_i as u64 * 0x20_0000 + - pt_i as u64 * 0x1000; - pt[pt_i] = addr | 1 << 1 | 1; - } - } - } - } - - { - // Create PDP for kernel mapping - let pdp = paging_allocate()?; - - // Link second to last PML4 entry to PDP - pml4[510] = pdp.as_ptr() as u64 | 1 << 1 | 1; - - // Map 1 GiB at kernel offset - for pdp_i in 0..1 { - let pd = paging_allocate()?; - pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1; - for pd_i in 0..pd.len() { - let pt = paging_allocate()?; - pd[pd_i] = pt.as_ptr() as u64 | 1 << 1 | 1; - for pt_i in 0..pt.len() { - let addr = - pdp_i as u64 * 0x4000_0000 + - pd_i as u64 * 0x20_0000 + - pt_i as u64 * 0x1000 + - kernel_phys; - pt[pt_i] = addr | 1 << 1 | 1; - } - } - } - } - - Ok(pml4.as_ptr() as u64) -} pub unsafe fn paging_enter(page_phys: u64) { // Enable OSXSAVE, FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension diff --git a/src/os/uefi/arch/x86_64/video_mode.rs b/src/os/uefi/arch/x86_64/video_mode.rs new file mode 100644 index 0000000..38bb1bb --- /dev/null +++ b/src/os/uefi/arch/x86_64/video_mode.rs @@ -0,0 +1,63 @@ +use core::{ops::{ControlFlow, Try}, ptr}; +use log::error; +use std::proto::Protocol; + +use crate::os::OsVideoMode; +use crate::os::uefi::display::Output; + +pub struct VideoModeIter { + output_opt: Option<Output>, + i: u32, +} + +impl VideoModeIter { + pub fn new() -> Self { + Self { + output_opt: Output::one().ok(), + i: 0, + } + } +} + +impl Iterator for VideoModeIter { + type Item = OsVideoMode; + fn next(&mut self) -> Option<Self::Item> { + if let Some(ref mut output) = self.output_opt { + while self.i < output.0.Mode.MaxMode { + let id = self.i; + self.i += 1; + + let mut mode_ptr = ::core::ptr::null_mut(); + let mut mode_size = 0; + match (output.0.QueryMode)(output.0, id, &mut mode_size, &mut mode_ptr).branch() { + ControlFlow::Continue(_) => (), + ControlFlow::Break(err) => { + error!("Failed to read mode {}: {:?}", id, err); + continue; + } + } + + //TODO: ensure mode_size is set correctly + let mode = unsafe { ptr::read(mode_ptr) }; + + let width = mode.HorizontalResolution; + let height = mode.VerticalResolution; + + //TODO: support resolutions that are not perfect multiples of 4 + if width % 4 != 0 { + continue; + } + + return Some(OsVideoMode { + id: id as u32, + width, + height, + // TODO + base: 0, + }); + } + } + + None + } +} diff --git a/src/os/uefi/display.rs b/src/os/uefi/display.rs index 8a6521c..95ce179 100644 --- a/src/os/uefi/display.rs +++ b/src/os/uefi/display.rs @@ -1,10 +1,5 @@ -use core::cell::Cell; -use core::ops::Try; -use core::ptr; -use orbclient::{Color, Mode, Renderer}; -use std::boxed::Box; use std::proto::Protocol; -use uefi::graphics::{GraphicsOutput, GraphicsBltOp, GraphicsBltPixel}; +use uefi::graphics::GraphicsOutput; use uefi::guid::{Guid, GRAPHICS_OUTPUT_PROTOCOL_GUID}; pub struct Output(pub &'static mut GraphicsOutput); @@ -18,171 +13,3 @@ impl Protocol<GraphicsOutput> for Output { Output(inner) } } - -pub struct Display<'a> { - output: &'a mut Output, - w: u32, - h: u32, - data: Box<[Color]>, - mode: Cell<Mode>, -} - -impl<'a> Display<'a> { - pub fn new(output: &'a mut Output) -> Self { - let w = output.0.Mode.Info.HorizontalResolution; - let h = output.0.Mode.Info.VerticalResolution; - Self { - output: output, - w: w, - h: h, - data: vec![Color::rgb(0, 0, 0); w as usize * h as usize].into_boxed_slice(), - mode: Cell::new(Mode::Blend), - } - } - - pub fn blit(&mut self, x: i32, y: i32, w: u32, h: u32) -> bool { - let status = (self.output.0.Blt)( - self.output.0, - self.data.as_mut_ptr() as *mut GraphicsBltPixel, - GraphicsBltOp::BufferToVideo, - x as usize, - y as usize, - x as usize, - y as usize, - w as usize, - h as usize, - 0 - ); - status.branch().is_continue() - } - - pub fn scroll(&mut self, rows: usize, color: Color) { - let width = self.w as usize; - let height = self.h as usize; - if rows > 0 && rows < height { - let off1 = rows * width; - let off2 = height * width - off1; - unsafe { - let data_ptr = self.data.as_mut_ptr() as *mut u32; - ptr::copy(data_ptr.offset(off1 as isize), data_ptr, off2 as usize); - for i in off2..off2 + off1 { - *data_ptr.add(i) = color.data; - } - } - } - } -} - -impl<'a> Renderer for Display<'a> { - fn width(&self) -> u32 { - self.w - } - - fn height(&self) -> u32 { - self.h - } - - fn data(&self) -> &[Color] { - &self.data - } - - fn data_mut(&mut self) -> &mut [Color] { - &mut self.data - } - - fn sync(&mut self) -> bool { - let w = self.width(); - let h = self.height(); - self.blit(0, 0, w, h) - } - - fn mode(&self) -> &Cell<Mode> { - &self.mode - } -} - -pub struct ScaledDisplay<'a> { - display: &'a mut Display<'a>, - scale: u32, -} - -impl<'a> ScaledDisplay<'a> { - pub fn new(display: &'a mut Display<'a>) -> Self { - let scale = if display.height() > 1440 { - 2 - } else { - 1 - }; - - Self { - display, - scale, - } - } - - pub fn scale(&self) -> u32 { - self.scale - } - - pub fn scroll(&mut self, rows: usize, color: Color) { - let scale = self.scale as usize; - self.display.scroll(rows * scale, color); - } - - pub fn blit(&mut self, x: i32, y: i32, w: u32, h: u32) -> bool { - let scale = self.scale; - self.display.blit( - x * scale as i32, - y * scale as i32, - w * scale, - h * scale - ) - } -} - -impl<'a> Renderer for ScaledDisplay<'a> { - fn width(&self) -> u32 { - self.display.width()/self.scale - } - - fn height(&self) -> u32 { - self.display.height()/self.scale - } - - fn data(&self) -> &[Color] { - unreachable!() - } - - fn data_mut(&mut self) -> &mut [Color] { - unreachable!() - } - - fn sync(&mut self) -> bool { - self.display.sync() - } - - fn pixel(&mut self, x: i32, y: i32, color: Color) { - self.rect(x, y, 1, 1, color); - } - - fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) { - let scale = self.scale; - self.display.rect( - x * scale as i32, - y * scale as i32, - w * scale, - h * scale, - color - ); - } - - fn set(&mut self, color: Color) { - let w = self.width(); - let h = self.height(); - self.rect(0, 0, w, h, color); - } - - fn mode(&self) -> &Cell<Mode> { - self.display.mode() - } -} diff --git a/src/os/uefi/key.rs b/src/os/uefi/key.rs deleted file mode 100644 index d1a97e6..0000000 --- a/src/os/uefi/key.rs +++ /dev/null @@ -1,95 +0,0 @@ -use core::char; -use uefi::status::Result; -use uefi::text::TextInputKey; - -#[derive(Debug, PartialEq)] -pub enum Key { - Backspace, - Tab, - Enter, - Character(char), - Up, - Down, - Right, - Left, - Home, - End, - Insert, - Delete, - PageUp, - PageDown, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - Escape, - Scancode(u16), -} - -impl From<TextInputKey> for Key { - fn from(raw_key: TextInputKey) -> Self { - match raw_key.ScanCode { - 0 => match unsafe { char::from_u32_unchecked(raw_key.UnicodeChar as u32) } { - '\u{8}' => Key::Backspace, - '\t' => Key::Tab, - '\r' => Key::Enter, - c => Key::Character(c), - }, - 1 => Key::Up, - 2 => Key::Down, - 3 => Key::Right, - 4 => Key::Left, - 5 => Key::Home, - 6 => Key::End, - 7 => Key::Insert, - 8 => Key::Delete, - 9 => Key::PageUp, - 10 => Key::PageDown, - 11 => Key::F1, - 12 => Key::F2, - 13 => Key::F3, - 14 => Key::F4, - 15 => Key::F5, - 16 => Key::F6, - 17 => Key::F7, - 18 => Key::F8, - 19 => Key::F9, - 20 => Key::F10, - 21 => Key::F11, - 22 => Key::F12, - 23 => Key::Escape, - scancode => Key::Scancode(scancode), - } - } -} - -pub fn raw_key(wait: bool) -> Result<TextInputKey> { - let uefi = std::system_table(); - - if wait { - let mut index = 0; - (uefi.BootServices.WaitForEvent)(1, &uefi.ConsoleIn.WaitForKey, &mut index)?; - } - - let mut key = TextInputKey { - ScanCode: 0, - UnicodeChar: 0 - }; - - (uefi.ConsoleIn.ReadKeyStroke)(uefi.ConsoleIn, &mut key)?; - - Ok(key) -} - -pub fn key(wait: bool) -> Result<Key> { - let raw_key = raw_key(wait)?; - Ok(Key::from(raw_key)) -} diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs index 1e5e220..c1c4f78 100644 --- a/src/os/uefi/mod.rs +++ b/src/os/uefi/mod.rs @@ -6,8 +6,6 @@ use uefi::status::{Result, Status}; mod arch; mod disk; mod display; -mod key; -pub mod text; fn set_max_mode(output: &uefi::text::TextOutput) -> Result<()> { let mut max_i = None; @@ -44,8 +42,7 @@ pub extern "C" fn main() -> Status { } if let Err(err) = arch::main() { - println!("App error: {:?}", err); - let _ = key::key(true); + panic!("App error: {:?}", err); } (uefi.RuntimeServices.ResetSystem)(ResetType::Cold, Status(0), 0, ptr::null()); diff --git a/src/os/uefi/text.rs b/src/os/uefi/text.rs deleted file mode 100644 index 5ebc6d6..0000000 --- a/src/os/uefi/text.rs +++ /dev/null @@ -1,252 +0,0 @@ -use core::{char, mem, ptr}; -use core::ops::Deref; -use orbclient::{Color, Renderer}; -use std::boxed::Box; -use std::proto::Protocol; -use uefi::Handle; -use uefi::boot::InterfaceType; -use uefi::guid::SIMPLE_TEXT_OUTPUT_GUID; -use uefi::status::{Result, Status}; -use uefi::text::TextOutputMode; - -use super::display::{Display, ScaledDisplay, Output}; - -#[repr(C)] -#[allow(non_snake_case)] -pub struct TextDisplay<'a> { - pub Reset: extern "win64" fn(&mut TextDisplay, bool) -> Status, - pub OutputString: extern "win64" fn(&mut TextDisplay, *const u16) -> Status, - pub TestString: extern "win64" fn(&mut TextDisplay, *const u16) -> Status, - pub QueryMode: extern "win64" fn(&mut TextDisplay, usize, &mut usize, &mut usize) -> Status, - pub SetMode: extern "win64" fn(&mut TextDisplay, usize) -> Status, - pub SetAttribute: extern "win64" fn(&mut TextDisplay, usize) -> Status, - pub ClearScreen: extern "win64" fn(&mut TextDisplay) -> Status, - pub SetCursorPosition: extern "win64" fn(&mut TextDisplay, usize, usize) -> Status, - pub EnableCursor: extern "win64" fn(&mut TextDisplay, bool) -> Status, - pub Mode: &'static TextOutputMode, - - pub mode: Box<TextOutputMode>, - pub off_x: i32, - pub off_y: i32, - pub cols: usize, - pub rows: usize, - pub display: ScaledDisplay<'a>, -} - -extern "win64" fn reset(_output: &mut TextDisplay, _extra: bool) -> Status { - Status(0) -} - -extern "win64" fn output_string(output: &mut TextDisplay, string: *const u16) -> Status { - output.write(string); - Status(0) -} - -extern "win64" fn test_string(_output: &mut TextDisplay, _string: *const u16) -> Status { - Status(0) -} - -extern "win64" fn query_mode(output: &mut TextDisplay, _mode: usize, columns: &mut usize, rows: &mut usize) -> Status { - *columns = output.cols; - *rows = output.rows; - Status(0) -} - -extern "win64" fn set_mode(_output: &mut TextDisplay, _mode: usize) -> Status { - Status(0) -} - -extern "win64" fn set_attribute(output: &mut TextDisplay, attribute: usize) -> Status { - output.mode.Attribute = attribute as i32; - Status(0) -} - -extern "win64" fn clear_screen(output: &mut TextDisplay) -> Status { - output.clear(); - Status(0) -} - -extern "win64" fn set_cursor_position(output: &mut TextDisplay, column: usize, row: usize) -> Status { - output.set_cursor_pos(column as i32, row as i32); - Status(0) -} - -extern "win64" fn enable_cursor(output: &mut TextDisplay, enable: bool) -> Status { - output.mode.CursorVisible = enable; - Status(0) -} - -impl<'a> TextDisplay<'a> { - pub fn new(display: ScaledDisplay<'a>) -> TextDisplay<'a> { - let mode = Box::new(TextOutputMode { - MaxMode: 0, - Mode: 0, - Attribute: 0, - CursorColumn: 0, - CursorRow: 0, - CursorVisible: false, - }); - - let cols = display.width() as usize/8; - let rows = display.height() as usize/16; - - TextDisplay { - Reset: reset, - OutputString: output_string, - TestString: test_string, - QueryMode: query_mode, - SetMode: set_mode, - SetAttribute: set_attribute, - ClearScreen: clear_screen, - SetCursorPosition: set_cursor_position, - EnableCursor: enable_cursor, - Mode: unsafe { mem::transmute(&*mode.deref()) }, - - mode, - off_x: 0, - off_y: 0, - cols, - rows, - display, - } - } - - pub fn pos(&self) -> (i32, i32) { - ( - self.mode.CursorColumn * 8 + self.off_x, - self.mode.CursorRow * 16 + self.off_y, - ) - } - - pub fn clear(&mut self) { - // Clears are ignored - //let bg = Color::rgb(0, 0, 0); - //self.display.rect(self.off_x, self.off_y, self.cols * 8, self.rows * 16, bg); - //self.display.blit(0, self.off_y, w, self.rows * 16); - self.display.sync(); - } - - pub fn scroll(&mut self, color: Color) { - if self.rows > 0 { - let w = self.display.width(); - - let dst = self.off_y * w as i32; - let src = (self.off_y + 16) * w as i32; - let len = (self.rows - 1) * 16 * w as usize; - unsafe { - let scale = self.display.scale() as isize; - let data_ptr = self.display.data_mut().as_mut_ptr() as *mut u32; - ptr::copy( - data_ptr.offset(src as isize * scale * scale), - data_ptr.offset(dst as isize * scale * scale), - len * (scale * scale) as usize); - } - - self.display.rect(self.off_x, self.off_y + (self.rows as i32 - 1) * 16, self.cols as u32 * 8, 16, color); - } - } - - pub fn set_cursor_pos(&mut self, column: i32, _row: i32) { - self.mode.CursorColumn = column; - } - - pub fn write(&mut self, string: *const u16) { - let bg = Color::rgb(0, 0, 0); - let fg = Color::rgb(255, 255, 255); - - let mut scrolled = false; - let mut changed = false; - let (_sx, sy) = self.pos(); - - let mut i = 0; - loop { - let w = unsafe { *string.offset(i) }; - if w == 0 { - break; - } - - let c = unsafe { char::from_u32_unchecked(w as u32) }; - - if self.mode.CursorColumn as usize >= self.cols { - self.mode.CursorColumn = 0; - self.mode.CursorRow += 1; - } - - while self.mode.CursorRow as usize >= self.rows { - self.scroll(bg); - self.mode.CursorRow -= 1; - scrolled = true; - } - - match c { - '\x08' => if self.mode.CursorColumn > 0 { - let (x, y) = self.pos(); - self.display.rect(x, y, 8, 16, bg); - self.mode.CursorColumn -= 1; - changed = true; - }, - '\r'=> { - self.mode.CursorColumn = 0; - }, - '\n' => { - self.mode.CursorRow += 1; - }, - _ => { - let (x, y) = self.pos(); - self.display.rect(x, y, 8, 16, bg); - self.display.char(x, y, c, fg); - self.mode.CursorColumn += 1; - changed = true; - } - } - - i += 1; - } - - if scrolled { - let (cx, cw) = (0, self.display.width() as i32); - let (cy, ch) = (self.off_y, self.rows as u32 * 16); - self.display.blit(cx, cy, cw as u32, ch as u32); - } else if changed { - let (_x, y) = self.pos(); - let (cx, cw) = (0, self.display.width() as i32); - let (cy, ch) = (sy, y + 16 - sy); - self.display.blit(cx, cy, cw as u32, ch as u32); - } - } - - pub fn pipe<T, F: FnMut() -> Result<T>>(&mut self, mut f: F) -> Result<T> { - let uefi = unsafe { std::system_table_mut() }; - - let stdout = self as *mut _; - let mut stdout_handle = Handle(0); - (uefi.BootServices.InstallProtocolInterface)(&mut stdout_handle, &SIMPLE_TEXT_OUTPUT_GUID, InterfaceType::Native, stdout as usize)?; - - let old_stdout_handle = uefi.ConsoleOutHandle; - let old_stdout = uefi.ConsoleOut as *mut _; - let old_stderr_handle = uefi.ConsoleErrorHandle; - let old_stderr = uefi.ConsoleError as *mut _; - - uefi.ConsoleOutHandle = stdout_handle; - uefi.ConsoleOut = unsafe { mem::transmute(&mut *stdout) }; - uefi.ConsoleErrorHandle = stdout_handle; - uefi.ConsoleError = unsafe { mem::transmute(&mut *stdout) }; - - let res = f(); - - uefi.ConsoleOutHandle = old_stdout_handle; - uefi.ConsoleOut = unsafe { mem::transmute(&mut *old_stdout) }; - uefi.ConsoleErrorHandle = old_stderr_handle; - uefi.ConsoleError = unsafe { mem::transmute(&mut *old_stderr) }; - - let _ = (uefi.BootServices.UninstallProtocolInterface)(stdout_handle, &SIMPLE_TEXT_OUTPUT_GUID, stdout as usize); - - res - } -} - -pub fn pipe<T, F: FnMut() -> Result<T>>(f: F) -> Result<T> { - let mut output = Output::one()?; - let mut display = Display::new(&mut output); - TextDisplay::new(ScaledDisplay::new(&mut display)).pipe(f) -} -- GitLab