@@ -9,6 +9,7 @@ use crate::proto::device_path::DevicePath;
99use crate :: proto:: loaded_image:: LoadedImage ;
1010use crate :: proto:: media:: fs:: SimpleFileSystem ;
1111use crate :: proto:: { Protocol , ProtocolPointer } ;
12+ use crate :: runtime:: { self , ResetType } ;
1213use crate :: table:: Revision ;
1314use crate :: util:: opt_nonnull_to_ptr;
1415use core:: ffi:: c_void;
@@ -1070,6 +1071,99 @@ pub unsafe fn exit(
10701071 )
10711072}
10721073
1074+ /// Get the current memory map and exit boot services.
1075+ unsafe fn get_memory_map_and_exit_boot_services ( buf : & mut [ u8 ] ) -> Result < MemoryMapMeta > {
1076+ let bt = boot_services_raw_panicking ( ) ;
1077+ let bt = unsafe { bt. as_ref ( ) } ;
1078+
1079+ // Get the memory map.
1080+ let memory_map = get_memory_map ( buf) ?;
1081+
1082+ // Try to exit boot services using the memory map key. Note that after
1083+ // the first call to `exit_boot_services`, there are restrictions on
1084+ // what boot services functions can be called. In UEFI 2.8 and earlier,
1085+ // only `get_memory_map` and `exit_boot_services` are allowed. Starting
1086+ // in UEFI 2.9 other memory allocation functions may also be called.
1087+ ( bt. exit_boot_services ) ( image_handle ( ) . as_ptr ( ) , memory_map. map_key . 0 )
1088+ . to_result_with_val ( || memory_map)
1089+ }
1090+
1091+ /// Exit UEFI boot services.
1092+ ///
1093+ /// After this function completes, UEFI hands over control of the hardware
1094+ /// to the executing OS loader, which implies that the UEFI boot services
1095+ /// are shut down and cannot be used anymore. Only UEFI configuration tables
1096+ /// and run-time services can be used.
1097+ ///
1098+ /// The memory map at the time of exiting boot services returned. The map is
1099+ /// backed by a pool allocation of the given `memory_type`. Since the boot
1100+ /// services function to free that memory is no longer available after calling
1101+ /// `exit_boot_services`, the allocation will not be freed on drop.
1102+ ///
1103+ /// Note that once the boot services are exited, associated loggers and
1104+ /// allocators can't use the boot services anymore. For the corresponding
1105+ /// abstractions provided by this crate (see the [`helpers`] module),
1106+ /// invoking this function will automatically disable them. If the
1107+ /// `global_allocator` feature is enabled, attempting to use the allocator
1108+ /// after exiting boot services will panic.
1109+ ///
1110+ /// # Safety
1111+ ///
1112+ /// The caller is responsible for ensuring that no references to
1113+ /// boot-services data remain. A non-exhaustive list of resources to check:
1114+ ///
1115+ /// * All protocols will be invalid after exiting boot services. This
1116+ /// includes the [`Output`] protocols attached to stdout/stderr. The
1117+ /// caller must ensure that no protocol references remain.
1118+ /// * The pool allocator is not usable after exiting boot services. Types
1119+ /// such as [`PoolString`] which call [`BootServices::free_pool`] on drop
1120+ /// must be cleaned up before calling `exit_boot_services`, or leaked to
1121+ /// avoid drop ever being called.
1122+ /// * All data in the memory map marked as
1123+ /// [`MemoryType::BOOT_SERVICES_CODE`] and
1124+ /// [`MemoryType::BOOT_SERVICES_DATA`] will become free memory.
1125+ ///
1126+ /// # Errors
1127+ ///
1128+ /// This function will fail if it is unable to allocate memory for
1129+ /// the memory map, if it fails to retrieve the memory map, or if
1130+ /// exiting boot services fails (with up to one retry).
1131+ ///
1132+ /// All errors are treated as unrecoverable because the system is
1133+ /// now in an undefined state. Rather than returning control to the
1134+ /// caller, the system will be reset.
1135+ ///
1136+ /// [`helpers`]: crate::helpers
1137+ /// [`Output`]: crate::proto::console::text::Output
1138+ /// [`PoolString`]: crate::proto::device_path::text::PoolString
1139+ #[ must_use]
1140+ pub unsafe fn exit_boot_services ( memory_type : MemoryType ) -> MemoryMapOwned {
1141+ crate :: helpers:: exit ( ) ;
1142+
1143+ let mut buf = MemoryMapBackingMemory :: new ( memory_type) . expect ( "Failed to allocate memory" ) ;
1144+
1145+ // Calling `exit_boot_services` can fail if the memory map key is not
1146+ // current. Retry a second time if that occurs. This matches the
1147+ // behavior of the Linux kernel:
1148+ // https://github.com/torvalds/linux/blob/e544a0743/drivers/firmware/efi/libstub/efi-stub-helper.c#L375
1149+ let mut status = Status :: ABORTED ;
1150+ for _ in 0 ..2 {
1151+ match unsafe { get_memory_map_and_exit_boot_services ( buf. as_mut_slice ( ) ) } {
1152+ Ok ( memory_map) => {
1153+ return MemoryMapOwned :: from_initialized_mem ( buf, memory_map) ;
1154+ }
1155+ Err ( err) => {
1156+ log:: error!( "Error retrieving the memory map for exiting the boot services" ) ;
1157+ status = err. status ( )
1158+ }
1159+ }
1160+ }
1161+
1162+ // Failed to exit boot services.
1163+ log:: warn!( "Resetting the machine" ) ;
1164+ runtime:: reset ( ResetType :: COLD , status, None ) ;
1165+ }
1166+
10731167/// Adds, updates, or removes a configuration table entry
10741168/// from the EFI System Table.
10751169///
0 commit comments