pvh 0.1.0

Xen's x86/HVM direct boot ABI (PVH).
Documentation
use core::{fmt, ptr};

use crate::start_info::reader::{MemMap, ModlistEntryReader, StartInfoReader};
use crate::start_info::{MemmapTableEntry, ModlistEntry};

impl<M: MemMap> fmt::Debug for StartInfoReader<'_, M> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("StartInfoReader")
            .field("paddr", &ptr::from_ref(self.start_info))
            .field("version", &self.start_info.version)
            .field("flags", &self.start_info.flags)
            .field("modlist", &self.modlist_debug())
            .field(
                "cmdline_paddr",
                &DebugU64AsPointer(self.start_info.cmdline_paddr),
            )
            .field("cmdline_vaddr", &self.cmdline_vaddr())
            .field("cmdline", &self.cmdline())
            .field("rsdp_paddr", &DebugU64AsPointer(self.start_info.rsdp_paddr))
            .field("memmap", &MemmapTableDebug(self.memmap()))
            .finish()
    }
}

impl<M: MemMap> StartInfoReader<'_, M> {
    #[must_use]
    fn modlist_debug(&self) -> ModlistDebug<'_, &M> {
        let modlist = self.raw_modlist();
        let mem_map = &self.mem_map;
        ModlistDebug { modlist, mem_map }
    }
}

impl<M: MemMap> fmt::Debug for ModlistEntryReader<'_, M> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ModlistEntryReader")
            .field("paddr", &DebugU64AsPointer(self.modlist_entry.paddr))
            .field("vaddr", &self.vaddr())
            .field("size", &DebugU64AsPointer(self.modlist_entry.size))
            .field(
                "cmdline_paddr",
                &DebugU64AsPointer(self.modlist_entry.cmdline_paddr),
            )
            .field("cmdline_vaddr", &self.cmdline_vaddr())
            .field("cmdline", &self.cmdline())
            .finish()
    }
}

struct MemmapTableEntryDebug<'a>(&'a MemmapTableEntry);

impl fmt::Debug for MemmapTableEntryDebug<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("MemmapTableEntryDebug")
            .field("addr", &DebugU64AsPointer(self.0.addr))
            .field("size", &DebugU64AsPointer(self.0.size))
            .field("type", &self.0.ty())
            .finish()
    }
}

struct MemmapTableDebug<'a>(&'a [MemmapTableEntry]);

impl fmt::Debug for MemmapTableDebug<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let entries = self.0.iter().map(MemmapTableEntryDebug);
        f.debug_list().entries(entries).finish()
    }
}

struct ModlistDebug<'a, M> {
    modlist: &'a [ModlistEntry],
    mem_map: M,
}

impl<M: MemMap> fmt::Debug for ModlistDebug<'_, M> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mem_map = &self.mem_map;
        let entries = self
            .modlist
            .iter()
            .map(move |modlist_entry| ModlistEntryReader {
                modlist_entry,
                mem_map,
            });
        f.debug_list().entries(entries).finish()
    }
}

struct DebugU64AsPointer(u64);

impl fmt::Debug for DebugU64AsPointer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ptr_addr = self.0;

        if f.alternate() {
            let width = (u64::BITS / 4) as usize + 2;
            return write!(f, "{ptr_addr:#0width$x}");
        }

        write!(f, "{ptr_addr:#x}")
    }
}

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

    #[cfg(target_pointer_width = "64")]
    #[test]
    fn debug_u64_as_pointer() {
        for ptr_addr in 0..0x10 {
            let ptr = ptr::without_provenance::<()>(ptr_addr);
            let debug = DebugU64AsPointer(ptr_addr as u64);

            assert_eq!(format!("{ptr:p}"), format!("{debug:?}"));
            assert_eq!(format!("{ptr:#p}"), format!("{debug:#?}"));
        }
    }
}