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