//! 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, 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, 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::` /// 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 = 0x7000_0000..=0x7fff_ffff; /// Range reserved for OS loaders. pub const RESERVED_FOR_OS_LOADER: RangeInclusive = 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;