| //! UEFI services available during boot. |
| |
| use crate::protocol::device_path::DevicePathProtocol; |
| use crate::table::Header; |
| use crate::{Char16, Event, Guid, Handle, PhysicalAddress, Status, VirtualAddress}; |
| use bitflags::bitflags; |
| use core::ffi::c_void; |
| use core::ops::RangeInclusive; |
| |
| /// Table of pointers to all the boot services. |
| #[derive(Debug)] |
| #[repr(C)] |
| pub struct BootServices { |
| pub header: Header, |
| |
| // Task Priority services |
| pub raise_tpl: unsafe extern "efiapi" fn(new_tpl: Tpl) -> Tpl, |
| pub restore_tpl: unsafe extern "efiapi" fn(old_tpl: Tpl), |
| |
| // Memory allocation functions |
| pub allocate_pages: unsafe extern "efiapi" fn( |
| alloc_ty: u32, |
| mem_ty: MemoryType, |
| count: usize, |
| addr: *mut PhysicalAddress, |
| ) -> Status, |
| pub free_pages: unsafe extern "efiapi" fn(addr: PhysicalAddress, pages: usize) -> Status, |
| pub get_memory_map: unsafe extern "efiapi" fn( |
| size: *mut usize, |
| map: *mut MemoryDescriptor, |
| key: *mut usize, |
| desc_size: *mut usize, |
| desc_version: *mut u32, |
| ) -> Status, |
| pub allocate_pool: unsafe extern "efiapi" fn( |
| pool_type: MemoryType, |
| size: usize, |
| buffer: *mut *mut u8, |
| ) -> Status, |
| pub free_pool: unsafe extern "efiapi" fn(buffer: *mut u8) -> Status, |
| |
| // Event & timer functions |
| pub create_event: unsafe extern "efiapi" fn( |
| ty: EventType, |
| notify_tpl: Tpl, |
| notify_func: Option<EventNotifyFn>, |
| notify_ctx: *mut c_void, |
| out_event: *mut Event, |
| ) -> Status, |
| pub set_timer: unsafe extern "efiapi" fn(event: Event, ty: u32, trigger_time: u64) -> Status, |
| pub wait_for_event: unsafe extern "efiapi" fn( |
| number_of_events: usize, |
| events: *mut Event, |
| out_index: *mut usize, |
| ) -> Status, |
| pub signal_event: unsafe extern "efiapi" fn(event: Event) -> Status, |
| pub close_event: unsafe extern "efiapi" fn(event: Event) -> Status, |
| pub check_event: unsafe extern "efiapi" fn(event: Event) -> Status, |
| |
| // Protocol handlers |
| pub install_protocol_interface: unsafe extern "efiapi" fn( |
| handle: *mut Handle, |
| guid: *const Guid, |
| interface_type: InterfaceType, |
| interface: *const c_void, |
| ) -> Status, |
| pub reinstall_protocol_interface: unsafe extern "efiapi" fn( |
| handle: Handle, |
| protocol: *const Guid, |
| old_interface: *const c_void, |
| new_interface: *const c_void, |
| ) -> Status, |
| pub uninstall_protocol_interface: unsafe extern "efiapi" fn( |
| handle: Handle, |
| protocol: *const Guid, |
| interface: *const c_void, |
| ) -> Status, |
| pub handle_protocol: unsafe extern "efiapi" fn( |
| handle: Handle, |
| proto: *const Guid, |
| out_proto: *mut *mut c_void, |
| ) -> Status, |
| pub reserved: *mut c_void, |
| pub register_protocol_notify: unsafe extern "efiapi" fn( |
| protocol: *const Guid, |
| event: Event, |
| registration: *mut *const c_void, |
| ) -> Status, |
| pub locate_handle: unsafe extern "efiapi" fn( |
| search_ty: i32, |
| proto: *const Guid, |
| key: *const c_void, |
| buf_sz: *mut usize, |
| buf: *mut Handle, |
| ) -> Status, |
| pub locate_device_path: unsafe extern "efiapi" fn( |
| proto: *const Guid, |
| device_path: *mut *const DevicePathProtocol, |
| out_handle: *mut Handle, |
| ) -> Status, |
| pub install_configuration_table: |
| unsafe extern "efiapi" fn(guid_entry: *const Guid, table_ptr: *const c_void) -> Status, |
| |
| // Image services |
| pub load_image: unsafe extern "efiapi" fn( |
| boot_policy: u8, |
| parent_image_handle: Handle, |
| device_path: *const DevicePathProtocol, |
| source_buffer: *const u8, |
| source_size: usize, |
| image_handle: *mut Handle, |
| ) -> Status, |
| pub start_image: unsafe extern "efiapi" fn( |
| image_handle: Handle, |
| exit_data_size: *mut usize, |
| exit_data: *mut *mut Char16, |
| ) -> Status, |
| pub exit: unsafe extern "efiapi" fn( |
| image_handle: Handle, |
| exit_status: Status, |
| exit_data_size: usize, |
| exit_data: *mut Char16, |
| ) -> !, |
| pub unload_image: unsafe extern "efiapi" fn(image_handle: Handle) -> Status, |
| pub exit_boot_services: |
| unsafe extern "efiapi" fn(image_handle: Handle, map_key: usize) -> Status, |
| |
| // Misc services |
| pub get_next_monotonic_count: unsafe extern "efiapi" fn(count: *mut u64) -> Status, |
| pub stall: unsafe extern "efiapi" fn(microseconds: usize) -> Status, |
| pub set_watchdog_timer: unsafe extern "efiapi" fn( |
| timeout: usize, |
| watchdog_code: u64, |
| data_size: usize, |
| watchdog_data: *const u16, |
| ) -> Status, |
| |
| // Driver support services |
| pub connect_controller: unsafe extern "efiapi" fn( |
| controller: Handle, |
| driver_image: Handle, |
| remaining_device_path: *const DevicePathProtocol, |
| recursive: bool, |
| ) -> Status, |
| pub disconnect_controller: unsafe extern "efiapi" fn( |
| controller: Handle, |
| driver_image: Handle, |
| child: Handle, |
| ) -> Status, |
| |
| // Protocol open / close services |
| pub open_protocol: unsafe extern "efiapi" fn( |
| handle: Handle, |
| protocol: *const Guid, |
| interface: *mut *mut c_void, |
| agent_handle: Handle, |
| controller_handle: Handle, |
| attributes: u32, |
| ) -> Status, |
| pub close_protocol: unsafe extern "efiapi" fn( |
| handle: Handle, |
| protocol: *const Guid, |
| agent_handle: Handle, |
| controller_handle: Handle, |
| ) -> Status, |
| pub open_protocol_information: unsafe extern "efiapi" fn( |
| handle: Handle, |
| protocol: *const Guid, |
| entry_buffer: *mut *const OpenProtocolInformationEntry, |
| entry_count: *mut usize, |
| ) -> Status, |
| |
| // Library services |
| pub protocols_per_handle: unsafe extern "efiapi" fn( |
| handle: Handle, |
| protocol_buffer: *mut *mut *const Guid, |
| protocol_buffer_count: *mut usize, |
| ) -> Status, |
| pub locate_handle_buffer: unsafe extern "efiapi" fn( |
| search_ty: i32, |
| proto: *const Guid, |
| key: *const c_void, |
| no_handles: *mut usize, |
| buf: *mut *mut Handle, |
| ) -> Status, |
| pub locate_protocol: unsafe extern "efiapi" fn( |
| proto: *const Guid, |
| registration: *mut c_void, |
| out_proto: *mut *mut c_void, |
| ) -> Status, |
| |
| /// Warning: this function pointer is declared as `extern "C"` rather than |
| /// `extern "efiapi". That means it will work correctly when called from a |
| /// UEFI target (`*-unknown-uefi`), but will not work when called from a |
| /// target with a different calling convention such as |
| /// `x86_64-unknown-linux-gnu`. |
| /// |
| /// Support for C-variadics with `efiapi` requires the unstable |
| /// [`extended_varargs_abi_support`](https://github.com/rust-lang/rust/issues/100189) |
| /// feature. |
| pub install_multiple_protocol_interfaces: |
| unsafe extern "C" fn(handle: *mut Handle, ...) -> Status, |
| |
| /// Warning: this function pointer is declared as `extern "C"` rather than |
| /// `extern "efiapi". That means it will work correctly when called from a |
| /// UEFI target (`*-unknown-uefi`), but will not work when called from a |
| /// target with a different calling convention such as |
| /// `x86_64-unknown-linux-gnu`. |
| /// |
| /// Support for C-variadics with `efiapi` requires the unstable |
| /// [`extended_varargs_abi_support`](https://github.com/rust-lang/rust/issues/100189) |
| /// feature. |
| pub uninstall_multiple_protocol_interfaces: unsafe extern "C" fn(handle: Handle, ...) -> Status, |
| |
| // CRC services |
| pub calculate_crc32: |
| unsafe extern "efiapi" fn(data: *const c_void, data_size: usize, crc32: *mut u32) -> Status, |
| |
| // Misc services |
| pub copy_mem: unsafe extern "efiapi" fn(dest: *mut u8, src: *const u8, len: usize), |
| pub set_mem: unsafe extern "efiapi" fn(buffer: *mut u8, len: usize, value: u8), |
| |
| // New event functions (UEFI 2.0 or newer) |
| pub create_event_ex: unsafe extern "efiapi" fn( |
| ty: EventType, |
| notify_tpl: Tpl, |
| notify_fn: Option<EventNotifyFn>, |
| notify_ctx: *mut c_void, |
| event_group: *mut Guid, |
| out_event: *mut Event, |
| ) -> Status, |
| } |
| |
| bitflags! { |
| /// Flags describing the type of an UEFI event and its attributes. |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| #[repr(transparent)] |
| pub struct EventType: u32 { |
| /// The event is a timer event and may be passed to `BootServices::set_timer()` |
| /// Note that timers only function during boot services time. |
| const TIMER = 0x8000_0000; |
| |
| /// The event is allocated from runtime memory. |
| /// This must be done if the event is to be signaled after ExitBootServices. |
| const RUNTIME = 0x4000_0000; |
| |
| /// Calling wait_for_event or check_event will enqueue the notification |
| /// function if the event is not already in the signaled state. |
| /// Mutually exclusive with `NOTIFY_SIGNAL`. |
| const NOTIFY_WAIT = 0x0000_0100; |
| |
| /// The notification function will be enqueued when the event is signaled |
| /// Mutually exclusive with `NOTIFY_WAIT`. |
| const NOTIFY_SIGNAL = 0x0000_0200; |
| |
| /// The event will be signaled at ExitBootServices time. |
| /// This event type should not be combined with any other. |
| /// Its notification function must follow some special rules: |
| /// - Cannot use memory allocation services, directly or indirectly |
| /// - Cannot depend on timer events, since those will be deactivated |
| const SIGNAL_EXIT_BOOT_SERVICES = 0x0000_0201; |
| |
| /// The event will be notified when SetVirtualAddressMap is performed. |
| /// This event type should not be combined with any other. |
| const SIGNAL_VIRTUAL_ADDRESS_CHANGE = 0x6000_0202; |
| } |
| } |
| |
| newtype_enum! { |
| /// Interface type of a protocol interface. |
| pub enum InterfaceType: u32 => { |
| /// Native interface |
| NATIVE_INTERFACE = 0, |
| }} |
| |
| /// Raw event notification function. |
| pub type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: *mut c_void); |
| |
| bitflags! { |
| /// Flags describing the capabilities of a memory range. |
| #[repr(transparent)] |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| pub struct MemoryAttribute: u64 { |
| /// Supports marking as uncacheable. |
| const UNCACHEABLE = 0x1; |
| /// Supports write-combining. |
| const WRITE_COMBINE = 0x2; |
| /// Supports write-through. |
| const WRITE_THROUGH = 0x4; |
| /// Support write-back. |
| const WRITE_BACK = 0x8; |
| /// Supports marking as uncacheable, exported and |
| /// supports the "fetch and add" semaphore mechanism. |
| const UNCACHABLE_EXPORTED = 0x10; |
| /// Supports write-protection. |
| const WRITE_PROTECT = 0x1000; |
| /// Supports read-protection. |
| const READ_PROTECT = 0x2000; |
| /// Supports disabling code execution. |
| const EXECUTE_PROTECT = 0x4000; |
| /// Persistent memory. |
| const NON_VOLATILE = 0x8000; |
| /// This memory region is more reliable than other memory. |
| const MORE_RELIABLE = 0x10000; |
| /// This memory range can be set as read-only. |
| const READ_ONLY = 0x20000; |
| /// This memory is earmarked for specific purposes such as for specific |
| /// device drivers or applications. This serves as a hint to the OS to |
| /// avoid this memory for core OS data or code that cannot be relocated. |
| const SPECIAL_PURPOSE = 0x4_0000; |
| /// This memory region is capable of being protected with the CPU's memory |
| /// cryptography capabilities. |
| const CPU_CRYPTO = 0x8_0000; |
| /// This memory must be mapped by the OS when a runtime service is called. |
| const RUNTIME = 0x8000_0000_0000_0000; |
| /// This memory region is described with additional ISA-specific memory |
| /// attributes as specified in `MemoryAttribute::ISA_MASK`. |
| const ISA_VALID = 0x4000_0000_0000_0000; |
| /// These bits are reserved for describing optional ISA-specific cache- |
| /// ability attributes that are not covered by the standard UEFI Memory |
| /// Attribute cacheability bits such as `UNCACHEABLE`, `WRITE_COMBINE`, |
| /// `WRITE_THROUGH`, `WRITE_BACK`, and `UNCACHEABLE_EXPORTED`. |
| /// |
| /// See Section 2.3 "Calling Conventions" in the UEFI Specification |
| /// for further information on each ISA that takes advantage of this. |
| const ISA_MASK = 0x0FFF_F000_0000_0000; |
| } |
| } |
| |
| /// A structure describing a region of memory. This type corresponds to [version] |
| /// of this struct in the UEFI spec and is always bound to a corresponding |
| /// UEFI memory map. |
| /// |
| /// # UEFI pitfalls |
| /// As of May 2024: |
| /// The memory descriptor struct might be extended in the future by a new UEFI |
| /// spec revision, which will be reflected in another `version` of that |
| /// descriptor. The version is reported when using `get_memory_map` of |
| /// [`BootServices`]. |
| /// |
| /// Also note that you **must never** work with `size_of::<MemoryDescriptor>` |
| /// but always with `desc_size`, which is reported when using `get_memory_map` |
| /// as well [[0]]. For example, although the actual size is of version 1 |
| /// descriptors is `40`, the reported `desc_size` is `48`. |
| /// |
| /// [version]: MemoryDescriptor::VERSION |
| /// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059 |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| #[repr(C)] |
| pub struct MemoryDescriptor { |
| /// Type of memory occupying this range. |
| pub ty: MemoryType, |
| // Implicit 32-bit padding. |
| /// Starting physical address. |
| pub phys_start: PhysicalAddress, |
| /// Starting virtual address. |
| pub virt_start: VirtualAddress, |
| /// Number of 4 KiB pages contained in this range. |
| pub page_count: u64, |
| /// The capability attributes of this memory range. |
| pub att: MemoryAttribute, |
| } |
| |
| impl MemoryDescriptor { |
| /// Memory descriptor version number. |
| pub const VERSION: u32 = 1; |
| } |
| |
| impl Default for MemoryDescriptor { |
| fn default() -> Self { |
| Self { |
| ty: MemoryType::RESERVED, |
| phys_start: 0, |
| virt_start: 0, |
| page_count: 0, |
| att: MemoryAttribute::empty(), |
| } |
| } |
| } |
| |
| newtype_enum! { |
| /// The type of a memory range. |
| /// |
| /// UEFI allows firmwares and operating systems to introduce new memory types |
| /// in the `0x7000_0000..=0xFFFF_FFFF` range. Therefore, we don't know the full set |
| /// of memory types at compile time, and it is _not_ safe to model this C enum |
| /// as a Rust enum. |
| pub enum MemoryType: u32 => { |
| /// Not usable. |
| RESERVED = 0, |
| /// The code portions of a loaded UEFI application. |
| LOADER_CODE = 1, |
| /// The data portions of a loaded UEFI applications, |
| /// as well as any memory allocated by it. |
| LOADER_DATA = 2, |
| /// Code of the boot drivers. |
| /// |
| /// Can be reused after OS is loaded. |
| BOOT_SERVICES_CODE = 3, |
| /// Memory used to store boot drivers' data. |
| /// |
| /// Can be reused after OS is loaded. |
| BOOT_SERVICES_DATA = 4, |
| /// Runtime drivers' code. |
| RUNTIME_SERVICES_CODE = 5, |
| /// Runtime services' code. |
| RUNTIME_SERVICES_DATA = 6, |
| /// Free usable memory. |
| CONVENTIONAL = 7, |
| /// Memory in which errors have been detected. |
| UNUSABLE = 8, |
| /// Memory that holds ACPI tables. |
| /// Can be reclaimed after they are parsed. |
| ACPI_RECLAIM = 9, |
| /// Firmware-reserved addresses. |
| ACPI_NON_VOLATILE = 10, |
| /// A region used for memory-mapped I/O. |
| MMIO = 11, |
| /// Address space used for memory-mapped port I/O. |
| MMIO_PORT_SPACE = 12, |
| /// Address space which is part of the processor. |
| PAL_CODE = 13, |
| /// Memory region which is usable and is also non-volatile. |
| PERSISTENT_MEMORY = 14, |
| /// Memory that must be accepted by the boot target before it can be used. |
| UNACCEPTED = 15, |
| /// End of the defined memory types. Higher values are possible though, see |
| /// [`MemoryType::RESERVED_FOR_OEM`] and [`MemoryType::RESERVED_FOR_OS_LOADER`]. |
| MAX = 16, |
| }} |
| |
| impl MemoryType { |
| /// Range reserved for OEM use. |
| pub const RESERVED_FOR_OEM: RangeInclusive<u32> = 0x7000_0000..=0x7fff_ffff; |
| |
| /// Range reserved for OS loaders. |
| pub const RESERVED_FOR_OS_LOADER: RangeInclusive<u32> = 0x8000_0000..=0xffff_ffff; |
| |
| /// Construct a custom `MemoryType`. Values in the range `0x8000_0000..=0xffff_ffff` are free for use if you are |
| /// an OS loader. |
| #[must_use] |
| pub const fn custom(value: u32) -> Self { |
| assert!(value >= 0x80000000); |
| Self(value) |
| } |
| } |
| |
| #[derive(Debug)] |
| #[repr(C)] |
| pub struct OpenProtocolInformationEntry { |
| pub agent_handle: Handle, |
| pub controller_handle: Handle, |
| pub attributes: u32, |
| pub open_count: u32, |
| } |
| |
| newtype_enum! { |
| /// Task priority level. |
| /// |
| /// Although the UEFI specification repeatedly states that only the variants |
| /// specified below should be used in application-provided input, as the other |
| /// are reserved for internal firmware use, it might still happen that the |
| /// firmware accidentally discloses one of these internal TPLs to us. |
| /// |
| /// Since feeding an unexpected variant to a Rust enum is UB, this means that |
| /// this C enum must be interfaced via the newtype pattern. |
| pub enum Tpl: usize => { |
| /// Normal task execution level. |
| APPLICATION = 4, |
| /// Async interrupt-style callbacks run at this TPL. |
| CALLBACK = 8, |
| /// Notifications are masked at this level. |
| /// |
| /// This is used in critical sections of code. |
| NOTIFY = 16, |
| /// Highest priority level. |
| /// |
| /// Even processor interrupts are disable at this level. |
| HIGH_LEVEL = 31, |
| }} |
| |
| /// Size in bytes of a UEFI page. |
| /// |
| /// Note that this is not necessarily the processor's page size. The UEFI page |
| /// size is always 4 KiB. |
| pub const PAGE_SIZE: usize = 4096; |