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