bootmgr_rs_core/system/
protos.rs

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