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