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 use crate::defs::{ 16 EfiDevicePathProtocol, EfiDevicePathToTextProtocol, EfiGuid, EFI_STATUS_NOT_FOUND, 17 }; 18 use crate::protocol::{Protocol, ProtocolInfo}; 19 use crate::{EfiEntry, EfiError, EfiResult}; 20 use core::fmt::Display; 21 22 /// `EFI_DEVICE_PATH_PROTOCOL` 23 pub struct DevicePathProtocol; 24 25 impl ProtocolInfo for DevicePathProtocol { 26 type InterfaceType = EfiDevicePathProtocol; 27 28 const GUID: EfiGuid = 29 EfiGuid::new(0x09576e91, 0x6d3f, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]); 30 } 31 32 /// `EFI_DEVICE_PATH_TO_TEXT_PROTOCOL` 33 pub struct DevicePathToTextProtocol; 34 35 impl ProtocolInfo for DevicePathToTextProtocol { 36 type InterfaceType = EfiDevicePathToTextProtocol; 37 38 const GUID: EfiGuid = 39 EfiGuid::new(0x8b843e20, 0x8132, 0x4852, [0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c]); 40 } 41 42 impl<'a> Protocol<'a, DevicePathToTextProtocol> { 43 /// 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, ) -> EfiResult<DevicePathText<'a>>44 pub fn convert_device_path_to_text( 45 &self, 46 device_path: &Protocol<DevicePathProtocol>, 47 display_only: bool, 48 allow_shortcuts: bool, 49 ) -> EfiResult<DevicePathText<'a>> { 50 let f = self 51 .interface()? 52 .convert_device_path_to_text 53 .as_ref() 54 .ok_or_else::<EfiError, _>(|| EFI_STATUS_NOT_FOUND.into())?; 55 // SAFETY: 56 // `self.interface()?` guarantees `self.interface` is non-null and points to a valid object 57 // established by `Protocol::new()`. 58 // `self.interface` is input parameter and will not be retained. It outlives the call. 59 let res = unsafe { f(device_path.interface_ptr(), display_only, allow_shortcuts) }; 60 Ok(DevicePathText::new(res, self.efi_entry)) 61 } 62 } 63 64 // `DevicePathText` is a wrapper for the return type of 65 // EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.ConvertDevicePathToText(). 66 pub struct DevicePathText<'a> { 67 text: Option<&'a [u16]>, 68 efi_entry: &'a EfiEntry, 69 } 70 71 impl<'a> DevicePathText<'a> { new(text: *mut u16, efi_entry: &'a EfiEntry) -> Self72 pub(crate) fn new(text: *mut u16, efi_entry: &'a EfiEntry) -> Self { 73 if text.is_null() { 74 return Self { text: None, efi_entry: efi_entry }; 75 } 76 77 let mut len: usize = 0; 78 // SAFETY: UEFI text is NULL terminated. 79 while unsafe { *text.add(len) } != 0 { 80 len += 1; 81 } 82 Self { 83 // SAFETY: Pointer is confirmed non-null with known length at this point. 84 text: Some(unsafe { core::slice::from_raw_parts(text, len) }), 85 efi_entry: efi_entry, 86 } 87 } 88 89 /// Get the text as a u16 slice. text(&self) -> Option<&[u16]>90 pub fn text(&self) -> Option<&[u16]> { 91 self.text 92 } 93 } 94 95 impl Display for DevicePathText<'_> { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result96 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 97 if let Some(text) = self.text { 98 for c in char::decode_utf16(text.into_iter().map(|v| *v)) { 99 match c.unwrap_or(char::REPLACEMENT_CHARACTER) { 100 '\0' => break, 101 ch => write!(f, "{}", ch)?, 102 }; 103 } 104 } 105 Ok(()) 106 } 107 } 108 109 impl Drop for DevicePathText<'_> { drop(&mut self)110 fn drop(&mut self) { 111 if let Some(text) = self.text { 112 self.efi_entry 113 .system_table() 114 .boot_services() 115 .free_pool(text.as_ptr() as *mut _) 116 .unwrap(); 117 } 118 } 119 } 120 121 #[cfg(test)] 122 mod test { 123 use super::*; 124 use crate::test::*; 125 use core::ptr::null_mut; 126 127 #[test] test_device_path_text_drop()128 fn test_device_path_text_drop() { 129 run_test(|image_handle, systab_ptr| { 130 let efi_entry = EfiEntry { image_handle, systab_ptr }; 131 let mut data: [u16; 4] = [1, 2, 3, 0]; 132 { 133 let path = DevicePathText::new(data.as_mut_ptr(), &efi_entry); 134 assert_eq!(path.text().unwrap().to_vec(), vec![1, 2, 3]); 135 } 136 efi_call_traces().with(|traces| { 137 assert_eq!( 138 traces.borrow_mut().free_pool_trace.inputs, 139 [data.as_mut_ptr() as *mut _] 140 ); 141 }); 142 }) 143 } 144 145 #[test] test_device_path_text_null()146 fn test_device_path_text_null() { 147 run_test(|image_handle, systab_ptr| { 148 let efi_entry = EfiEntry { image_handle, systab_ptr }; 149 { 150 assert_eq!(DevicePathText::new(null_mut(), &efi_entry).text(), None); 151 } 152 efi_call_traces().with(|traces| { 153 assert_eq!(traces.borrow_mut().free_pool_trace.inputs.len(), 0); 154 }); 155 }) 156 } 157 } 158