use core::{
fmt::{Display, Write},
iter::Iterator,
};
use alloc::{
boxed::Box,
string::{String, ToString},
};
use scroll::{
Pread, Pwrite,
ctx::{TryFromCtx, TryIntoCtx},
};
use crate::{
device_path::parse_node::{self, DevicePathNode, UnknownDevicePathNode},
device_path_node,
};
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum DevicePathType {
Hardware = 1,
Acpi = 2,
Messaging = 3,
Media = 4,
Bios = 5,
End = 0x7F,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum HardwareSubType {
Pci = 1,
Pccard = 2,
MemoryMapped = 3,
Vendor = 4,
Controller = 5,
Bmc = 6,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum AcpiSubType {
Acpi = 1,
ExtendedAcpi = 2,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum MessagingSubType {
Atapi = 1,
Scsi = 2,
FiberChannel = 3,
FiberChannelEx = 21,
_1394 = 4,
Usb = 5,
Sata = 18,
UsbWwid = 16,
DeviceLogicalUnit = 17,
UsbClass = 15,
I2oRandomBlockStorageClass = 6,
MacAddress = 11,
IpV4 = 12,
IpV6 = 13,
Vlan = 20,
InfiniBand = 9,
Uart = 14,
Vendor = 10,
SasEx = 22,
Iscsi = 19,
NvmExpress = 23,
Uri = 24,
Ufs = 25,
Sd = 26,
Bluetooth = 27,
WiFi = 28,
Emmc = 29,
BluetoothLE = 30,
Dns = 31,
Nvdimm = 32,
RestService = 33,
NvmeOf = 34,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum MediaSubType {
HardDrive = 1,
CdRom = 2,
Vendor = 3,
FilePath = 4,
MediaProtocol = 5,
PiwgFirmwareFile = 6,
PiwgFirmwareVolume = 7,
RelativeOffsetRange = 8,
RamDisk = 9,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum BiosSubType {
BiosBootSpecification = 1,
}
pub enum EndSubType {
Entire = 0xFF,
Instance = 0x01,
}
pub fn cast_to_dyn_device_path_node(unknown: UnknownDevicePathNode<'_>) -> Box<dyn DevicePathNode + '_> {
macro_rules! cast {
($unknown:expr, $($ty:ty),*) => {
match unknown.header {
$(
h if <$ty>::is_type(h.r#type, h.sub_type) => {
match unknown.data.pread_with::<$ty>(0, scroll::LE) {
Ok(n) => Some(Box::new(n) as Box<dyn DevicePathNode>),
Err(_) => {
debug_assert!(false);
None
}
}
}
)*,
_ => None
}
};
}
match cast!(
&unknown,
Pci,
PcCard,
MemoryMapped,
Controller,
Bmc,
Acpi,
NvmExpress,
FilePath,
Bios,
EndEntire,
EndInstance
) {
Some(n) => n,
None => Box::new(unknown),
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Hardware, HardwareSubType::Pci)]
@[DevicePathNodeDerive(Debug, Display)]
#[derive(Pwrite, Pread, Clone)]
pub struct Pci {
pub function: u8,
pub device: u8,
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Hardware, HardwareSubType::Pccard)]
@[DevicePathNodeDerive(Debug, Display)]
#[derive(Pwrite, Pread, Clone)]
pub struct PcCard {
pub function_number: u8,
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Hardware, HardwareSubType::MemoryMapped)]
@[DevicePathNodeDerive(Debug, Display)]
#[derive(Pwrite, Pread, Clone)]
pub struct MemoryMapped {
pub memory_type: u32,
pub start_address: u64,
pub end_address: u64,
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Hardware, HardwareSubType::Controller)]
@[DevicePathNodeDerive(Debug, Display)]
#[derive(Pwrite, Pread, Clone)]
pub struct Controller {
pub number: u32,
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Hardware, HardwareSubType::Bmc)]
@[DevicePathNodeDerive(Debug, Display)]
#[derive(Pwrite, Pread, Clone)]
pub struct Bmc {
pub interface_type: u8,
pub base_address: u64,
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Acpi, AcpiSubType::Acpi)]
@[DevicePathNodeDerive(Debug)]
#[derive(Pwrite, Pread, Clone)]
pub struct Acpi {
pub hid: u32,
pub uid: u32,
}
}
impl Acpi {
pub const PCI_ROOT_HID: u32 = Acpi::eisa_id("PNP0A03");
pub const PCIE_ROOT_HID: u32 = Acpi::eisa_id("PNP0A08");
pub fn new_pci_root(uid: u32) -> Self {
Self { hid: Acpi::PCI_ROOT_HID, uid }
}
pub const fn eisa_id(hid: &str) -> u32 {
let bytes = hid.as_bytes();
let c1 = (bytes[0] - 0x40) & 0x1F;
let c2 = (bytes[1] - 0x40) & 0x1F;
let c3 = (bytes[2] - 0x40) & 0x1F;
let h1 = (bytes[3] as char).to_digit(16).unwrap() as u8;
let h2 = (bytes[4] as char).to_digit(16).unwrap() as u8;
let h3 = (bytes[5] as char).to_digit(16).unwrap() as u8;
let h4 = (bytes[6] as char).to_digit(16).unwrap() as u8;
let byte_0 = (c1 << 2) | (c2 >> 3);
let byte_1 = (c2 << 5) | c3;
let byte_2 = (h1 << 4) | h2;
let byte_3 = (h3 << 4) | h4;
u32::from_le_bytes([byte_3, byte_2, byte_1, byte_0])
}
}
impl Display for Acpi {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.hid {
Acpi::PCI_ROOT_HID => f.debug_tuple("PciRoot").field(&self.uid).finish(),
_ => f.debug_tuple("Acpi").field(&self.hid).field(&self.uid).finish(),
}
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::Bios, BiosSubType::BiosBootSpecification)]
@[DevicePathNodeDerive(Debug, Display)]
pub struct Bios {
pub device_type: u16,
pub status_flag: u16,
pub description_str: String,
}
}
impl TryIntoCtx<scroll::Endian> for Bios {
type Error = scroll::Error;
fn try_into_ctx(self, dest: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let mut offset = 0;
dest.gwrite_with(self.device_type, &mut offset, ctx)?;
dest.gwrite_with(self.status_flag, &mut offset, ctx)?;
dest.gwrite_with(self.description_str.as_bytes(), &mut offset, ())?;
dest.gwrite_with(0, &mut offset, ctx)?; Ok(offset)
}
}
impl TryFromCtx<'_, scroll::Endian> for Bios {
type Error = scroll::Error;
fn try_from_ctx(buffer: &[u8], ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;
let device_type = buffer.gread_with::<u16>(&mut offset, ctx)?;
let status_flag = buffer.gread_with::<u16>(&mut offset, ctx)?;
let end_str_idx = &buffer[offset..]
.iter()
.position(|c| c == &0)
.ok_or(scroll::Error::TooBig { size: buffer.len() + 1, len: buffer.len() })?;
let description_str = String::from_utf8_lossy(&buffer[offset..offset + end_str_idx]).to_string();
Ok((Self { device_type, status_flag, description_str }, offset))
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::End, EndSubType::Entire)]
@[DevicePathNodeDerive(Debug)]
#[derive(Clone, Copy)]
pub struct EndEntire;
}
impl Display for EndEntire {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_char('.')
}
}
impl TryIntoCtx<scroll::Endian> for EndEntire {
type Error = scroll::Error;
fn try_into_ctx(self, _: &mut [u8], _: scroll::Endian) -> Result<usize, Self::Error> {
Ok(0)
}
}
impl TryFromCtx<'_, scroll::Endian> for EndEntire {
type Error = scroll::Error;
fn try_from_ctx(_: &[u8], _: scroll::Endian) -> Result<(Self, usize), Self::Error> {
Ok((Self, 0))
}
}
device_path_node! {
@[DevicePathNode(DevicePathType::End, EndSubType::Instance)]
@[DevicePathNodeDerive(Debug)]
#[derive(Clone, Copy)]
pub struct EndInstance;
}
impl Display for EndInstance {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_char(';')
}
}
impl TryIntoCtx<scroll::Endian> for EndInstance {
type Error = scroll::Error;
fn try_into_ctx(self, _: &mut [u8], _: scroll::Endian) -> Result<usize, Self::Error> {
Ok(0)
}
}
impl TryFromCtx<'_, scroll::Endian> for EndInstance {
type Error = scroll::Error;
fn try_from_ctx(_: &[u8], _: scroll::Endian) -> Result<(Self, usize), Self::Error> {
Ok((Self, 0))
}
}
#[derive(Clone)]
pub struct Sata {
pub hba_port: u16,
pub port_multiplier_port: u16,
pub lun: u16,
}
impl Sata {
const DATA_SIZE: usize = 6;
pub fn new(hba_port: u16, port_multiplier_port: u16, lun: u16) -> Self {
Self { hba_port, port_multiplier_port, lun }
}
}
impl DevicePathNode for Sata {
fn header(&self) -> parse_node::Header {
parse_node::Header {
r#type: DevicePathType::Messaging as u8,
sub_type: MessagingSubType::Sata as u8,
length: parse_node::Header::size_of_header() + Self::DATA_SIZE,
}
}
fn is_type(r#type: u8, sub_type: u8) -> bool {
r#type == DevicePathType::Messaging as u8 && sub_type == MessagingSubType::Sata as u8
}
fn write_into(self, buffer: &mut [u8]) -> Result<usize, scroll::Error> {
let header = self.header();
let mut offset = 0;
buffer.gwrite_with(header, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.hba_port, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.port_multiplier_port, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.lun, &mut offset, scroll::Endian::Little)?;
Ok(offset)
}
}
impl core::fmt::Debug for Sata {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Sata")
.field("hba_port", &self.hba_port)
.field("port_multiplier_port", &self.port_multiplier_port)
.field("lun", &self.lun)
.finish()
}
}
impl Display for Sata {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Sata").field(&self.hba_port).field(&self.port_multiplier_port).field(&self.lun).finish()
}
}
impl TryIntoCtx<scroll::Endian> for Sata {
type Error = scroll::Error;
fn try_into_ctx(self, dest: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let mut offset = 0;
dest.gwrite_with(self.hba_port, &mut offset, ctx)?;
dest.gwrite_with(self.port_multiplier_port, &mut offset, ctx)?;
dest.gwrite_with(self.lun, &mut offset, ctx)?;
Ok(offset)
}
}
impl TryFromCtx<'_, scroll::Endian> for Sata {
type Error = scroll::Error;
fn try_from_ctx(buffer: &[u8], ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;
let hba_port = buffer.gread_with(&mut offset, ctx)?;
let port_multiplier_port = buffer.gread_with(&mut offset, ctx)?;
let lun = buffer.gread_with(&mut offset, ctx)?;
Ok((Self { hba_port, port_multiplier_port, lun }, offset))
}
}
#[derive(Clone)]
pub struct NvmExpress {
pub namespace_id: u32,
pub eui64: u64,
}
impl NvmExpress {
const DATA_SIZE: usize = 4 + 8;
pub fn new(namespace_id: u32, eui64: u64) -> Self {
Self { namespace_id, eui64 }
}
}
impl parse_node::DevicePathNode for NvmExpress {
fn header(&self) -> crate::device_path::parse_node::Header {
parse_node::Header {
r#type: DevicePathType::Messaging as u8,
sub_type: MessagingSubType::NvmExpress as u8,
length: parse_node::Header::size_of_header() + Self::DATA_SIZE,
}
}
fn is_type(r#type: u8, sub_type: u8) -> bool {
r#type == DevicePathType::Messaging as u8 && sub_type == MessagingSubType::NvmExpress as u8
}
fn write_into(self, buffer: &mut [u8]) -> Result<usize, scroll::Error> {
let header = self.header();
let mut offset = 0;
buffer.gwrite_with(header, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.namespace_id, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.eui64, &mut offset, scroll::Endian::Little)?;
Ok(offset)
}
}
impl core::fmt::Debug for NvmExpress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("NvmExpress").field("namespace_id", &self.namespace_id).field("eui64", &self.eui64).finish()
}
}
impl Display for NvmExpress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("NvmExpress").field(&self.namespace_id).field(&self.eui64).finish()
}
}
impl TryIntoCtx<scroll::Endian> for NvmExpress {
type Error = scroll::Error;
fn try_into_ctx(self, dest: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let mut offset = 0;
dest.gwrite_with(self.namespace_id, &mut offset, ctx)?;
dest.gwrite_with(self.eui64, &mut offset, ctx)?;
Ok(offset)
}
}
impl TryFromCtx<'_, scroll::Endian> for NvmExpress {
type Error = scroll::Error;
fn try_from_ctx(buffer: &[u8], ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;
let namespace_id = buffer.gread_with(&mut offset, ctx)?;
let eui64 = buffer.gread_with(&mut offset, ctx)?;
Ok((Self { namespace_id, eui64 }, offset))
}
}
#[derive(Clone)]
pub struct HardDrive {
pub partition_number: u32,
pub partition_start: u64,
pub partition_size: u64,
pub partition_signature: [u8; 16],
pub partition_format: u8,
pub signature_type: u8,
}
impl HardDrive {
const DATA_SIZE: usize = 4 + 8 + 8 + 16 + 1 + 1;
pub const FORMAT_GPT: u8 = 0x02;
pub const FORMAT_MBR: u8 = 0x01;
pub const SIGNATURE_TYPE_GUID: u8 = 0x02;
pub const SIGNATURE_TYPE_MBR: u8 = 0x01;
pub fn new_gpt(partition_number: u32, partition_start: u64, partition_size: u64, partition_guid: [u8; 16]) -> Self {
Self {
partition_number,
partition_start,
partition_size,
partition_signature: partition_guid,
partition_format: Self::FORMAT_GPT,
signature_type: Self::SIGNATURE_TYPE_GUID,
}
}
}
impl parse_node::DevicePathNode for HardDrive {
fn header(&self) -> parse_node::Header {
parse_node::Header {
r#type: DevicePathType::Media as u8,
sub_type: MediaSubType::HardDrive as u8,
length: parse_node::Header::size_of_header() + Self::DATA_SIZE,
}
}
fn is_type(r#type: u8, sub_type: u8) -> bool {
r#type == DevicePathType::Media as u8 && sub_type == MediaSubType::HardDrive as u8
}
fn write_into(self, buffer: &mut [u8]) -> Result<usize, scroll::Error> {
let header = self.header();
let mut offset = 0;
buffer.gwrite_with(header, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.partition_number, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.partition_start, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.partition_size, &mut offset, scroll::Endian::Little)?;
for byte in &self.partition_signature {
buffer.gwrite_with(*byte, &mut offset, scroll::Endian::Little)?;
}
buffer.gwrite_with(self.partition_format, &mut offset, scroll::Endian::Little)?;
buffer.gwrite_with(self.signature_type, &mut offset, scroll::Endian::Little)?;
Ok(offset)
}
}
impl core::fmt::Debug for HardDrive {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HardDrive")
.field("partition_number", &self.partition_number)
.field("partition_start", &self.partition_start)
.field("partition_size", &self.partition_size)
.field("partition_format", &self.partition_format)
.field("signature_type", &self.signature_type)
.finish()
}
}
impl Display for HardDrive {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "HD({}, GPT, ...)", self.partition_number)
}
}
impl TryIntoCtx<scroll::Endian> for HardDrive {
type Error = scroll::Error;
fn try_into_ctx(self, dest: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let mut offset = 0;
dest.gwrite_with(self.partition_number, &mut offset, ctx)?;
dest.gwrite_with(self.partition_start, &mut offset, ctx)?;
dest.gwrite_with(self.partition_size, &mut offset, ctx)?;
for byte in &self.partition_signature {
dest.gwrite_with(*byte, &mut offset, ctx)?;
}
dest.gwrite_with(self.partition_format, &mut offset, ctx)?;
dest.gwrite_with(self.signature_type, &mut offset, ctx)?;
Ok(offset)
}
}
impl TryFromCtx<'_, scroll::Endian> for HardDrive {
type Error = scroll::Error;
fn try_from_ctx(buffer: &[u8], ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;
let partition_number = buffer.gread_with(&mut offset, ctx)?;
let partition_start = buffer.gread_with(&mut offset, ctx)?;
let partition_size = buffer.gread_with(&mut offset, ctx)?;
let mut partition_signature = [0u8; 16];
for byte in &mut partition_signature {
*byte = buffer.gread_with(&mut offset, ctx)?;
}
let partition_format = buffer.gread_with(&mut offset, ctx)?;
let signature_type = buffer.gread_with(&mut offset, ctx)?;
Ok((
Self {
partition_number,
partition_start,
partition_size,
partition_signature,
partition_format,
signature_type,
},
offset,
))
}
}
#[derive(Clone)]
pub struct FilePath {
pub path: String,
}
impl FilePath {
pub fn new(path: impl Into<String>) -> Self {
Self { path: path.into() }
}
fn utf16_size(&self) -> usize {
(self.path.chars().count() + 1) * 2
}
}
impl parse_node::DevicePathNode for FilePath {
fn header(&self) -> parse_node::Header {
parse_node::Header {
r#type: DevicePathType::Media as u8,
sub_type: MediaSubType::FilePath as u8,
length: parse_node::Header::size_of_header() + self.utf16_size(),
}
}
fn is_type(r#type: u8, sub_type: u8) -> bool {
r#type == DevicePathType::Media as u8 && sub_type == MediaSubType::FilePath as u8
}
fn write_into(self, buffer: &mut [u8]) -> Result<usize, scroll::Error> {
let header = self.header();
let mut offset = 0;
buffer.gwrite_with(header, &mut offset, scroll::Endian::Little)?;
for c in self.path.chars() {
let code_point = c as u16;
buffer.gwrite_with(code_point, &mut offset, scroll::LE)?;
}
buffer.gwrite_with(0u16, &mut offset, scroll::LE)?;
Ok(offset)
}
}
impl core::fmt::Debug for FilePath {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("FilePath").field("path", &self.path).finish()
}
}
impl Display for FilePath {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.path)
}
}
impl TryIntoCtx<scroll::Endian> for FilePath {
type Error = scroll::Error;
fn try_into_ctx(self, dest: &mut [u8], _ctx: scroll::Endian) -> Result<usize, Self::Error> {
let mut offset = 0;
for c in self.path.chars() {
let code_point = c as u16;
dest.gwrite_with(code_point, &mut offset, scroll::LE)?;
}
dest.gwrite_with(0u16, &mut offset, scroll::LE)?;
Ok(offset)
}
}
impl TryFromCtx<'_, scroll::Endian> for FilePath {
type Error = scroll::Error;
fn try_from_ctx(buffer: &[u8], _ctx: scroll::Endian) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;
let mut path = String::new();
loop {
if offset + 2 > buffer.len() {
break;
}
let code_point: u16 = buffer.gread_with(&mut offset, scroll::LE)?;
if code_point == 0 {
break;
}
if let Some(c) = char::from_u32(code_point as u32) {
path.push(c);
}
}
Ok((Self { path }, offset))
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use scroll::{Pread, Pwrite};
#[test]
fn test_sata_new() {
let sata = Sata::new(1, 0xFFFF, 0);
assert_eq!(sata.hba_port, 1);
assert_eq!(sata.port_multiplier_port, 0xFFFF);
assert_eq!(sata.lun, 0);
}
#[test]
fn test_sata_serialization_roundtrip() {
let sata = Sata::new(2, 0, 1);
let mut buf = [0u8; 6];
let written = buf.pwrite_with(sata.clone(), 0, scroll::LE).unwrap();
assert_eq!(written, 6);
let parsed: Sata = buf.pread_with(0, scroll::LE).unwrap();
assert_eq!(parsed.hba_port, 2);
assert_eq!(parsed.port_multiplier_port, 0);
assert_eq!(parsed.lun, 1);
}
#[test]
fn test_sata_display() {
let sata = Sata::new(1, 0xFFFF, 0);
let display = std::format!("{}", sata);
assert!(display.contains("Sata"));
}
#[test]
fn test_nvme_new() {
let nvme = NvmExpress::new(1, 0x123456789ABCDEF0);
assert_eq!(nvme.namespace_id, 1);
assert_eq!(nvme.eui64, 0x123456789ABCDEF0);
}
#[test]
fn test_nvme_serialization_roundtrip() {
let nvme = NvmExpress::new(1, 0xDEADBEEF12345678);
let mut buf = [0u8; 12];
let written = buf.pwrite_with(nvme.clone(), 0, scroll::LE).unwrap();
assert_eq!(written, 12);
let parsed: NvmExpress = buf.pread_with(0, scroll::LE).unwrap();
assert_eq!(parsed.namespace_id, 1);
assert_eq!(parsed.eui64, 0xDEADBEEF12345678);
}
#[test]
fn test_nvme_display() {
let nvme = NvmExpress::new(1, 0);
let display = std::format!("{}", nvme);
assert!(display.contains("NvmExpress"));
}
#[test]
fn test_hard_drive_new_gpt() {
let guid = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10];
let hd = HardDrive::new_gpt(1, 2048, 1000000, guid);
assert_eq!(hd.partition_number, 1);
assert_eq!(hd.partition_start, 2048);
assert_eq!(hd.partition_size, 1000000);
assert_eq!(hd.partition_signature, guid);
assert_eq!(hd.partition_format, HardDrive::FORMAT_GPT);
assert_eq!(hd.signature_type, HardDrive::SIGNATURE_TYPE_GUID);
}
#[test]
fn test_hard_drive_serialization_roundtrip() {
let guid = [0xAA; 16];
let hd = HardDrive::new_gpt(2, 4096, 500000, guid);
let mut buf = [0u8; 38];
let written = buf.pwrite_with(hd.clone(), 0, scroll::LE).unwrap();
assert_eq!(written, 38);
let parsed: HardDrive = buf.pread_with(0, scroll::LE).unwrap();
assert_eq!(parsed.partition_number, 2);
assert_eq!(parsed.partition_start, 4096);
assert_eq!(parsed.partition_size, 500000);
assert_eq!(parsed.partition_signature, guid);
assert_eq!(parsed.partition_format, HardDrive::FORMAT_GPT);
assert_eq!(parsed.signature_type, HardDrive::SIGNATURE_TYPE_GUID);
}
#[test]
fn test_hard_drive_display() {
let hd = HardDrive::new_gpt(1, 0, 0, [0; 16]);
let display = std::format!("{}", hd);
assert!(display.contains("HD"));
}
#[test]
fn test_file_path_new() {
let fp = FilePath::new("\\EFI\\BOOT\\BOOTX64.EFI");
assert_eq!(fp.path, "\\EFI\\BOOT\\BOOTX64.EFI");
}
#[test]
fn test_file_path_serialization_roundtrip() {
let fp = FilePath::new("\\test.efi");
let mut buf = [0u8; 20];
let written = buf.pwrite_with(fp.clone(), 0, scroll::LE).unwrap();
assert_eq!(written, 20);
let parsed: FilePath = buf.pread_with(0, scroll::LE).unwrap();
assert_eq!(parsed.path, "\\test.efi");
}
#[test]
fn test_file_path_display() {
let fp = FilePath::new("\\EFI\\BOOT\\BOOTX64.EFI");
let display = std::format!("{}", fp);
assert_eq!(display, "\\EFI\\BOOT\\BOOTX64.EFI");
}
#[test]
fn test_file_path_utf16_encoding() {
let fp = FilePath::new("A");
let mut buf = [0u8; 4];
let written = buf.pwrite_with(fp, 0, scroll::LE).unwrap();
assert_eq!(written, 4);
assert_eq!(buf[0], 0x41);
assert_eq!(buf[1], 0x00);
assert_eq!(buf[2], 0x00);
assert_eq!(buf[3], 0x00);
}
}