|
12 | 12 | use r_efi::efi::{self, Guid};
|
13 | 13 | use r_efi::protocols::{device_path, device_path_to_text};
|
14 | 14 |
|
15 |
| -use crate::ffi::OsString; |
| 15 | +use crate::ffi::{OsString, OsStr}; |
16 | 16 | use crate::io::{self, const_io_error};
|
17 | 17 | use crate::mem::{size_of, MaybeUninit};
|
18 | 18 | use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt};
|
@@ -221,3 +221,73 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>>
|
221 | 221 | let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services };
|
222 | 222 | NonNull::new(runtime_services)
|
223 | 223 | }
|
| 224 | + |
| 225 | +pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>); |
| 226 | + |
| 227 | +impl DevicePath { |
| 228 | + pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> { |
| 229 | + fn inner( |
| 230 | + p: &OsStr, |
| 231 | + protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>, |
| 232 | + ) -> io::Result<DevicePath> { |
| 233 | + let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>(); |
| 234 | + let path = |
| 235 | + unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; |
| 236 | + |
| 237 | + NonNull::new(path).map(DevicePath).ok_or_else(|| { |
| 238 | + const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") |
| 239 | + }) |
| 240 | + } |
| 241 | + |
| 242 | + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = |
| 243 | + AtomicPtr::new(crate::ptr::null_mut()); |
| 244 | + |
| 245 | + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { |
| 246 | + if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>( |
| 247 | + handle, |
| 248 | + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, |
| 249 | + ) { |
| 250 | + return inner(p, protocol); |
| 251 | + } |
| 252 | + } |
| 253 | + |
| 254 | + let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?; |
| 255 | + for handle in handles { |
| 256 | + if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>( |
| 257 | + handle, |
| 258 | + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, |
| 259 | + ) { |
| 260 | + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); |
| 261 | + return inner(p, protocol); |
| 262 | + } |
| 263 | + } |
| 264 | + |
| 265 | + io::Result::Err(const_io_error!( |
| 266 | + io::ErrorKind::NotFound, |
| 267 | + "DevicePathFromText Protocol not found" |
| 268 | + )) |
| 269 | + } |
| 270 | +} |
| 271 | + |
| 272 | +impl AsRef<r_efi::protocols::device_path::Protocol> for DevicePath { |
| 273 | + fn as_ref(&self) -> &r_efi::protocols::device_path::Protocol { |
| 274 | + unsafe { self.0.as_ref() } |
| 275 | + } |
| 276 | +} |
| 277 | + |
| 278 | +impl AsMut<r_efi::protocols::device_path::Protocol> for DevicePath { |
| 279 | + fn as_mut(&mut self) -> &mut r_efi::protocols::device_path::Protocol { |
| 280 | + unsafe { self.0.as_mut() } |
| 281 | + } |
| 282 | +} |
| 283 | + |
| 284 | +impl Drop for DevicePath { |
| 285 | + fn drop(&mut self) { |
| 286 | + if let Some(bt) = boot_services() { |
| 287 | + let bt: NonNull<r_efi::efi::BootServices> = bt.cast(); |
| 288 | + unsafe { |
| 289 | + ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void); |
| 290 | + } |
| 291 | + } |
| 292 | + } |
| 293 | +} |
0 commit comments