|  | 
| 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