|
| 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 | +}; |
| 12 | + |
| 13 | +/// Load File Protocol. |
| 14 | +/// |
| 15 | +/// Used to obtain files, that are primarily boot options, from arbitrary |
| 16 | +/// devices. |
| 17 | +/// |
| 18 | +/// # UEFI Spec Description |
| 19 | +/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from |
| 20 | +/// arbitrary devices. |
| 21 | +/// |
| 22 | +/// When the firmware is attempting to load a file, it first attempts to use the |
| 23 | +/// device’s Simple File System protocol to read the file. If the file system |
| 24 | +/// protocol is found, the firmware implements the policy of interpreting the |
| 25 | +/// File Path value of the file being loaded. If the device does not support the |
| 26 | +/// file system protocol, the firmware then attempts to read the file via the |
| 27 | +/// EFI_LOAD_FILE_PROTOCOL and the LoadFile() function. In this case the |
| 28 | +/// LoadFile() function implements the policy of interpreting the File Path |
| 29 | +/// value. |
| 30 | +#[derive(Debug)] |
| 31 | +#[repr(transparent)] |
| 32 | +#[unsafe_protocol(LoadFileProtocol::GUID)] |
| 33 | +pub struct LoadFile(LoadFileProtocol); |
| 34 | + |
| 35 | +impl LoadFile { |
| 36 | + /// Causes the driver to load a specified file. |
| 37 | + /// |
| 38 | + /// # Parameters |
| 39 | + /// - `file_path` The device specific path of the file to load. |
| 40 | + /// - `boot_policy` If TRUE, indicates that the request originates from the |
| 41 | + /// boot manager, and that the boot manager is attempting to load FilePath |
| 42 | + /// as a boot selection. If FALSE, then FilePath must match an exact file |
| 43 | + /// to be loaded. |
| 44 | + /// |
| 45 | + /// # Errors |
| 46 | + /// - `uefi::status::EFI_SUCCESS` The file was loaded. |
| 47 | + /// - `uefi::status::EFI_UNSUPPORTED` The device does not support the |
| 48 | + /// provided BootPolicy. |
| 49 | + /// - `uefi::status::EFI_INVALID_PARAMETER` FilePath is not a valid device |
| 50 | + /// path, or BufferSize is NULL. |
| 51 | + /// - `uefi::status::EFI_NO_MEDIA` No medium was present to load the file. |
| 52 | + /// - `uefi::status::EFI_DEVICE_ERROR` The file was not loaded due to a |
| 53 | + /// device error. |
| 54 | + /// - `uefi::status::EFI_NO_RESPONSE` The remote system did not respond. |
| 55 | + /// - `uefi::status::EFI_NOT_FOUND` The file was not found. |
| 56 | + /// - `uefi::status::EFI_ABORTED` The file load process was manually |
| 57 | + /// cancelled. |
| 58 | + /// - `uefi::status::EFI_BUFFER_TOO_SMALL` The BufferSize is too small to |
| 59 | + /// read the current directory entry. BufferSize has been updated with the |
| 60 | + /// size needed to complete the request. |
| 61 | + /// - `uefi::status::EFI_WARN_FILE_SYSTEM` The resulting Buffer contains |
| 62 | + /// UEFI-compliant file system. |
| 63 | + #[cfg(feature = "alloc")] |
| 64 | + #[allow(clippy::extra_unused_lifetimes)] // false positive, it is used |
| 65 | + pub fn load_file<'a>( |
| 66 | + &mut self, |
| 67 | + file_path: &DevicePath, |
| 68 | + boot_policy: bool, |
| 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, |
| 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 |
| 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 | +} |
0 commit comments