diff --git a/src/efi/file.rs b/src/efi/file.rs index ce2b6a42..19634536 100644 --- a/src/efi/file.rs +++ b/src/efi/file.rs @@ -6,19 +6,12 @@ use core::ffi::c_void; use r_efi::{ efi::{self, Char16, Guid, Status}, protocols::{ - device_path::Protocol as DevicePathProtocol, file::Protocol as FileProtocol, - simple_file_system::Protocol as SimpleFileSystemProtocol, + file::Protocol as FileProtocol, simple_file_system::Protocol as SimpleFileSystemProtocol, }, }; use crate::block::SectorBuf; -#[repr(C)] -pub struct FileDevicePathProtocol { - pub device_path: DevicePathProtocol, - pub filename: [u16; 64], -} - pub extern "efiapi" fn filesystem_open_volume( fs_proto: *mut SimpleFileSystemProtocol, file: *mut *mut FileProtocol, diff --git a/src/efi/mod.rs b/src/efi/mod.rs index c4cf434a..91e3ed33 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -8,6 +8,7 @@ use core::{ ffi::c_void, mem::{size_of, transmute}, ptr::null_mut, + slice::from_raw_parts, }; use atomic_refcell::AtomicRefCell; @@ -28,9 +29,9 @@ use r_efi::{ #[cfg(target_arch = "riscv64")] use r_efi::{eficall, eficall_abi}; -use crate::bootinfo; -use crate::layout; use crate::rtc; +use crate::{block::SectorBuf, layout}; +use crate::{bootinfo, fat}; mod alloc; mod block; @@ -643,6 +644,71 @@ pub extern "efiapi" fn install_configuration_table(guid: *mut Guid, table: *mut Status::OUT_OF_RESOURCES } +struct MemoryFile { + address: u64, + size: u32, + position: u32, +} + +impl MemoryFile { + fn new(address: u64, size: u32) -> Self { + MemoryFile { + address, + size, + position: 0, + } + } +} + +impl fat::Read for MemoryFile { + fn get_size(&self) -> u32 { + self.size + } + + fn read(&mut self, data: &mut [u8]) -> Result { + let sector_size = SectorBuf::len() as u32; + assert_eq!(data.len(), SectorBuf::len()); + + if (self.position + sector_size) > self.size { + let bytes_read = self.size - self.position; + let memory = unsafe { + from_raw_parts( + (self.address + self.position as u64) as *const u8, + bytes_read as usize, + ) + }; + data[0..bytes_read as usize].copy_from_slice(memory); + self.position = self.size; + Ok(bytes_read) + } else { + let memory = unsafe { + from_raw_parts( + (self.address + self.position as u64) as *const u8, + sector_size as usize, + ) + }; + data[0..sector_size as usize].copy_from_slice(memory); + self.position += sector_size; + Ok(sector_size) + } + } + + fn seek(&mut self, position: u32) -> Result<(), fat::Error> { + let sector_size = SectorBuf::len() as u32; + if position % sector_size != 0 { + return Err(fat::Error::InvalidOffset); + } + + if position >= self.size { + return Err(fat::Error::EndOfFile); + } + + self.position = position; + + Ok(()) + } +} + pub extern "efiapi" fn load_image( _boot_policy: Boolean, parent_image_handle: Handle, @@ -651,21 +717,44 @@ pub extern "efiapi" fn load_image( _source_size: usize, image_handle: *mut Handle, ) -> Status { - use crate::fat::Read; - - let mut path = [0_u8; 256]; let device_path = unsafe { &*device_path }; - extract_path(device_path, &mut path); - let path = crate::common::ascii_strip(&path); - - let li = parent_image_handle as *const LoadedImageWrapper; - let dh = unsafe { (*li).proto.device_handle }; - let wrapped_fs_ref = unsafe { &*(dh as *const file::FileSystemWrapper) }; - let mut file = match wrapped_fs_ref.fs.open(path) { - Ok(file) => file, - Err(_) => return Status::DEVICE_ERROR, - }; + match &DevicePath::parse(device_path) { + dp @ DevicePath::File(path) => { + let path = crate::common::ascii_strip(path); + + let li = parent_image_handle as *const LoadedImageWrapper; + let dh = unsafe { (*li).proto.device_handle }; + let wrapped_fs_ref = unsafe { &*(dh as *const file::FileSystemWrapper) }; + let device_handle = wrapped_fs_ref as *const _ as Handle; + + let mut file = match wrapped_fs_ref.fs.open(path) { + Ok(file) => file, + Err(_) => return Status::DEVICE_ERROR, + }; + + load_from_file( + &mut file, + dp, + parent_image_handle, + device_handle, + image_handle, + ) + } + dp @ DevicePath::Memory(_memory_type, start, end) => { + let mut file = MemoryFile::new(*start, (*end - *start) as u32); + load_from_file(&mut file, dp, parent_image_handle, null_mut(), image_handle) + } + _ => Status::UNSUPPORTED, + } +} +fn load_from_file( + file: &mut dyn fat::Read, + dp: &DevicePath, + parent_image_handle: *mut c_void, + device_handle: *mut c_void, + image_handle: *mut *mut c_void, +) -> Status { let file_size = (file.get_size() as u64 + PAGE_SIZE - 1) / PAGE_SIZE; // Get free pages address let load_addr = @@ -677,7 +766,7 @@ pub extern "efiapi" fn load_image( None => return Status::OUT_OF_RESOURCES, }; - let mut l = crate::pe::Loader::new(&mut file); + let mut l = crate::pe::Loader::new(file); let (entry_addr, load_addr, load_size) = match l.load(load_addr) { Ok(load_info) => load_info, Err(_) => return Status::DEVICE_ERROR, @@ -690,15 +779,15 @@ pub extern "efiapi" fn load_image( ); let image = new_image_handle( - path, + dp.generate(), parent_image_handle, - wrapped_fs_ref as *const _ as Handle, + device_handle, load_addr, load_size, entry_addr, ); - unsafe { *image_handle = image as *mut _ as *mut c_void }; + unsafe { *image_handle = image as *mut c_void }; Status::SUCCESS } @@ -934,20 +1023,57 @@ extern "efiapi" fn image_unload(_: Handle) -> Status { efi::Status::UNSUPPORTED } -fn extract_path(device_path: &DevicePathProtocol, path: &mut [u8]) { - let mut dp = device_path; - loop { - if dp.r#type == r_efi::protocols::device_path::TYPE_MEDIA && dp.sub_type == 0x04 { - let ptr = - (dp as *const _ as u64 + size_of::() as u64) as *const u16; - crate::common::ucs2_to_ascii(ptr, path); - return; +#[allow(clippy::large_enum_variant)] +enum DevicePath { + File([u8; 256]), + Memory(MemoryType, u64, u64), + Unsupported, +} + +impl DevicePath { + fn parse(dpp: &DevicePathProtocol) -> DevicePath { + let mut dpp = dpp; + loop { + if dpp.r#type == r_efi::protocols::device_path::TYPE_MEDIA && dpp.sub_type == 0x04 { + let ptr = (dpp as *const _ as usize + offset_of!(FileDevicePathProtocol, filename)) + as *const u16; + let mut path = [0u8; 256]; + crate::common::ucs2_to_ascii(ptr, &mut path); + return DevicePath::File(path); + } + if dpp.r#type == r_efi::protocols::device_path::TYPE_HARDWARE + && dpp.sub_type == r_efi::protocols::device_path::Hardware::SUBTYPE_MMAP + { + let memory_type_ptr = (dpp as *const _ as usize + + offset_of!(MemoryDevicePathProtocol, memory_type)) + as *const MemoryType; + let start_ptr = (dpp as *const _ as usize + + offset_of!(MemoryDevicePathProtocol, start)) + as *const u64; + let end_ptr = (dpp as *const _ as usize + offset_of!(MemoryDevicePathProtocol, end)) + as *const u64; + return DevicePath::Memory( + unsafe { *memory_type_ptr }, + unsafe { *start_ptr }, + unsafe { *end_ptr }, + ); + } + + if dpp.r#type == r_efi::protocols::device_path::TYPE_END && dpp.sub_type == 0xff { + log!("Unexpected end of device path"); + return DevicePath::Unsupported; + } + let len = unsafe { core::mem::transmute::<[u8; 2], u16>(dpp.length) }; + dpp = unsafe { &*((dpp as *const _ as u64 + len as u64) as *const _) }; } - if dp.r#type == r_efi::protocols::device_path::TYPE_END && dp.sub_type == 0xff { - panic!("Failed to extract path"); + } + + fn generate(&self) -> *mut r_efi::protocols::device_path::Protocol { + match self { + Self::File(path) => file_device_path(crate::common::ascii_strip(path)), + Self::Memory(memory_type, start, end) => memory_device_path(*memory_type, *start, *end), + Self::Unsupported => panic!("Cannot generate from unsupported Device Path type"), } - let len = unsafe { core::mem::transmute::<[u8; 2], u16>(dp.length) }; - dp = unsafe { &*((dp as *const _ as u64 + len as u64) as *const _) }; } } @@ -1031,47 +1157,106 @@ struct LoadedImageWrapper { entry_point: u64, } -type DevicePaths = [file::FileDevicePathProtocol; 2]; +#[repr(C)] +struct FileDevicePathProtocol { + pub device_path: DevicePathProtocol, + pub filename: [u16; 256], +} -fn new_image_handle( - path: &str, - parent_handle: Handle, - device_handle: Handle, - load_addr: u64, - load_size: u64, - entry_addr: u64, -) -> *mut LoadedImageWrapper { +type FileDevicePaths = [FileDevicePathProtocol; 2]; + +fn file_device_path(path: &str) -> *mut r_efi::protocols::device_path::Protocol { let mut file_paths = null_mut(); let status = allocate_pool( efi::LOADER_DATA, - size_of::(), + size_of::(), &mut file_paths as *mut *mut c_void, ); assert!(status == Status::SUCCESS); - let file_paths = unsafe { &mut *(file_paths as *mut DevicePaths) }; + let file_paths = unsafe { &mut *(file_paths as *mut FileDevicePaths) }; *file_paths = [ - file::FileDevicePathProtocol { + FileDevicePathProtocol { device_path: DevicePathProtocol { r#type: r_efi::protocols::device_path::TYPE_MEDIA, sub_type: 4, // Media Path type file - length: [132, 0], + length: (size_of::() as u16).to_le_bytes(), }, - filename: [0; 64], + filename: [0; 256], }, - file::FileDevicePathProtocol { + FileDevicePathProtocol { device_path: DevicePathProtocol { r#type: r_efi::protocols::device_path::TYPE_END, - sub_type: 0xff, // End of full path - length: [4, 0], + sub_type: r_efi::protocols::device_path::End::SUBTYPE_ENTIRE, + length: (size_of::() as u16).to_le_bytes(), }, - filename: [0; 64], + filename: [0; 256], }, ]; crate::common::ascii_to_ucs2(path, &mut file_paths[0].filename); + &mut file_paths[0].device_path // Pointer to first path entry +} + +#[repr(C)] +struct MemoryDevicePathProtocol { + pub device_path: DevicePathProtocol, + pub memory_type: u32, + pub start: u64, + pub end: u64, +} + +type MemoryDevicePaths = [MemoryDevicePathProtocol; 2]; + +fn memory_device_path( + memory_type: MemoryType, + start: u64, + end: u64, +) -> *mut r_efi::protocols::device_path::Protocol { + let mut memory_paths = null_mut(); + let status = allocate_pool( + efi::LOADER_DATA, + size_of::(), + &mut memory_paths as *mut *mut c_void, + ); + assert!(status == Status::SUCCESS); + let memory_paths = unsafe { &mut *(memory_paths as *mut MemoryDevicePaths) }; + *memory_paths = [ + MemoryDevicePathProtocol { + device_path: DevicePathProtocol { + r#type: r_efi::protocols::device_path::TYPE_HARDWARE, + sub_type: r_efi::protocols::device_path::Hardware::SUBTYPE_MMAP, + length: (size_of::() as u16).to_le_bytes(), + }, + memory_type, + start, + end, + }, + MemoryDevicePathProtocol { + device_path: DevicePathProtocol { + r#type: r_efi::protocols::device_path::TYPE_END, + sub_type: r_efi::protocols::device_path::End::SUBTYPE_ENTIRE, + length: (size_of::() as u16).to_le_bytes(), + }, + memory_type: 0, + start: 0, + end: 0, + }, + ]; + + &mut memory_paths[0].device_path // Pointer to first path entry +} + +fn new_image_handle( + file_path: *mut r_efi::protocols::device_path::Protocol, + parent_handle: Handle, + device_handle: Handle, + load_addr: u64, + load_size: u64, + entry_addr: u64, +) -> *mut LoadedImageWrapper { let mut image = null_mut(); - allocate_pool( + let status = allocate_pool( efi::LOADER_DATA, size_of::(), &mut image as *mut *mut c_void, @@ -1087,7 +1272,7 @@ fn new_image_handle( parent_handle, system_table: unsafe { &mut ST }, device_handle, - file_path: &mut file_paths[0].device_path, // Pointer to first path entry + file_path, load_options_size: 0, load_options: null_mut(), image_base: load_addr as *mut _, @@ -1182,8 +1367,12 @@ pub fn efi_exec( let wrapped_fs = file::FileSystemWrapper::new(fs, efi_part_id); + let mut path = [0u8; 256]; + path[0..crate::efi::EFI_BOOT_PATH.as_bytes().len()] + .copy_from_slice(crate::efi::EFI_BOOT_PATH.as_bytes()); + let device_path = DevicePath::File(path); let image = new_image_handle( - crate::efi::EFI_BOOT_PATH, + device_path.generate(), 0 as Handle, &wrapped_fs as *const _ as Handle, loaded_address,