Skip to content

Commit 3b10e70

Browse files
committed
protocol: implement block and disk IO protocols
1 parent e83f46c commit 3b10e70

File tree

3 files changed

+292
-0
lines changed

3 files changed

+292
-0
lines changed

src/protocol/block.rs

+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Copyright 2017 CoreOS, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
16+
use core::slice;
17+
18+
use base::Status;
19+
use guid::Guid;
20+
use protocol::Protocol;
21+
use void::CVoid;
22+
23+
#[repr(C)]
24+
#[derive(Debug)]
25+
pub struct BlockIOMedia {
26+
media_id: u32,
27+
removable: u8,
28+
present: u8,
29+
logical_partition: u8,
30+
read_only: u8,
31+
write_caching: u8,
32+
block_size: u32,
33+
pub io_align: u32,
34+
pub last_block: u64,
35+
lowest_aligned_lba: u64,
36+
logical_blocks_per_physical_block: u32,
37+
optimal_transfer_length_granularity: u32,
38+
}
39+
40+
pub static EFI_BLOCK_IO_PROTOCOL_GUID: Guid = Guid(
41+
0x964E_5B21,
42+
0x6459,
43+
0x11D2,
44+
[0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B],
45+
);
46+
47+
/// Bindings to the EFI Block I/O protocol. This protocol provides synchronous access to block
48+
/// devices, and allows block-by-block access.
49+
#[repr(C)]
50+
pub struct BlockIOProtocol {
51+
revision: u64,
52+
pub media: *const BlockIOMedia,
53+
reset: unsafe extern "win64" fn(this: *const BlockIOProtocol, extended_verification: u8)
54+
-> Status,
55+
read_blocks: unsafe extern "win64" fn(
56+
this: *const BlockIOProtocol,
57+
media_id: u32,
58+
lba: u64,
59+
buffer_size: usize,
60+
buffer: *mut CVoid,
61+
) -> Status,
62+
write_blocks: unsafe extern "win64" fn(
63+
this: *const BlockIOProtocol,
64+
media_id: u32,
65+
lba: u64,
66+
buffer_size: usize,
67+
buffer: *const CVoid,
68+
) -> Status,
69+
flush_blocks: unsafe extern "win64" fn(this: *const BlockIOProtocol) -> Status,
70+
}
71+
72+
impl Protocol for BlockIOProtocol {
73+
fn guid() -> &'static Guid {
74+
&EFI_BLOCK_IO_PROTOCOL_GUID
75+
}
76+
}
77+
78+
impl BlockIOProtocol {
79+
/// Indicates whether or not the device is removable.
80+
pub fn is_removable(&self) -> bool {
81+
unsafe { (*self.media).removable == 1 }
82+
}
83+
84+
/// Indicates whether or not the device is present.
85+
pub fn is_present(&self) -> bool {
86+
unsafe { (*self.media).present == 1 }
87+
}
88+
89+
/// Indicates whether or not the device is a logical partition.
90+
pub fn is_logical_partition(&self) -> bool {
91+
unsafe { (*self.media).logical_partition == 1 }
92+
}
93+
94+
/// Indicates whether or not the device is read only.
95+
pub fn is_read_only(&self) -> bool {
96+
unsafe { (*self.media).read_only == 1 }
97+
}
98+
99+
/// Indicates whether or not the device performs write caching.
100+
pub fn write_caching(&self) -> bool {
101+
unsafe { (*self.media).write_caching == 1 }
102+
}
103+
104+
/// Indicates whether or not the device has alignment requirements for buffers.
105+
pub fn must_align(&self) -> bool {
106+
unsafe { (*self.media).io_align > 1 }
107+
}
108+
109+
/// Calculates the required number of pages to read `blocks` blocks from this block device.
110+
pub fn required_pages_block(&self, blocks: usize) -> usize {
111+
let block_size = unsafe { (*self.media).block_size } as usize;
112+
let bytes = block_size * blocks;
113+
114+
self.required_pages(bytes)
115+
}
116+
117+
/// Calculates the required number of pages to read `read_size` bytes from this block device.
118+
pub fn required_pages(&self, read_size: usize) -> usize {
119+
let block_size = unsafe { (*self.media).block_size } as usize;
120+
let mut actual_read_size = read_size;
121+
122+
if read_size % block_size != 0 {
123+
actual_read_size = block_size * ((read_size / block_size) + 1);
124+
}
125+
126+
let mut num_pages = actual_read_size / 4096;
127+
if actual_read_size % 4096 != 0 {
128+
num_pages += 1;
129+
}
130+
131+
num_pages
132+
}
133+
134+
/// Free some data read by this protocol.
135+
pub fn free_read(&self, buffer: &mut [u8]) {
136+
::get_system_table().boot_services().free_pages(
137+
buffer.as_ptr(),
138+
self.required_pages(buffer.len()),
139+
);
140+
}
141+
142+
/// Reset the device.
143+
pub fn reset(&self, extended_verification: bool) -> Result<(), Status> {
144+
match unsafe { (self.reset)(self, extended_verification as u8) } {
145+
Status::Success => Ok(()),
146+
e => Err(e),
147+
}
148+
}
149+
150+
/// Read `num_bytes` bytes from the disk starting at block `start`. The returned slice includes
151+
/// memory allocated with `allocate_pages`, and it is the caller's responsibility to free it
152+
/// with `free_read`.
153+
pub fn read_bytes(&self, start: u64, num_bytes: usize) -> Result<&mut [u8], Status> {
154+
let bs = ::get_system_table().boot_services();
155+
let mut read_size = num_bytes;
156+
let buffer: Result<*mut u8, Status>;
157+
158+
// Reads can only be performed in multiples of the block size, so round up to the nearest
159+
// block.
160+
let block_size = unsafe { (*self.media).block_size } as usize;
161+
if num_bytes % block_size != 0 {
162+
read_size = block_size * ((num_bytes / block_size) + 1);
163+
}
164+
165+
// The read buffer must be aligned to the value of `media.io_align`. UEFI doesn't provide
166+
// any sort of memalign, so in order to be safe, use `allocate_pages` to obtain a 4K-aligned
167+
// address instead of `allocate_pool`. This isn't an ideal solution, but it does work in
168+
// lieu of implementing memalign and keeping track of the original allocation.
169+
buffer = bs.allocate_pages(self.required_pages(read_size)).map(|buf| buf as *mut u8);
170+
171+
buffer.and_then(|buffer| unsafe {
172+
let out_slice = slice::from_raw_parts_mut(buffer, num_bytes);
173+
match (self.read_blocks)(
174+
self,
175+
(*self.media).media_id,
176+
start,
177+
num_bytes,
178+
buffer as *mut CVoid,
179+
) {
180+
Status::Success => Ok(out_slice),
181+
e => {
182+
self.free_read(out_slice);
183+
Err(e)
184+
}
185+
}
186+
})
187+
}
188+
189+
/// Read `num_blocks` blocks from the disk starting at block `start`. The returned slice
190+
/// includes memory allocated with `allocate_pages`, and it is the caller's responsibility to
191+
/// free it.
192+
pub fn read_blocks(&self, start: u64, num_blocks: usize) -> Result<&mut [u8], Status> {
193+
let block_size = unsafe { (*self.media).block_size };
194+
let read_size_bytes = num_blocks * block_size as usize;
195+
self.read_bytes(start, read_size_bytes)
196+
}
197+
198+
/// Write `buffer` to the disk starting at block `start`. `buffer.len()` must be a multiple of
199+
/// the disks's block size, or else this call will fail.
200+
pub fn write_bytes(&self, start: u64, buffer: &[u8]) -> Result<(), Status> {
201+
match unsafe {
202+
(self.write_blocks)(
203+
self,
204+
(*self.media).media_id,
205+
start,
206+
buffer.len(),
207+
buffer.as_ptr() as *const CVoid,
208+
)
209+
} {
210+
Status::Success => Ok(()),
211+
e => Err(e),
212+
}
213+
}
214+
215+
/// Flush any pending writes to this disk.
216+
pub fn flush_blocks(&self) -> Result<(), Status> {
217+
match unsafe { (self.flush_blocks)(self) } {
218+
Status::Success => Ok(()),
219+
e => Err(e),
220+
}
221+
}
222+
}

