1 //! `LoadedImage` protocol. 2 3 use crate::data_types::FromSliceWithNulError; 4 use crate::mem::memory_map::MemoryType; 5 use crate::proto::device_path::DevicePath; 6 use crate::proto::unsafe_protocol; 7 use crate::util::usize_from_u32; 8 use crate::{CStr16, Handle, Status}; 9 use core::ffi::c_void; 10 use core::{mem, slice}; 11 use uefi_raw::protocol::loaded_image::LoadedImageProtocol; 12 13 /// The LoadedImage protocol. This can be opened on any image handle using the `HandleProtocol` boot service. 14 #[derive(Debug)] 15 #[repr(transparent)] 16 #[unsafe_protocol(LoadedImageProtocol::GUID)] 17 pub struct LoadedImage(LoadedImageProtocol); 18 19 /// Errors that can be raised during parsing of the load options. 20 #[derive(Debug)] 21 pub enum LoadOptionsError { 22 /// Load options are not set. 23 NotSet, 24 25 /// The start and/or length of the load options is not [`u16`]-aligned. 26 NotAligned, 27 28 /// Not a valid null-terminated UCS-2 string. 29 InvalidString(FromSliceWithNulError), 30 } 31 32 impl LoadedImage { 33 /// Returns a handle to the storage device on which the image is located. 34 #[must_use] device(&self) -> Option<Handle>35 pub fn device(&self) -> Option<Handle> { 36 unsafe { Handle::from_ptr(self.0.device_handle) } 37 } 38 39 /// Get a reference to the `file_path` portion of the DeviceHandle that the 40 /// EFI image was loaded from. 41 /// 42 /// For a full device path, consider using the [`LoadedImageDevicePath`] 43 /// protocol. 44 /// 45 /// Returns `None` if `file_path` is null. 46 /// 47 /// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath 48 #[must_use] file_path(&self) -> Option<&DevicePath>49 pub fn file_path(&self) -> Option<&DevicePath> { 50 if self.0.file_path.is_null() { 51 None 52 } else { 53 unsafe { Some(DevicePath::from_ffi_ptr(self.0.file_path.cast())) } 54 } 55 } 56 57 /// Get the load options of the image as a [`&CStr16`]. 58 /// 59 /// Load options are typically used to pass command-line options as 60 /// a null-terminated UCS-2 string. This format is not required 61 /// though; use [`load_options_as_bytes`] to access the raw bytes. 62 /// 63 /// [`&CStr16`]: `CStr16` 64 /// [`load_options_as_bytes`]: `Self::load_options_as_bytes` load_options_as_cstr16(&self) -> Result<&CStr16, LoadOptionsError>65 pub fn load_options_as_cstr16(&self) -> Result<&CStr16, LoadOptionsError> { 66 let load_options_size = usize_from_u32(self.0.load_options_size); 67 68 if self.0.load_options.is_null() { 69 Err(LoadOptionsError::NotSet) 70 } else if (load_options_size % mem::size_of::<u16>() != 0) 71 || (((self.0.load_options as usize) % mem::align_of::<u16>()) != 0) 72 { 73 Err(LoadOptionsError::NotAligned) 74 } else { 75 let s = unsafe { 76 slice::from_raw_parts( 77 self.0.load_options.cast::<u16>(), 78 load_options_size / mem::size_of::<u16>(), 79 ) 80 }; 81 CStr16::from_u16_with_nul(s).map_err(LoadOptionsError::InvalidString) 82 } 83 } 84 85 /// Get the load options of the image as raw bytes. 86 /// 87 /// UEFI allows arbitrary binary data in load options, but typically 88 /// the data is a null-terminated UCS-2 string. Use 89 /// [`load_options_as_cstr16`] to more conveniently access the load 90 /// options as a string. 91 /// 92 /// Returns `None` if load options are not set. 93 /// 94 /// [`load_options_as_cstr16`]: `Self::load_options_as_cstr16` 95 #[must_use] load_options_as_bytes(&self) -> Option<&[u8]>96 pub fn load_options_as_bytes(&self) -> Option<&[u8]> { 97 if self.0.load_options.is_null() { 98 None 99 } else { 100 unsafe { 101 Some(slice::from_raw_parts( 102 self.0.load_options.cast(), 103 usize_from_u32(self.0.load_options_size), 104 )) 105 } 106 } 107 } 108 109 /// Set the image data address and size. 110 /// 111 /// This is useful in the following scenario: 112 /// 1. Secure boot is enabled, so images loaded with `LoadImage` must be 113 /// signed with an appropriate key known to the firmware. 114 /// 2. The bootloader has its own key embedded, and uses that key to 115 /// verify the next stage. This key is not known to the firmware, so 116 /// the next stage's image can't be loaded with `LoadImage`. 117 /// 3. Since image handles are created by `LoadImage`, which we can't 118 /// call, we have to make use of an existing image handle -- the one 119 /// passed into the bootloader's entry function. By modifying that 120 /// image handle (after appropriately verifying the signature of the 121 /// new data), we can repurpose the image handle for the next stage. 122 /// 123 /// See [shim] for an example of this scenario. 124 /// 125 /// # Safety 126 /// 127 /// This function takes `data` as a raw pointer because the data is not 128 /// owned by `LoadedImage`. The caller must ensure that the memory lives 129 /// long enough. 130 /// 131 /// [shim]: https://github.com/rhboot/shim/blob/4d64389c6c941d21548b06423b8131c872e3c3c7/pe.c#L1143 set_image(&mut self, data: *const c_void, size: u64)132 pub unsafe fn set_image(&mut self, data: *const c_void, size: u64) { 133 self.0.image_base = data; 134 self.0.image_size = size; 135 } 136 137 /// Set the callback handler to unload the image. 138 /// 139 /// Drivers that wish to support unloading have to register their unload handler 140 /// using this protocol. It is responsible for cleaning up any resources the 141 /// image is using before returning. Unloading a driver is done with 142 /// [`boot::unload_image`]. 143 /// 144 /// # Safety 145 /// 146 /// Only the driver that this [`LoadedImage`] is attached to should register an 147 /// unload handler. 148 /// 149 /// [`boot::unload_image`]: crate::boot::unload_image set_unload( &mut self, unload: extern "efiapi" fn(image_handle: Handle) -> Status, )150 pub unsafe fn set_unload( 151 &mut self, 152 unload: extern "efiapi" fn(image_handle: Handle) -> Status, 153 ) { 154 type RawFn = unsafe extern "efiapi" fn(image_handle: uefi_raw::Handle) -> uefi_raw::Status; 155 let unload: RawFn = mem::transmute(unload); 156 self.0.unload = Some(unload); 157 } 158 159 /// Set the load options for the image. This can be used prior to 160 /// calling [`boot::start_image`] to control the command line 161 /// passed to the image. 162 /// 163 /// `size` is in bytes. 164 /// 165 /// # Safety 166 /// 167 /// This function takes `options` as a raw pointer because the 168 /// load options data is not owned by `LoadedImage`. The caller 169 /// must ensure that the memory lives long enough. 170 /// 171 /// [`boot::start_image`]: crate::boot::start_image set_load_options(&mut self, options: *const u8, size: u32)172 pub unsafe fn set_load_options(&mut self, options: *const u8, size: u32) { 173 self.0.load_options = options.cast(); 174 self.0.load_options_size = size; 175 } 176 177 /// Returns the base address and the size in bytes of the loaded image. 178 #[must_use] info(&self) -> (*const c_void, u64)179 pub const fn info(&self) -> (*const c_void, u64) { 180 (self.0.image_base, self.0.image_size) 181 } 182 183 /// Get the memory type of the image's code sections. 184 /// 185 /// Normally the returned value is one of: 186 /// - `MemoryType::LOADER_CODE` for UEFI applications 187 /// - `MemoryType::BOOT_SERVICES_CODE` for UEFI boot drivers 188 /// - `MemoryType::RUNTIME_SERVICES_CODE` for UEFI runtime drivers 189 #[must_use] code_type(&self) -> MemoryType190 pub const fn code_type(&self) -> MemoryType { 191 self.0.image_code_type 192 } 193 194 /// Get the memory type of the image's data sections. 195 /// 196 /// Normally the returned value is one of: 197 /// - `MemoryType::LOADER_DATA` for UEFI applications 198 /// - `MemoryType::BOOT_SERVICES_DATA` for UEFI boot drivers 199 /// - `MemoryType::RUNTIME_SERVICES_DATA` for UEFI runtime drivers 200 #[must_use] data_type(&self) -> MemoryType201 pub const fn data_type(&self) -> MemoryType { 202 self.0.image_data_type 203 } 204 } 205