1//! Device Path protocol
2//!
3//! A UEFI device path is a very flexible structure for encoding a
4//! programmatic path such as a hard drive or console.
5//!
6//! A device path is made up of a packed list of variable-length nodes of
7//! various types. The entire device path is terminated with an
8//! [`END_ENTIRE`] node. A device path _may_ contain multiple device-path
9//! instances separated by [`END_INSTANCE`] nodes, but typical paths contain
10//! only a single instance (in which case no `END_INSTANCE` node is needed).
11//!
12//! Example of what a device path containing two instances (each comprised of
13//! three nodes) might look like:
14//!
15//! ```text
16//! ┌──────┬─────┬──────────────╥───────┬──────────┬────────────┐
17//! │ ACPI │ PCI │ END_INSTANCE ║ CDROM │ FILEPATH │ END_ENTIRE │
18//! └──────┴─────┴──────────────╨───────┴──────────┴────────────┘
19//! ↑ ↑ ↑
20//! ├─── DevicePathInstance ────╨────── DevicePathInstance ─────┤
21//! │ │
22//! └─────────────────── Entire DevicePath ─────────────────────┘
23//! ```
24//!
25//! # Types
26//!
27//! To represent device paths, this module provides several types:
28//!
29//! * [`DevicePath`] is the root type that represents a full device
30//! path, containing one or more device path instance. It ends with an
31//! [`END_ENTIRE`] node. It implements [`Protocol`] (corresponding to
32//! `EFI_DEVICE_PATH_PROTOCOL`).
33//!
34//! * [`DevicePathInstance`] represents a single path instance within a
35//! device path. It ends with either an [`END_INSTANCE`] or [`END_ENTIRE`]
36//! node.
37//!
38//! * [`DevicePathNode`] represents a single node within a path. The
39//! node's [`device_type`] and [`sub_type`] must be examined to
40//! determine what type of data it contains.
41//!
42//! Specific node types have their own structures in these submodules:
43//! * [`acpi`]
44//! * [`bios_boot_spec`]
45//! * [`end`]
46//! * [`hardware`]
47//! * [`media`]
48//! * [`messaging`]
49//!
50//! * [`DevicePathNodeEnum`] contains variants for references to each
51//! type of node. Call [`DevicePathNode::as_enum`] to convert from a
52//! [`DevicePathNode`] reference to a `DevicePathNodeEnum`.
53//!
54//! * [`DevicePathHeader`] is a header present at the start of every
55//! node. It describes the type of node as well as the node's size.
56//!
57//! * [`FfiDevicePath`] is an opaque type used whenever a device path
58//! pointer is passed to or from external UEFI interfaces (i.e. where
59//! the UEFI spec uses `const* EFI_DEVICE_PATH_PROTOCOL`, `*const
60//! FfiDevicePath` should be used in the Rust definition). Many of the
61//! other types in this module are DSTs, so pointers to the type are
62//! "fat" and not suitable for FFI.
63//!
64//! All of these types use a packed layout and may appear on any byte
65//! boundary.
66//!
67//! Note: the API provided by this module is currently mostly limited to
68//! reading existing device paths rather than constructing new ones.
69//!
70//! [`END_ENTIRE`]: DeviceSubType::END_ENTIRE
71//! [`END_INSTANCE`]: DeviceSubType::END_INSTANCE
72//! [`Protocol`]: crate::proto::Protocol
73//! [`device_type`]: DevicePathNode::device_type
74//! [`sub_type`]: DevicePathNode::sub_type
75
76pub mod build;
77pub mod text;
78
79mod device_path_gen;
80pub use device_path_gen::{
81 acpi, bios_boot_spec, end, hardware, media, messaging, DevicePathNodeEnum,
82};
83
84use crate::proto::{unsafe_protocol, ProtocolPointer};
85use core::ffi::c_void;
86use core::fmt::{self, Debug, Formatter};
87use core::marker::{PhantomData, PhantomPinned};
88use core::mem;
89use ptr_meta::Pointee;
90
91/// Opaque type that should be used to represent a pointer to a
92/// [`DevicePath`] or [`DevicePathNode`] in foreign function interfaces. This
93/// type produces a thin pointer, unlike [`DevicePath`] and
94/// [`DevicePathNode`].
95#[repr(C, packed)]
96pub struct FfiDevicePath {
97 // This representation is recommended by the nomicon:
98 // https://doc.rust-lang.org/stable/nomicon/ffi.html#representing-opaque-structs
99 _data: [u8; 0],
100 _marker: PhantomData<(*mut u8, PhantomPinned)>,
101}
102
103/// Header that appears at the start of every [`DevicePathNode`].
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105#[repr(C, packed)]
106pub struct DevicePathHeader {
107 /// Type of device
108 pub device_type: DeviceType,
109 /// Sub type of device
110 pub sub_type: DeviceSubType,
111 /// Size (in bytes) of the [`DevicePathNode`], including this header.
112 pub length: u16,
113}
114
115/// A single node within a [`DevicePath`].
116///
117/// Each node starts with a [`DevicePathHeader`]. The rest of the data
118/// in the node depends on the type of node.
119///
120/// See the [module-level documentation] for more details.
121///
122/// [module-level documentation]: crate::proto::device_path
123#[derive(Eq, Pointee)]
124#[repr(C, packed)]
125pub struct DevicePathNode {
126 header: DevicePathHeader,
127 data: [u8],
128}
129
130impl DevicePathNode {
131 /// Create a [`DevicePathNode`] reference from an opaque pointer.
132 ///
133 /// # Safety
134 ///
135 /// The input pointer must point to valid data. That data must
136 /// remain valid for the lifetime `'a`, and cannot be mutated during
137 /// that lifetime.
138 #[must_use]
139 pub unsafe fn from_ffi_ptr<'a>(ptr: *const FfiDevicePath) -> &'a DevicePathNode {
140 let header = *ptr.cast::<DevicePathHeader>();
141
142 let data_len = usize::from(header.length) - mem::size_of::<DevicePathHeader>();
143 &*ptr_meta::from_raw_parts(ptr.cast(), data_len)
144 }
145
146 /// Cast to a [`FfiDevicePath`] pointer.
147 #[must_use]
148 pub const fn as_ffi_ptr(&self) -> *const FfiDevicePath {
149 let ptr: *const Self = self;
150 ptr.cast::<FfiDevicePath>()
151 }
152
153 /// Type of device
154 #[must_use]
155 pub const fn device_type(&self) -> DeviceType {
156 self.header.device_type
157 }
158
159 /// Sub type of device
160 #[must_use]
161 pub const fn sub_type(&self) -> DeviceSubType {
162 self.header.sub_type
163 }
164
165 /// Tuple of the node's type and subtype.
166 #[must_use]
167 pub const fn full_type(&self) -> (DeviceType, DeviceSubType) {
168 (self.header.device_type, self.header.sub_type)
169 }
170
171 /// Size (in bytes) of the full [`DevicePathNode`], including the header.
172 #[must_use]
173 pub const fn length(&self) -> u16 {
174 self.header.length
175 }
176
177 /// True if this node ends an entire [`DevicePath`].
178 #[must_use]
179 pub fn is_end_entire(&self) -> bool {
180 self.full_type() == (DeviceType::END, DeviceSubType::END_ENTIRE)
181 }
182
183 /// Convert from a generic [`DevicePathNode`] reference to an enum
184 /// of more specific node types.
185 pub fn as_enum(&self) -> Result<DevicePathNodeEnum, NodeConversionError> {
186 DevicePathNodeEnum::try_from(self)
187 }
188}
189
190impl Debug for DevicePathNode {
191 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
192 f&mut DebugStruct<'_, '_>.debug_struct("DevicePathNode")
193 .field("header", &self.header)
194 .field(name:"data", &&self.data)
195 .finish()
196 }
197}
198
199impl PartialEq for DevicePathNode {
200 fn eq(&self, other: &Self) -> bool {
201 self.header == other.header && self.data == other.data
202 }
203}
204
205/// A single device path instance that ends with either an [`END_INSTANCE`]
206/// or [`END_ENTIRE`] node. Use [`DevicePath::instance_iter`] to get the
207/// path instances in a [`DevicePath`].
208///
209/// See the [module-level documentation] for more details.
210///
211/// [`END_ENTIRE`]: DeviceSubType::END_ENTIRE
212/// [`END_INSTANCE`]: DeviceSubType::END_INSTANCE
213/// [module-level documentation]: crate::proto::device_path
214#[repr(C, packed)]
215#[derive(Eq, Pointee)]
216pub struct DevicePathInstance {
217 data: [u8],
218}
219
220impl DevicePathInstance {
221 /// Get an iterator over the [`DevicePathNodes`] in this
222 /// instance. Iteration ends when any [`DeviceType::END`] node is
223 /// reached.
224 ///
225 /// [`DevicePathNodes`]: DevicePathNode
226 #[must_use]
227 pub const fn node_iter(&self) -> DevicePathNodeIterator {
228 DevicePathNodeIterator {
229 nodes: &self.data,
230 stop_condition: StopCondition::AnyEndNode,
231 }
232 }
233}
234
235impl Debug for DevicePathInstance {
236 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
237 f&mut DebugStruct<'_, '_>.debug_struct("DevicePathInstance")
238 .field(name:"data", &&self.data)
239 .finish()
240 }
241}
242
243impl PartialEq for DevicePathInstance {
244 fn eq(&self, other: &Self) -> bool {
245 self.data == other.data
246 }
247}
248
249/// Device path protocol.
250///
251/// A device path contains one or more device path instances made of up
252/// variable-length nodes. It ends with an [`END_ENTIRE`] node.
253///
254/// See the [module-level documentation] for more details.
255///
256/// [module-level documentation]: crate::proto::device_path
257/// [`END_ENTIRE`]: DeviceSubType::END_ENTIRE
258#[repr(C, packed)]
259#[unsafe_protocol("09576e91-6d3f-11d2-8e39-00a0c969723b")]
260#[derive(Eq, Pointee)]
261pub struct DevicePath {
262 data: [u8],
263}
264
265impl ProtocolPointer for DevicePath {
266 unsafe fn ptr_from_ffi(ptr: *const c_void) -> *const Self {
267 ptr_meta::from_raw_parts(data_address:ptr.cast(), Self::size_in_bytes_from_ptr(ptr))
268 }
269
270 unsafe fn mut_ptr_from_ffi(ptr: *mut c_void) -> *mut Self {
271 ptr_meta::from_raw_parts_mut(data_address:ptr.cast(), Self::size_in_bytes_from_ptr(ptr))
272 }
273}
274
275impl DevicePath {
276 /// Calculate the size in bytes of the entire `DevicePath` starting
277 /// at `ptr`. This adds up each node's length, including the
278 /// end-entire node.
279 unsafe fn size_in_bytes_from_ptr(ptr: *const c_void) -> usize {
280 let mut ptr = ptr.cast::<u8>();
281 let mut total_size_in_bytes: usize = 0;
282 loop {
283 let node = DevicePathNode::from_ffi_ptr(ptr.cast::<FfiDevicePath>());
284 let node_size_in_bytes = usize::from(node.length());
285 total_size_in_bytes += node_size_in_bytes;
286 if node.is_end_entire() {
287 break;
288 }
289 ptr = ptr.add(node_size_in_bytes);
290 }
291
292 total_size_in_bytes
293 }
294
295 /// Create a [`DevicePath`] reference from an opaque pointer.
296 ///
297 /// # Safety
298 ///
299 /// The input pointer must point to valid data. That data must
300 /// remain valid for the lifetime `'a`, and cannot be mutated during
301 /// that lifetime.
302 #[must_use]
303 pub unsafe fn from_ffi_ptr<'a>(ptr: *const FfiDevicePath) -> &'a DevicePath {
304 &*Self::ptr_from_ffi(ptr.cast::<c_void>())
305 }
306
307 /// Cast to a [`FfiDevicePath`] pointer.
308 #[must_use]
309 pub const fn as_ffi_ptr(&self) -> *const FfiDevicePath {
310 let p = self as *const Self;
311 p.cast()
312 }
313
314 /// Get an iterator over the [`DevicePathInstance`]s in this path.
315 #[must_use]
316 pub const fn instance_iter(&self) -> DevicePathInstanceIterator {
317 DevicePathInstanceIterator {
318 remaining_path: Some(self),
319 }
320 }
321
322 /// Get an iterator over the [`DevicePathNode`]s starting at
323 /// `self`. Iteration ends when a path is reached where
324 /// [`is_end_entire`][DevicePathNode::is_end_entire] is true. That ending
325 /// path is not returned by the iterator.
326 #[must_use]
327 pub const fn node_iter(&self) -> DevicePathNodeIterator {
328 DevicePathNodeIterator {
329 nodes: &self.data,
330 stop_condition: StopCondition::EndEntireNode,
331 }
332 }
333}
334
335impl Debug for DevicePath {
336 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
337 f&mut DebugStruct<'_, '_>.debug_struct("DevicePath")
338 .field(name:"data", &&self.data)
339 .finish()
340 }
341}
342
343impl PartialEq for DevicePath {
344 fn eq(&self, other: &Self) -> bool {
345 self.data == other.data
346 }
347}
348
349/// Iterator over the [`DevicePathInstance`]s in a [`DevicePath`].
350///
351/// This struct is returned by [`DevicePath::instance_iter`].
352pub struct DevicePathInstanceIterator<'a> {
353 remaining_path: Option<&'a DevicePath>,
354}
355
356impl<'a> Iterator for DevicePathInstanceIterator<'a> {
357 type Item = &'a DevicePathInstance;
358
359 fn next(&mut self) -> Option<Self::Item> {
360 let remaining_path = self.remaining_path?;
361
362 let mut instance_size: usize = 0;
363
364 // Find the end of the instance, which can be either kind of end
365 // node (end-instance or end-entire). Count the number of bytes
366 // up to and including that end node.
367 let node_iter = DevicePathNodeIterator {
368 nodes: &remaining_path.data,
369 stop_condition: StopCondition::NoMoreNodes,
370 };
371 for node in node_iter {
372 instance_size += usize::from(node.length());
373 if node.device_type() == DeviceType::END {
374 break;
375 }
376 }
377
378 let (head, rest) = remaining_path.data.split_at(instance_size);
379
380 if rest.is_empty() {
381 self.remaining_path = None;
382 } else {
383 self.remaining_path = unsafe {
384 Some(&*ptr_meta::from_raw_parts(
385 rest.as_ptr().cast::<()>(),
386 rest.len(),
387 ))
388 };
389 }
390
391 unsafe {
392 Some(&*ptr_meta::from_raw_parts(
393 head.as_ptr().cast::<()>(),
394 head.len(),
395 ))
396 }
397 }
398}
399
400enum StopCondition {
401 AnyEndNode,
402 EndEntireNode,
403 NoMoreNodes,
404}
405
406/// Iterator over [`DevicePathNode`]s.
407///
408/// This struct is returned by [`DevicePath::node_iter`] and
409/// [`DevicePathInstance::node_iter`].
410pub struct DevicePathNodeIterator<'a> {
411 nodes: &'a [u8],
412 stop_condition: StopCondition,
413}
414
415impl<'a> Iterator for DevicePathNodeIterator<'a> {
416 type Item = &'a DevicePathNode;
417
418 fn next(&mut self) -> Option<Self::Item> {
419 if self.nodes.is_empty() {
420 return None;
421 }
422
423 let node =
424 unsafe { DevicePathNode::from_ffi_ptr(self.nodes.as_ptr().cast::<FfiDevicePath>()) };
425
426 // Check if an early stop condition has been reached.
427 let stop = match self.stop_condition {
428 StopCondition::AnyEndNode => node.device_type() == DeviceType::END,
429 StopCondition::EndEntireNode => node.is_end_entire(),
430 StopCondition::NoMoreNodes => false,
431 };
432
433 if stop {
434 // Clear the remaining node data so that future calls to
435 // next() immediately return `None`.
436 self.nodes = &[];
437 None
438 } else {
439 // Advance to next node.
440 let node_size = usize::from(node.length());
441 self.nodes = &self.nodes[node_size..];
442 Some(node)
443 }
444 }
445}
446
447newtype_enum! {
448/// Type identifier for a DevicePath
449pub enum DeviceType: u8 => {
450 /// Hardware Device Path.
451 ///
452 /// This Device Path defines how a device is attached to the resource domain of a system, where resource domain is
453 /// simply the shared memory, memory mapped I/ O, and I/O space of the system.
454 HARDWARE = 0x01,
455 /// ACPI Device Path.
456 ///
457 /// This Device Path is used to describe devices whose enumeration is not described in an industry-standard fashion.
458 /// These devices must be described using ACPI AML in the ACPI namespace; this Device Path is a linkage to the ACPI
459 /// namespace.
460 ACPI = 0x02,
461 /// Messaging Device Path.
462 ///
463 /// This Device Path is used to describe the connection of devices outside the resource domain of the system. This
464 /// Device Path can describe physical messaging information such as a SCSI ID, or abstract information such as
465 /// networking protocol IP addresses.
466 MESSAGING = 0x03,
467 /// Media Device Path.
468 ///
469 /// This Device Path is used to describe the portion of a medium that is being abstracted by a boot service.
470 /// For example, a Media Device Path could define which partition on a hard drive was being used.
471 MEDIA = 0x04,
472 /// BIOS Boot Specification Device Path.
473 ///
474 /// This Device Path is used to point to boot legacy operating systems; it is based on the BIOS Boot Specification
475 /// Version 1.01.
476 BIOS_BOOT_SPEC = 0x05,
477 /// End of Hardware Device Path.
478 ///
479 /// Depending on the Sub-Type, this Device Path node is used to indicate the end of the Device Path instance or
480 /// Device Path structure.
481 END = 0x7F,
482}}
483
484/// Sub-type identifier for a DevicePath
485#[derive(Clone, Copy, Debug, Eq, PartialEq)]
486pub struct DeviceSubType(pub u8);
487
488impl DeviceSubType {
489 /// PCI Device Path.
490 pub const HARDWARE_PCI: DeviceSubType = DeviceSubType(1);
491 /// PCCARD Device Path.
492 pub const HARDWARE_PCCARD: DeviceSubType = DeviceSubType(2);
493 /// Memory-mapped Device Path.
494 pub const HARDWARE_MEMORY_MAPPED: DeviceSubType = DeviceSubType(3);
495 /// Vendor-Defined Device Path.
496 pub const HARDWARE_VENDOR: DeviceSubType = DeviceSubType(4);
497 /// Controller Device Path.
498 pub const HARDWARE_CONTROLLER: DeviceSubType = DeviceSubType(5);
499 /// BMC Device Path.
500 pub const HARDWARE_BMC: DeviceSubType = DeviceSubType(6);
501
502 /// ACPI Device Path.
503 pub const ACPI: DeviceSubType = DeviceSubType(1);
504 /// Expanded ACPI Device Path.
505 pub const ACPI_EXPANDED: DeviceSubType = DeviceSubType(2);
506 /// ACPI _ADR Device Path.
507 pub const ACPI_ADR: DeviceSubType = DeviceSubType(3);
508 /// NVDIMM Device Path.
509 pub const ACPI_NVDIMM: DeviceSubType = DeviceSubType(4);
510
511 /// ATAPI Device Path.
512 pub const MESSAGING_ATAPI: DeviceSubType = DeviceSubType(1);
513 /// SCSI Device Path.
514 pub const MESSAGING_SCSI: DeviceSubType = DeviceSubType(2);
515 /// Fibre Channel Device Path.
516 pub const MESSAGING_FIBRE_CHANNEL: DeviceSubType = DeviceSubType(3);
517 /// 1394 Device Path.
518 pub const MESSAGING_1394: DeviceSubType = DeviceSubType(4);
519 /// USB Device Path.
520 pub const MESSAGING_USB: DeviceSubType = DeviceSubType(5);
521 /// I2O Device Path.
522 pub const MESSAGING_I2O: DeviceSubType = DeviceSubType(6);
523 /// Infiniband Device Path.
524 pub const MESSAGING_INFINIBAND: DeviceSubType = DeviceSubType(9);
525 /// Vendor-Defined Device Path.
526 pub const MESSAGING_VENDOR: DeviceSubType = DeviceSubType(10);
527 /// MAC Address Device Path.
528 pub const MESSAGING_MAC_ADDRESS: DeviceSubType = DeviceSubType(11);
529 /// IPV4 Device Path.
530 pub const MESSAGING_IPV4: DeviceSubType = DeviceSubType(12);
531 /// IPV6 Device Path.
532 pub const MESSAGING_IPV6: DeviceSubType = DeviceSubType(13);
533 /// UART Device Path.
534 pub const MESSAGING_UART: DeviceSubType = DeviceSubType(14);
535 /// USB Class Device Path.
536 pub const MESSAGING_USB_CLASS: DeviceSubType = DeviceSubType(15);
537 /// USB WWID Device Path.
538 pub const MESSAGING_USB_WWID: DeviceSubType = DeviceSubType(16);
539 /// Device Logical Unit.
540 pub const MESSAGING_DEVICE_LOGICAL_UNIT: DeviceSubType = DeviceSubType(17);
541 /// SATA Device Path.
542 pub const MESSAGING_SATA: DeviceSubType = DeviceSubType(18);
543 /// iSCSI Device Path node (base information).
544 pub const MESSAGING_ISCSI: DeviceSubType = DeviceSubType(19);
545 /// VLAN Device Path node.
546 pub const MESSAGING_VLAN: DeviceSubType = DeviceSubType(20);
547 /// Fibre Channel Ex Device Path.
548 pub const MESSAGING_FIBRE_CHANNEL_EX: DeviceSubType = DeviceSubType(21);
549 /// Serial Attached SCSI (SAS) Ex Device Path.
550 pub const MESSAGING_SCSI_SAS_EX: DeviceSubType = DeviceSubType(22);
551 /// NVM Express Namespace Device Path.
552 pub const MESSAGING_NVME_NAMESPACE: DeviceSubType = DeviceSubType(23);
553 /// Uniform Resource Identifiers (URI) Device Path.
554 pub const MESSAGING_URI: DeviceSubType = DeviceSubType(24);
555 /// UFS Device Path.
556 pub const MESSAGING_UFS: DeviceSubType = DeviceSubType(25);
557 /// SD (Secure Digital) Device Path.
558 pub const MESSAGING_SD: DeviceSubType = DeviceSubType(26);
559 /// Bluetooth Device Path.
560 pub const MESSAGING_BLUETOOTH: DeviceSubType = DeviceSubType(27);
561 /// Wi-Fi Device Path.
562 pub const MESSAGING_WIFI: DeviceSubType = DeviceSubType(28);
563 /// eMMC (Embedded Multi-Media Card) Device Path.
564 pub const MESSAGING_EMMC: DeviceSubType = DeviceSubType(29);
565 /// BluetoothLE Device Path.
566 pub const MESSAGING_BLUETOOTH_LE: DeviceSubType = DeviceSubType(30);
567 /// DNS Device Path.
568 pub const MESSAGING_DNS: DeviceSubType = DeviceSubType(31);
569 /// NVDIMM Namespace Device Path.
570 pub const MESSAGING_NVDIMM_NAMESPACE: DeviceSubType = DeviceSubType(32);
571 /// REST Service Device Path.
572 pub const MESSAGING_REST_SERVICE: DeviceSubType = DeviceSubType(33);
573 /// NVME over Fabric (NVMe-oF) Namespace Device Path.
574 pub const MESSAGING_NVME_OF_NAMESPACE: DeviceSubType = DeviceSubType(34);
575
576 /// Hard Drive Media Device Path.
577 pub const MEDIA_HARD_DRIVE: DeviceSubType = DeviceSubType(1);
578 /// CD-ROM Media Device Path.
579 pub const MEDIA_CD_ROM: DeviceSubType = DeviceSubType(2);
580 /// Vendor-Defined Media Device Path.
581 pub const MEDIA_VENDOR: DeviceSubType = DeviceSubType(3);
582 /// File Path Media Device Path.
583 pub const MEDIA_FILE_PATH: DeviceSubType = DeviceSubType(4);
584 /// Media Protocol Device Path.
585 pub const MEDIA_PROTOCOL: DeviceSubType = DeviceSubType(5);
586 /// PIWG Firmware File.
587 pub const MEDIA_PIWG_FIRMWARE_FILE: DeviceSubType = DeviceSubType(6);
588 /// PIWG Firmware Volume.
589 pub const MEDIA_PIWG_FIRMWARE_VOLUME: DeviceSubType = DeviceSubType(7);
590 /// Relative Offset Range.
591 pub const MEDIA_RELATIVE_OFFSET_RANGE: DeviceSubType = DeviceSubType(8);
592 /// RAM Disk Device Path.
593 pub const MEDIA_RAM_DISK: DeviceSubType = DeviceSubType(9);
594
595 /// BIOS Boot Specification Device Path.
596 pub const BIOS_BOOT_SPECIFICATION: DeviceSubType = DeviceSubType(1);
597
598 /// End this instance of a Device Path and start a new one.
599 pub const END_INSTANCE: DeviceSubType = DeviceSubType(0x01);
600 /// End entire Device Path.
601 pub const END_ENTIRE: DeviceSubType = DeviceSubType(0xff);
602}
603
604/// Error returned when converting from a [`DevicePathNode`] to a more
605/// specific node type.
606#[derive(Clone, Copy, Debug, Eq, PartialEq)]
607pub enum NodeConversionError {
608 /// The length of the node data is not valid for its type.
609 InvalidLength,
610
611 /// The node type is not currently supported.
612 UnsupportedType,
613}
614
615#[cfg(test)]
616mod tests {
617 use super::*;
618 use alloc::vec::Vec;
619
620 /// Create a node to `path` from raw data.
621 fn add_node(path: &mut Vec<u8>, device_type: u8, sub_type: u8, node_data: &[u8]) {
622 path.push(device_type);
623 path.push(sub_type);
624 path.extend(
625 u16::try_from(mem::size_of::<DevicePathHeader>() + node_data.len())
626 .unwrap()
627 .to_le_bytes(),
628 );
629 path.extend(node_data);
630 }
631
632 /// Create a test device path list as raw bytes.
633 fn create_raw_device_path() -> Vec<u8> {
634 let mut raw_data = Vec::new();
635
636 // First path instance.
637 add_node(&mut raw_data, 0xa0, 0xb0, &[10, 11]);
638 add_node(&mut raw_data, 0xa1, 0xb1, &[20, 21, 22, 23]);
639 add_node(
640 &mut raw_data,
641 DeviceType::END.0,
642 DeviceSubType::END_INSTANCE.0,
643 &[],
644 );
645 // Second path instance.
646 add_node(&mut raw_data, 0xa2, 0xb2, &[30, 31]);
647 add_node(&mut raw_data, 0xa3, 0xb3, &[40, 41, 42, 43]);
648 add_node(
649 &mut raw_data,
650 DeviceType::END.0,
651 DeviceSubType::END_ENTIRE.0,
652 &[],
653 );
654
655 raw_data
656 }
657
658 /// Check that `node` has the expected content.
659 fn check_node(node: &DevicePathNode, device_type: u8, sub_type: u8, node_data: &[u8]) {
660 assert_eq!(node.device_type().0, device_type);
661 assert_eq!(node.sub_type().0, sub_type);
662 assert_eq!(
663 node.length(),
664 u16::try_from(mem::size_of::<DevicePathHeader>() + node_data.len()).unwrap()
665 );
666 assert_eq!(&node.data, node_data);
667 }
668
669 #[test]
670 fn test_device_path_nodes() {
671 let raw_data = create_raw_device_path();
672 let dp = unsafe { DevicePath::from_ffi_ptr(raw_data.as_ptr().cast()) };
673
674 // Check that the size is the sum of the nodes' lengths.
675 assert_eq!(mem::size_of_val(dp), 6 + 8 + 4 + 6 + 8 + 4);
676
677 // Check the list's node iter.
678 let nodes: Vec<_> = dp.node_iter().collect();
679 check_node(nodes[0], 0xa0, 0xb0, &[10, 11]);
680 check_node(nodes[1], 0xa1, 0xb1, &[20, 21, 22, 23]);
681 check_node(
682 nodes[2],
683 DeviceType::END.0,
684 DeviceSubType::END_INSTANCE.0,
685 &[],
686 );
687 check_node(nodes[3], 0xa2, 0xb2, &[30, 31]);
688 check_node(nodes[4], 0xa3, 0xb3, &[40, 41, 42, 43]);
689 // The end-entire node is not returned by the iterator.
690 assert_eq!(nodes.len(), 5);
691 }
692
693 #[test]
694 fn test_device_path_instances() {
695 let raw_data = create_raw_device_path();
696 let dp = unsafe { DevicePath::from_ffi_ptr(raw_data.as_ptr().cast()) };
697
698 // Check the list's instance iter.
699 let mut iter = dp.instance_iter();
700 let mut instance = iter.next().unwrap();
701 assert_eq!(mem::size_of_val(instance), 6 + 8 + 4);
702
703 // Check the first instance's node iter.
704 let nodes: Vec<_> = instance.node_iter().collect();
705 check_node(nodes[0], 0xa0, 0xb0, &[10, 11]);
706 check_node(nodes[1], 0xa1, 0xb1, &[20, 21, 22, 23]);
707 // The end node is not returned by the iterator.
708 assert_eq!(nodes.len(), 2);
709
710 // Check second instance.
711 instance = iter.next().unwrap();
712 assert_eq!(mem::size_of_val(instance), 6 + 8 + 4);
713
714 let nodes: Vec<_> = instance.node_iter().collect();
715 check_node(nodes[0], 0xa2, 0xb2, &[30, 31]);
716 check_node(nodes[1], 0xa3, 0xb3, &[40, 41, 42, 43]);
717 // The end node is not returned by the iterator.
718 assert_eq!(nodes.len(), 2);
719
720 // Only two instances.
721 assert!(iter.next().is_none());
722 }
723}
724