1 //! LoadFile and LoadFile2 protocols.
2 
3 use crate::proto::unsafe_protocol;
4 #[cfg(all(feature = "alloc", feature = "unstable"))]
5 use alloc::alloc::Global;
6 use uefi_raw::protocol::media::{LoadFile2Protocol, LoadFileProtocol};
7 #[cfg(feature = "alloc")]
8 use {
9     crate::{mem::make_boxed, proto::device_path::DevicePath, Result, StatusExt},
10     alloc::boxed::Box,
11     uefi::proto::BootPolicy,
12 };
13 
14 /// Load File Protocol.
15 ///
16 /// Used to obtain files, that are primarily boot options, from arbitrary
17 /// devices.
18 ///
19 /// # UEFI Spec Description
20 /// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from
21 /// arbitrary devices.
22 ///
23 /// When the firmware is attempting to load a file, it first attempts to use the
24 /// device’s Simple File System protocol to read the file. If the file system
25 /// protocol is found, the firmware implements the policy of interpreting the
26 /// File Path value of the file being loaded. If the device does not support the
27 /// file system protocol, the firmware then attempts to read the file via the
28 /// EFI_LOAD_FILE_PROTOCOL and the LoadFile() function. In this case the
29 /// LoadFile() function implements the policy of interpreting the File Path
30 /// value.
31 #[derive(Debug)]
32 #[repr(transparent)]
33 #[unsafe_protocol(LoadFileProtocol::GUID)]
34 pub struct LoadFile(LoadFileProtocol);
35 
36 impl LoadFile {
37     /// Causes the driver to load a specified file.
38     ///
39     /// # Parameters
40     /// - `file_path` The device specific path of the file to load.
41     /// - `boot_policy` The [`BootPolicy`] to use.
42     ///
43     /// # Errors
44     /// - `uefi::status::EFI_SUCCESS` The file was loaded.
45     /// - `uefi::status::EFI_UNSUPPORTED` The device does not support the
46     ///   provided BootPolicy.
47     /// - `uefi::status::EFI_INVALID_PARAMETER` FilePath is not a valid device
48     ///   path, or BufferSize is NULL.
49     /// - `uefi::status::EFI_NO_MEDIA` No medium was present to load the file.
50     /// - `uefi::status::EFI_DEVICE_ERROR` The file was not loaded due to a
51     ///   device error.
52     /// - `uefi::status::EFI_NO_RESPONSE` The remote system did not respond.
53     /// - `uefi::status::EFI_NOT_FOUND` The file was not found.
54     /// - `uefi::status::EFI_ABORTED` The file load process was manually
55     ///   cancelled.
56     /// - `uefi::status::EFI_BUFFER_TOO_SMALL` The BufferSize is too small to
57     ///   read the current directory entry. BufferSize has been updated with the
58     ///   size needed to complete the request.
59     /// - `uefi::status::EFI_WARN_FILE_SYSTEM` The resulting Buffer contains
60     ///   UEFI-compliant file system.
61     ///
62     /// [`BootPolicy`]: uefi::proto::BootPolicy
63     #[cfg(feature = "alloc")]
64     #[allow(clippy::extra_unused_lifetimes)] // false positive, it is used
load_file<'a>( &mut self, file_path: &DevicePath, boot_policy: BootPolicy, ) -> Result<Box<[u8]>>65     pub fn load_file<'a>(
66         &mut self,
67         file_path: &DevicePath,
68         boot_policy: BootPolicy,
69     ) -> Result<Box<[u8]>> {
70         let this = core::ptr::addr_of_mut!(*self).cast();
71 
72         let fetch_data_fn = |buf: &'a mut [u8]| {
73             let mut size = buf.len();
74             let status = unsafe {
75                 (self.0.load_file)(
76                     this,
77                     file_path.as_ffi_ptr().cast(),
78                     boot_policy.into(),
79                     &mut size,
80                     buf.as_mut_ptr().cast(),
81                 )
82             };
83             status.to_result_with_err(|_| Some(size)).map(|_| buf)
84         };
85 
86         #[cfg(not(feature = "unstable"))]
87         let file: Box<[u8]> = make_boxed::<[u8], _>(fetch_data_fn)?;
88 
89         #[cfg(feature = "unstable")]
90         let file = make_boxed::<[u8], _, _>(fetch_data_fn, Global)?;
91 
92         Ok(file)
93     }
94 }
95 
96 /// Load File2 Protocol.
97 ///
98 /// The Load File2 protocol is used to obtain files from arbitrary devices that
99 /// are not boot options.
100 ///
101 /// # UEFI Spec Description
102 ///
103 /// The EFI_LOAD_FILE2_PROTOCOL is a simple protocol used to obtain files from
104 /// arbitrary devices that are not boot options. It is used by LoadImage() when
105 /// its BootOption parameter is FALSE and the FilePath does not have an instance
106 /// of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
107 #[derive(Debug)]
108 #[repr(transparent)]
109 #[unsafe_protocol(LoadFile2Protocol::GUID)]
110 pub struct LoadFile2(LoadFile2Protocol);
111 
112 impl LoadFile2 {
113     /// Causes the driver to load a specified file.
114     ///
115     /// # Parameters
116     /// - `file_path` The device specific path of the file to load.
117     ///
118     /// # Errors
119     /// - `uefi::status::EFI_SUCCESS` The file was loaded.
120     /// - `uefi::status::EFI_UNSUPPORTED` BootPolicy is TRUE.
121     /// - `uefi::status::EFI_INVALID_PARAMETER` FilePath is not a valid device
122     ///   path, or BufferSize is NULL.
123     /// - `uefi::status::EFI_NO_MEDIA` No medium was present to load the file.
124     /// - `uefi::status::EFI_DEVICE_ERROR` The file was not loaded due to a
125     ///   device error.
126     /// - `uefi::status::EFI_NO_RESPONSE` The remote system did not respond.
127     /// - `uefi::status::EFI_NOT_FOUND` The file was not found.
128     /// - `uefi::status::EFI_ABORTED` The file load process was manually
129     ///   cancelled.
130     /// - `uefi::status::EFI_BUFFER_TOO_SMALL` The BufferSize is too small to
131     ///   read the current directory entry. BufferSize has been updated with the
132     ///   size needed to complete the request.
133     #[cfg(feature = "alloc")]
134     #[allow(clippy::extra_unused_lifetimes)] // false positive, it is used
load_file<'a>(&mut self, file_path: &DevicePath) -> Result<Box<[u8]>>135     pub fn load_file<'a>(&mut self, file_path: &DevicePath) -> Result<Box<[u8]>> {
136         let this = core::ptr::addr_of_mut!(*self).cast();
137 
138         let fetch_data_fn = |buf: &'a mut [u8]| {
139             let mut size = buf.len();
140             let status = unsafe {
141                 (self.0.load_file)(
142                     this,
143                     file_path.as_ffi_ptr().cast(),
144                     false, /* always false - see spec */
145                     &mut size,
146                     buf.as_mut_ptr().cast(),
147                 )
148             };
149             status.to_result_with_err(|_| Some(size)).map(|_| buf)
150         };
151 
152         #[cfg(not(feature = "unstable"))]
153         let file: Box<[u8]> = make_boxed::<[u8], _>(fetch_data_fn)?;
154 
155         #[cfg(feature = "unstable")]
156         let file = make_boxed::<[u8], _, _>(fetch_data_fn, Global)?;
157 
158         Ok(file)
159     }
160 }
161