|
| 1 | +// SPDX-License-Identifier: MIT OR Apache-2.0 |
| 2 | + |
| 3 | +//! Very basic tests for the BlockIo and BlockIo2 protocols. |
| 4 | +//! |
| 5 | +//! We look for some well-known data on a few well-known disks |
| 6 | +//! of our test environment. |
| 7 | +
|
| 8 | +use alloc::string::String; |
| 9 | +use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol}; |
| 10 | +use uefi::proto::Protocol; |
| 11 | +use uefi::proto::device_path::DevicePath; |
| 12 | +use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; |
| 13 | +use uefi::proto::media::block::{BlockIO, BlockIO2}; |
| 14 | +use uefi::{CString16, Handle, boot}; |
| 15 | +use uefi_raw::protocol::device_path::DeviceSubType; |
| 16 | + |
| 17 | +fn verify_block_device(dvp: &DevicePath, first_block: &[u8]) { |
| 18 | + // We only look for storage technologies that we are interested in. |
| 19 | + let storage_device_types = [ |
| 20 | + DeviceSubType::MESSAGING_SCSI, |
| 21 | + DeviceSubType::MESSAGING_NVME_NAMESPACE, |
| 22 | + DeviceSubType::MESSAGING_SATA, |
| 23 | + ]; |
| 24 | + let storage_node = dvp |
| 25 | + .node_iter() |
| 26 | + .skip_while(|x| !storage_device_types.contains(&x.sub_type())) |
| 27 | + .next() |
| 28 | + .unwrap(); |
| 29 | + let storage_node_string = storage_node |
| 30 | + .to_string(DisplayOnly(true), AllowShortcuts(true)) |
| 31 | + .unwrap(); |
| 32 | + debug!("Storage technology: {storage_node_string}"); |
| 33 | + |
| 34 | + //debug!("First 512 bytes: {first_block:?}"); |
| 35 | + match storage_node.sub_type() { |
| 36 | + DeviceSubType::MESSAGING_SCSI => { /* empty disks so far, nothing to check for */ } |
| 37 | + DeviceSubType::MESSAGING_NVME_NAMESPACE => { |
| 38 | + /* empty disks so far, nothing to check for */ |
| 39 | + } |
| 40 | + DeviceSubType::MESSAGING_SATA => { |
| 41 | + // We check that the right SATA disk indeed contains a correct |
| 42 | + // FAT16 volume. |
| 43 | + let expected = "MbrTestDisk"; |
| 44 | + let contains_volume_label = first_block |
| 45 | + .windows(expected.len()) |
| 46 | + .any(|w| w == expected.as_bytes()); |
| 47 | + let oem_name = { |
| 48 | + let bytes = &first_block[3..10]; |
| 49 | + String::from_utf8(bytes.to_vec()) |
| 50 | + }; |
| 51 | + let is_valid_fat = first_block[0] != 0 && oem_name.is_ok(); |
| 52 | + if is_valid_fat && storage_node.data() == &[0x2, 0, 0xff, 0xff, 0x0, 0x0] { |
| 53 | + if !contains_volume_label { |
| 54 | + panic!( |
| 55 | + "Sata disk {storage_node_string} does not contain {expected} in its first 512 bytes" |
| 56 | + ) |
| 57 | + } else { |
| 58 | + debug!( |
| 59 | + "Found volume label {expected} with OEM name: {}", |
| 60 | + oem_name.unwrap() |
| 61 | + ); |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + _ => unreachable!(), |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +fn open_proto_and_dvp<P: Protocol>( |
| 70 | + handle: Handle, |
| 71 | +) -> (ScopedProtocol<P>, ScopedProtocol<DevicePath>, CString16) { |
| 72 | + let proto = unsafe { |
| 73 | + boot::open_protocol::<P>( |
| 74 | + OpenProtocolParams { |
| 75 | + handle, |
| 76 | + agent: boot::image_handle(), |
| 77 | + controller: None, |
| 78 | + }, |
| 79 | + OpenProtocolAttributes::GetProtocol, |
| 80 | + ) |
| 81 | + .unwrap() |
| 82 | + }; |
| 83 | + let dvp = unsafe { |
| 84 | + boot::open_protocol::<DevicePath>( |
| 85 | + OpenProtocolParams { |
| 86 | + handle, |
| 87 | + agent: boot::image_handle(), |
| 88 | + controller: None, |
| 89 | + }, |
| 90 | + OpenProtocolAttributes::GetProtocol, |
| 91 | + ) |
| 92 | + .unwrap() |
| 93 | + }; |
| 94 | + let dvp_string = dvp |
| 95 | + .to_string(DisplayOnly(true), AllowShortcuts(true)) |
| 96 | + .unwrap(); |
| 97 | + |
| 98 | + (proto, dvp, dvp_string) |
| 99 | +} |
| 100 | + |
| 101 | +fn test_blockio_protocol() { |
| 102 | + info!("Testing BLOCKIO protocol"); |
| 103 | + for handle in boot::find_handles::<BlockIO>().unwrap() { |
| 104 | + let (proto, dvp, dvp_string) = open_proto_and_dvp::<BlockIO>(handle); |
| 105 | + debug!("Found handle supporting protocol: {dvp_string}"); |
| 106 | + debug!("media: {:?}", proto.media()); |
| 107 | + let mut first_block = vec![0; 512]; |
| 108 | + proto |
| 109 | + .read_blocks(proto.media().media_id(), 0, &mut first_block) |
| 110 | + .unwrap(); |
| 111 | + |
| 112 | + verify_block_device(&dvp, first_block.as_slice()); |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +fn test_blockio2_protocol() { |
| 117 | + info!("Testing BLOCKIO 2 protocol"); |
| 118 | + |
| 119 | + for handle in boot::find_handles::<BlockIO2>().unwrap() { |
| 120 | + let (proto, dvp, dvp_string) = open_proto_and_dvp::<BlockIO2>(handle); |
| 121 | + debug!("Found handle supporting protocol: {dvp_string}"); |
| 122 | + debug!("media: {:?}", proto.media()); |
| 123 | + let mut first_block = vec![0; 512]; |
| 124 | + unsafe { |
| 125 | + proto |
| 126 | + .read_blocks_ex(proto.media().media_id(), 0, None, &mut first_block) |
| 127 | + .unwrap(); |
| 128 | + } |
| 129 | + |
| 130 | + verify_block_device(&dvp, first_block.as_slice()); |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +pub fn test() { |
| 135 | + test_blockio_protocol(); |
| 136 | + test_blockio2_protocol(); |
| 137 | +} |
0 commit comments