pvh 0.1.0

Xen's x86/HVM direct boot ABI (PVH).
Documentation
//! Xen ELF notes.
//!
//! This module helps with adding Xen ELF notes to guests kernels.

mod elf;
mod xen;

pub use crate::elfnote::elf::ElfNote;
use crate::elfnote::xen::XenElfNoteType;

/// Creates a `XEN_ELFNOTE_PHYS32_ENTRY` note.
///
/// This macro creates an ELF note that tells the loader how to load and jump
/// into the kernel entry point.
///
/// # Machine state
///
/// The kernel is responsible for setting up its stack, GDT, and IDT.
///
/// <div class="warning">
///
/// The CPU is in 32-bit mode without paging. 64-bit kernels need to set up
/// long mode before entering 64-bit code.
///
/// </div>
///
/// For details, see [x86/HVM direct boot ABI].
///
/// # Native ELF entry point
///
/// Note that the PVH entry point can be different from the native ELF entry
/// point (`e_entry`, usually `_start`).
///
/// <div class="warning">
///
/// `linux-loader` currently checks that the native ELF entry point is valid,
/// even though it is unused for PVH boot.
/// See [rust-vmm/linux-loader/src/loader/elf/mod.rs#L217-L223]. If a native ELF
/// entry point is not provided, `e_entry` is zero and the check fails.
///
/// </div>
///
/// # Examples
///
/// ```ignore (_start)
/// pvh::xen_elfnote_phys32_entry!(pvh_start32);
///
/// /// The PVH entry point.
/// #[unsafe(naked)]
/// unsafe extern "C" fn pvh_start32() -> ! {
///     core::arch::naked_asm!(
///         ".code32", // `start_info_paddr` is now in ebx.
///         // ...
///         ".code64",
///     );
/// }
///
/// /// The native ELF entry point.
/// #[unsafe(no_mangle)]
/// #[unsafe(naked)]
/// unsafe extern "C" fn _start() -> ! {
///     core::arch::naked_asm!(
///         # "2: jmp 2b",
///         // ...
///     );
/// }
/// ```
///
/// [x86/HVM direct boot ABI]: https://xenbits.xen.org/docs/unstable/misc/pvh.html
/// [rust-vmm/linux-loader/src/loader/elf/mod.rs#L217-L223]: https://github.com/rust-vmm/linux-loader/blob/v0.13.2/src/loader/elf/mod.rs#L217-L223
#[macro_export]
macro_rules! xen_elfnote_phys32_entry {
    ($phys32_entry:expr) => {
        #[used]
        #[unsafe(link_section = ".note.Xen")]
        static _XEN_ELFNOTE_PHYS32_ENTRY: $crate::elfnote::ElfnotePhys32Entry =
            $crate::elfnote::ElfnotePhys32Entry::phys32_entry($phys32_entry);
    };
}

pub type Name = [u8; 4];
pub type Phys32Entry = unsafe extern "C" fn() -> !;
pub type ElfnotePhys32Entry = ElfNote<Name, Phys32Entry>;

impl ElfnotePhys32Entry {
    #[must_use]
    pub const fn phys32_entry(phys32_entry: Phys32Entry) -> Self {
        Self::new(XenElfNoteType::Phys32Entry as u32, *b"Xen\0", phys32_entry)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Tests for correct ELF note alignment.
    ///
    /// QEMU aligns the name size up to the alignment of the program header:
    /// [hw/i386/x86-common.c]. [elf(5)] describes an alignment of 4,
    /// though, independent of the program header. This is might a QEMU bug.
    ///
    /// At the same time, `linux-loader` uses the correct alignment, but looks
    /// at the note name and its length: [src/loader/elf/mod.rs].
    ///
    /// The only way to make both happy is by reducing the alignment of the note
    /// to 4.
    ///
    /// [hw/i386/x86-common.c]: https://gitlab.com/qemu-project/qemu/-/blob/v10.2.2/hw/i386/x86-common.c#L540-592
    /// [elf(5)]: https://www.man7.org/linux/man-pages/man5/elf.5.html
    /// [src/loader/elf/mod.rs]: https://github.com/rust-vmm/linux-loader/blob/v0.13.2/src/loader/elf/mod.rs#L301-L409
    #[test]
    fn align() {
        assert_eq!(align_of::<ElfnotePhys32Entry>(), 4);
    }
}