xref: /aosp_15_r20/bootable/libbootloader/gbl/efi/src/utils.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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;
16 use ::efi::EfiMemoryAttributesTable;
17 use efi::{
18     protocol::{
19         device_path::{DevicePathProtocol, DevicePathText, DevicePathToTextProtocol},
20         loaded_image::LoadedImageProtocol,
21         simple_text_input::SimpleTextInputProtocol,
22     },
23     utils::Timeout,
24     DeviceHandle, EfiEntry,
25 };
26 use efi_types::{EfiGuid, EfiInputKey};
27 use fdt::FdtHeader;
28 use liberror::Error;
29 use libgbl::Result;
30 
31 pub const EFI_DTB_TABLE_GUID: EfiGuid =
32     EfiGuid::new(0xb1b621d5, 0xf19c, 0x41a5, [0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0]);
33 
34 /// Helper function to get the `DevicePathText` from a `DeviceHandle`.
get_device_path<'a>( entry: &'a EfiEntry, handle: DeviceHandle, ) -> Result<DevicePathText<'a>>35 pub fn get_device_path<'a>(
36     entry: &'a EfiEntry,
37     handle: DeviceHandle,
38 ) -> Result<DevicePathText<'a>> {
39     let bs = entry.system_table().boot_services();
40     let path = bs.open_protocol::<DevicePathProtocol>(handle)?;
41     let path_to_text = bs.find_first_and_open::<DevicePathToTextProtocol>()?;
42     Ok(path_to_text.convert_device_path_to_text(&path, false, false)?)
43 }
44 
45 /// Helper function to get the loaded image path.
loaded_image_path(entry: &EfiEntry) -> Result<DevicePathText>46 pub fn loaded_image_path(entry: &EfiEntry) -> Result<DevicePathText> {
47     get_device_path(
48         entry,
49         entry
50             .system_table()
51             .boot_services()
52             .open_protocol::<LoadedImageProtocol>(entry.image_handle())?
53             .device_handle()?,
54     )
55 }
56 
57 /// Find FDT from EFI configuration table.
get_efi_fdt<'a>(entry: &'a EfiEntry) -> Option<(&FdtHeader, &[u8])>58 pub fn get_efi_fdt<'a>(entry: &'a EfiEntry) -> Option<(&FdtHeader, &[u8])> {
59     if let Some(config_tables) = entry.system_table().configuration_table() {
60         for table in config_tables {
61             if table.vendor_guid == EFI_DTB_TABLE_GUID {
62                 // SAFETY: Buffer provided by EFI configuration table.
63                 return unsafe { FdtHeader::from_raw(table.vendor_table as *const _).ok() };
64             }
65         }
66     }
67     None
68 }
69 
70 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
efi_to_e820_mem_type(efi_mem_type: u32) -> u3271 pub fn efi_to_e820_mem_type(efi_mem_type: u32) -> u32 {
72     match efi_mem_type {
73         efi_types::EFI_MEMORY_TYPE_LOADER_CODE
74         | efi_types::EFI_MEMORY_TYPE_LOADER_DATA
75         | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_CODE
76         | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_DATA
77         | efi_types::EFI_MEMORY_TYPE_CONVENTIONAL_MEMORY => boot::x86::E820_ADDRESS_TYPE_RAM,
78         efi_types::EFI_MEMORY_TYPE_RUNTIME_SERVICES_CODE
79         | efi_types::EFI_MEMORY_TYPE_RUNTIME_SERVICES_DATA
80         | efi_types::EFI_MEMORY_TYPE_MEMORY_MAPPED_IO
81         | efi_types::EFI_MEMORY_TYPE_MEMORY_MAPPED_IOPORT_SPACE
82         | efi_types::EFI_MEMORY_TYPE_PAL_CODE
83         | efi_types::EFI_MEMORY_TYPE_RESERVED_MEMORY_TYPE => boot::x86::E820_ADDRESS_TYPE_RESERVED,
84         efi_types::EFI_MEMORY_TYPE_UNUSABLE_MEMORY => boot::x86::E820_ADDRESS_TYPE_UNUSABLE,
85         efi_types::EFI_MEMORY_TYPE_ACPIRECLAIM_MEMORY => boot::x86::E820_ADDRESS_TYPE_ACPI,
86         efi_types::EFI_MEMORY_TYPE_ACPIMEMORY_NVS => boot::x86::E820_ADDRESS_TYPE_NVS,
87         efi_types::EFI_MEMORY_TYPE_PERSISTENT_MEMORY => boot::x86::E820_ADDRESS_TYPE_PMEM,
88         v => panic!("Unmapped EFI memory type {v}"),
89     }
90 }
91 
92 /// Repetitively runs a closure until it signals completion or timeout.
93 ///
94 /// * If `f` returns `Ok(R)`, an `Ok(Some(R))` is returned immediately.
95 /// * If `f` has been repetitively called and returning `Err(false)` for `timeout_ms`,  an
96 ///   `Ok(None)` is returned. This is the time out case.
97 /// * 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>,98 pub fn loop_with_timeout<F, R>(efi_entry: &EfiEntry, timeout_ms: u64, mut f: F) -> Result<Option<R>>
99 where
100     F: FnMut() -> core::result::Result<R, bool>,
101 {
102     let timeout = Timeout::new(efi_entry, timeout_ms)?;
103     while !timeout.check()? {
104         match f() {
105             Ok(v) => return Ok(Some(v)),
106             Err(true) => timeout.reset(timeout_ms)?,
107             _ => {}
108         }
109     }
110     Ok(None)
111 }
112 
113 /// Waits for a key stroke value from simple text input.
114 ///
115 /// 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_ms: u64, ) -> Result<bool>116 pub fn wait_key_stroke(
117     efi_entry: &EfiEntry,
118     pred: impl Fn(EfiInputKey) -> bool,
119     timeout_ms: u64,
120 ) -> Result<bool> {
121     let input = efi_entry
122         .system_table()
123         .boot_services()
124         .find_first_and_open::<SimpleTextInputProtocol>()?;
125     loop_with_timeout(efi_entry, timeout_ms, || -> core::result::Result<Result<bool>, bool> {
126         match input.read_key_stroke() {
127             Ok(Some(key)) if pred(key) => Ok(Ok(true)),
128             Err(e) => Ok(Err(e.into())),
129             _ => Err(false),
130         }
131     })?
132     .unwrap_or(Ok(false))
133 }
134 
135 // Converts an EFI memory type to a zbi_mem_range_t type.
efi_to_zbi_mem_range_type(efi_mem_type: u32) -> u32136 pub fn efi_to_zbi_mem_range_type(efi_mem_type: u32) -> u32 {
137     match efi_mem_type {
138         efi_types::EFI_MEMORY_TYPE_LOADER_CODE
139         | efi_types::EFI_MEMORY_TYPE_LOADER_DATA
140         | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_CODE
141         | efi_types::EFI_MEMORY_TYPE_BOOT_SERVICES_DATA
142         | efi_types::EFI_MEMORY_TYPE_CONVENTIONAL_MEMORY => zbi::zbi_format::ZBI_MEM_TYPE_RAM,
143         _ => zbi::zbi_format::ZBI_MEM_TYPE_RESERVED,
144     }
145 }
146 
147 /// Find Memory attributes from EFI configuration_table
148 #[allow(unused)]
get_efi_mem_attr<'a>(entry: &'a EfiEntry) -> Option<EfiMemoryAttributesTable<'static>>149 pub fn get_efi_mem_attr<'a>(entry: &'a EfiEntry) -> Option<EfiMemoryAttributesTable<'static>> {
150     entry.system_table().configuration_table().and_then(|config_tables| {
151         config_tables
152             .iter()
153             .find_map(|&table| {
154                 // SAFETY:
155                 // `table` is valid EFI Configuration table provided by EFI
156                 match unsafe { EfiMemoryAttributesTable::new(table) } {
157                     Err(Error::NotFound) => None,
158                     other => Some(other.ok()),
159                 }
160             })
161             .flatten()
162     })
163 }
164