1 //! Utilities for creating new [`DevicePaths`]. 2 //! 3 //! This module contains [`DevicePathBuilder`], as well as submodules 4 //! containing types for building each type of device path node. 5 //! 6 //! [`DevicePaths`]: DevicePath 7 8 pub use crate::proto::device_path::device_path_gen::build::*; 9 10 use crate::polyfill::{maybe_uninit_slice_as_mut_ptr, maybe_uninit_slice_assume_init_ref}; 11 use crate::proto::device_path::{DevicePath, DevicePathNode}; 12 use core::fmt::{self, Display, Formatter}; 13 use core::mem::MaybeUninit; 14 15 #[cfg(feature = "alloc")] 16 use alloc::vec::Vec; 17 18 /// A builder for [`DevicePaths`]. 19 /// 20 /// The builder can be constructed with either a fixed-length buffer or 21 /// (if the `alloc` feature is enabled) a `Vec`. 22 /// 23 /// Nodes are added via the [`push`] method. To construct a node, use one 24 /// of the structs in these submodules: 25 /// * [`acpi`] 26 /// * [`bios_boot_spec`] 27 /// * [`end`] 28 /// * [`hardware`] 29 /// * [`media`] 30 /// * [`messaging`] 31 /// 32 /// A node can also be constructed by copying a node from an existing 33 /// device path. 34 /// 35 /// To complete a path, call the [`finalize`] method. This adds an 36 /// [`END_ENTIRE`] node and returns a [`DevicePath`] reference tied to 37 /// the lifetime of the buffer the builder was constructed with. 38 /// 39 /// [`DevicePaths`]: DevicePath 40 /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE 41 /// [`finalize`]: DevicePathBuilder::finalize 42 /// [`push`]: DevicePathBuilder::push 43 /// 44 /// # Examples 45 /// 46 /// ``` 47 /// use core::mem::MaybeUninit; 48 /// use uefi::guid; 49 /// use uefi::proto::device_path::DevicePath; 50 /// use uefi::proto::device_path::build; 51 /// 52 /// # fn main() -> Result<(), build::BuildError> { 53 /// let mut buf = [MaybeUninit::uninit(); 256]; 54 /// let path: &DevicePath = build::DevicePathBuilder::with_buf(&mut buf) 55 /// .push(&build::acpi::Acpi { 56 /// hid: 0x41d0_0a03, 57 /// uid: 0x0000_0000, 58 /// })? 59 /// .push(&build::hardware::Pci { 60 /// function: 0x00, 61 /// device: 0x1f, 62 /// })? 63 /// .push(&build::hardware::Vendor { 64 /// vendor_guid: guid!("15e39a00-1dd2-1000-8d7f-00a0c92408fc"), 65 /// vendor_defined_data: &[1, 2, 3, 4, 5, 6], 66 /// })? 67 /// .finalize()?; 68 /// 69 /// assert_eq!(path.node_iter().count(), 3); 70 /// # Ok(()) 71 /// # } 72 /// ``` 73 #[derive(Debug)] 74 pub struct DevicePathBuilder<'a> { 75 storage: BuilderStorage<'a>, 76 } 77 78 impl<'a> DevicePathBuilder<'a> { 79 /// Create a builder backed by a statically-sized buffer. with_buf(buf: &'a mut [MaybeUninit<u8>]) -> Self80 pub fn with_buf(buf: &'a mut [MaybeUninit<u8>]) -> Self { 81 Self { 82 storage: BuilderStorage::Buf { buf, offset: 0 }, 83 } 84 } 85 86 /// Create a builder backed by a `Vec`. 87 /// 88 /// The `Vec` is cleared before use. 89 #[cfg(feature = "alloc")] with_vec(v: &'a mut Vec<u8>) -> Self90 pub fn with_vec(v: &'a mut Vec<u8>) -> Self { 91 v.clear(); 92 Self { 93 storage: BuilderStorage::Vec(v), 94 } 95 } 96 97 /// Add a node to the device path. 98 /// 99 /// An error will be returned if an [`END_ENTIRE`] node is passed to 100 /// this function, as that node will be added when `finalize` is 101 /// called. 102 /// 103 /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE push(mut self, node: &dyn BuildNode) -> Result<Self, BuildError>104 pub fn push(mut self, node: &dyn BuildNode) -> Result<Self, BuildError> { 105 let node_size = usize::from(node.size_in_bytes()?); 106 107 match &mut self.storage { 108 BuilderStorage::Buf { buf, offset } => { 109 node.write_data( 110 buf.get_mut(*offset..*offset + node_size) 111 .ok_or(BuildError::BufferTooSmall)?, 112 ); 113 *offset += node_size; 114 } 115 #[cfg(feature = "alloc")] 116 BuilderStorage::Vec(vec) => { 117 let old_size = vec.len(); 118 vec.reserve(node_size); 119 let buf = &mut vec.spare_capacity_mut()[..node_size]; 120 node.write_data(buf); 121 unsafe { 122 vec.set_len(old_size + node_size); 123 } 124 } 125 } 126 127 Ok(self) 128 } 129 130 /// Add an [`END_ENTIRE`] node and return the resulting [`DevicePath`]. 131 /// 132 /// This method consumes the builder. 133 /// 134 /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE finalize(self) -> Result<&'a DevicePath, BuildError>135 pub fn finalize(self) -> Result<&'a DevicePath, BuildError> { 136 let this = self.push(&end::Entire)?; 137 138 let data: &[u8] = match &this.storage { 139 BuilderStorage::Buf { buf, offset } => unsafe { 140 maybe_uninit_slice_assume_init_ref(&buf[..*offset]) 141 }, 142 #[cfg(feature = "alloc")] 143 BuilderStorage::Vec(vec) => vec, 144 }; 145 146 let ptr: *const () = data.as_ptr().cast(); 147 Ok(unsafe { &*ptr_meta::from_raw_parts(ptr, data.len()) }) 148 } 149 } 150 151 #[derive(Debug)] 152 enum BuilderStorage<'a> { 153 Buf { 154 buf: &'a mut [MaybeUninit<u8>], 155 offset: usize, 156 }, 157 158 #[cfg(feature = "alloc")] 159 Vec(&'a mut Vec<u8>), 160 } 161 162 /// Error type used by [`DevicePathBuilder`]. 163 #[derive(Clone, Copy, Debug)] 164 pub enum BuildError { 165 /// A node was too big to fit in the remaining buffer space. 166 BufferTooSmall, 167 168 /// An individual node's length is too big to fit in a [`u16`]. 169 NodeTooBig, 170 171 /// An [`END_ENTIRE`] node was passed to the builder. Use 172 /// [`DevicePathBuilder::finalize`] instead. 173 /// 174 /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE 175 UnexpectedEndEntire, 176 } 177 178 impl Display for BuildError { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result179 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 180 write!( 181 f, 182 "{}", 183 match self { 184 Self::BufferTooSmall => "a node was too big to fit in remaining buffer space", 185 Self::NodeTooBig => "a node was too big", 186 Self::UnexpectedEndEntire => "unexpected END_ENTIRE", 187 } 188 ) 189 } 190 } 191 192 #[cfg(feature = "unstable")] 193 impl core::error::Error for BuildError {} 194 195 /// Trait for types that can be used to build a node via 196 /// [`DevicePathBuilder::push`]. 197 /// 198 /// This trait is implemented for all the node types in 199 /// [`uefi::proto::device_path::build`]. It is also implemented for 200 /// [`&DevicePathNode`], which allows an existing node to be copied by 201 /// the builder. 202 /// 203 /// # Safety 204 /// 205 /// The methods of this trait are safe to call, but the trait itself is 206 /// `unsafe` because an incorrect implementation could cause 207 /// unsafety. In particular, the `write_data` is required to 208 /// completely initialize all bytes in the output slice. 209 /// 210 /// [`&DevicePathNode`]: DevicePathNode 211 pub unsafe trait BuildNode { 212 /// Size of the node in bytes, including the standard node 213 /// header. Returns [`BuildError::NodeTooBig`] if the node's size 214 /// does not fit in a [`u16`]. size_in_bytes(&self) -> Result<u16, BuildError>215 fn size_in_bytes(&self) -> Result<u16, BuildError>; 216 217 /// Write out the node data. 218 /// 219 /// The length of `out` must be equal to the node's `size_in_bytes`. 220 /// 221 /// The `out` slice will be fully initialized after the call. write_data(&self, out: &mut [MaybeUninit<u8>])222 fn write_data(&self, out: &mut [MaybeUninit<u8>]); 223 } 224 225 unsafe impl BuildNode for &DevicePathNode { size_in_bytes(&self) -> Result<u16, BuildError>226 fn size_in_bytes(&self) -> Result<u16, BuildError> { 227 Ok(self.header.length) 228 } 229 write_data(&self, out: &mut [MaybeUninit<u8>])230 fn write_data(&self, out: &mut [MaybeUninit<u8>]) { 231 let src: *const u8 = self.as_ffi_ptr().cast(); 232 233 let dst: *mut u8 = maybe_uninit_slice_as_mut_ptr(out); 234 unsafe { 235 dst.copy_from_nonoverlapping(src, out.len()); 236 } 237 } 238 } 239 240 #[cfg(test)] 241 mod tests { 242 use super::*; 243 use crate::guid; 244 use crate::proto::device_path::media::{PartitionFormat, PartitionSignature}; 245 use crate::proto::device_path::messaging::{ 246 Ipv4AddressOrigin, IscsiLoginOptions, IscsiProtocol, RestServiceAccessMode, RestServiceType, 247 }; 248 use core::{mem, slice}; 249 path_to_bytes(path: &DevicePath) -> &[u8]250 fn path_to_bytes(path: &DevicePath) -> &[u8] { 251 unsafe { slice::from_raw_parts(path.as_ffi_ptr().cast::<u8>(), mem::size_of_val(path)) } 252 } 253 254 /// Test building an ACPI ADR node. 255 #[test] test_acpi_adr() -> Result<(), BuildError>256 fn test_acpi_adr() -> Result<(), BuildError> { 257 assert!(acpi::AdrSlice::new(&[]).is_none()); 258 259 let mut v = Vec::new(); 260 let path = DevicePathBuilder::with_vec(&mut v) 261 .push(&acpi::Adr { 262 adr: acpi::AdrSlice::new(&[1, 2]).unwrap(), 263 })? 264 .finalize()?; 265 266 let node: &crate::proto::device_path::acpi::Adr = 267 path.node_iter().next().unwrap().try_into().unwrap(); 268 assert_eq!(node.adr().iter().collect::<Vec<_>>(), [1, 2]); 269 270 let bytes = path_to_bytes(path); 271 #[rustfmt::skip] 272 assert_eq!(bytes, [ 273 // ACPI ADR node 274 0x02, 0x03, 0x0c, 0x00, 275 // Values 276 0x01, 0x00, 0x00, 0x00, 277 0x02, 0x00, 0x00, 0x00, 278 279 // End-entire node 280 0x7f, 0xff, 0x04, 0x00, 281 ]); 282 283 Ok(()) 284 } 285 286 /// Test building an ACPI Expanded node. 287 #[test] test_acpi_expanded() -> Result<(), BuildError>288 fn test_acpi_expanded() -> Result<(), BuildError> { 289 let mut v = Vec::new(); 290 let path = DevicePathBuilder::with_vec(&mut v) 291 .push(&acpi::Expanded { 292 hid: 1, 293 uid: 2, 294 cid: 3, 295 hid_str: b"a\0", 296 uid_str: b"bc\0", 297 cid_str: b"def\0", 298 })? 299 .finalize()?; 300 301 let node: &crate::proto::device_path::acpi::Expanded = 302 path.node_iter().next().unwrap().try_into().unwrap(); 303 assert_eq!(node.hid(), 1); 304 assert_eq!(node.uid(), 2); 305 assert_eq!(node.cid(), 3); 306 assert_eq!(node.hid_str(), b"a\0"); 307 assert_eq!(node.uid_str(), b"bc\0"); 308 assert_eq!(node.cid_str(), b"def\0"); 309 310 let bytes = path_to_bytes(path); 311 #[rustfmt::skip] 312 assert_eq!(bytes, [ 313 // ACPI Expanded node 314 0x02, 0x02, 0x19, 0x00, 315 // HID 316 0x01, 0x00, 0x00, 0x00, 317 // UID 318 0x02, 0x00, 0x00, 0x00, 319 // CID 320 0x03, 0x00, 0x00, 0x00, 321 322 // HID str 323 0x61, 0x00, 324 325 // UID str 326 0x62, 0x63, 0x00, 327 328 // CID str 329 0x64, 0x65, 0x66, 0x00, 330 331 // End-entire node 332 0x7f, 0xff, 0x04, 0x00, 333 ]); 334 335 Ok(()) 336 } 337 338 /// Test building a messaging REST Service node. 339 #[test] test_messaging_rest_service() -> Result<(), BuildError>340 fn test_messaging_rest_service() -> Result<(), BuildError> { 341 let mut v = Vec::new(); 342 let vendor_guid = guid!("a1005a90-6591-4596-9bab-1c4249a6d4ff"); 343 let path = DevicePathBuilder::with_vec(&mut v) 344 .push(&messaging::RestService { 345 service_type: RestServiceType::REDFISH, 346 access_mode: RestServiceAccessMode::IN_BAND, 347 vendor_guid_and_data: None, 348 })? 349 .push(&messaging::RestService { 350 service_type: RestServiceType::VENDOR, 351 access_mode: RestServiceAccessMode::OUT_OF_BAND, 352 vendor_guid_and_data: Some(messaging::RestServiceVendorData { 353 vendor_guid, 354 vendor_defined_data: &[1, 2, 3, 4, 5], 355 }), 356 })? 357 .finalize()?; 358 359 let mut iter = path.node_iter(); 360 let mut node: &crate::proto::device_path::messaging::RestService = 361 iter.next().unwrap().try_into().unwrap(); 362 assert!(node.vendor_guid_and_data().is_none()); 363 node = iter.next().unwrap().try_into().unwrap(); 364 assert_eq!(node.vendor_guid_and_data().unwrap().0, vendor_guid); 365 assert_eq!(node.vendor_guid_and_data().unwrap().1, &[1, 2, 3, 4, 5]); 366 367 let bytes = path_to_bytes(path); 368 #[rustfmt::skip] 369 assert_eq!(bytes, [ 370 // Messaging REST Service node. 371 0x03, 0x21, 0x06, 0x00, 372 // Type and access mode 373 0x01, 0x01, 374 375 // Messaging REST Service node. The spec incorrectly says 376 // the length is 21+n bytes, it's actually 22+n bytes. 377 0x03, 0x21, 0x1b, 0x00, 378 // Type and access mode 379 0xff, 0x02, 380 // Vendor guid 381 0x90, 0x5a, 0x00, 0xa1, 382 0x91, 0x65, 0x96, 0x45, 383 0x9b, 0xab, 0x1c, 0x42, 384 0x49, 0xa6, 0xd4, 0xff, 385 // Vendor data 386 0x01, 0x02, 0x03, 0x04, 0x05, 387 388 // End-entire node 389 0x7f, 0xff, 0x04, 0x00, 390 ]); 391 392 Ok(()) 393 } 394 395 /// Test that packed nodes can be passed into the builder. 396 #[test] test_build_with_packed_node() -> Result<(), BuildError>397 fn test_build_with_packed_node() -> Result<(), BuildError> { 398 // Build a path with both a statically-sized and DST nodes. 399 let mut v = Vec::new(); 400 let path1 = DevicePathBuilder::with_vec(&mut v) 401 .push(&acpi::Acpi { 402 hid: 0x41d0_0a03, 403 uid: 0x0000_0000, 404 })? 405 .push(&hardware::Vendor { 406 vendor_guid: guid!("15e39a00-1dd2-1000-8d7f-00a0c92408fc"), 407 vendor_defined_data: &[1, 2, 3, 4, 5, 6], 408 })? 409 .finalize()?; 410 411 // Create a second path by copying in the packed nodes from the 412 // first path. 413 let mut v = Vec::new(); 414 let mut builder = DevicePathBuilder::with_vec(&mut v); 415 for node in path1.node_iter() { 416 builder = builder.push(&node)?; 417 } 418 let path2 = builder.finalize()?; 419 420 // Verify the copied path is identical. 421 assert_eq!(path1, path2); 422 423 Ok(()) 424 } 425 426 /// This test is based on the "Fibre Channel Ex Device Path Example" 427 /// from the UEFI Specification. 428 #[test] test_fibre_channel_ex_device_path_example() -> Result<(), BuildError>429 fn test_fibre_channel_ex_device_path_example() -> Result<(), BuildError> { 430 // Arbitrarily choose this test to use a statically-sized 431 // buffer, just to make sure that code path is tested. 432 let mut buf = [MaybeUninit::uninit(); 256]; 433 let path = DevicePathBuilder::with_buf(&mut buf) 434 .push(&acpi::Acpi { 435 hid: 0x41d0_0a03, 436 uid: 0x0000_0000, 437 })? 438 .push(&hardware::Pci { 439 function: 0x00, 440 device: 0x1f, 441 })? 442 .push(&messaging::FibreChannelEx { 443 world_wide_name: [0, 1, 2, 3, 4, 5, 6, 7], 444 logical_unit_number: [0, 1, 2, 3, 4, 5, 6, 7], 445 })? 446 .finalize()?; 447 448 let bytes = path_to_bytes(path); 449 #[rustfmt::skip] 450 assert_eq!(bytes, [ 451 // ACPI node 452 0x02, 0x01, 0x0c, 0x00, 453 // HID 454 0x03, 0x0a, 0xd0, 0x41, 455 // UID 456 0x00, 0x00, 0x00, 0x00, 457 458 // PCI node 459 0x01, 0x01, 0x06, 0x00, 460 // Function 461 0x00, 462 // Device 463 0x1f, 464 465 // Fibre Channel Ex node 466 0x03, 0x15, 467 // The example in the spec is wrong here; it says 0x14 for 468 // the length and leaves out the four-byte reserved field. 469 0x18, 0x00, 470 // Reserved 471 0x00, 0x00, 0x00, 0x00, 472 // World wide name 473 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 474 // Logical unit number 475 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 476 477 // End-entire node 478 0x7f, 0xff, 0x04, 0x00, 479 ]); 480 481 Ok(()) 482 } 483 484 /// This test is based on the "IPv4 configuration" example from the 485 /// UEFI Specification. 486 #[test] test_ipv4_configuration_example() -> Result<(), BuildError>487 fn test_ipv4_configuration_example() -> Result<(), BuildError> { 488 let mut v = Vec::new(); 489 let path = DevicePathBuilder::with_vec(&mut v) 490 .push(&acpi::Acpi { 491 hid: 0x41d0_0a03, 492 uid: 0x0000_0000, 493 })? 494 .push(&hardware::Pci { 495 function: 0x00, 496 device: 0x19, 497 })? 498 .push(&messaging::MacAddress { 499 mac_address: [ 500 0x00, 0x13, 0x20, 0xf5, 0xfa, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 503 ], 504 interface_type: 0x01, 505 })? 506 .push(&messaging::Ipv4 { 507 local_ip_address: [192, 168, 0, 1], 508 remote_ip_address: [192, 168, 0, 100], 509 local_port: 0, 510 remote_port: 3260, 511 protocol: 6, 512 ip_address_origin: Ipv4AddressOrigin::STATIC, 513 gateway_ip_address: [0, 0, 0, 0], 514 subnet_mask: [0, 0, 0, 0], 515 })? 516 .push(&messaging::Iscsi { 517 protocol: IscsiProtocol::TCP, 518 options: IscsiLoginOptions::AUTH_METHOD_NONE, 519 logical_unit_number: 0u64.to_le_bytes(), 520 target_portal_group_tag: 1, 521 iscsi_target_name: b"iqn.1991-05.com.microsoft:iscsitarget-iscsidisk-target\0", 522 })? 523 .push(&media::HardDrive { 524 partition_number: 1, 525 partition_start: 0x22, 526 partition_size: 0x2710000, 527 partition_format: PartitionFormat::GPT, 528 partition_signature: PartitionSignature::Guid(guid!( 529 "15e39a00-1dd2-1000-8d7f-00a0c92408fc" 530 )), 531 })? 532 .finalize()?; 533 534 let bytes = path_to_bytes(path); 535 #[rustfmt::skip] 536 assert_eq!(bytes, [ 537 // ACPI node 538 0x02, 0x01, 0x0c, 0x00, 539 // HID 540 0x03, 0x0a, 0xd0, 0x41, 541 // UID 542 0x00, 0x00, 0x00, 0x00, 543 544 // PCI node 545 0x01, 0x01, 0x06, 0x00, 546 // Function 547 0x00, 548 // Device 549 0x19, 550 551 // MAC address node 552 0x03, 0x0b, 0x25, 0x00, 553 // MAC address 554 0x00, 0x13, 0x20, 0xf5, 0xfa, 0x77, 0x00, 0x00, 555 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 556 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 557 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 558 // Network interface type 559 0x01, 560 561 // IPv4 node 562 0x03, 0x0c, 0x1b, 0x00, 563 // Local address 564 0xc0, 0xa8, 0x00, 0x01, 565 // Remote address 566 0xc0, 0xa8, 0x00, 0x64, 567 // Local port 568 0x00, 0x00, 569 // Remote port 570 0xbc, 0x0c, 571 // Protocol 572 0x06, 0x00, 573 // Static IP 574 0x01, 575 // Gateway IP 576 0x00, 0x00, 0x00, 0x00, 577 // Subnet mask 578 0x00, 0x00, 0x00, 0x00, 579 580 // iSCSI node 581 0x03, 0x13, 0x49, 0x00, 582 // Protocol 583 0x00, 0x00, 584 // Login options 585 0x00, 0x08, 586 // LUN 587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 588 // Target portal group tag 589 0x01, 0x00, 590 // Node name 591 0x69, 0x71, 0x6e, 0x2e, 0x31, 0x39, 0x39, 0x31, 592 0x2d, 0x30, 0x35, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 593 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 594 0x74, 0x3a, 0x69, 0x73, 0x63, 0x73, 0x69, 0x74, 595 0x61, 0x72, 0x67, 0x65, 0x74, 0x2d, 0x69, 0x73, 596 0x63, 0x73, 0x69, 0x64, 0x69, 0x73, 0x6b, 0x2d, 597 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 598 599 // Hard drive node 600 0x04, 0x01, 0x2a, 0x00, 601 // Partition number 602 0x01, 0x00, 0x00, 0x00, 603 // Partition start 604 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 605 // Partition size 606 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 607 // Partition signature 608 0x00, 0x9a, 0xe3, 0x15, 0xd2, 0x1d, 0x00, 0x10, 609 0x8d, 0x7f, 0x00, 0xa0, 0xc9, 0x24, 0x08, 0xfc, 610 // Partition format 611 0x02, 612 // Signature type 613 0x02, 614 615 // End-entire node 616 0x7f, 0xff, 0x04, 0x00, 617 ]); 618 619 Ok(()) 620 } 621 } 622