Skip to content

Commit 8791ec3

Browse files
authored
Merge pull request #1325 from rust-osdev/bishop-exit-boot-services
boot: Add freestanding exit_boot_services
2 parents 942e316 + 749eff2 commit 8791ec3

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

uefi/src/boot.rs

+94
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::proto::device_path::DevicePath;
99
use crate::proto::loaded_image::LoadedImage;
1010
use crate::proto::media::fs::SimpleFileSystem;
1111
use crate::proto::{Protocol, ProtocolPointer};
12+
use crate::runtime::{self, ResetType};
1213
use crate::table::Revision;
1314
use crate::util::opt_nonnull_to_ptr;
1415
use 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 [`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

Comments
 (0)