1 // Copyright 2024, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Rust wrapper for `EFI_DEVICE_PATH_PROTOCOL`. 16 17 use crate::protocol::{Protocol, ProtocolInfo}; 18 use crate::EfiEntry; 19 use core::fmt::Display; 20 use efi_types::{EfiDevicePathProtocol, EfiDevicePathToTextProtocol, EfiGuid}; 21 use liberror::{Error, Result}; 22 23 /// `EFI_DEVICE_PATH_PROTOCOL` 24 pub struct DevicePathProtocol; 25 26 impl ProtocolInfo for DevicePathProtocol { 27 type InterfaceType = EfiDevicePathProtocol; 28 29 const GUID: EfiGuid = 30 EfiGuid::new(0x09576e91, 0x6d3f, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]); 31 } 32 33 /// `EFI_DEVICE_PATH_TO_TEXT_PROTOCOL` 34 pub struct DevicePathToTextProtocol; 35 36 impl ProtocolInfo for DevicePathToTextProtocol { 37 type InterfaceType = EfiDevicePathToTextProtocol; 38 39 const GUID: EfiGuid = 40 EfiGuid::new(0x8b843e20, 0x8132, 0x4852, [0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c]); 41 } 42 43 impl<'a> Protocol<'a, DevicePathToTextProtocol> { 44 /// Wrapper of `EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.ConvertDevicePathToText()` convert_device_path_to_text( &self, device_path: &Protocol<DevicePathProtocol>, display_only: bool, allow_shortcuts: bool, ) -> Result<DevicePathText<'a>>45 pub fn convert_device_path_to_text( 46 &self, 47 device_path: &Protocol<DevicePathProtocol>, 48 display_only: bool, 49 allow_shortcuts: bool, 50 ) -> Result<DevicePathText<'a>> { 51 let f = self.interface()?.convert_device_path_to_text.as_ref().ok_or(Error::NotFound)?; 52 // SAFETY: 53 // `self.interface()?` guarantees `self.interface` is non-null and points to a valid object 54 // established by `Protocol::new()`. 55 // `self.interface` is input parameter and will not be retained. It outlives the call. 56 let res = unsafe { f(device_path.interface_ptr(), display_only, allow_shortcuts) }; 57 Ok(DevicePathText::new(res, self.efi_entry)) 58 } 59 } 60 61 /// `DevicePathText` is a wrapper for the return type of 62 /// EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.ConvertDevicePathToText(). 63 pub struct DevicePathText<'a> { 64 text: Option<&'a [u16]>, 65 efi_entry: &'a EfiEntry, 66 } 67 68 impl<'a> DevicePathText<'a> { new(text: *mut u16, efi_entry: &'a EfiEntry) -> Self69 pub(crate) fn new(text: *mut u16, efi_entry: &'a EfiEntry) -> Self { 70 if text.is_null() { 71 return Self { text: None, efi_entry: efi_entry }; 72 } 73 74 let mut len: usize = 0; 75 // SAFETY: UEFI text is NULL terminated. 76 while unsafe { *text.add(len) } != 0 { 77 len += 1; 78 } 79 Self { 80 // SAFETY: Pointer is confirmed non-null with known length at this point. 81 text: Some(unsafe { core::slice::from_raw_parts(text, len) }), 82 efi_entry: efi_entry, 83 } 84 } 85 86 /// Get the text as a u16 slice. text(&self) -> Option<&[u16]>87 pub fn text(&self) -> Option<&[u16]> { 88 self.text 89 } 90 } 91 92 impl Display for DevicePathText<'_> { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result93 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 94 if let Some(text) = self.text { 95 for c in char::decode_utf16(text.into_iter().map(|v| *v)) { 96 match c.unwrap_or(char::REPLACEMENT_CHARACTER) { 97 '\0' => break, 98 ch => write!(f, "{}", ch)?, 99 }; 100 } 101 } 102 Ok(()) 103 } 104 } 105 106 impl Drop for DevicePathText<'_> { drop(&mut self)107 fn drop(&mut self) { 108 if let Some(text) = self.text { 109 self.efi_entry 110 .system_table() 111 .boot_services() 112 .free_pool(text.as_ptr() as *mut _) 113 .unwrap(); 114 } 115 } 116 } 117 118 #[cfg(test)] 119 mod test { 120 use super::*; 121 use crate::test::*; 122 use core::ptr::null_mut; 123 124 #[test] test_device_path_text_drop()125 fn test_device_path_text_drop() { 126 run_test(|image_handle, systab_ptr| { 127 let efi_entry = EfiEntry { image_handle, systab_ptr }; 128 let mut data: [u16; 4] = [1, 2, 3, 0]; 129 { 130 let path = DevicePathText::new(data.as_mut_ptr(), &efi_entry); 131 assert_eq!(path.text().unwrap().to_vec(), vec![1, 2, 3]); 132 } 133 efi_call_traces().with(|traces| { 134 assert_eq!( 135 traces.borrow_mut().free_pool_trace.inputs, 136 [data.as_mut_ptr() as *mut _] 137 ); 138 }); 139 }) 140 } 141 142 #[test] test_device_path_text_null()143 fn test_device_path_text_null() { 144 run_test(|image_handle, systab_ptr| { 145 let efi_entry = EfiEntry { image_handle, systab_ptr }; 146 { 147 assert_eq!(DevicePathText::new(null_mut(), &efi_entry).text(), None); 148 } 149 efi_call_traces().with(|traces| { 150 assert_eq!(traces.borrow_mut().free_pool_trace.inputs.len(), 0); 151 }); 152 }) 153 } 154 } 155