src/protocol/disk.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2017 CoreOS, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use core::slice;
16+
17+
use base::Status;
18+
use guid::Guid;
19+
use protocol::Protocol;
20+
use void::{CVoid, NotYetDef};
21+
22+
pub static EFI_DISK_IO_PROTOCOL_GUID: Guid = Guid(
23+
0xCE34_5171,
24+
0xBA0B,
25+
0x11D2,
26+
[0x8E, 0x4F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B],
27+
);
28+
29+
/// Bindings to the EFI Disk I/O protocol. This protocol is a synchronous abstraction on top of the
30+
/// Block I/O protocol, and allows accessing arbitrary offsets/lengths instead of the block-based
31+
/// accesses the Block I/O protocol provides.
32+
#[repr(C)]
33+
pub struct DiskIOProtocol {
34+
revision: u64,
35+
read_disk: unsafe extern "win64" fn(this: *const DiskIOProtocol,
36+
media_id: u32,
37+
offset: u64,
38+
buffer_size: usize,
39+
buffer: *mut CVoid)
40+
-> Status,
41+
write_disk: *const NotYetDef,
42+
}
43+
44+
impl Protocol for DiskIOProtocol {
45+
fn guid() -> &'static Guid {
46+
&EFI_DISK_IO_PROTOCOL_GUID
47+
}
48+
}
49+
50+
impl DiskIOProtocol {
51+
/// Read data from the disk at the given offset and size. `media_id` should be derived from the
52+
/// Block I/O protocol (see specifically the `BlockIOMedia` struct). The returned slice
53+
/// includes memory allocated with `allocate_pool`, and it is the caller's responsibility to
54+
/// free it.
55+
pub fn read_disk(&self, media_id: u32, offset: u64, size: usize) -> Result<&[u8], Status> {
56+
::get_system_table()
57+
.boot_services()
58+
.allocate_pool::<u8>(size)
59+
.and_then(|buffer| unsafe {
60+
match (self.read_disk)(self, media_id, offset, size, buffer as *mut CVoid) {
61+
Status::Success => Ok(slice::from_raw_parts(buffer, size)),
62+
e => Err(e),
63+
}
64+
})
65+
}
66+
}

src/protocol/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ use base::{Handle, MemoryType, Status};
22
use guid::Guid;
33
use void::{CVoid, NotYetDef};
44

5+
mod block;
56
mod device_path;
7+
mod disk;
68
mod serial;
79

10+
pub use self::block::*;
811
pub use self::device_path::*;
12+
pub use self::disk::*;
913
pub use self::serial::*;
1014

1115
pub trait Protocol {

0 commit comments

Comments
 (0)