|
| 1 | +// This crate provides the core functionality for initializing a hypervisor environment |
| 2 | +// within a UEFI application. It demonstrates advanced features such as custom panic handlers, |
| 3 | +// early logging, and direct manipulation of loaded image properties for hypervisor initialization. |
| 4 | + |
1 | 5 | #![feature(new_uninit)]
|
2 | 6 | #![feature(panic_info_message)]
|
3 | 7 | #![no_main]
|
4 | 8 | #![no_std]
|
5 | 9 |
|
6 | 10 | extern crate alloc;
|
7 | 11 |
|
8 |
| -use core::ffi::c_void; |
9 | 12 | use {
|
10 |
| - crate::{ |
11 |
| - processor::MpManager, |
12 |
| - virtualize::{switch_stack_and_virtualize_core, zap_relocations}, |
13 |
| - }, |
14 |
| - hypervisor::{ |
15 |
| - global::GlobalState, |
16 |
| - intel::capture::{capture_registers, GuestRegisters}, |
17 |
| - logger, |
18 |
| - }, |
| 13 | + crate::processor::start_hypervisor_on_all_processors, |
| 14 | + hypervisor::logger, |
19 | 15 | log::*,
|
20 |
| - uefi::prelude::*, |
| 16 | + uefi::{prelude::*, proto::loaded_image::LoadedImage}, |
21 | 17 | };
|
22 | 18 |
|
23 | 19 | pub mod processor;
|
24 | 20 | pub mod virtualize;
|
25 | 21 |
|
26 |
| -// Change as you like |
| 22 | +/// Custom panic handler for the UEFI application. |
| 23 | +/// |
| 24 | +/// # Arguments |
| 25 | +/// |
| 26 | +/// * `info` - Information about the panic, including the location and optional message. |
27 | 27 | #[cfg(not(test))]
|
28 | 28 | #[panic_handler]
|
29 | 29 | fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
| 30 | + // Log the file, line, and column of the panic. |
30 | 31 | if let Some(location) = info.location() {
|
31 | 32 | error!(
|
32 | 33 | "[-] Panic in {} at ({}, {}):",
|
33 | 34 | location.file(),
|
34 | 35 | location.line(),
|
35 | 36 | location.column()
|
36 | 37 | );
|
| 38 | + // Log the panic message if available. |
37 | 39 | if let Some(message) = info.message() {
|
38 | 40 | error!("[-] {}", message);
|
39 | 41 | }
|
40 | 42 | }
|
41 | 43 |
|
| 44 | + // Enter an infinite loop as the panic handler should not return. |
42 | 45 | loop {}
|
43 | 46 | }
|
44 | 47 |
|
| 48 | +/// Entry point for the UEFI application. |
| 49 | +/// |
| 50 | +/// Initializes logging, UEFI services, and attempts to start the hypervisor on all processors. |
| 51 | +/// |
| 52 | +/// # Arguments |
| 53 | +/// |
| 54 | +/// * `_image_handle` - Handle to the loaded image of the application. |
| 55 | +/// * `system_table` - Reference to the UEFI System Table. |
| 56 | +/// |
| 57 | +/// # Returns |
| 58 | +/// |
| 59 | +/// The status of the application execution. Returns `Status::SUCCESS` on successful execution, |
| 60 | +/// or `Status::ABORTED` if the hypervisor fails to install. |
45 | 61 | #[entry]
|
46 | 62 | fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
47 |
| - // Initialize the COM2 port logger with level filter set to Info. |
| 63 | + // Initialize logging with the COM2 port and set the level filter to Trace. |
48 | 64 | logger::init(LevelFilter::Trace);
|
49 | 65 |
|
50 |
| - //com_logger::builder().base(0x2f8).filter(LevelFilter::Trace).setup(); |
51 |
| - |
| 66 | + // Initialize UEFI services. |
52 | 67 | uefi_services::init(&mut system_table).unwrap();
|
53 | 68 |
|
54 | 69 | info!("The Matrix is an illusion");
|
55 | 70 |
|
56 |
| - // Get the MP Services (MultiProcessor Services) Protocol |
57 |
| - let mp_manager = match MpManager::new(system_table.boot_services()) { |
58 |
| - Ok(mp_manager) => mp_manager, |
59 |
| - Err(e) => panic!("Failed to get MP Services: {:?}", e), |
60 |
| - }; |
61 |
| - |
62 |
| - // Get the processor count |
63 |
| - let processor_count = match mp_manager.processor_count() { |
64 |
| - Ok(processor_count) => processor_count, |
65 |
| - Err(e) => panic!("Failed to get processor count: {:?}", e), |
66 |
| - }; |
67 |
| - |
68 |
| - info!("Total processors: {}", processor_count.total); |
69 |
| - info!("Enabled processors: {}", processor_count.enabled); |
70 |
| - |
71 | 71 | zap_relocations(&system_table);
|
72 | 72 |
|
73 |
| - // Capture the register values to be used as an initial state of the VM. |
74 |
| - let mut guest_registers = GuestRegisters::default(); |
75 |
| - unsafe { capture_registers(&mut guest_registers) } |
76 |
| - |
77 |
| - // Since we captured RIP just above, the VM will start running from here. |
78 |
| - // Check if our hypervisor is already loaded. If so, done, otherwise, continue installing the hypervisor. |
79 |
| - if !mp_manager.is_virtualized() { |
80 |
| - debug!("Virtualizing the system"); |
81 |
| - mp_manager.set_virtualized(); |
82 |
| - |
83 |
| - let mut global_state = GlobalState::new(guest_registers); |
84 |
| - |
85 |
| - if processor_count.enabled == 1 { |
86 |
| - info!("Found only one processor, virtualizing it"); |
87 |
| - switch_stack_and_virtualize_core(&mut global_state as *mut _ as *mut c_void); |
88 |
| - } else { |
89 |
| - info!("Found multiple processors, virtualizing all of them"); |
90 |
| - match mp_manager.start_virtualization_on_all_processors( |
91 |
| - switch_stack_and_virtualize_core, |
92 |
| - &mut global_state as *mut _ as *mut c_void, |
93 |
| - ) { |
94 |
| - Ok(_) => debug!("Virtualization started on all processors"), |
95 |
| - Err(e) => panic!("Failed to start virtualization on all processors: {:?}", e), |
96 |
| - } |
| 73 | + // Attempt to start the hypervisor on all processors. |
| 74 | + match start_hypervisor_on_all_processors(&system_table) { |
| 75 | + Ok(_) => debug!("Hypervisor installed successfully"), |
| 76 | + Err(e) => { |
| 77 | + error!("Failed to install hypervisor: {:?}", e); |
| 78 | + return Status::ABORTED; |
97 | 79 | }
|
98 | 80 | }
|
99 | 81 |
|
100 |
| - info!("The hypervisor has been installed successfully!"); |
101 |
| - |
102 |
| - system_table.boot_services().stall(20_000_000); |
103 |
| - |
| 82 | + // Return success status to UEFI environment. |
104 | 83 | Status::SUCCESS
|
105 | 84 | }
|
| 85 | + |
| 86 | +/// Nullifies the relocation table of the loaded UEFI image to prevent relocation. |
| 87 | +/// |
| 88 | +/// This function manipulates the loaded image's PE header to zero out the relocation table, |
| 89 | +/// preventing UEFI from applying patches to the hypervisor code during the transition |
| 90 | +/// from physical-mode to virtual-mode addressing by the operating system. |
| 91 | +/// |
| 92 | +/// # Arguments |
| 93 | +/// |
| 94 | +/// * `system_table` - Reference to the UEFI System Table. |
| 95 | +pub fn zap_relocations(system_table: &SystemTable<Boot>) { |
| 96 | + let boot_service = system_table.boot_services(); |
| 97 | + |
| 98 | + // Obtain the current loaded image protocol. |
| 99 | + let loaded_image = boot_service |
| 100 | + .open_protocol_exclusive::<LoadedImage>(boot_service.image_handle()) |
| 101 | + .unwrap(); |
| 102 | + |
| 103 | + // Extract the image base address and size. |
| 104 | + let (image_base, image_size) = loaded_image.info(); |
| 105 | + let image_base = image_base as usize; |
| 106 | + let image_range = image_base..image_base + image_size as usize; |
| 107 | + |
| 108 | + // Log the image base address range for debugging purposes. |
| 109 | + debug!("Image base: {:#x?}", image_range); |
| 110 | + |
| 111 | + // Unsafe block to directly modify the PE header of the loaded image. |
| 112 | + // This operation nullifies the relocation table to prevent UEFI from |
| 113 | + // applying relocations to the hypervisor code. |
| 114 | + unsafe { |
| 115 | + *((image_base + 0x128) as *mut u32) = 0; // Zero out the relocation table offset. |
| 116 | + *((image_base + 0x12c) as *mut u32) = 0; // Zero out the relocation table size. |
| 117 | + } |
| 118 | +} |
0 commit comments