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 | |
76 | pub mod build; |
77 | pub mod text; |
78 | |
79 | mod device_path_gen; |
80 | pub use device_path_gen::{ |
81 | acpi, bios_boot_spec, end, hardware, media, messaging, DevicePathNodeEnum, |
82 | }; |
83 | |
84 | use crate::proto::{unsafe_protocol , ProtocolPointer}; |
85 | use core::ffi::c_void; |
86 | use core::fmt::{self, Debug, Formatter}; |
87 | use core::marker::{PhantomData, PhantomPinned}; |
88 | use core::mem; |
89 | use 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)] |
96 | pub 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)] |
106 | pub 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)] |
125 | pub struct DevicePathNode { |
126 | header: DevicePathHeader, |
127 | data: [u8], |
128 | } |
129 | |
130 | impl 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 | |
190 | impl 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 | |
199 | impl 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)] |
216 | pub struct DevicePathInstance { |
217 | data: [u8], |
218 | } |
219 | |
220 | impl 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 | |
235 | impl 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 | |
243 | impl 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)] |
261 | pub struct DevicePath { |
262 | data: [u8], |
263 | } |
264 | |
265 | impl 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 | |
275 | impl 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 | |
335 | impl 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 | |
343 | impl 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`]. |
352 | pub struct DevicePathInstanceIterator<'a> { |
353 | remaining_path: Option<&'a DevicePath>, |
354 | } |
355 | |
356 | impl<'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 | |
400 | enum 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`]. |
410 | pub struct DevicePathNodeIterator<'a> { |
411 | nodes: &'a [u8], |
412 | stop_condition: StopCondition, |
413 | } |
414 | |
415 | impl<'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 | |
447 | newtype_enum! { |
448 | /// Type identifier for a DevicePath |
449 | pub 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)] |
486 | pub struct DeviceSubType(pub u8); |
487 | |
488 | impl 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)] |
607 | pub 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)] |
616 | mod 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 | |