• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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