virtfw-libefi 0.1.0

library to read + write efi data structures
Documentation
//! efi device paths

extern crate alloc;
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;

use core::fmt;
use core::mem::size_of;
use core::net::{Ipv4Addr, Ipv6Addr};

use uguid::Guid;
use zerocopy_derive::{FromBytes, Immutable, IntoBytes};

use crate::nocasestr::NoCaseString;

pub const TYPE_HW: u8 = 1;
pub const TYPE_ACPI: u8 = 2;
pub const TYPE_MSG: u8 = 3;
pub const TYPE_MEDIA: u8 = 4;
pub const TYPE_BBS: u8 = 5;
pub const TYPE_END: u8 = 0x7f;

pub const TYPE_HW_PCI: u8 = 1;

pub const TYPE_ACPI_DP: u8 = 1;
pub const TYPE_ACPI_DPX: u8 = 2;
pub const TYPE_ACPI_ADR: u8 = 3;

pub const TYPE_MSG_SCSI: u8 = 2;
pub const TYPE_MSG_USB: u8 = 5;
pub const TYPE_MSG_VENDOR: u8 = 0x0a;
pub const TYPE_MSG_MAC: u8 = 0x0b;
pub const TYPE_MSG_IPV4: u8 = 0x0c;
pub const TYPE_MSG_IPV6: u8 = 0x0d;
pub const TYPE_MSG_UART: u8 = 0x0e;
pub const TYPE_MSG_USB_CLASS: u8 = 0x0f;
pub const TYPE_MSG_SATA: u8 = 0x12;
pub const TYPE_MSG_URI: u8 = 0x18;

pub const TYPE_MEDIA_HD: u8 = 1;
pub const TYPE_MEDIA_CD: u8 = 2;
pub const TYPE_MEDIA_VENDOR: u8 = 3;
pub const TYPE_MEDIA_FILEPATH: u8 = 4;
pub const TYPE_MEDIA_PROTOCOL: u8 = 5;
pub const TYPE_MEDIA_FW_FILE: u8 = 6;
pub const TYPE_MEDIA_FW_VOL: u8 = 7;

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathHeader {
    pub devtype: u8,
    pub subtype: u8,
    pub length: u16,
}

impl DevPathHeader {
    pub fn new(devtype: u8, subtype: u8, length: usize) -> Self {
        DevPathHeader {
            devtype,
            subtype,
            length: (size_of::<DevPathHeader>() + length) as u16,
        }
    }

    pub fn new_hw_pci() -> Self {
        DevPathHeader::new(TYPE_HW, TYPE_HW_PCI, size_of::<DevPathHwPci>())
    }

    pub fn new_acpi_dp() -> Self {
        DevPathHeader::new(TYPE_ACPI, TYPE_ACPI_DP, size_of::<DevPathAcpiDp>())
    }

    pub fn new_msg_scsi() -> Self {
        DevPathHeader::new(TYPE_MSG, TYPE_MSG_SCSI, size_of::<DevPathMsgScsi>())
    }

    pub fn new_msg_usb() -> Self {
        DevPathHeader::new(TYPE_MSG, TYPE_MSG_USB, size_of::<DevPathMsgUsb>())
    }

    pub fn new_msg_mac() -> Self {
        DevPathHeader::new(TYPE_MSG, TYPE_MSG_MAC, size_of::<DevPathMsgMac>())
    }

    pub fn new_msg_sata() -> Self {
        DevPathHeader::new(TYPE_MSG, TYPE_MSG_SATA, size_of::<DevPathMsgSata>())
    }
}

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathHwPci {
    pub function: u8,
    pub device: u8,
}

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathAcpiDp {
    hid: u32,
    uid: u32,
}

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathMsgScsi {
    target: u16,
    lun: u16,
}

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathMsgUsb {
    port: u8,
    interface: u8,
}

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathMsgMac {
    mac: [u8; 32],
    iftype: u8,
}

#[repr(C)]
#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
pub struct DevPathMsgSata {
    port: u16,
    mul_port: u16,
    lun: u16,
}

#[derive(Debug, PartialEq)]
pub enum DevPathNode {
    HwPci(DevPathHwPci),

    AcpiDp(DevPathAcpiDp),
    AcpiAdr {
        adr: Vec<u32>,
    },

    MsgScsi(DevPathMsgScsi),
    MsgUsb(DevPathMsgUsb),
    MsgVendor {
        guid: Guid,
    },
    MsgMac(DevPathMsgMac),
    MsgIPv4 {
        local_addr: Ipv4Addr,
        remote_addr: Ipv4Addr,
        local_port: u16,
        remote_port: u16,
        protocol: u16,
        static_ip: bool,
        gw_addr: Ipv4Addr,
        net_mask: Ipv4Addr,
    },
    MsgIPv6 {
        local_addr: Ipv6Addr,
        remote_addr: Ipv6Addr,
        local_port: u16,
        remote_port: u16,
        protocol: u16,
        addr_origin: u8,
        prefix_len: u8,
        gw_addr: Ipv6Addr,
    },
    MsgUart {
        reserved: u32,
        baudrate: u64,
        databits: u8,
        parity: u8,
        stopbits: u8,
    },
    MsgSata(DevPathMsgSata),
    MsgUsbClass {
        vendor: u16,
        product: u16,
        class: u8,
        subclass: u8,
        protocol: u8,
    },
    MsgUri {
        uri: Option<String>,
    },

