Skip to main content

virtfw_libefi/efivar/
devpath.rs

1//! efi device paths
2
3extern crate alloc;
4use alloc::format;
5use alloc::string::String;
6use alloc::vec::Vec;
7
8use core::fmt;
9use core::mem::size_of;
10use core::net::{Ipv4Addr, Ipv6Addr};
11
12use uguid::Guid;
13use zerocopy::{FromBytes, Immutable, IntoBytes};
14
15use crate::nocasestr::NoCaseString;
16
17pub const TYPE_HW: u8 = 1;
18pub const TYPE_ACPI: u8 = 2;
19pub const TYPE_MSG: u8 = 3;
20pub const TYPE_MEDIA: u8 = 4;
21pub const TYPE_BBS: u8 = 5;
22pub const TYPE_END: u8 = 0x7f;
23
24pub const TYPE_HW_PCI: u8 = 1;
25
26pub const TYPE_ACPI_DP: u8 = 1;
27pub const TYPE_ACPI_DPX: u8 = 2;
28pub const TYPE_ACPI_ADR: u8 = 3;
29
30pub const TYPE_MSG_SCSI: u8 = 2;
31pub const TYPE_MSG_USB: u8 = 5;
32pub const TYPE_MSG_VENDOR: u8 = 0x0a;
33pub const TYPE_MSG_MAC: u8 = 0x0b;
34pub const TYPE_MSG_IPV4: u8 = 0x0c;
35pub const TYPE_MSG_IPV6: u8 = 0x0d;
36pub const TYPE_MSG_UART: u8 = 0x0e;
37pub const TYPE_MSG_USB_CLASS: u8 = 0x0f;
38pub const TYPE_MSG_SATA: u8 = 0x12;
39pub const TYPE_MSG_URI: u8 = 0x18;
40
41pub const TYPE_MEDIA_HD: u8 = 1;
42pub const TYPE_MEDIA_CD: u8 = 2;
43pub const TYPE_MEDIA_VENDOR: u8 = 3;
44pub const TYPE_MEDIA_FILEPATH: u8 = 4;
45pub const TYPE_MEDIA_PROTOCOL: u8 = 5;
46pub const TYPE_MEDIA_FW_FILE: u8 = 6;
47pub const TYPE_MEDIA_FW_VOL: u8 = 7;
48
49#[repr(C)]
50#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
51pub struct DevPathHeader {
52    pub devtype: u8,
53    pub subtype: u8,
54    pub length: u16,
55}
56
57impl DevPathHeader {
58    pub fn new(devtype: u8, subtype: u8, length: usize) -> Self {
59        DevPathHeader {
60            devtype,
61            subtype,
62            length: (size_of::<DevPathHeader>() + length) as u16,
63        }
64    }
65
66    pub fn new_hw_pci() -> Self {
67        DevPathHeader::new(TYPE_HW, TYPE_HW_PCI, size_of::<DevPathHwPci>())
68    }
69
70    pub fn new_acpi_dp() -> Self {
71        DevPathHeader::new(TYPE_ACPI, TYPE_ACPI_DP, size_of::<DevPathAcpiDp>())
72    }
73
74    pub fn new_acpi_adr(elems: usize) -> Self {
75        DevPathHeader::new(TYPE_ACPI, TYPE_ACPI_ADR, elems * size_of::<u32>())
76    }
77
78    pub fn new_msg_scsi() -> Self {
79        DevPathHeader::new(TYPE_MSG, TYPE_MSG_SCSI, size_of::<DevPathMsgScsi>())
80    }
81
82    pub fn new_msg_usb() -> Self {
83        DevPathHeader::new(TYPE_MSG, TYPE_MSG_USB, size_of::<DevPathMsgUsb>())
84    }
85
86    pub fn new_msg_vendor() -> Self {
87        DevPathHeader::new(TYPE_MSG, TYPE_MSG_VENDOR, size_of::<Guid>())
88    }
89
90    pub fn new_msg_mac() -> Self {
91        DevPathHeader::new(TYPE_MSG, TYPE_MSG_MAC, size_of::<DevPathMsgMac>())
92    }
93
94    pub fn new_msg_ipv4() -> Self {
95        DevPathHeader::new(TYPE_MSG, TYPE_MSG_IPV4, 23)
96    }
97
98    pub fn new_msg_ipv6() -> Self {
99        DevPathHeader::new(TYPE_MSG, TYPE_MSG_IPV6, 56)
100    }
101
102    pub fn new_msg_uart() -> Self {
103        DevPathHeader::new(TYPE_MSG, TYPE_MSG_UART, size_of::<DevPathMsgUart>())
104    }
105
106    pub fn new_msg_sata() -> Self {
107        DevPathHeader::new(TYPE_MSG, TYPE_MSG_SATA, size_of::<DevPathMsgSata>())
108    }
109
110    pub fn new_msg_usb_class() -> Self {
111        DevPathHeader::new(
112            TYPE_MSG,
113            TYPE_MSG_USB_CLASS,
114            size_of::<DevPathMsgUsbClass>(),
115        )
116    }
117
118    pub fn new_msg_uri(bytes: usize) -> Self {
119        DevPathHeader::new(TYPE_MSG, TYPE_MSG_URI, bytes)
120    }
121
122    pub fn new_media_hd() -> Self {
123        DevPathHeader::new(TYPE_MEDIA, TYPE_MEDIA_HD, 38)
124    }
125
126    pub fn new_media_vendor() -> Self {
127        DevPathHeader::new(TYPE_MEDIA, TYPE_MEDIA_VENDOR, size_of::<Guid>())
128    }
129
130    pub fn new_media_fw_file() -> Self {
131        DevPathHeader::new(TYPE_MEDIA, TYPE_MEDIA_FW_FILE, size_of::<Guid>())
132    }
133
134    pub fn new_media_fw_vol() -> Self {
135        DevPathHeader::new(TYPE_MEDIA, TYPE_MEDIA_FW_VOL, size_of::<Guid>())
136    }
137
138    pub fn new_media_filepath(bytes: usize) -> Self {
139        DevPathHeader::new(TYPE_MEDIA, TYPE_MEDIA_FILEPATH, bytes)
140    }
141
142    pub fn new_end(subtype: u8) -> Self {
143        DevPathHeader::new(TYPE_END, subtype, 0)
144    }
145}
146
147#[repr(C)]
148#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
149pub struct DevPathHwPci {
150    pub function: u8,
151    pub device: u8,
152}
153
154#[repr(C)]
155#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
156pub struct DevPathAcpiDp {
157    pub hid: u32,
158    pub uid: u32,
159}
160
161#[repr(C)]
162#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
163pub struct DevPathMsgScsi {
164    pub target: u16,
165    pub lun: u16,
166}
167
168#[repr(C)]
169#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
170pub struct DevPathMsgUsb {
171    pub port: u8,
172    pub interface: u8,
173}
174
175#[repr(C)]
176#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
177pub struct DevPathMsgMac {
178    pub mac: [u8; 32],
179    pub iftype: u8,
180}
181
182#[derive(Debug, PartialEq)]
183pub struct DevPathMsgIPv4 {
184    pub local_addr: Ipv4Addr,
185    pub remote_addr: Ipv4Addr,
186    pub local_port: u16,
187    pub remote_port: u16,
188    pub protocol: u16,
189    pub static_ip: bool,
190    pub gw_addr: Ipv4Addr,
191    pub net_mask: Ipv4Addr,
192}
193
194#[derive(Debug, PartialEq)]
195pub struct DevPathMsgIPv6 {
196    pub local_addr: Ipv6Addr,
197    pub remote_addr: Ipv6Addr,
198    pub local_port: u16,
199    pub remote_port: u16,
200    pub protocol: u16,
201    pub addr_origin: u8,
202    pub prefix_len: u8,
203    pub gw_addr: Ipv6Addr,
204}
205
206#[repr(C, packed)]
207#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
208pub struct DevPathMsgUart {
209    pub reserved: u32,
210    pub baudrate: u64,
211    pub databits: u8,
212    pub parity: u8,
213    pub stopbits: u8,
214}
215
216#[repr(C)]
217#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
218pub struct DevPathMsgSata {
219    pub port: u16,
220    pub mul_port: u16,
221    pub lun: u16,
222}
223
224#[repr(C, packed)]
225#[derive(Debug, PartialEq, FromBytes, IntoBytes, Immutable)]
226pub struct DevPathMsgUsbClass {
227    pub vendor: u16,
228    pub product: u16,
229    pub class: u8,
230    pub subclass: u8,
231    pub protocol: u8,
232}
233
234#[derive(Debug, PartialEq)]
235pub struct DevPathMediaHd {
236    pub nr: u32,
237    pub start: u64,
238    pub size: u64,
239    pub signature: Guid,
240    pub mbrtype: u8, // use enum ?
241    pub sigtype: u8, // use enum ?
242}
243
244#[derive(Debug, PartialEq)]
245pub enum DevPathNode {
246    HwPci(DevPathHwPci),
247
248    AcpiDp(DevPathAcpiDp),
249    AcpiAdr(Vec<u32>),
250
251    MsgScsi(DevPathMsgScsi),
252    MsgUsb(DevPathMsgUsb),
253    MsgVendor(Guid),
254    MsgMac(DevPathMsgMac),
255    MsgIPv4(DevPathMsgIPv4),
256    MsgIPv6(DevPathMsgIPv6),
257    MsgUart(DevPathMsgUart),
258    MsgSata(DevPathMsgSata),
259    MsgUsbClass(DevPathMsgUsbClass),
260    MsgUri(Option<String>),
261
262    MediaHd(DevPathMediaHd),
263    MediaVendor(Guid),
264    MediaFilePath(NoCaseString),
265    MediaFwFile(Guid),
266    MediaFwVol(Guid),
267
268    End {
269        subtype: u8,
270    },
271
272    // generic catch for unimplemented types
273    Other {
274        devtype: u8,
275        subtype: u8,
276        blob: Vec<u8>,
277    },
278}
279
280impl fmt::Display for DevPathNode {
281    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
282        match self {
283            DevPathNode::HwPci(pci) => {
284                write!(f, "Pci({:02x}.{:x})", pci.device, pci.function)
285            }
286
287            DevPathNode::AcpiDp(acpi) => match (acpi.hid, acpi.uid) {
288                (0xa0341d0, _) => {
289                    write!(f, "PciRoot()")
290                }
291                (h, u) => {
292                    write!(f, "Acpi(hid=0x{h:x},uid=0x{u:x})")
293                }
294            },
295
296            DevPathNode::AcpiAdr(adr) => {
297                let s: Vec<String> = adr.iter().map(|a| format!("0x{a:x}")).collect();
298                write!(f, "Acpi(adr={})", s.join(","))
299            }
300
301            DevPathNode::MsgScsi(scsi) => {
302                write!(f, "Scsi(target={},lun={})", scsi.target, scsi.lun)
303            }
304
305            DevPathNode::MsgUsb(usb) => {
306                write!(f, "Usb(port={},if={})", usb.port, usb.interface)
307            }
308
309            DevPathNode::MsgVendor(guid) => {
310                write!(f, "Vendor({guid})")
311            }
312
313            DevPathNode::MsgMac(_) => {
314                write!(f, "MAC()")
315            }
316
317            DevPathNode::MsgIPv4(ipv4) => {
318                if ipv4.static_ip {
319                    write!(f, "IPv4(static)")
320                } else {
321                    write!(f, "IPv4(dhcp)")
322                }
323            }
324
325            DevPathNode::MsgIPv6(ipv6) => match ipv6.addr_origin {
326                0 => write!(f, "IPv6(static)"),
327                1 => write!(f, "IPv6(stateless)"),
328                2 => write!(f, "IPv6(stateful)"),
329                _ => write!(f, "IPv6(invalid)"),
330            },
331
332            DevPathNode::MsgUart(_) => {
333                write!(f, "UART()")
334            }
335
336            DevPathNode::MsgSata(sata) => {
337                write!(f, "SATA(port={})", sata.port)
338            }
339
340            DevPathNode::MsgUsbClass(_) => {
341                write!(f, "UsbClass()")
342            }
343            DevPathNode::MsgUri(uri) => match &uri {
344                None => write!(f, "URI()"),
345                Some(u) => write!(f, "URI(\"{u}\")"),
346            },
347
348            DevPathNode::MediaHd(part) => {
349                write!(f, "HD({})", part.nr)
350            }
351
352            DevPathNode::MediaVendor(guid) => {
353                write!(f, "Vendor({guid})")
354            }
355
356            DevPathNode::MediaFilePath(path) => {
357                write!(f, "File(\"{path}\")")
358            }
359
360            DevPathNode::MediaFwFile(filename) => {
361                write!(f, "FwFile({filename})")
362            }
363
364            DevPathNode::MediaFwVol(volume) => {
365                write!(f, "FwVol({volume})")
366            }
367
368            DevPathNode::End { subtype: _ } => {
369                write!(f, "End()")
370            }
371
372            DevPathNode::Other {
373                devtype,
374                subtype,
375                blob,
376            } => {
377                write!(
378                    f,
379                    "Other(type=0x{:02x}.0x{:02x},len={})",
380                    devtype,
381                    subtype,
382                    blob.len()
383                )
384            }
385        }
386    }
387}
388
389#[derive(Debug, PartialEq)]
390pub struct DevPath {
391    pub nodes: Vec<DevPathNode>,
392}
393
394impl DevPath {
395    pub fn new() -> DevPath {
396        let nodes = Vec::new();
397        DevPath { nodes }
398    }
399    pub fn push(&mut self, node: DevPathNode) {
400        self.nodes.push(node);
401    }
402}
403
404impl Default for DevPath {
405    fn default() -> Self {
406        Self::new()
407    }
408}
409
410impl fmt::Display for DevPath {
411    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
412        let s: Vec<String> = self.nodes.iter().map(|a| format!("{a}")).collect();
413        write!(f, "{}", s.join("/"))
414    }
415}
416
417#[test]
418fn test_struct_sizes() {
419    use core::mem::size_of;
420
421    assert_eq!(size_of::<Guid>(), 16);
422
423    assert_eq!(size_of::<DevPathHwPci>(), 2);
424    assert_eq!(size_of::<DevPathAcpiDp>(), 8);
425
426    assert_eq!(size_of::<DevPathMsgScsi>(), 4);
427    assert_eq!(size_of::<DevPathMsgUsb>(), 2);
428    assert_eq!(size_of::<DevPathMsgMac>(), 33);
429    assert_eq!(size_of::<DevPathMsgSata>(), 6);
430    assert_eq!(size_of::<DevPathMsgUart>(), 15);
431    assert_eq!(size_of::<DevPathMsgUsbClass>(), 7);
432}