This repository has been archived by the owner on Sep 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from x1tan/loader-application
Add EFI loader application to first load the hypervisor and subsequently start Windows
- Loading branch information
Showing
6 changed files
with
204 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,8 @@ resolver = "2" | |
|
||
members = [ | ||
"driver", | ||
"hypervisor", | ||
"hypervisor", | ||
"loader", | ||
] | ||
|
||
[profile.release] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[build] | ||
target = "x86_64-unknown-uefi" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Box<DevicePath>> { | ||
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::<SimpleFileSystem>(*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::<DevicePath>(*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<Box<DevicePath>> { | ||
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<Box<DevicePath>> { | ||
find_device_path(boot_services, HYPERVISOR_PATH) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Boot>) -> 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 | ||
} |