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}