From 152155fd03016ea26286d0da762fbedb5b429f3c Mon Sep 17 00:00:00 2001 From: xitan Date: Sun, 31 Mar 2024 21:49:22 +0200 Subject: [PATCH] Add EFI loader application which loads the hypervisor and then starts the Windows boot manager --- Cargo.toml | 3 +- loader/.cargo/config.toml | 2 + loader/Cargo.toml | 10 ++++ loader/README.md | 7 +++ loader/src/images.rs | 99 +++++++++++++++++++++++++++++++++++++++ loader/src/main.rs | 84 +++++++++++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 loader/.cargo/config.toml create mode 100644 loader/Cargo.toml create mode 100644 loader/README.md create mode 100644 loader/src/images.rs create mode 100644 loader/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 8052740..27493b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ resolver = "2" members = [ "driver", - "hypervisor", + "hypervisor", + "loader", ] [profile.release] diff --git a/loader/.cargo/config.toml b/loader/.cargo/config.toml new file mode 100644 index 0000000..5324240 --- /dev/null +++ b/loader/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "x86_64-unknown-uefi" \ No newline at end of file diff --git a/loader/Cargo.toml b/loader/Cargo.toml new file mode 100644 index 0000000..124b5f4 --- /dev/null +++ b/loader/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "loader" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4.20" + +uefi = { version = "0.27.0", features = ["alloc"] } +uefi-services = { version = "0.24.0", default-features = true } \ No newline at end of file diff --git a/loader/README.md b/loader/README.md new file mode 100644 index 0000000..0f2a906 --- /dev/null +++ b/loader/README.md @@ -0,0 +1,7 @@ +# Loader + +The loader is a simple EFI application which searches the available file systems to first load the illusion hypervisor (`illusion.efi`) and then start Windows (`bootmgfw.efi`). + +## Usage + +Build the loader (`$ cargo build --target x86_64-unknown-uefi --profile release --package loader`) and copy the loader (`EFI/Boot/bootx64.efi`) as well as the hypervisor (`EFI/Boot/illusion.efi`) to your boot disk. The loader will automatically start the hypervisor and Windows. \ No newline at end of file diff --git a/loader/src/images.rs b/loader/src/images.rs new file mode 100644 index 0000000..77679ce --- /dev/null +++ b/loader/src/images.rs @@ -0,0 +1,99 @@ +extern crate alloc; + +use { + alloc::{borrow::ToOwned, boxed::Box, vec::Vec}, + uefi::{ + prelude::*, + proto::{ + device_path::{ + build::{media::FilePath, DevicePathBuilder}, + DevicePath, + }, + media::{ + file::{File, FileAttribute, FileMode}, + fs::SimpleFileSystem, + }, + }, + table::boot::{HandleBuffer, SearchType}, + CStr16, Identify, + }, +}; + +const WINDOWS_BOOT_MANAGER_PATH: &CStr16 = cstr16!("\\efi\\microsoft\\boot\\bootmgfw.efi"); +const HYPERVISOR_PATH: &CStr16 = cstr16!("\\efi\\boot\\illusion.efi"); + +/// Finds the device path for a given file path. +/// +/// # Arguments +/// +/// * `boot_services` - A reference to the UEFI boot services. +/// * `path` - The file path to search for, as a `CStr16`. +/// +/// # Returns +/// +/// If a device containing the specified file is found, this function returns an `Option` containing +/// a `DevicePath` to the file. If no such device is found, it returns `None`. +pub(crate) fn find_device_path( + boot_services: &BootServices, + path: &CStr16, +) -> Option> { + let handles: HandleBuffer = boot_services + .locate_handle_buffer(SearchType::ByProtocol(&SimpleFileSystem::GUID)) + .ok()?; + + handles.iter().find_map(|handle| { + let mut file_system = boot_services + .open_protocol_exclusive::(*handle) + .ok()?; + + let mut root = file_system.open_volume().ok()?; + root.open(path, FileMode::Read, FileAttribute::READ_ONLY) + .ok()?; + + let device_path = boot_services + .open_protocol_exclusive::(*handle) + .ok()?; + + let mut storage = Vec::new(); + let boot_path = device_path + .node_iter() + .fold( + DevicePathBuilder::with_vec(&mut storage), + |builder, item| builder.push(&item).unwrap(), + ) + .push(&FilePath { path_name: path }) + .ok()? + .finalize() + .ok()?; + + Some(boot_path.to_owned()) + }) +} + +/// Finds the device path of the Windows boot manager. +/// +/// # Arguments +/// +/// * `boot_services` - A reference to the UEFI boot services. +/// +/// # Returns +/// +/// If a device containing the Windows boot manager is found, this function returns an `Option` containing +/// a `DevicePath` to the file. If no such device is found, it returns `None`. +pub(crate) fn find_windows_boot_manager(boot_services: &BootServices) -> Option> { + find_device_path(boot_services, WINDOWS_BOOT_MANAGER_PATH) +} + +/// Finds the device path of the Illusion hypervisor. +/// +/// # Arguments +/// +/// * `boot_services` - A reference to the UEFI boot services. +/// +/// # Returns +/// +/// If a device containing the Illusion hypervisor is found, this function returns an `Option` containing +/// a `DevicePath` to the file. If no such device is found, it returns `None`. +pub(crate) fn find_hypervisor(boot_services: &BootServices) -> Option> { + find_device_path(boot_services, HYPERVISOR_PATH) +} diff --git a/loader/src/main.rs b/loader/src/main.rs new file mode 100644 index 0000000..3e2dbd2 --- /dev/null +++ b/loader/src/main.rs @@ -0,0 +1,84 @@ +#![no_main] +#![no_std] + +mod images; + +use uefi::{prelude::*, table::boot::LoadImageSource}; + +#[entry] +unsafe fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { + if let Err(error) = uefi_services::init(&mut system_table) { + log::error!("Failed to initialize UEFI services ({:?})", error); + return Status::ABORTED; + } + + log::info!("Searching Illusion hypervisor (illusion.efi).."); + + match images::find_hypervisor(system_table.boot_services()) { + Some(hypervisor_device_path) => { + log::info!("Found! Loading hypervisor into memory.."); + + match system_table.boot_services().load_image( + image_handle, + LoadImageSource::FromDevicePath { + device_path: &hypervisor_device_path, + from_boot_manager: false, + }, + ) { + Ok(handle) => { + log::info!("Loaded hypervisor into mermoy, starting.."); + + if let Err(error) = system_table.boot_services().start_image(handle) { + log::error!("Failed to start hypervisor ({:?})", error); + return Status::ABORTED; + } + } + Err(error) => { + log::error!("Failed to load hypervisor ({:?})", error); + return Status::ABORTED; + } + } + } + None => { + log::info!("Failed to find hypervisor image"); + return Status::ABORTED; + } + }; + + log::info!("Searching Windows boot manager (bootmgfw.efi).."); + + match images::find_windows_boot_manager(system_table.boot_services()) { + Some(bootmgr_device_path) => { + log::info!("Found! Loading boot manager into memory.."); + + system_table.boot_services().stall(3_000_000); + + match system_table.boot_services().load_image( + image_handle, + LoadImageSource::FromDevicePath { + device_path: &bootmgr_device_path, + from_boot_manager: false, + }, + ) { + Ok(handle) => { + log::info!("Loaded boot manager into memory, starting.."); + + if let Err(error) = system_table.boot_services().start_image(handle) { + log::error!("Failed to start boot manager ({:?})", error); + return Status::ABORTED; + } + } + Err(error) => { + log::error!("Failed to load boot manager ({:?})", error); + return Status::ABORTED; + } + } + } + None => { + log::info!("Failed to find Windows boot manager image"); + return Status::ABORTED; + } + } + + Status::SUCCESS +}