bootmgr_rs_core/system/protos.rs
1//! UEFI protocols that are not implemented in the [`uefi`] crate.
2//!
3//! This exposes the following protocols:
4//! - [`DevicetreeFixup`]
5//! - [`SecurityArch`]
6//! - [`Security2Arch`]
7//!
8//! Technically, it also provides [`ShimImageLoader`], however that isn't really used for anything as if Shim
9//! is loaded, it will have already hooked onto `LoadImage` and such. It only exists to detect its existence.
10//!
11//! # Safety
12//!
13//! This module uses unsafe blocks in 3 places, and unsafe in general in even more places. This is completely unavoidable as
14//! it interacts with raw UEFI protocols. This will only document the usages of unsafe blocks, as those are what the rest of
15//! the program will interact with.
16//!
17//! 1. The inner `fixup` member is only unsafe if the raw pointers are invalid. Because mutable references or at the very least
18//! [`NonNull<u8>`] are parameters, this is safe since the raw pointers will be guaranteed to be valid.
19//! 2. Similarly, the inner `auth_state` member is only unsafe if the raw pointers are invalid. An immutable reference to
20//! [`DevicePath`] is passed as a parameter, which can always be converted to [`FfiDevicePath`] validly. Therefore, this is safe.
21//! 3. The inner `authentication` member is unsafe when invalid raw pointers are passed, or the size is invalid. The safe method takes
22//! no raw pointers, only references. These references are safely and validly converted into their FFI counterparts. In addition, the
23//! size is guaranteed to be valid as it is derived from the length of the slice. Therefore, this is safe.
24
25use core::{ffi::c_void, ptr::NonNull};
26
27use uefi::{
28 Status, guid,
29 proto::{
30 device_path::{DevicePath, FfiDevicePath},
31 unsafe_protocol,
32 },
33};
34
35/// A "boolean" that is actually a [`u8`]. Used for FFI interop.
36type Bool = u8;
37
38/// A raw binding for `EFI_DT_FIXUP_PROTOCOL`. Provides only one function, which is to fixup DTB blobs.
39#[derive(Clone, Copy, Debug)]
40#[repr(C)]
41pub struct DevicetreeFixupProtocol {
42 /// The version of the protocol.
43 revision: u64,
44
45 /// Applies firmware fixups to a buffer.
46 pub(crate) fixup: unsafe extern "efiapi" fn(
47 this: *mut Self,
48 fdt: *mut c_void,
49 buffer_size: *mut usize,
50 flags: u32,
51 ) -> Status,
52}
53
54impl DevicetreeFixupProtocol {
55 /// The GUID of the protocol.
56 const GUID: uefi::Guid = guid!("e617d64c-fe08-46da-f4dc-bbd5870c7300");
57}
58
59/// Devicetree fixup protocol.
60///
61/// In ARM hardware, devicetrees are used to supply information about the hardware to the software.
62/// However, some of the properties of the hardware can only be known at boot time. Therefore, the firmware
63/// may apply fixups to the devicetree in order for it to be more accurate and aligned with the hardware.
64#[derive(Clone, Copy, Debug)]
65#[repr(transparent)]
66#[unsafe_protocol(DevicetreeFixupProtocol::GUID)]
67pub struct DevicetreeFixup(DevicetreeFixupProtocol);
68
69impl DevicetreeFixup {
70 /// Apply fixups to a devicetree buffer.
71 pub fn fixup(&mut self, fdt: NonNull<u8>, buffer_size: &mut usize, flags: u32) -> Status {
72 let fdt = fdt.as_ptr().cast::<c_void>();
73 // SAFETY: fdt is verified non null and valid, as it is wrapped around NonNull, so this is safe.
74 unsafe { (self.0.fixup)(&raw mut self.0, fdt, buffer_size, flags) }
75 }
76}
77
78/// The raw Security Arch protocol implementation.
79///
80/// You should rarely ever need to use this, unless you are installing a custom validator.
81#[derive(Clone, Copy, Debug)]
82#[repr(C)]
83pub struct SecurityArchProtocol {
84 /// Check the authentication status of a file using the `auth_status` parameter.
85 ///
86 /// Very rarely should you ever need to use this directly, unless you are hijacking it and replacing it with a
87 /// custom validator.
88 pub(crate) auth_state: unsafe extern "efiapi" fn(
89 this: *const Self,
90 auth_status: u32,
91 file: *const FfiDevicePath,
92 ) -> Status,
93}
94
95impl SecurityArchProtocol {
96 /// The GUID of the protocol.
97 const GUID: uefi::Guid = guid!("a46423e3-4617-49f1-b9ff-d1bfa9115839");
98}
99
100/// Security Arch Protocol.
101///
102/// When Secure Boot is enabled, the Security Arch protocols are responsible for ensuring that files are authenticated
103/// according to platform security policy.
104///
105/// Its main purpose is to authenticate files according to abstracted platform specific security policies.
106#[derive(Clone, Copy, Debug)]
107#[repr(transparent)]
108#[unsafe_protocol(SecurityArchProtocol::GUID)]
109pub struct SecurityArch(SecurityArchProtocol);
110
111impl SecurityArch {
112 /// Check the authentication status of a file using the `auth_status` parameter.
113 ///
114 /// You should never need to use this, `LoadImage` will call it automatically whenever UEFI Secure Boot is enabled.
115 pub fn auth_state(&self, auth_status: u32, file: &DevicePath) -> Status {
116 let file = file.as_ffi_ptr();
117
118 // SAFETY: file is derived from the safe DevicePath, so this is safe.
119 unsafe { (self.0.auth_state)(&raw const self.0, auth_status, file) }
120 }
121
122 /// Get a clone of the inner raw [`SecurityArchProtocol`].
123 #[must_use = "Has no effect if the result is unused"]
124 pub(crate) const fn get_inner(&self) -> &SecurityArchProtocol {
125 &self.0
126 }
127
128 /// Get a mutable reference to the inner raw [`SecurityArchProtocol`].
129 pub(crate) const fn get_inner_mut(&mut self) -> &mut SecurityArchProtocol {
130 &mut self.0
131 }
132}
133
134/// The raw Security2 Arch protocol implementation.
135///
136/// You should rarely ever need to use this, unless you are installing a custom validator.
137#[derive(Clone, Copy, Debug)]
138#[repr(C)]
139pub struct Security2ArchProtocol {
140 /// Check the authentication status of a file from either a raw pointer to an [`FfiDevicePath`], or
141 /// a file buffer.
142 ///
143 /// Very rarely should you ever need to use this directly, unless you are hijacking it and replacing it with a
144 /// custom validator.
145 pub(crate) authentication: unsafe extern "efiapi" fn(
146 this: *const Self,
147 device_path: *const FfiDevicePath,
148 file_buffer: *mut c_void,
149 file_size: usize,
150 boot_policy: Bool,
151 ) -> Status,
152}
153
154impl Security2ArchProtocol {
155 /// The GUID of the protocol.
156 const GUID: uefi::Guid = guid!("94ab2f58-1438-4ef1-9152-18941a3a0e68");
157}
158
159/// Security2 Arch Protocol.
160///
161/// When Secure Boot is enabled, the Security Arch protocols are responsible for ensuring that files are authenticated
162/// according to platform security policy.
163///
164/// Its main purpose is to authenticate files according to the security policy of the firmware.
165#[derive(Clone, Copy, Debug)]
166#[repr(transparent)]
167#[unsafe_protocol(Security2ArchProtocol::GUID)]
168pub struct Security2Arch(Security2ArchProtocol);
169
170impl Security2Arch {
171 /// Check the authentication status of a file from either a reference to a [`DevicePath`], or a mutable slice
172 /// of a file buffer.
173 ///
174 /// You should never need to use this, `LoadImage` will call it automatically whenever UEFI Secure Boot is enabled.
175 pub fn authentication(
176 &self,
177 device_path: Option<&DevicePath>,
178 file_buffer: &mut [u8],
179 boot_policy: bool,
180 ) -> Status {
181 let device_path = device_path.map_or(core::ptr::null(), DevicePath::as_ffi_ptr);
182 let file_size = file_buffer.len();
183 let file_buffer = file_buffer.as_mut_ptr().cast::<c_void>();
184 // SAFETY: device_path and file_buffer are derived from their safe equivalents, a DevicePath and mutable slice.
185 // also, file_size is guaranteed to be the exact size of the file buffer, as it is the length of the slice, so this is
186 // safe.
187 unsafe {
188 (self.0.authentication)(
189 &raw const self.0,
190 device_path,
191 file_buffer,
192 file_size,
193 Bool::from(boot_policy),
194 )
195 }
196 }
197
198 /// Get a shared reference to the inner raw [`Security2ArchProtocol`].
199 #[must_use = "Has no effect if the result is unused"]
200 pub(crate) const fn get_inner(&self) -> &Security2ArchProtocol {
201 &self.0
202 }
203
204 /// Get a mutable reference to the inner raw [`Security2ArchProtocol`].
205 pub(crate) const fn get_inner_mut(&mut self) -> &mut Security2ArchProtocol {
206 &mut self.0
207 }
208}
209
210/// The raw Shim Image Loader protocol.
211///
212/// None of this is actually used, since Shim loader hooks onto `LoadImage` directly.
213/// This is here so we can detect its existence for Shim v16+
214#[derive(Clone, Debug)]
215#[repr(C)]
216pub struct ShimImageLoaderProtocol {
217 /// Load an image. The parameters are identical to the `uefi-raw` `LoadImage` implementation.
218 pub(crate) load_image: unsafe extern "efiapi" fn(
219 boot_policy: Bool,
220 parent: *mut c_void,
221 device_path: *mut FfiDevicePath,
222 src: *mut c_void,
223 src_size: usize,
224 image: *mut c_void,
225 ),
226 /// Start an image. The parameters are identical to the `uefi-raw` `StartImage` implementation.
227 pub(crate) start_image: unsafe extern "efiapi" fn(
228 image: *mut c_void,
229 exit_data_size: *mut usize,
230 exit_data: *mut u16,
231 ),
232 /// Exit the image. The parameters are identical to the `uefi-raw` `Exit` implementation.
233 pub(crate) exit: unsafe extern "efiapi" fn(
234 image: *mut c_void,
235 status: Status,
236 exit_data_size: usize,
237 exit_data: *mut u16,
238 ),
239
240 /// Unload an image. The parameters are identical to the `uefi-raw` `UnloadImage` implementation.
241 pub(crate) unload_image: unsafe extern "efiapi" fn(image: *mut c_void),
242}
243
244impl ShimImageLoaderProtocol {
245 /// The GUID of the protocol.
246 const GUID: uefi::Guid = guid!("1f492041-fadb-4e59-9e57-7cafe73a55ab");
247}
248
249/// Shim Image Loader protocol.
250///
251/// This is never used directly, since Shim will automatically hook onto `LoadImage` and other similar functions.
252#[derive(Clone, Debug)]
253#[repr(transparent)]
254#[unsafe_protocol(ShimImageLoaderProtocol::GUID)]
255pub struct ShimImageLoader(ShimImageLoaderProtocol);