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