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