1 // Copyright 2023, 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 alloc::vec::Vec;
16 use core::ffi::CStr;
17 
18 use crate::error::{EfiAppError, Result};
19 use efi::{
20     defs::{EfiGuid, EFI_TIMER_DELAY_TIMER_RELATIVE},
21     protocol::{
22         block_io::BlockIoProtocol,
23         device_path::{DevicePathProtocol, DevicePathText, DevicePathToTextProtocol},
24         loaded_image::LoadedImageProtocol,
25         simple_text_input::SimpleTextInputProtocol,
26         Protocol,
27     },
28     DeviceHandle, EfiEntry, EventType,
29 };
30 use fdt::FdtHeader;
31 use gbl_storage::{
32     required_scratch_size, AsBlockDevice, AsMultiBlockDevices, BlockInfo, BlockIo, BlockIoError,
33 };
34 
35 pub const EFI_DTB_TABLE_GUID: EfiGuid =
36     EfiGuid::new(0xb1b621d5, 0xf19c, 0x41a5, [0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0]);
37 
38 /// Checks and converts an integer into usize
to_usize<T: TryInto<usize>>(val: T) -> Result<usize>39 pub fn to_usize<T: TryInto<usize>>(val: T) -> Result<usize> {
40     Ok(val.try_into().map_err(|_| EfiAppError::ArithmeticOverflow)?)
41 }
42 
43 /// Rounds up a usize convertible number.
usize_roundup<L: TryInto<usize>, R: TryInto<usize>>(lhs: L, rhs: R) -> Result<usize>44 pub fn usize_roundup<L: TryInto<usize>, R: TryInto<usize>>(lhs: L, rhs: R) -> Result<usize> {
45     // (lhs + rhs - 1) / rhs * rhs
46     let lhs = to_usize(lhs)?;
47     let rhs = to_usize(rhs)?;
48     let compute = || lhs.checked_add(rhs.checked_sub(1)?)?.checked_div(rhs)?.checked_mul(rhs);
49     Ok(compute().ok_or_else(|| EfiAppError::ArithmeticOverflow)?)
50 }
51 
52 /// Adds two usize convertible numbers and checks overflow.
usize_add<L: TryInto<usize>, R: TryInto<usize>>(lhs: L, rhs: R) -> Result<usize>53 pub fn usize_add<L: TryInto<usize>, R: TryInto<usize>>(lhs: L, rhs: R) -> Result<usize> {
54     Ok(to_usize(lhs)?.checked_add(to_usize(rhs)?).ok_or_else(|| EfiAppError::ArithmeticOverflow)?)
55 }
56 
57 /// Gets a subslice of the given slice with aligned address according to `alignment`
aligned_subslice(bytes: &mut [u8], alignment: usize) -> Result<&mut [u8]>58 pub fn aligned_subslice(bytes: &mut [u8], alignment: usize) -> Result<&mut [u8]> {
59     let addr = bytes.as_ptr() as usize;
60     Ok(&mut bytes[usize_roundup(addr, alignment)? - addr..])
61 }
62 
63 // Implement a block device on top of BlockIoProtocol
64 pub struct EfiBlockIo<'a>(pub Protocol<'a, BlockIoProtocol>);
65 
66 impl BlockIo for EfiBlockIo<'_> {
info(&mut self) -> BlockInfo67     fn info(&mut self) -> BlockInfo {
68         BlockInfo {
69             block_size: self.0.media().unwrap().block_size as u64,
70             num_blocks: (self.0.media().unwrap().last_block + 1) as u64,
71             alignment: core::cmp::max(1, self.0.media().unwrap().io_align as u64),
72         }
73     }
74 
read_blocks( &mut self, blk_offset: u64, out: &mut [u8], ) -> core::result::Result<(), BlockIoError>75     fn read_blocks(
76         &mut self,
77         blk_offset: u64,
78         out: &mut [u8],
79     ) -> core::result::Result<(), BlockIoError> {
80         self.0
81             .read_blocks(blk_offset, out)
82             .map_err(|_| BlockIoError::Others(Some("EFI BLOCK_IO protocol read error")))
83     }
84 
write_blocks( &mut self, blk_offset: u64, data: &mut [u8], ) -> core::result::Result<(), BlockIoError>85     fn write_blocks(
86         &mut self,
87         blk_offset: u64,
88         data: &mut [u8],
89     ) -> core::result::Result<(), BlockIoError> {
90         self.0
91             .write_blocks(blk_offset, data)
92             .map_err(|_| BlockIoError::Others(Some("EFI BLOCK_IO protocol write error")))
93     }
94 }
95 
96 /// `EfiGptDevice` wraps a `EfiBlockIo` and implements `AsBlockDevice` interface.
97 pub struct EfiGptDevice<'a> {
98     io: EfiBlockIo<'a>,
99     scratch: Vec<u8>,
100 }
101 
102 const MAX_GPT_ENTRIES: u64 = 128;
103 
104 impl<'a> EfiGptDevice<'a> {
105     /// Initialize from a `BlockIoProtocol` EFI protocol
new(protocol: Protocol<'a, BlockIoProtocol>) -> Result<Self>106     pub fn new(protocol: Protocol<'a, BlockIoProtocol>) -> Result<Self> {
107         let mut io = EfiBlockIo(protocol);
108         let scratch = vec![0u8; required_scratch_size(&mut io, MAX_GPT_ENTRIES)?];
109         Ok(Self { io, scratch })
110     }
111 }
112 
113 impl AsBlockDevice for EfiGptDevice<'_> {
with(&mut self, f: &mut dyn FnMut(&mut dyn BlockIo, &mut [u8], u64))114     fn with(&mut self, f: &mut dyn FnMut(&mut dyn BlockIo, &mut [u8], u64)) {
115         f(&mut self.io, &mut self.scratch[..], MAX_GPT_ENTRIES)
116     }
117 }
118 
119 pub struct EfiMultiBlockDevices<'a>(pub alloc::vec::Vec<EfiGptDevice<'a>>);
120 
121 impl AsMultiBlockDevices for EfiMultiBlockDevices<'_> {
for_each( &mut self, f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64), ) -> core::result::Result<(), Option<&'static str>>122     fn for_each(
123         &mut self,
124         f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64),
125     ) -> core::result::Result<(), Option<&'static str>> {
126         for (idx, ele) in self.0.iter_mut().enumerate() {
127             f(ele, u64::try_from(idx).unwrap());
128         }
129         Ok(())
130     }
131 }
132 
133 /// Finds and returns all block devices that have a valid GPT.
find_gpt_devices(efi_entry: &EfiEntry) -> Result<EfiMultiBlockDevices>134 pub fn find_gpt_devices(efi_entry: &EfiEntry) -> Result<EfiMultiBlockDevices> {
135     let bs = efi_entry.system_table().boot_services();
136     let block_dev_handles = bs.locate_handle_buffer_by_protocol::<BlockIoProtocol>()?;
137     let mut gpt_devices = Vec::<EfiGptDevice>::new();
138     for handle in block_dev_handles.handles() {
139         let mut gpt_dev = EfiGptDevice::new(bs.open_protocol::<BlockIoProtocol>(*handle)?)?;
140         match gpt_dev.sync_gpt() {
141             Ok(_) => {
142                 gpt_devices.push(gpt_dev);
143             }
144             _ => {}
145         };
146     }
147     Ok(EfiMultiBlockDevices(gpt_devices))
148 }
149 
150 /// Helper function to get the `DevicePathText` from a `DeviceHandle`.
get_device_path<'a>( entry: &'a EfiEntry, handle: DeviceHandle, ) -> Result<DevicePathText<'a>>151 pub fn get_device_path<'a>(
152     entry: &'a EfiEntry,
153     handle: DeviceHandle,
154 ) -> Result<DevicePathText<'a>> {
155     let bs = entry.system_table().boot_services();
156     let path = bs.open_protocol::<DevicePathProtocol>(handle)?;
157     let path_to_text = bs.find_first_and_open::<DevicePathToTextProtocol>()?;
158     Ok(path_to_text.convert_device_path_to_text(&path, false, false)?)
159 }
160 
161 /// Helper function to get the loaded image path.
loaded_image_path(entry: &EfiEntry) -> Result<DevicePathText>162 pub fn loaded_image_path(entry: &EfiEntry) -> Result<DevicePathText> {
163     get_device_path(
164         entry,
165         entry
166             .system_table()
167             .boot_services()
168             .open_protocol::<LoadedImageProtocol>(entry.image_handle())?
169             .device_handle()?,
170     )
171 }
172 
173 /// Find FDT from EFI configuration table.
get_efi_fdt<'a>(entry: &'a EfiEntry) -> Option<(&FdtHeader, &[u8])>174 pub fn get_efi_fdt<'a>(entry: &'a EfiEntry) -> Option<(&FdtHeader, &[u8])> {
175     if let Some(config_tables) = entry.system_table().configuration_table() {
176         for table in config_tables {
177             if table.vendor_guid == EFI_DTB_TABLE_GUID {
178                 // SAFETY: Buffer provided by EFI configuration table.
179                 return unsafe { FdtHeader::from_raw(table.vendor_table as *const _).ok() };
180             }
181         }
182     }
183     None
184 }
185 
186 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
efi_to_e820_mem_type(efi_mem_type: u32) -> u32187 pub fn efi_to_e820_mem_type(efi_mem_type: u32) -> u32 {
188     match efi_mem_type {
189         efi::defs::EFI_MEMORY_TYPE_LOADER_CODE
190         | efi::defs::EFI_MEMORY_TYPE_LOADER_DATA
191         | efi::defs::EFI_MEMORY_TYPE_BOOT_SERVICES_CODE
192         | efi::defs::EFI_MEMORY_TYPE_BOOT_SERVICES_DATA
193         | efi::defs::EFI_MEMORY_TYPE_CONVENTIONAL_MEMORY => boot::x86::E820_ADDRESS_TYPE_RAM,
194         efi::defs::EFI_MEMORY_TYPE_RUNTIME_SERVICES_CODE
195         | efi::defs::EFI_MEMORY_TYPE_RUNTIME_SERVICES_DATA
196         | efi::defs::EFI_MEMORY_TYPE_MEMORY_MAPPED_IO
197         | efi::defs::EFI_MEMORY_TYPE_MEMORY_MAPPED_IOPORT_SPACE
198         | efi::defs::EFI_MEMORY_TYPE_PAL_CODE
199         | efi::defs::EFI_MEMORY_TYPE_RESERVED_MEMORY_TYPE => boot::x86::E820_ADDRESS_TYPE_RESERVED,
200         efi::defs::EFI_MEMORY_TYPE_UNUSABLE_MEMORY => boot::x86::E820_ADDRESS_TYPE_UNUSABLE,
201         efi::defs::EFI_MEMORY_TYPE_ACPIRECLAIM_MEMORY => boot::x86::E820_ADDRESS_TYPE_ACPI,
202         efi::defs::EFI_MEMORY_TYPE_ACPIMEMORY_NVS => boot::x86::E820_ADDRESS_TYPE_NVS,
203         efi::defs::EFI_MEMORY_TYPE_PERSISTENT_MEMORY => boot::x86::E820_ADDRESS_TYPE_PMEM,
204         v => panic!("Unmapped EFI memory type {v}"),
205     }
206 }
207 
208 /// A helper to convert a bytes slice containing a null-terminated string to `str`
cstr_bytes_to_str(data: &[u8]) -> Result<&str>209 pub fn cstr_bytes_to_str(data: &[u8]) -> Result<&str> {
210     Ok(CStr::from_bytes_until_nul(data)
211         .map_err(|_| EfiAppError::InvalidString)?
212         .to_str()
213         .map_err(|_| EfiAppError::InvalidString)?)
214 }
215 
216 /// Converts 1 ms to number of 100 nano seconds
ms_to_100ns(ms: u64) -> Result<u64>217 pub fn ms_to_100ns(ms: u64) -> Result<u64> {
218     Ok(ms.checked_mul(1000 * 10).ok_or(EfiAppError::ArithmeticOverflow)?)
219 }
220 
221 /// Repetitively runs a closure until it signals completion or timeout.
222 ///
223 /// * If `f` returns `Ok(R)`, an `Ok(Some(R))` is returned immediately.
224 /// * If `f` has been repetitively called and returning `Err(false)` for `timeout_ms`,  an
225 ///   `Ok(None)` is returned. This is the time out case.
226 /// * If `f` returns `Err(true)` the timeout is reset.
loop_with_timeout<F, R>(efi_entry: &EfiEntry, timeout_ms: u64, mut f: F) -> Result<Option<R>> where F: FnMut() -> core::result::Result<R, bool>,227 pub fn loop_with_timeout<F, R>(efi_entry: &EfiEntry, timeout_ms: u64, mut f: F) -> Result<Option<R>>
228 where
229     F: FnMut() -> core::result::Result<R, bool>,
230 {
231     let bs = efi_entry.system_table().boot_services();
232     let timer = bs.create_event(EventType::Timer, None)?;
233     bs.set_timer(&timer, EFI_TIMER_DELAY_TIMER_RELATIVE, ms_to_100ns(timeout_ms)?)?;
234     while !bs.check_event(&timer)? {
235         match f() {
236             Ok(v) => {
237                 return Ok(Some(v));
238             }
239             Err(true) => {
240                 bs.set_timer(&timer, EFI_TIMER_DELAY_TIMER_RELATIVE, ms_to_100ns(timeout_ms)?)?;
241             }
242             _ => {}
243         }
244     }
245     Ok(None)
246 }
247 
248 /// Waits for a key stroke value from simple text input.
249 ///
250 /// Returns `Ok(true)` if the expected key stroke is read, `Ok(false)` if timeout, `Err` otherwise.
wait_key_stroke(efi_entry: &EfiEntry, expected: char, timeout_ms: u64) -> Result<bool>251 pub fn wait_key_stroke(efi_entry: &EfiEntry, expected: char, timeout_ms: u64) -> Result<bool> {
252     let input = efi_entry
253         .system_table()
254         .boot_services()
255         .find_first_and_open::<SimpleTextInputProtocol>()?;
256     loop_with_timeout(efi_entry, timeout_ms, || -> core::result::Result<Result<bool>, bool> {
257         match input.read_key_stroke() {
258             Ok(Some(key)) => match char::decode_utf16([key.unicode_char]).next().unwrap() {
259                 Ok(ch) if ch == expected => Ok(Ok(true)),
260                 _ => Err(false),
261             },
262             Ok(None) => Err(false),
263             Err(e) => Ok(Err(e.into())),
264         }
265     })?
266     .unwrap_or(Ok(false))
267 }
268