virtfw-igvm-tools 0.1.10

igvm related linux applications
Documentation
use igvm::{IgvmDirectiveHeader, IgvmFile, IgvmInitializationHeader, IgvmPlatformHeader};
use igvm_defs::{IgvmPageDataFlags, IgvmPageDataType, IgvmPlatformType, PAGE_SIZE_4K};

use crate::x86regs::{ControlRegister0, ExtFeatureEnableReg};

pub fn format_compat(igvm: &IgvmFile, compat: u32) -> String {
    let platforms: Vec<IgvmPlatformType> = igvm
        .platforms()
        .iter()
        .filter_map(|p| {
            let IgvmPlatformHeader::SupportedPlatform(sp) = p;
            if sp.compatibility_mask & compat != 0 {
                Some(sp.platform_type)
            } else {
                None
            }
        })
        .collect();

    format!("compat 0x{compat:x} {platforms:?}")
}

pub fn format_entry(efer: u64, cr0: u64, cs: u16, rip: u64) -> String {
    let lma = u64::from(ExtFeatureEnableReg::new().with_lma(true));
    if (efer & lma) != 0 {
        return format!("lm64, rip {rip:x}");
    }

    let pe = u64::from(ControlRegister0::new().with_pe(true));
    if (cr0 & pe) != 0 {
        return format!("pm32, eip {rip:x}");
    }

    format!("rm16, cs:ip {cs:x}:{rip:x}")
}

pub fn inspect_platforms(igvm: &IgvmFile) {
    for p in igvm.platforms() {
        let IgvmPlatformHeader::SupportedPlatform(sp) = p;
        println!(
            "platform: {:?}, compat 0x{:x}",
            sp.platform_type, sp.compatibility_mask
        );
    }
}

pub fn inspect_initializations(igvm: &IgvmFile) {
    for i in igvm.initializations() {
        match i {
            IgvmInitializationHeader::GuestPolicy {
                policy,
                compatibility_mask,
            } => {
                println!(
                    "initialization: policy 0x{:x}, {}",
                    policy,
                    format_compat(igvm, *compatibility_mask)
                );
            }
            _ => {
                println!("initialization: {i:?}");
            }
        }
    }
}

pub fn inspect_directives(igvm: &IgvmFile) {
    for d in igvm
        .directives()
        .iter()
        .filter(|x| !matches! {x, IgvmDirectiveHeader::PageData { .. }})
    {
        match d {
            IgvmDirectiveHeader::SnpVpContext {
                compatibility_mask,
                vmsa,
                ..
            } => {
                println!(
                    "SnpVpContext: {}, {}",
                    format_entry(vmsa.efer, vmsa.cr0, vmsa.cs.selector, vmsa.rip),
                    format_compat(igvm, *compatibility_mask)
                );
            }
            IgvmDirectiveHeader::X64NativeVpContext {
                compatibility_mask,
                context,
                ..
            } => {
                println!(
                    "X64NativeVpContext: {}, {}",
                    format_entry(
                        context.efer,
                        context.cr0,
                        context.code_selector,
                        context.rip
                    ),
                    format_compat(igvm, *compatibility_mask)
                );
            }

            IgvmDirectiveHeader::RequiredMemory {
                gpa,
                compatibility_mask,
                number_of_bytes,
                ..
            } => {
                println!(
                    "RequiredMemory: gpa 0x{:x}, bytes 0x{:X}, {}",
                    gpa,
                    number_of_bytes,
                    format_compat(igvm, *compatibility_mask)
                );
            }

            IgvmDirectiveHeader::ParameterArea {
                number_of_bytes,
                parameter_area_index,
                ..
            } => {
                println!("ParameterArea: idx {parameter_area_index}, bytes 0x{number_of_bytes:x}");
            }

            IgvmDirectiveHeader::EnvironmentInfo(p) => {
                println!(
                    "Parameter/EnvironmentInfo: idx {}, offset 0x{:x}",
                    p.parameter_area_index, p.byte_offset
                );
            }
            IgvmDirectiveHeader::MemoryMap(p) => {
                println!(
                    "Parameter/MemoryMap: idx {}, offset 0x{:x}",
                    p.parameter_area_index, p.byte_offset
                );
            }
            IgvmDirectiveHeader::VpCount(p) => {
                println!(
                    "Parameter/VpCount: idx {}, offset 0x{:x}",
                    p.parameter_area_index, p.byte_offset
                );
            }
            IgvmDirectiveHeader::Madt(p) => {
                println!(
                    "Parameter/Madt: idx {}, offset 0x{:x}",
                    p.parameter_area_index, p.byte_offset
                );
            }

            IgvmDirectiveHeader::ParameterInsert(p) => {
                println!(
                    "ParameterInsert: gpa 0x{:x}, idx {}, {}",
                    p.gpa,
                    p.parameter_area_index,
                    format_compat(igvm, p.compatibility_mask)
                );
            }

            _ => {
                println!("directive: {d}");
            }
        }
    }
}

pub fn inspect_pagedata(igvm: &IgvmFile) {
    let mut next_gpa: Option<u64> = None;
    let mut prev_type: Option<IgvmPageDataType> = None;
    let mut prev_flags: Option<IgvmPageDataFlags> = None;
    let mut prev_compat: Option<u32> = None;
    let mut prev_empty: Option<bool> = None;
    let mut page_count: u64 = 0;

    for d in igvm
        .directives()
        .iter()
        .filter(|x| matches! {x, IgvmDirectiveHeader::PageData { .. }})
    {
        if let IgvmDirectiveHeader::PageData {
            gpa,
            flags,
            data_type,
            compatibility_mask,
            data,
            ..
        } = d
        {
            if next_gpa == Some(*gpa)
                && prev_type == Some(*data_type)
                && prev_flags == Some(*flags)
                && prev_compat == Some(*compatibility_mask)
                && prev_empty == Some(data.is_empty())
            {
                next_gpa = Some(*gpa + PAGE_SIZE_4K);
                page_count += 1;
                continue;
            }
            if page_count > 0 {
                println!(
                    "PageData: end 0x{:x} ({} more pages)",
                    next_gpa.unwrap() - 1,
                    page_count
                );
                page_count = 0;
            }
            println!(
                "PageData: gpa 0x{:x}, type {:?}, flags {}{}{}{}, {}",
                gpa,
                data_type,
                if flags.is_2mb_page() { "2m" } else { "4k" },
                if flags.unmeasured() {
                    ", unmeasured"
                } else {
                    ""
                },
                if flags.shared() { ", shared" } else { "" },
                if data.is_empty() { ", empty" } else { "" },
                format_compat(igvm, *compatibility_mask)
            );
            next_gpa = Some(*gpa + PAGE_SIZE_4K);
            prev_type = Some(*data_type);
            prev_flags = Some(*flags);
            prev_compat = Some(*compatibility_mask);
            prev_empty = Some(data.is_empty());
        }
    }
    if page_count > 0 {
        println!(
            "PageData: end 0x{:x} ({} more pages)",
            next_gpa.unwrap() - 1,
            page_count
        );
    }
}

pub fn inspect_igvm(igvm: &IgvmFile) {
    inspect_platforms(igvm);
    inspect_initializations(igvm);
    inspect_directives(igvm);
    inspect_pagedata(igvm);
}

// usage: igvm.directives().iter().filter_map(resetvector)
pub fn find_resetvector(d: &IgvmDirectiveHeader) -> Option<&[u8]> {
    match d {
        IgvmDirectiveHeader::PageData { gpa, data, .. } => {
            if *gpa == 0xfffff000 {
                Some(data)
            } else {
                None
            }
        }
        _ => None,
    }
}