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 crate::{efi, ops::get_buffer_from_protocol};
16 use ::efi::{efi_print, efi_println, EfiMemoryAttributesTable};
17 use core::{fmt::Write, slice::from_raw_parts_mut, time::Duration};
18 use efi::{
19 protocol::{
20 device_path::{DevicePathProtocol, DevicePathText, DevicePathToTextProtocol},
21 gbl_efi_image_loading::EfiImageBufferInfo,
22 loaded_image::LoadedImageProtocol,
23 simple_text_input::SimpleTextInputProtocol,
24 },
25 utils::Timeout,
26 DeviceHandle, EfiEntry,
27 };
28 use efi_types::{EfiGuid, EfiInputKey};
29 use fdt::FdtHeader;
30 use liberror::Error;
31 use libgbl::Result;
32
33 pub const EFI_DTB_TABLE_GUID: EfiGuid =
34 EfiGuid::new(0xb1b621d5, 0xf19c, 0x41a5, [0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0]);
35
36 /// Helper function to get the `DevicePathText` from a `DeviceHandle`.
get_device_path<'a>( entry: &'a EfiEntry, handle: DeviceHandle, ) -> Result<DevicePathText<'a>>37 pub fn get_device_path<'a>(
38 entry: &'a EfiEntry,
39 handle: DeviceHandle,
40 ) -> Result<DevicePathText<'a>> {
41 let bs = entry.system_table().boot_services();
42 let path = bs.open_protocol::<DevicePathProtocol>(handle)?;
43 let path_to_text = bs.find_first_and_open::<DevicePathToTextProtocol>()?;
44 Ok(path_to_text.convert_device_path_to_text(&path, false, false)?)
45 }
46
47 /// Helper function to get the loaded image path.
loaded_image_path(entry: &EfiEntry) -> Result<DevicePathText>48 pub fn loaded_image_path(entry: &EfiEntry) -> Result<DevicePathText> {
49 get_device_path(
50 entry,
51 entry
52 .system_table()
53 .boot_services()
54 .open_protocol::<LoadedImageProtocol>(entry.image_handle())?
55 .device_handle()?,
56 )
57 }
58
59 /// Find FDT from EFI configuration table.
get_efi_fdt(entry: &EfiEntry) -> Option<(&FdtHeader, &[u8])>60 pub fn get_efi_fdt(entry: &EfiEntry) -> Option<(&FdtHeader, &[u8])> {
61 if let Some(config_tables) = entry.system_table().configuration_table() {
62 for table in config_tables {
63 if table.vendor_guid == EFI_DTB_TABLE_GUID {
64 // SAFETY: Buffer provided by EFI configuration table.
65 return unsafe { FdtHeader::from_raw(table.vendor_table as *const _).ok() };
66 }
67 }
68 }
69 None
70 }
71
72 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
efi_to_e820_mem_type(efi_mem_type: u32) -> u3273 pub fn efi_to_e820_mem_type(efi_mem_type: u32) -> u32 {
74 match efi_mem_type {
75 efi_types::EFI_MEMORY_TYPE_LOADER_CODE
76 | efi_types::EFI_MEMORY_TYPE_LOADER_DATA
77 | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_CODE
78 | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_DATA
79 | efi_types::EFI_MEMORY_TYPE_CONVENTIONAL_MEMORY => boot::x86::E820_ADDRESS_TYPE_RAM,
80 efi_types::EFI_MEMORY_TYPE_RUNTIME_SERVICES_CODE
81 | efi_types::EFI_MEMORY_TYPE_RUNTIME_SERVICES_DATA
82 | efi_types::EFI_MEMORY_TYPE_MEMORY_MAPPED_IO
83 | efi_types::EFI_MEMORY_TYPE_MEMORY_MAPPED_IOPORT_SPACE
84 | efi_types::EFI_MEMORY_TYPE_PAL_CODE
85 | efi_types::EFI_MEMORY_TYPE_RESERVED_MEMORY_TYPE => boot::x86::E820_ADDRESS_TYPE_RESERVED,
86 efi_types::EFI_MEMORY_TYPE_UNUSABLE_MEMORY => boot::x86::E820_ADDRESS_TYPE_UNUSABLE,
87 efi_types::EFI_MEMORY_TYPE_ACPIRECLAIM_MEMORY => boot::x86::E820_ADDRESS_TYPE_ACPI,
88 efi_types::EFI_MEMORY_TYPE_ACPIMEMORY_NVS => boot::x86::E820_ADDRESS_TYPE_NVS,
89 efi_types::EFI_MEMORY_TYPE_PERSISTENT_MEMORY => boot::x86::E820_ADDRESS_TYPE_PMEM,
90 v => panic!("Unmapped EFI memory type {v}"),
91 }
92 }
93
94 /// Repetitively runs a closure until it signals completion or timeout.
95 ///
96 /// * If `f` returns `Ok(R)`, an `Ok(Some(R))` is returned immediately.
97 /// * If `f` has been repetitively called and returning `Err(false)` for `timeout_duration`, an
98 /// `Ok(None)` is returned. This is the time out case.
99 /// * If `f` returns `Err(true)` the timeout is reset.
loop_with_timeout<F, R>( efi_entry: &EfiEntry, timeout_duration: Duration, mut f: F, ) -> Result<Option<R>> where F: FnMut() -> core::result::Result<R, bool>,100 pub fn loop_with_timeout<F, R>(
101 efi_entry: &EfiEntry,
102 timeout_duration: Duration,
103 mut f: F,
104 ) -> Result<Option<R>>
105 where
106 F: FnMut() -> core::result::Result<R, bool>,
107 {
108 let timeout = Timeout::new(efi_entry, timeout_duration)?;
109 while !timeout.check()? {
110 match f() {
111 Ok(v) => return Ok(Some(v)),
112 Err(true) => timeout.reset(timeout_duration)?,
113 _ => {}
114 }
115 }
116 Ok(None)
117 }
118
119 /// Waits for a key stroke value from simple text input.
120 ///
121 /// Returns `Ok(true)` if the expected key stroke is read, `Ok(false)` if timeout, `Err` otherwise.
wait_key_stroke( efi_entry: &EfiEntry, pred: impl Fn(EfiInputKey) -> bool, timeout: Duration, ) -> Result<bool>122 pub fn wait_key_stroke(
123 efi_entry: &EfiEntry,
124 pred: impl Fn(EfiInputKey) -> bool,
125 timeout: Duration,
126 ) -> Result<bool> {
127 let input = efi_entry
128 .system_table()
129 .boot_services()
130 .find_first_and_open::<SimpleTextInputProtocol>()?;
131 loop_with_timeout(efi_entry, timeout, || -> core::result::Result<Result<bool>, bool> {
132 match input.read_key_stroke() {
133 Ok(Some(key)) if pred(key) => Ok(Ok(true)),
134 Err(e) => Ok(Err(e.into())),
135 _ => Err(false),
136 }
137 })?
138 .unwrap_or(Ok(false))
139 }
140
141 // Converts an EFI memory type to a zbi_mem_range_t type.
efi_to_zbi_mem_range_type(efi_mem_type: u32) -> u32142 pub fn efi_to_zbi_mem_range_type(efi_mem_type: u32) -> u32 {
143 match efi_mem_type {
144 efi_types::EFI_MEMORY_TYPE_LOADER_CODE
145 | efi_types::EFI_MEMORY_TYPE_LOADER_DATA
146 | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_CODE
147 | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_DATA
148 | efi_types::EFI_MEMORY_TYPE_CONVENTIONAL_MEMORY => zbi::zbi_format::ZBI_MEM_TYPE_RAM,
149 _ => zbi::zbi_format::ZBI_MEM_TYPE_RESERVED,
150 }
151 }
152
153 /// Find Memory attributes from EFI configuration_table
154 #[allow(unused)]
get_efi_mem_attr<'a>(entry: &'a EfiEntry) -> Option<EfiMemoryAttributesTable<'static>>155 pub fn get_efi_mem_attr<'a>(entry: &'a EfiEntry) -> Option<EfiMemoryAttributesTable<'static>> {
156 entry.system_table().configuration_table().and_then(|config_tables| {
157 config_tables
158 .iter()
159 .find_map(|&table| {
160 // SAFETY:
161 // `table` is valid EFI Configuration table provided by EFI
162 match unsafe { EfiMemoryAttributesTable::new(table) } {
163 Err(Error::NotFound) => None,
164 other => Some(other.ok()),
165 }
166 })
167 .flatten()
168 })
169 }
170
171 /// Represents either an initialized static memory space or memory to be allocated by the given
172 /// size.
173 pub(crate) enum BufferInfo {
174 // A static memory space, i.e. memory space reserved by platform
175 Static(&'static mut [u8]),
176 Alloc(usize),
177 }
178
179 /// A helper for getting platform buffer info from EFI image loading protocol.
get_platform_buffer_info( efi_entry: &EfiEntry, image_type: &str, default_aloc_size: usize, ) -> BufferInfo180 pub(crate) fn get_platform_buffer_info(
181 efi_entry: &EfiEntry,
182 image_type: &str,
183 default_aloc_size: usize,
184 ) -> BufferInfo {
185 match get_buffer_from_protocol(efi_entry, image_type, 0) {
186 Ok(EfiImageBufferInfo::Buffer(mut buffer)) => {
187 let buffer = buffer.take();
188 buffer.fill(core::mem::MaybeUninit::zeroed());
189 efi_println!(
190 efi_entry,
191 "Found \"{image_type}\" buffer from EFI protocol: addr {:#x}, sz: {:#x}.",
192 buffer.as_mut_ptr() as usize,
193 buffer.len()
194 );
195 // SAFETY:
196 // * `buffer` is a &'static [MaybeUninit<u8>] and fully initialized by the previous
197 // line.
198 // * MaybeUninit::zeroed() is a valid initialized value for u8.
199 BufferInfo::Static(unsafe {
200 from_raw_parts_mut(buffer.as_mut_ptr() as _, buffer.len())
201 })
202 }
203 Ok(EfiImageBufferInfo::AllocSize(sz)) if sz != 0 => BufferInfo::Alloc(sz),
204 _ => BufferInfo::Alloc(default_aloc_size),
205 }
206 }
207