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)
    }
}