morpheus_bootloader/installer/
operations.rsuse super::{EspInfo, InstallError};
use crate::uefi::file_system::{
FileProtocol, LoadedImageProtocol, EFI_FILE_MODE_READ, LOADED_IMAGE_PROTOCOL_GUID,
};
use crate::BootServices;
use morpheus_core::disk::partition::PartitionType;
use morpheus_persistent::pe::header::PeHeaders;
pub fn find_esp(bs: &BootServices) -> Result<EspInfo, InstallError> {
let mut temp_disk_manager = morpheus_core::disk::manager::DiskManager::new();
crate::uefi::disk::enumerate_disks(bs, &mut temp_disk_manager)
.map_err(|_| InstallError::ProtocolError)?;
let disk_count = temp_disk_manager.disk_count();
for disk_idx in 0..disk_count {
let block_io_ptr = match crate::uefi::disk::get_disk_protocol(bs, disk_idx) {
Ok(ptr) => ptr,
Err(_) => continue,
};
let block_io = unsafe { &mut *block_io_ptr };
let media = unsafe { &*block_io.media };
let block_size = media.block_size as usize;
let adapter = match crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io) {
Ok(a) => a,
Err(_) => continue,
};
let mut partition_table = morpheus_core::disk::partition::PartitionTable::new();
if morpheus_core::disk::gpt_ops::scan_partitions(adapter, &mut partition_table, block_size)
.is_err()
{
continue;
}
for part_idx in 0..partition_table.count() {
if let Some(part) = partition_table.get(part_idx) {
if matches!(
part.partition_type,
morpheus_core::disk::partition::PartitionType::EfiSystem
) {
let size_mb = part.size_mb();
if size_mb < 100 {
continue;
}
return Ok(EspInfo {
disk_index: disk_idx,
partition_index: part_idx,
start_lba: part.start_lba,
size_mb,
});
}
}
}
}
Err(InstallError::NoEsp)
}
#[cfg(feature = "fat32_debug")]
fn verify_written_file<B: gpt_disk_io::BlockIo>(
adapter: &mut B,
partition_start: u64,
expected_data: &[u8],
) -> Result<(), InstallError> {
use gpt_disk_types::Lba;
use morpheus_core::fs::fat32_ops;
let exists = fat32_ops::file_exists(adapter, partition_start, "/EFI/BOOT/BOOTX64.EFI")
.map_err(|_| InstallError::IoError)?;
if !exists {
morpheus_core::logger::log("VERIFY FAIL: File does not exist after write");
return Err(InstallError::IoError);
}
morpheus_core::logger::log("File exists - checking byte corruption...");
let check_offset = 0x400usize;
let check_len = 256usize;
if expected_data.len() > check_offset + check_len {
let expected_slice = &expected_data[check_offset..check_offset + check_len];
let mut af_count = 0u32;
for &byte in expected_slice {
if byte == 0xAF {
af_count += 1;
}
}
if af_count == check_len as u32 {
morpheus_core::logger::log("WARN: Expected data at 0x400 is all 0xAF - can't verify");
} else {
morpheus_core::logger::log("Expected non-0xAF bytes at 0x400 - verification possible");
}
}
Ok(())
}
pub fn install_to_esp(
bs: &BootServices,
esp: &EspInfo,
image_handle: *mut (),
) -> Result<(), InstallError> {
install_to_esp_with_progress(bs, esp, image_handle, None)
}
pub type ProgressCallback<'a> = Option<&'a mut dyn FnMut(usize, usize, &str)>;
pub fn install_to_esp_with_progress(
bs: &BootServices,
esp: &EspInfo,
image_handle: *mut (),
mut progress: ProgressCallback,
) -> Result<(), InstallError> {
unsafe {
let loaded_image = crate::uefi::file_system::get_loaded_image(bs, image_handle)
.map_err(|_| InstallError::ProtocolError)?;
let image_base = (*loaded_image).image_base as *const u8;
let image_size = (*loaded_image).image_size as usize;
use morpheus_persistent::pe::header::PeHeaders;
let headers =
PeHeaders::parse(image_base, image_size).map_err(|_| InstallError::ProtocolError)?;
let mut binary_data = alloc::vec![0u8; image_size];
core::ptr::copy_nonoverlapping(image_base, binary_data.as_mut_ptr(), image_size);
if binary_data.len() > 0x404 {
let b0 = binary_data[0x400];
let b1 = binary_data[0x401];
let b2 = binary_data[0x402];
let b3 = binary_data[0x403];
if b0 == 0xAF && b1 == 0xAF && b2 == 0xAF && b3 == 0xAF {
morpheus_core::logger::log("BUG: Memory at 0x400 is 0xAF BEFORE unrelocate!");
} else {
morpheus_core::logger::log("OK: Memory at 0x400 has code BEFORE unrelocate");
}
}
let actual_load = image_base as u64;
let delta_used = headers
.unrelocate_image(&mut binary_data, actual_load)
.map_err(|_| InstallError::ProtocolError)?;
let file_layout_data = headers
.rva_to_file_layout(&binary_data)
.map_err(|_| InstallError::ProtocolError)?;
let binary_data = file_layout_data;
if delta_used == 0 {
morpheus_core::logger::log("ERROR: Delta is ZERO - heuristic failed!");
} else if delta_used > 0 {
morpheus_core::logger::log("Delta is positive (loaded higher than original)");
} else {
morpheus_core::logger::log("Delta is negative (loaded lower than original)");
}
if binary_data.len() > 0x404 {
let b0 = binary_data[0x400];
let b1 = binary_data[0x401];
let b2 = binary_data[0x402];
let b3 = binary_data[0x403];
if b0 == 0xAF && b1 == 0xAF && b2 == 0xAF && b3 == 0xAF {
morpheus_core::logger::log("BUG: Buffer at 0x400 is 0xAF AFTER unrelocate!");
} else {
morpheus_core::logger::log("OK: Buffer at 0x400 has code AFTER unrelocate");
}
}
if binary_data.len() > 0x410 {
let byte_1024 = binary_data[0x400];
let byte_1025 = binary_data[0x401];
let byte_1026 = binary_data[0x402];
let byte_1027 = binary_data[0x403];
if byte_1024 == 0xAF && byte_1025 == 0xAF && byte_1026 == 0xAF && byte_1027 == 0xAF {
morpheus_core::logger::log(
"!!! Buffer already corrupted at 0x400 before FAT32 write !!!",
);
} else {
morpheus_core::logger::log("Buffer OK at 0x400 - contains code, not 0xAF");
}
}
let block_io = crate::uefi::disk::get_disk_protocol(bs, esp.disk_index)
.map_err(|_| InstallError::ProtocolError)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(&mut *block_io)
.map_err(|_| InstallError::IoError)?;
use morpheus_core::fs::fat32_ops;
if let Some(ref mut cb) = progress {
cb(0, binary_data.len(), "Writing BOOTX64.EFI...");
}
fat32_ops::write_file_with_progress(
&mut adapter,
esp.start_lba,
"/EFI/BOOT/BOOTX64.EFI",
&binary_data,
&mut progress,
)
.map_err(|_| InstallError::IoError)?;
#[cfg(feature = "fat32_debug")]
{
morpheus_core::logger::log("Verifying written file...");
verify_written_file(&mut adapter, esp.start_lba, &binary_data)?;
}
Ok(())
}
}
pub fn is_installed(bs: &BootServices) -> Result<bool, InstallError> {
let esp = find_esp(bs)?;
unsafe {
let block_io = crate::uefi::disk::get_disk_protocol(bs, esp.disk_index)
.map_err(|_| InstallError::ProtocolError)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(&mut *block_io)
.map_err(|_| InstallError::IoError)?;
use morpheus_core::fs::fat32_ops;
let exists = fat32_ops::file_exists(&mut adapter, esp.start_lba, "/EFI/BOOT/BOOTX64.EFI")
.map_err(|_| InstallError::IoError)?;
Ok(exists)
}
}
pub fn create_esp_and_install(
bs: &BootServices,
disk_index: usize,
) -> Result<EspInfo, InstallError> {
let block_io_ptr = crate::uefi::disk::get_disk_protocol(bs, disk_index)
.map_err(|_| InstallError::ProtocolError)?;
let block_io = unsafe { &mut *block_io_ptr };
let media = unsafe { &*block_io.media };
let block_size = media.block_size as usize;
let total_blocks = media.last_block + 1;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io)
.map_err(|_| InstallError::ProtocolError)?;
let mut partition_table = morpheus_core::disk::partition::PartitionTable::new();
morpheus_core::disk::gpt_ops::scan_partitions(adapter, &mut partition_table, block_size)
.map_err(|_| InstallError::IoError)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io)
.map_err(|_| InstallError::ProtocolError)?;
let min_size_mb = 512;
let min_sectors = (min_size_mb * 1024 * 1024) / block_size as u64;
let free_regions = morpheus_core::disk::gpt_ops::find_free_space(adapter, block_size)
.map_err(|_| InstallError::IoError)?;
let region = free_regions
.iter()
.filter_map(|r| r.as_ref())
.find(|r| (r.end_lba - r.start_lba + 1) >= min_sectors)
.ok_or(InstallError::NoFreeSpc)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io)
.map_err(|_| InstallError::ProtocolError)?;
let available_sectors = region.end_lba - region.start_lba + 1;
let esp_sectors = min_sectors.min(available_sectors);
let esp_end_lba = region.start_lba + esp_sectors - 1;
let partition_type = morpheus_core::disk::partition::PartitionType::EfiSystem;
morpheus_core::disk::gpt_ops::create_partition(
adapter,
partition_type,
region.start_lba,
esp_end_lba,
)
.map_err(|_| InstallError::IoError)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io)
.map_err(|_| InstallError::ProtocolError)?;
partition_table = morpheus_core::disk::partition::PartitionTable::new();
morpheus_core::disk::gpt_ops::scan_partitions(adapter, &mut partition_table, block_size)
.map_err(|_| InstallError::IoError)?;
let mut esp_partition_index = None;
for idx in 0..partition_table.count() {
if let Some(part) = partition_table.get(idx) {
if matches!(
part.partition_type,
morpheus_core::disk::partition::PartitionType::EfiSystem
) && part.start_lba == region.start_lba
{
esp_partition_index = Some(idx);
break;
}
}
}
let partition_index = esp_partition_index.ok_or(InstallError::IoError)?;
let part = partition_table
.get(partition_index)
.ok_or(InstallError::IoError)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io)
.map_err(|_| InstallError::ProtocolError)?;
let partition_sectors = part.end_lba - part.start_lba + 1;
morpheus_core::fs::format_fat32(&mut adapter, part.start_lba, partition_sectors)
.map_err(|_| InstallError::FormatFailed)?;
let mut adapter = crate::uefi::gpt_adapter::UefiBlockIoAdapter::new(block_io)
.map_err(|_| InstallError::ProtocolError)?;
morpheus_core::fs::verify_fat32(&mut adapter, part.start_lba)
.map_err(|_| InstallError::FormatFailed)?;
Ok(EspInfo {
disk_index,
partition_index,
start_lba: part.start_lba,
size_mb: part.size_mb(),
})
}