1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Block I/O protocols.

use crate::proto::Protocol;
use crate::{unsafe_guid, Result, Status};

/// The Block I/O protocol.
#[repr(C)]
#[unsafe_guid("964e5b21-6459-11d2-8e39-00a0c969723b")]
#[derive(Protocol)]
pub struct BlockIO {
    revision: u64,
    media: BlockIOMedia,

    reset: extern "efiapi" fn(this: &BlockIO, extended_verification: bool) -> Status,
    read_blocks: extern "efiapi" fn(
        this: &BlockIO,
        media_id: u32,
        lba: Lba,
        buffer_size: usize,
        buffer: *mut u8,
    ) -> Status,
    write_blocks: extern "efiapi" fn(
        this: &BlockIO,
        media_id: u32,
        lba: Lba,
        buffer_size: usize,
        buffer: *const u8,
    ) -> Status,
    flush_blocks: extern "efiapi" fn(this: &BlockIO) -> Status,
}

impl BlockIO {
    /// Pointer for block IO media.
    pub fn media(&self) -> &BlockIOMedia {
        &self.media
    }

    /// Resets the block device hardware.
    ///
    /// # Arguments
    /// * `extended_verification`   Indicates that the driver may perform a more exhaustive verification operation of
    ///     the device during reset.
    ///
    /// # Errors
    /// * `uefi::Status::DEVICE_ERROR`  The block device is not functioning correctly and could not be reset.
    pub fn reset(&mut self, extended_verification: bool) -> Result {
        (self.reset)(self, extended_verification).into()
    }

    /// Read the requested number of blocks from the device.
    ///
    /// # Arguments
    /// * `media_id` - The media ID that the read request is for.
    /// * `lba` - The starting logical block address to read from on the device.
    /// * `buffer` - The target buffer of the read operation
    ///
    /// # Errors
    /// * `uefi::Status::DEVICE_ERROR`       The device reported an error while attempting to perform the read
    ///     operation.
    /// * `uefi::Status::NO_MEDIA`           There is no media in the device.
    /// * `uefi::Status::MEDIA_CHANGED`      The `media_id` is not for the current media.
    /// * `uefi::Status::BAD_BUFFER_SIZE`    The buffer size parameter is not a multiple of the intrinsic block size of
    ///     the device.
    /// * `uefi::Status::INVALID_PARAMETER`  The read request contains LBAs that are not valid, or the buffer is not on
    ///     proper alignment.
    pub fn read_blocks(&self, media_id: u32, lba: Lba, buffer: &mut [u8]) -> Result {
        let buffer_size = buffer.len();
        (self.read_blocks)(self, media_id, lba, buffer_size, buffer.as_mut_ptr()).into()
    }

    /// Writes the requested number of blocks to the device.
    ///
    /// # Arguments
    /// * `media_id`    The media ID that the write request is for.
    /// * `lba`         The starting logical block address to be written.
    /// * `buffer`      Buffer to be written
    ///
    /// # Errors
    /// * `uefi::Status::WRITE_PROTECTED`       The device cannot be written to.
    /// * `uefi::Status::NO_MEDIA`              There is no media in the device.
    /// * `uefi::Status::MEDIA_CHANGED`         The `media_id` is not for the current media.
    /// * `uefi::Status::DEVICE_ERROR`          The device reported an error while attempting to perform the write
    ///     operation.
    /// * `uefi::Status::BAD_BUFFER_SIZE`       The buffer size parameter is not a multiple of the intrinsic block size
    ///     of the device.
    /// * `uefi::Status::INVALID_PARAMETER`     The write request contains LBAs that are not valid, or the buffer is not
    ///     on proper alignment.
    pub fn write_blocks(&mut self, media_id: u32, lba: Lba, buffer: &[u8]) -> Result {
        let buffer_size = buffer.len();
        (self.write_blocks)(self, media_id, lba, buffer_size, buffer.as_ptr()).into()
    }

    /// Flushes all modified data to a physical block device.
    ///
    /// # Errors
    /// * `uefi::Status::DEVICE_ERROR`          The device reported an error while attempting to write data.
    /// * `uefi::Status::NO_MEDIA`              There is no media in the device.
    pub fn flush_blocks(&mut self) -> Result {
        (self.flush_blocks)(self).into()
    }
}

/// EFI LBA type
pub type Lba = u64;

/// Media information structure
#[repr(C)]
#[derive(Debug)]
pub struct BlockIOMedia {
    media_id: u32,
    removable_media: bool,
    media_present: bool,
    logical_partition: bool,
    read_only: bool,
    write_caching: bool,

    block_size: u32,
    io_align: u32,
    last_block: Lba,

    // Revision 2
    lowest_aligned_lba: Lba,
    logical_blocks_per_physical_block: u32,

    // Revision 3
    optimal_transfer_length_granularity: u32,
}

impl BlockIOMedia {
    /// The current media ID.
    pub fn media_id(&self) -> u32 {
        self.media_id
    }

    /// True if the media is removable.
    pub fn is_removable_media(&self) -> bool {
        self.removable_media
    }

    /// True if there is a media currently present in the device.
    pub fn is_media_preset(&self) -> bool {
        self.media_present
    }

    /// True if block IO was produced to abstract partition structure.
    pub fn is_logical_partition(&self) -> bool {
        self.logical_partition
    }

    /// True if the media is marked read-only.
    pub fn is_read_only(&self) -> bool {
        self.read_only
    }

    /// True if `writeBlocks` function writes data.
    pub fn is_write_caching(&self) -> bool {
        self.write_caching
    }

    /// The intrinsic block size of the device.
    ///
    /// If the media changes, then this field is updated. Returns the number of bytes per logical block.
    pub fn block_size(&self) -> u32 {
        self.block_size
    }

    /// Supplies the alignment requirement for any buffer used in a data transfer.
    pub fn io_align(&self) -> u32 {
        self.io_align
    }

    /// The last LBA on the device. If the media changes, then this field is updated.
    pub fn last_block(&self) -> Lba {
        self.last_block
    }

    /// Returns the first LBA that is aligned to a physical block boundary.
    pub fn lowest_aligned_lba(&self) -> Lba {
        self.lowest_aligned_lba
    }

    /// Returns the number of logical blocks per physical block.
    pub fn logical_blocks_per_physical_block(&self) -> u32 {
        self.logical_blocks_per_physical_block
    }

    /// Returns the optimal transfer length granularity as a number of logical blocks.
    pub fn optimal_transfer_length_granularity(&self) -> u32 {
        self.optimal_transfer_length_granularity
    }
}