|
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::{OsStr, OsString}; |
16 | 16 | use crate::io::{self, const_io_error};
|
17 | 17 | use crate::mem::{size_of, MaybeUninit};
|
18 |
| -use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; |
| 18 | +use crate::os::uefi::{self, env::boot_services, ffi::OsStrExt, ffi::OsStringExt}; |
19 | 19 | use crate::ptr::NonNull;
|
20 | 20 | use crate::slice;
|
21 | 21 | use crate::sync::atomic::{AtomicPtr, Ordering};
|
22 | 22 | use crate::sys_common::wstr::WStrUnits;
|
23 | 23 |
|
| 24 | +type BootInstallMultipleProtocolInterfaces = |
| 25 | + unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; |
| 26 | + |
| 27 | +type BootUninstallMultipleProtocolInterfaces = |
| 28 | + unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; |
| 29 | + |
24 | 30 | const BOOT_SERVICES_UNAVAILABLE: io::Error =
|
25 | 31 | const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available");
|
26 | 32 |
|
@@ -221,3 +227,192 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>>
|
221 | 227 | let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services };
|
222 | 228 | NonNull::new(runtime_services)
|
223 | 229 | }
|
| 230 | + |
| 231 | +pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>); |
| 232 | + |
| 233 | +impl DevicePath { |
| 234 | + pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> { |
| 235 | + fn inner( |
| 236 | + p: &OsStr, |
| 237 | + protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>, |
| 238 | + ) -> io::Result<DevicePath> { |
| 239 | + let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>(); |
| 240 | + if path_vec[..path_vec.len() - 1].contains(&0) { |
| 241 | + return Err(const_io_error!( |
| 242 | + io::ErrorKind::InvalidInput, |
| 243 | + "strings passed to UEFI cannot contain NULs", |
| 244 | + )); |
| 245 | + } |
| 246 | + |
| 247 | + let path = |
| 248 | + unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; |
| 249 | + |
| 250 | + NonNull::new(path).map(DevicePath).ok_or_else(|| { |
| 251 | + const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") |
| 252 | + }) |
| 253 | + } |
| 254 | + |
| 255 | + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = |
| 256 | + AtomicPtr::new(crate::ptr::null_mut()); |
| 257 | + |
| 258 | + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { |
| 259 | + if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>( |
| 260 | + handle, |
| 261 | + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, |
| 262 | + ) { |
| 263 | + return inner(p, protocol); |
| 264 | + } |
| 265 | + } |
| 266 | + |
| 267 | + let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?; |
| 268 | + for handle in handles { |
| 269 | + if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>( |
| 270 | + handle, |
| 271 | + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, |
| 272 | + ) { |
| 273 | + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); |
| 274 | + return inner(p, protocol); |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + io::Result::Err(const_io_error!( |
| 279 | + io::ErrorKind::NotFound, |
| 280 | + "DevicePathFromText Protocol not found" |
| 281 | + )) |
| 282 | + } |
| 283 | + |
| 284 | + pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { |
| 285 | + self.0.as_ptr() |
| 286 | + } |
| 287 | +} |
| 288 | + |
| 289 | +impl Drop for DevicePath { |
| 290 | + fn drop(&mut self) { |
| 291 | + if let Some(bt) = boot_services() { |
| 292 | + let bt: NonNull<r_efi::efi::BootServices> = bt.cast(); |
| 293 | + unsafe { |
| 294 | + ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void); |
| 295 | + } |
| 296 | + } |
| 297 | + } |
| 298 | +} |
| 299 | + |
| 300 | +pub(crate) struct OwnedProtocol<T> { |
| 301 | + guid: r_efi::efi::Guid, |
| 302 | + handle: NonNull<crate::ffi::c_void>, |
| 303 | + protocol: *mut T, |
| 304 | +} |
| 305 | + |
| 306 | +impl<T> OwnedProtocol<T> { |
| 307 | + // FIXME: Consider using unsafe trait for matching protocol with guid |
| 308 | + pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result<Self> { |
| 309 | + let bt: NonNull<r_efi::efi::BootServices> = |
| 310 | + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); |
| 311 | + let protocol: *mut T = Box::into_raw(Box::new(protocol)); |
| 312 | + let mut handle: r_efi::efi::Handle = crate::ptr::null_mut(); |
| 313 | + |
| 314 | + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized |
| 315 | + let func: BootInstallMultipleProtocolInterfaces = |
| 316 | + unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) }; |
| 317 | + |
| 318 | + let r = unsafe { |
| 319 | + func( |
| 320 | + &mut handle, |
| 321 | + &mut guid as *mut _ as *mut crate::ffi::c_void, |
| 322 | + protocol as *mut crate::ffi::c_void, |
| 323 | + crate::ptr::null_mut() as *mut crate::ffi::c_void, |
| 324 | + ) |
| 325 | + }; |
| 326 | + |
| 327 | + if r.is_error() { |
| 328 | + drop(unsafe { Box::from_raw(protocol) }); |
| 329 | + return Err(crate::io::Error::from_raw_os_error(r.as_usize())); |
| 330 | + }; |
| 331 | + |
| 332 | + let handle = NonNull::new(handle) |
| 333 | + .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; |
| 334 | + |
| 335 | + Ok(Self { guid, handle, protocol }) |
| 336 | + } |
| 337 | + |
| 338 | + pub(crate) fn handle(&self) -> NonNull<crate::ffi::c_void> { |
| 339 | + self.handle |
| 340 | + } |
| 341 | +} |
| 342 | + |
| 343 | +impl<T> Drop for OwnedProtocol<T> { |
| 344 | + fn drop(&mut self) { |
| 345 | + // Do not deallocate a runtime protocol |
| 346 | + if let Some(bt) = boot_services() { |
| 347 | + let bt: NonNull<r_efi::efi::BootServices> = bt.cast(); |
| 348 | + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized |
| 349 | + let func: BootUninstallMultipleProtocolInterfaces = unsafe { |
| 350 | + crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces) |
| 351 | + }; |
| 352 | + let status = unsafe { |
| 353 | + func( |
| 354 | + self.handle.as_ptr(), |
| 355 | + &mut self.guid as *mut _ as *mut crate::ffi::c_void, |
| 356 | + self.protocol as *mut crate::ffi::c_void, |
| 357 | + crate::ptr::null_mut() as *mut crate::ffi::c_void, |
| 358 | + ) |
| 359 | + }; |
| 360 | + |
| 361 | + // Leak the protocol in case uninstall fails |
| 362 | + if status == r_efi::efi::Status::SUCCESS { |
| 363 | + let _ = unsafe { Box::from_raw(self.protocol) }; |
| 364 | + } |
| 365 | + } |
| 366 | + } |
| 367 | +} |
| 368 | + |
| 369 | +impl<T> AsRef<T> for OwnedProtocol<T> { |
| 370 | + fn as_ref(&self) -> &T { |
| 371 | + unsafe { self.protocol.as_ref().unwrap() } |
| 372 | + } |
| 373 | +} |
| 374 | + |
| 375 | +pub(crate) struct OwnedTable<T> { |
| 376 | + layout: crate::alloc::Layout, |
| 377 | + ptr: *mut T, |
| 378 | +} |
| 379 | + |
| 380 | +impl<T> OwnedTable<T> { |
| 381 | + pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self { |
| 382 | + let header_size = hdr.header_size as usize; |
| 383 | + let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap(); |
| 384 | + let ptr = unsafe { crate::alloc::alloc(layout) as *mut T }; |
| 385 | + Self { layout, ptr } |
| 386 | + } |
| 387 | + |
| 388 | + pub(crate) const fn as_ptr(&self) -> *const T { |
| 389 | + self.ptr |
| 390 | + } |
| 391 | + |
| 392 | + pub(crate) const fn as_mut_ptr(&self) -> *mut T { |
| 393 | + self.ptr |
| 394 | + } |
| 395 | +} |
| 396 | + |
| 397 | +impl OwnedTable<r_efi::efi::SystemTable> { |
| 398 | + pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self { |
| 399 | + let hdr = unsafe { (*tbl).hdr }; |
| 400 | + |
| 401 | + let owned_tbl = Self::from_table_header(&hdr); |
| 402 | + unsafe { |
| 403 | + crate::ptr::copy_nonoverlapping( |
| 404 | + tbl as *const u8, |
| 405 | + owned_tbl.as_mut_ptr() as *mut u8, |
| 406 | + hdr.header_size as usize, |
| 407 | + ) |
| 408 | + }; |
| 409 | + |
| 410 | + owned_tbl |
| 411 | + } |
| 412 | +} |
| 413 | + |
| 414 | +impl<T> Drop for OwnedTable<T> { |
| 415 | + fn drop(&mut self) { |
| 416 | + unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) }; |
| 417 | + } |
| 418 | +} |
0 commit comments