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