use core::ffi::c_void; use crate::efi::{ EFI_LOADED_IMAGE_PROTOCOL_GUID, EFI_NOT_FOUND, EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, EFI_SUCCESS, EfiBootServices, EfiGuid, EfiHandle, EfiLoadedImageProtocol, EfiStatus, }; use crate::memory::{PAGE_SIZE, PhysicalMemoryManager}; pub const BASH_PATH: &[u16] = &[ '\\' as u16, 'b' as u16, 'i' as u16, 'n' as u16, '\\' as u16, 'b' as u16, 'a' as u16, 's' as u16, 'h' as u16, 0, ]; pub const SH_PATH: &[u16] = &[ '\\' as u16, 'b' as u16, 'i' as u16, 'n' as u16, '\\' as u16, 's' as u16, 'h' as u16, 0, ]; const EFI_FILE_MODE_READ: u64 = 1; const EFI_FILE_INFO_GUID: EfiGuid = EfiGuid::new( 0x09576e92, 0x6d3f, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], ); pub struct LoadedFile { pub ptr: *const u8, pub len: usize, } #[repr(C)] struct EfiSimpleFileSystemProtocol { revision: u64, open_volume: extern "efiapi" fn( this: *mut EfiSimpleFileSystemProtocol, root: *mut *mut EfiFileProtocol, ) -> EfiStatus, } #[repr(C)] struct EfiFileProtocol { revision: u64, open: extern "efiapi" fn( this: *mut EfiFileProtocol, new_handle: *mut *mut EfiFileProtocol, file_name: *const u16, open_mode: u64, attributes: u64, ) -> EfiStatus, close: extern "efiapi" fn(this: *mut EfiFileProtocol) -> EfiStatus, delete: usize, read: extern "efiapi" fn( this: *mut EfiFileProtocol, buffer_size: *mut usize, buffer: *mut c_void, ) -> EfiStatus, write: usize, get_position: usize, set_position: usize, get_info: extern "efiapi" fn( this: *mut EfiFileProtocol, information_type: *const EfiGuid, buffer_size: *mut usize, buffer: *mut c_void, ) -> EfiStatus, } #[repr(C)] struct EfiFileInfoPrefix { size: u64, file_size: u64, physical_size: u64, } pub unsafe fn load_file( image_handle: EfiHandle, boot_services: *mut EfiBootServices, memory: &mut PhysicalMemoryManager, path: &[u16], ) -> Option { let mut loaded_image = core::ptr::null_mut(); let status = unsafe { ((*boot_services).handle_protocol)( image_handle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, &mut loaded_image, ) }; if status != EFI_SUCCESS || loaded_image.is_null() { return None; } let loaded_image = loaded_image as *mut EfiLoadedImageProtocol; let mut fs = core::ptr::null_mut(); let status = unsafe { ((*boot_services).handle_protocol)( (*loaded_image).device_handle, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, &mut fs, ) }; if status != EFI_SUCCESS || fs.is_null() { return None; } let fs = fs as *mut EfiSimpleFileSystemProtocol; let mut root = core::ptr::null_mut(); let status = unsafe { ((*fs).open_volume)(fs, &mut root) }; if status != EFI_SUCCESS || root.is_null() { return None; } let mut file = core::ptr::null_mut(); let status = unsafe { ((*root).open)(root, &mut file, path.as_ptr(), EFI_FILE_MODE_READ, 0) }; unsafe { ((*root).close)(root); } if status == EFI_NOT_FOUND || status != EFI_SUCCESS || file.is_null() { return None; } let size = unsafe { file_size(file)? }; let pages = size.div_ceil(PAGE_SIZE).max(1); let buffer = memory.alloc_pages(pages)?; let mut read_len = size; let status = unsafe { ((*file).read)(file, &mut read_len, buffer.cast::()) }; unsafe { ((*file).close)(file); } if status != EFI_SUCCESS || read_len == 0 { unsafe { memory.free_pages(buffer, pages); } return None; } Some(LoadedFile { ptr: buffer as *const u8, len: read_len, }) } unsafe fn file_size(file: *mut EfiFileProtocol) -> Option { let mut info_buffer = [0u8; 1024]; let mut info_size = info_buffer.len(); let status = unsafe { ((*file).get_info)( file, &EFI_FILE_INFO_GUID, &mut info_size, info_buffer.as_mut_ptr().cast::(), ) }; if status != EFI_SUCCESS { return None; } if info_size < core::mem::size_of::() { return None; } let info = unsafe { &*(info_buffer.as_ptr() as *const EfiFileInfoPrefix) }; usize::try_from(info.file_size).ok() }