1extern 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, pub sigtype: u8, }
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 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}