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