    MediaHd {
        nr: u32,
        start: u64,
        size: u64,
        signature: Guid,
        mbrtype: u8, // use enum ?
        sigtype: u8, // use enum ?
    },
    MediaVendor {
        guid: Guid,
    },
    MediaFilePath {
        path: NoCaseString,
    },
    MediaFwFile {
        filename: Guid,
    },
    MediaFwVol {
        volume: Guid,
    },

    End {
        subtype: u8,
    },

    // generic catch for unimplemented types
    Other {
        devtype: u8,
        subtype: u8,
        blob: Vec<u8>,
    },
}

impl fmt::Display for DevPathNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            DevPathNode::HwPci(pci) => {
                write!(f, "Pci({:02x}.{:x})", pci.device, pci.function)
            }

            DevPathNode::AcpiDp(acpi) => match (acpi.hid, acpi.uid) {
                (0xa0341d0, _) => {
                    write!(f, "PciRoot()")
                }
                (h, u) => {
                    write!(f, "Acpi(hid=0x{:x},uid=0x{:x})", h, u)
                }
            },

            DevPathNode::AcpiAdr { adr } => {
                let s: Vec<String> = adr.iter().map(|a| format!("0x{:x}", a)).collect();
                write!(f, "Acpi(adr={})", s.join(","))
            }

            DevPathNode::MsgScsi(scsi) => {
                write!(f, "Scsi(target={},lun={})", scsi.target, scsi.lun)
            }

            DevPathNode::MsgUsb(usb) => {
                write!(f, "Usb(port={},if={})", usb.port, usb.interface)
            }

            DevPathNode::MsgVendor { guid } => {
                write!(f, "Vendor({})", guid)
            }

            DevPathNode::MsgMac(_) => {
                write!(f, "MAC()")
            }

            DevPathNode::MsgIPv4 {
                local_addr: _,
                remote_addr: _,
                local_port: _,
                remote_port: _,
                protocol: _,
                static_ip,
                gw_addr: _,
                net_mask: _,
            } => {
                if *static_ip {
                    write!(f, "IPv4(static)")
                } else {
                    write!(f, "IPv4(dhcp)")
                }
            }

            DevPathNode::MsgIPv6 {
                local_addr: _,
                remote_addr: _,
                local_port: _,
                remote_port: _,
                protocol: _,
                addr_origin,
                prefix_len: _,
                gw_addr: _,
            } => match addr_origin {
                0 => write!(f, "IPv6(static)"),
                1 => write!(f, "IPv6(stateless)"),
                2 => write!(f, "IPv6(stateful)"),
                _ => write!(f, "IPv6(invalid)"),
            },

            DevPathNode::MsgUart {
                reserved: _,
                baudrate: _,
                databits: _,
                parity: _,
                stopbits: _,
            } => {
                write!(f, "UART()")
            }

            DevPathNode::MsgSata(sata) => {
                write!(f, "SATA(port={})", sata.port)
            }

            DevPathNode::MsgUsbClass {
                vendor: _,
                product: _,
                class: _,
                subclass: _s,
                protocol: _,
            } => {
                write!(f, "UsbClass()")
            }
            DevPathNode::MsgUri { uri } => match uri {
                None => write!(f, "URI()"),
                Some(u) => write!(f, "URI(\"{}\")", u),
            },

            DevPathNode::MediaHd {
                nr,
                start: _,
                size: _,
                signature: _,
                mbrtype: _,
                sigtype: _,
            } => {
                write!(f, "HD({})", nr)
            }

            DevPathNode::MediaVendor { guid } => {
                write!(f, "Vendor({})", guid)
            }

            DevPathNode::MediaFilePath { path } => {
                write!(f, "File(\"{}\")", path)
            }

            DevPathNode::MediaFwFile { filename } => {
                write!(f, "FwFile({})", filename)
            }

            DevPathNode::MediaFwVol { volume } => {
                write!(f, "FwVol({})", volume)
            }

            DevPathNode::End { subtype: _ } => {
                write!(f, "End()")
            }

            DevPathNode::Other {
                devtype,
                subtype,
                blob,
            } => {
                write!(
                    f,
                    "Other(type=0x{:02x}.0x{:02x},len={})",
                    devtype,
                    subtype,
                    blob.len()
                )
            }
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct DevPath {
    pub nodes: Vec<DevPathNode>,
}

impl DevPath {
    pub fn new() -> DevPath {
        let nodes = Vec::new();
        DevPath { nodes }
    }
    pub fn push(&mut self, node: DevPathNode) {
        self.nodes.push(node);
    }
}

impl Default for DevPath {
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Display for DevPath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s: Vec<String> = self.nodes.iter().map(|a| format!("{}", a)).collect();
        write!(f, "{}", s.join("/"))
    }
}