Skip to content

Commit 798797e

Browse files
committed
uefi: Add TryFrom u8 slice to DevicePath
Adds the capability to convert a byte slice to a DevicePath. This allows for users to easily get a DevicePath from the data returned by UEFI variables, etc.
1 parent ed28e51 commit 798797e

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

uefi/CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
- Added `table::{set_system_table, system_table_boot, system_table_runtime}`.
1212
This provides an initial API for global tables that do not require passing
1313
around a reference.
14-
- Added `TryFrom<&[u8]>` for `DevicePathHeader` and `DevicePathNode`.
14+
- Added `TryFrom<&[u8]>` for `DevicePathHeader`, `DevicePathNode` and `DevicePath`.
1515
- Added `ByteConversionError`.
1616

1717
## Changed

uefi/src/proto/device_path/mod.rs

+62
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,35 @@ impl DevicePath {
397397
total_size_in_bytes
398398
}
399399

400+
/// Calculate the size in bytes of the entire `DevicePath` starting
401+
/// at `bytes`. This adds up each node's length, including the
402+
/// end-entire node.
403+
///
404+
/// # Errors
405+
///
406+
/// The [`ByteConversionError::InvalidLength`] error will be returned
407+
/// when the length of the given bytes slice cannot contain the full
408+
/// [`DevicePath`] represented by the slice.
409+
fn size_in_bytes_from_slice(mut bytes: &[u8]) -> Result<usize, ByteConversionError> {
410+
let max_size_in_bytes = bytes.len();
411+
let mut total_size_in_bytes: usize = 0;
412+
loop {
413+
let node = <&DevicePathNode>::try_from(bytes)?;
414+
let node_size_in_bytes = usize::from(node.length());
415+
total_size_in_bytes += node_size_in_bytes;
416+
// Length of last processed node extends past the bytes slice.
417+
if total_size_in_bytes > max_size_in_bytes {
418+
return Err(ByteConversionError::InvalidLength);
419+
}
420+
if node.is_end_entire() {
421+
break;
422+
}
423+
bytes = &bytes[node_size_in_bytes..];
424+
}
425+
426+
Ok(total_size_in_bytes)
427+
}
428+
400429
/// Create a [`DevicePath`] reference from an opaque pointer.
401430
///
402431
/// # Safety
@@ -488,6 +517,15 @@ impl PartialEq for DevicePath {
488517
}
489518
}
490519

520+
impl<'a> TryFrom<&[u8]> for &'a DevicePath {
521+
type Error = ByteConversionError;
522+
523+
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
524+
let len = DevicePath::size_in_bytes_from_slice(bytes)?;
525+
unsafe { Ok(&*ptr_meta::from_raw_parts(bytes.as_ptr().cast(), len)) }
526+
}
527+
}
528+
491529
#[cfg(feature = "alloc")]
492530
impl ToOwned for DevicePath {
493531
type Owned = Box<DevicePath>;
@@ -1017,4 +1055,28 @@ mod tests {
10171055
raw_data[2] += 1;
10181056
assert!(<&DevicePathNode>::try_from(raw_data.as_slice()).is_err());
10191057
}
1058+
1059+
#[test]
1060+
fn test_device_path_nodes_from_bytes() {
1061+
let raw_data = create_raw_device_path();
1062+
let dp = <&DevicePath>::try_from(raw_data.as_slice()).unwrap();
1063+
1064+
// Check that the size is the sum of the nodes' lengths.
1065+
assert_eq!(mem::size_of_val(dp), 6 + 8 + 4 + 6 + 8 + 4);
1066+
1067+
// Check the list's node iter.
1068+
let nodes: Vec<_> = dp.node_iter().collect();
1069+
check_node(nodes[0], 0xa0, 0xb0, &[10, 11]);
1070+
check_node(nodes[1], 0xa1, 0xb1, &[20, 21, 22, 23]);
1071+
check_node(
1072+
nodes[2],
1073+
DeviceType::END.0,
1074+
DeviceSubType::END_INSTANCE.0,
1075+
&[],
1076+
);
1077+
check_node(nodes[3], 0xa2, 0xb2, &[30, 31]);
1078+
check_node(nodes[4], 0xa3, 0xb3, &[40, 41, 42, 43]);
1079+
// The end-entire node is not returned by the iterator.
1080+
assert_eq!(nodes.len(), 5);
1081+
}
10201082
}

0 commit comments

Comments
 (0)