morpheus_bootloader/boot/arch/x86_64/transitions.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
//! CPU mode transitions for x86_64
//!
//! Handles transitions between 64-bit long mode (UEFI) and 32-bit protected mode (Linux kernel).
// External assembly function for 64→32 bit transition
// Both the trampoline and wrapper are defined in assembly (trampoline32.asm)
extern "C" {
/// Drop from 64-bit long mode to 32-bit protected mode
///
/// This is implemented in external assembly (trampoline32.asm) to properly
/// handle the mode transition without compiler interference.
///
/// ONLY used for legacy kernels that don't support EFI handover protocol.
/// Modern kernels (2.6.30+) use EFI handover and stay in 64-bit mode.
///
/// When using this path, the bootloader MUST call ExitBootServices first.
///
/// # Safety
/// - Must only be called after ExitBootServices
/// - entry_point must be valid 32-bit kernel entry address
/// - boot_params must point to valid Linux boot_params struct
///
/// Does NOT return - jumps to kernel.
pub fn drop_to_protected_mode(entry_point: u32, boot_params: u32) -> !;
}
/// Check if kernel supports EFI handover protocol
///
/// Modern kernels (2.6.30+) support direct EFI handoff in 64-bit mode.
/// This is preferred when available - no mode switching needed.
///
/// Returns handover_offset if supported, None otherwise.
pub unsafe fn check_efi_handover_support(setup_header: &[u8]) -> Option<u32> {
// handover_offset is at offset 0x264 in setup header (since kernel 2.6.30)
if setup_header.len() < 0x268 {
return None;
}
// Read handover_offset (u32 at 0x264)
let offset = u32::from_le_bytes([
setup_header[0x264],
setup_header[0x265],
setup_header[0x266],
setup_header[0x267],
]);
// Zero means not supported
if offset == 0 {
None
} else {
Some(offset)
}
}