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