uefi_raw/table/boot.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! UEFI services available during boot.
4
5use crate::protocol::device_path::DevicePathProtocol;
6use crate::table::Header;
7use crate::{Char16, Event, Guid, Handle, PhysicalAddress, Status, VirtualAddress};
8use bitflags::bitflags;
9use core::ffi::c_void;
10use core::ops::RangeInclusive;
11
12/// Table of pointers to all the boot services.
13#[derive(Debug)]
14#[repr(C)]
15pub struct BootServices {
16 pub header: Header,
17
18 // Task Priority services
19 pub raise_tpl: unsafe extern "efiapi" fn(new_tpl: Tpl) -> Tpl,
20 pub restore_tpl: unsafe extern "efiapi" fn(old_tpl: Tpl),
21
22 // Memory allocation functions
23 pub allocate_pages: unsafe extern "efiapi" fn(
24 alloc_ty: u32,
25 mem_ty: MemoryType,
26 count: usize,
27 addr: *mut PhysicalAddress,
28 ) -> Status,
29 pub free_pages: unsafe extern "efiapi" fn(addr: PhysicalAddress, pages: usize) -> Status,
30 pub get_memory_map: unsafe extern "efiapi" fn(
31 size: *mut usize,
32 map: *mut MemoryDescriptor,
33 key: *mut usize,
34 desc_size: *mut usize,
35 desc_version: *mut u32,
36 ) -> Status,
37 pub allocate_pool: unsafe extern "efiapi" fn(
38 pool_type: MemoryType,
39 size: usize,
40 buffer: *mut *mut u8,
41 ) -> Status,
42 pub free_pool: unsafe extern "efiapi" fn(buffer: *mut u8) -> Status,
43
44 // Event & timer functions
45 pub create_event: unsafe extern "efiapi" fn(
46 ty: EventType,
47 notify_tpl: Tpl,
48 notify_func: Option<EventNotifyFn>,
49 notify_ctx: *mut c_void,
50 out_event: *mut Event,
51 ) -> Status,
52 pub set_timer:
53 unsafe extern "efiapi" fn(event: Event, ty: TimerDelay, trigger_time: u64) -> Status,
54 pub wait_for_event: unsafe extern "efiapi" fn(
55 number_of_events: usize,
56 events: *mut Event,
57 out_index: *mut usize,
58 ) -> Status,
59 pub signal_event: unsafe extern "efiapi" fn(event: Event) -> Status,
60 pub close_event: unsafe extern "efiapi" fn(event: Event) -> Status,
61 pub check_event: unsafe extern "efiapi" fn(event: Event) -> Status,
62
63 // Protocol handlers
64 pub install_protocol_interface: unsafe extern "efiapi" fn(
65 handle: *mut Handle,
66 guid: *const Guid,
67 interface_type: InterfaceType,
68 interface: *const c_void,
69 ) -> Status,
70 pub reinstall_protocol_interface: unsafe extern "efiapi" fn(
71 handle: Handle,
72 protocol: *const Guid,
73 old_interface: *const c_void,
74 new_interface: *const c_void,
75 ) -> Status,
76 pub uninstall_protocol_interface: unsafe extern "efiapi" fn(
77 handle: Handle,
78 protocol: *const Guid,
79 interface: *const c_void,
80 ) -> Status,
81 pub handle_protocol: unsafe extern "efiapi" fn(
82 handle: Handle,
83 proto: *const Guid,
84 out_proto: *mut *mut c_void,
85 ) -> Status,
86 pub reserved: *mut c_void,
87 pub register_protocol_notify: unsafe extern "efiapi" fn(
88 protocol: *const Guid,
89 event: Event,
90 registration: *mut *const c_void,
91 ) -> Status,
92 pub locate_handle: unsafe extern "efiapi" fn(
93 search_ty: i32,
94 proto: *const Guid,
95 key: *const c_void,
96 buf_sz: *mut usize,
97 buf: *mut Handle,
98 ) -> Status,
99 pub locate_device_path: unsafe extern "efiapi" fn(
100 proto: *const Guid,
101 device_path: *mut *const DevicePathProtocol,
102 out_handle: *mut Handle,
103 ) -> Status,
104 pub install_configuration_table:
105 unsafe extern "efiapi" fn(guid_entry: *const Guid, table_ptr: *const c_void) -> Status,
106
107 // Image services
108 pub load_image: unsafe extern "efiapi" fn(
109 boot_policy: u8,
110 parent_image_handle: Handle,
111 device_path: *const DevicePathProtocol,
112 source_buffer: *const u8,
113 source_size: usize,
114 image_handle: *mut Handle,
115 ) -> Status,
116 pub start_image: unsafe extern "efiapi" fn(
117 image_handle: Handle,
118 exit_data_size: *mut usize,
119 exit_data: *mut *mut Char16,
120 ) -> Status,
121 pub exit: unsafe extern "efiapi" fn(
122 image_handle: Handle,
123 exit_status: Status,
124 exit_data_size: usize,
125 exit_data: *mut Char16,
126 ) -> !,
127 pub unload_image: unsafe extern "efiapi" fn(image_handle: Handle) -> Status,
128 pub exit_boot_services:
129 unsafe extern "efiapi" fn(image_handle: Handle, map_key: usize) -> Status,
130
131 // Misc services
132 pub get_next_monotonic_count: unsafe extern "efiapi" fn(count: *mut u64) -> Status,
133 pub stall: unsafe extern "efiapi" fn(microseconds: usize) -> Status,
134 pub set_watchdog_timer: unsafe extern "efiapi" fn(
135 timeout: usize,
136 watchdog_code: u64,
137 data_size: usize,
138 watchdog_data: *const u16,
139 ) -> Status,
140
141 // Driver support services
142 pub connect_controller: unsafe extern "efiapi" fn(
143 controller: Handle,
144 driver_image: Handle,
145 remaining_device_path: *const DevicePathProtocol,
146 recursive: bool,
147 ) -> Status,
148 pub disconnect_controller: unsafe extern "efiapi" fn(
149 controller: Handle,
150 driver_image: Handle,
151 child: Handle,
152 ) -> Status,
153
154 // Protocol open / close services
155 pub open_protocol: unsafe extern "efiapi" fn(
156 handle: Handle,
157 protocol: *const Guid,
158 interface: *mut *mut c_void,
159 agent_handle: Handle,
160 controller_handle: Handle,
161 attributes: u32,
162 ) -> Status,
163 pub close_protocol: unsafe extern "efiapi" fn(
164 handle: Handle,
165 protocol: *const Guid,
166 agent_handle: Handle,
167 controller_handle: Handle,
168 ) -> Status,
169 pub open_protocol_information: unsafe extern "efiapi" fn(
170 handle: Handle,
171 protocol: *const Guid,
172 entry_buffer: *mut *const OpenProtocolInformationEntry,
173 entry_count: *mut usize,
174 ) -> Status,
175
176 // Library services
177 pub protocols_per_handle: unsafe extern "efiapi" fn(
178 handle: Handle,
179 protocol_buffer: *mut *mut *const Guid,
180 protocol_buffer_count: *mut usize,
181 ) -> Status,
182 pub locate_handle_buffer: unsafe extern "efiapi" fn(
183 search_ty: i32,
184 proto: *const Guid,
185 key: *const c_void,
186 no_handles: *mut usize,
187 buf: *mut *mut Handle,
188 ) -> Status,
189 pub locate_protocol: unsafe extern "efiapi" fn(
190 proto: *const Guid,
191 registration: *mut c_void,
192 out_proto: *mut *mut c_void,
193 ) -> Status,
194
195 /// Warning: this function pointer is declared as `extern "C"` rather than
196 /// `extern "efiapi". That means it will work correctly when called from a
197 /// UEFI target (`*-unknown-uefi`), but will not work when called from a
198 /// target with a different calling convention such as
199 /// `x86_64-unknown-linux-gnu`.
200 ///
201 /// Support for C-variadics with `efiapi` requires the unstable
202 /// [`extended_varargs_abi_support`](https://github.com/rust-lang/rust/issues/100189)
203 /// feature.
204 pub install_multiple_protocol_interfaces:
205 unsafe extern "C" fn(handle: *mut Handle, ...) -> Status,
206
207 /// Warning: this function pointer is declared as `extern "C"` rather than
208 /// `extern "efiapi". That means it will work correctly when called from a
209 /// UEFI target (`*-unknown-uefi`), but will not work when called from a
210 /// target with a different calling convention such as
211 /// `x86_64-unknown-linux-gnu`.
212 ///
213 /// Support for C-variadics with `efiapi` requires the unstable
214 /// [`extended_varargs_abi_support`](https://github.com/rust-lang/rust/issues/100189)
215 /// feature.
216 pub uninstall_multiple_protocol_interfaces: unsafe extern "C" fn(handle: Handle, ...) -> Status,
217
218 // CRC services
219 pub calculate_crc32:
220 unsafe extern "efiapi" fn(data: *const c_void, data_size: usize, crc32: *mut u32) -> Status,
221
222 // Misc services
223 pub copy_mem: unsafe extern "efiapi" fn(dest: *mut u8, src: *const u8, len: usize),
224 pub set_mem: unsafe extern "efiapi" fn(buffer: *mut u8, len: usize, value: u8),
225
226 // New event functions (UEFI 2.0 or newer)
227 pub create_event_ex: unsafe extern "efiapi" fn(
228 ty: EventType,
229 notify_tpl: Tpl,
230 notify_fn: Option<EventNotifyFn>,
231 notify_ctx: *mut c_void,
232 event_group: *mut Guid,
233 out_event: *mut Event,
234 ) -> Status,
235}
236
237bitflags! {
238 /// Flags describing the type of an UEFI event and its attributes.
239 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
240 #[repr(transparent)]
241 pub struct EventType: u32 {
242 /// The event is a timer event and may be passed to `BootServices::set_timer()`
243 /// Note that timers only function during boot services time.
244 const TIMER = 0x8000_0000;
245
246 /// The event is allocated from runtime memory.
247 /// This must be done if the event is to be signaled after ExitBootServices.
248 const RUNTIME = 0x4000_0000;
249
250 /// Calling wait_for_event or check_event will enqueue the notification
251 /// function if the event is not already in the signaled state.
252 /// Mutually exclusive with `NOTIFY_SIGNAL`.
253 const NOTIFY_WAIT = 0x0000_0100;
254
255 /// The notification function will be enqueued when the event is signaled
256 /// Mutually exclusive with `NOTIFY_WAIT`.
257 const NOTIFY_SIGNAL = 0x0000_0200;
258
259 /// The event will be signaled at ExitBootServices time.
260 /// This event type should not be combined with any other.
261 /// Its notification function must follow some special rules:
262 /// - Cannot use memory allocation services, directly or indirectly
263 /// - Cannot depend on timer events, since those will be deactivated
264 const SIGNAL_EXIT_BOOT_SERVICES = 0x0000_0201;
265
266 /// The event will be notified when SetVirtualAddressMap is performed.
267 /// This event type should not be combined with any other.
268 const SIGNAL_VIRTUAL_ADDRESS_CHANGE = 0x6000_0202;
269 }
270}
271
272newtype_enum! {
273/// Interface type of a protocol interface.
274pub enum InterfaceType: u32 => {
275 /// Native interface
276 NATIVE_INTERFACE = 0,
277}}
278
279/// Raw event notification function.
280pub type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: *mut c_void);
281
282bitflags! {
283 /// Flags describing the capabilities of a memory range.
284 #[repr(transparent)]
285 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
286 pub struct MemoryAttribute: u64 {
287 /// Supports marking as uncacheable.
288 const UNCACHEABLE = 0x1;
289 /// Supports write-combining.
290 const WRITE_COMBINE = 0x2;
291 /// Supports write-through.
292 const WRITE_THROUGH = 0x4;
293 /// Support write-back.
294 const WRITE_BACK = 0x8;
295 /// Supports marking as uncacheable, exported and
296 /// supports the "fetch and add" semaphore mechanism.
297 const UNCACHABLE_EXPORTED = 0x10;
298 /// Supports write-protection.
299 const WRITE_PROTECT = 0x1000;
300 /// Supports read-protection.
301 const READ_PROTECT = 0x2000;
302 /// Supports disabling code execution.
303 const EXECUTE_PROTECT = 0x4000;
304 /// Persistent memory.
305 const NON_VOLATILE = 0x8000;
306 /// This memory region is more reliable than other memory.
307 const MORE_RELIABLE = 0x10000;
308 /// This memory range can be set as read-only.
309 const READ_ONLY = 0x20000;
310 /// This memory is earmarked for specific purposes such as for specific
311 /// device drivers or applications. This serves as a hint to the OS to
312 /// avoid this memory for core OS data or code that cannot be relocated.
313 const SPECIAL_PURPOSE = 0x4_0000;
314 /// This memory region is capable of being protected with the CPU's memory
315 /// cryptography capabilities.
316 const CPU_CRYPTO = 0x8_0000;
317 /// This memory must be mapped by the OS when a runtime service is called.
318 const RUNTIME = 0x8000_0000_0000_0000;
319 /// This memory region is described with additional ISA-specific memory
320 /// attributes as specified in `MemoryAttribute::ISA_MASK`.
321 const ISA_VALID = 0x4000_0000_0000_0000;
322 /// These bits are reserved for describing optional ISA-specific cache-
323 /// ability attributes that are not covered by the standard UEFI Memory
324 /// Attribute cacheability bits such as `UNCACHEABLE`, `WRITE_COMBINE`,
325 /// `WRITE_THROUGH`, `WRITE_BACK`, and `UNCACHEABLE_EXPORTED`.
326 ///
327 /// See Section 2.3 "Calling Conventions" in the UEFI Specification
328 /// for further information on each ISA that takes advantage of this.
329 const ISA_MASK = 0x0FFF_F000_0000_0000;
330 }
331}
332
333/// A structure describing a region of memory. This type corresponds to [version]
334/// of this struct in the UEFI spec and is always bound to a corresponding
335/// UEFI memory map.
336///
337/// # UEFI pitfalls
338/// As of May 2024:
339/// The memory descriptor struct might be extended in the future by a new UEFI
340/// spec revision, which will be reflected in another `version` of that
341/// descriptor. The version is reported when using `get_memory_map` of
342/// [`BootServices`].
343///
344/// Also note that you **must never** work with `size_of::<MemoryDescriptor>`
345/// but always with `desc_size`, which is reported when using `get_memory_map`
346/// as well [[0]]. For example, although the actual size is of version 1
347/// descriptors is `40`, the reported `desc_size` is `48`.
348///
349/// [version]: MemoryDescriptor::VERSION
350/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
351#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
352#[repr(C)]
353pub struct MemoryDescriptor {
354 /// Type of memory occupying this range.
355 pub ty: MemoryType,
356 // Implicit 32-bit padding.
357 /// Starting physical address.
358 pub phys_start: PhysicalAddress,
359 /// Starting virtual address.
360 pub virt_start: VirtualAddress,
361 /// Number of 4 KiB pages contained in this range.
362 pub page_count: u64,
363 /// The capability attributes of this memory range.
364 pub att: MemoryAttribute,
365}
366
367impl MemoryDescriptor {
368 /// Memory descriptor version number.
369 pub const VERSION: u32 = 1;
370}
371
372impl Default for MemoryDescriptor {
373 fn default() -> Self {
374 Self {
375 ty: MemoryType::RESERVED,
376 phys_start: 0,
377 virt_start: 0,
378 page_count: 0,
379 att: MemoryAttribute::empty(),
380 }
381 }
382}
383
384newtype_enum! {
385/// The type of a memory range.
386///
387/// UEFI allows firmwares and operating systems to introduce new memory types
388/// in the `0x7000_0000..=0xFFFF_FFFF` range. Therefore, we don't know the full set
389/// of memory types at compile time, and it is _not_ safe to model this C enum
390/// as a Rust enum.
391pub enum MemoryType: u32 => {
392 /// Not usable.
393 RESERVED = 0,
394 /// The code portions of a loaded UEFI application.
395 LOADER_CODE = 1,
396 /// The data portions of a loaded UEFI applications,
397 /// as well as any memory allocated by it.
398 LOADER_DATA = 2,
399 /// Code of the boot drivers.
400 ///
401 /// Can be reused after OS is loaded.
402 BOOT_SERVICES_CODE = 3,
403 /// Memory used to store boot drivers' data.
404 ///
405 /// Can be reused after OS is loaded.
406 BOOT_SERVICES_DATA = 4,
407 /// Runtime drivers' code.
408 RUNTIME_SERVICES_CODE = 5,
409 /// Runtime services' code.
410 RUNTIME_SERVICES_DATA = 6,
411 /// Free usable memory.
412 CONVENTIONAL = 7,
413 /// Memory in which errors have been detected.
414 UNUSABLE = 8,
415 /// Memory that holds ACPI tables.
416 /// Can be reclaimed after they are parsed.
417 ACPI_RECLAIM = 9,
418 /// Firmware-reserved addresses.
419 ACPI_NON_VOLATILE = 10,
420 /// A region used for memory-mapped I/O.
421 MMIO = 11,
422 /// Address space used for memory-mapped port I/O.
423 MMIO_PORT_SPACE = 12,
424 /// Address space which is part of the processor.
425 PAL_CODE = 13,
426 /// Memory region which is usable and is also non-volatile.
427 PERSISTENT_MEMORY = 14,
428 /// Memory that must be accepted by the boot target before it can be used.
429 UNACCEPTED = 15,
430 /// End of the defined memory types. Higher values are possible though, see
431 /// [`MemoryType::RESERVED_FOR_OEM`] and [`MemoryType::RESERVED_FOR_OS_LOADER`].
432 MAX = 16,
433}}
434
435impl MemoryType {
436 /// Range reserved for OEM use.
437 pub const RESERVED_FOR_OEM: RangeInclusive<u32> = 0x7000_0000..=0x7fff_ffff;
438
439 /// Range reserved for OS loaders.
440 pub const RESERVED_FOR_OS_LOADER: RangeInclusive<u32> = 0x8000_0000..=0xffff_ffff;
441
442 /// Construct a custom `MemoryType`. Values in the range `0x8000_0000..=0xffff_ffff` are free for use if you are
443 /// an OS loader.
444 ///
445 /// **Warning**: Some EFI firmware versions (e.g., OVMF r11337) may crash or [behave incorrectly](https://wiki.osdev.org/UEFI#My_bootloader_hangs_if_I_use_user_defined_EFI_MEMORY_TYPE_values) when using a custom `MemoryType`.
446 #[must_use]
447 pub const fn custom(value: u32) -> Self {
448 assert!(value >= 0x80000000);
449 Self(value)
450 }
451}
452
453#[derive(Debug)]
454#[repr(C)]
455pub struct OpenProtocolInformationEntry {
456 pub agent_handle: Handle,
457 pub controller_handle: Handle,
458 pub attributes: u32,
459 pub open_count: u32,
460}
461
462newtype_enum! {
463/// Task priority level.
464///
465/// Although the UEFI specification repeatedly states that only the variants
466/// specified below should be used in application-provided input, as the other
467/// are reserved for internal firmware use, it might still happen that the
468/// firmware accidentally discloses one of these internal TPLs to us.
469///
470/// Since feeding an unexpected variant to a Rust enum is UB, this means that
471/// this C enum must be interfaced via the newtype pattern.
472pub enum Tpl: usize => {
473 /// Normal task execution level.
474 APPLICATION = 4,
475 /// Async interrupt-style callbacks run at this TPL.
476 CALLBACK = 8,
477 /// Notifications are masked at this level.
478 ///
479 /// This is used in critical sections of code.
480 NOTIFY = 16,
481 /// Highest priority level.
482 ///
483 /// Even processor interrupts are disable at this level.
484 HIGH_LEVEL = 31,
485}}
486
487/// Size in bytes of a UEFI page.
488///
489/// Note that this is not necessarily the processor's page size. The UEFI page
490/// size is always 4 KiB.
491pub const PAGE_SIZE: usize = 4096;
492
493newtype_enum! {
494 pub enum TimerDelay: i32 => {
495 CANCEL = 0,
496 PERIODIC = 1,
497 RELATIVE = 2,
498 }
499}