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