Skip to content

Commit 50e2139

Browse files
committed
protocol: implement block and disk IO protocols
1 parent e83f46c commit 50e2139

File tree

3 files changed

+286
-0
lines changed

3 files changed

+286
-0
lines changed

src/protocol/block.rs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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+
pub fn required_pages_block(&self, blocks: usize) -> usize {
110+
let block_size = unsafe { (*self.media).block_size } as usize;
111+
let mut bytes = block_size * blocks;
112+
113+
if bytes % block_size != 0 {
114+
bytes = block_size * ((bytes / block_size) + 1);
115+
}
116+
117+
self.required_pages(bytes)
118+
}
119+
pub fn required_pages(&self, read_size: usize) -> usize {
120+
let mut num_pages = read_size / 4096;
121+
if read_size % 4096 != 0 {
122+
num_pages += 1;
123+
}
124+
125+
num_pages
126+
}
127+
128+
/// Reset the device.
129+
pub fn reset(&self, extended_verification: bool) -> Result<(), Status> {
130+
match unsafe { (self.reset)(self, extended_verification as u8) } {
131+
Status::Success => Ok(()),
132+
e => Err(e),
133+
}
134+
}
135+
136+
/// Read `num_bytes` bytes from the disk starting at block `start`. The returned slice includes
137+
/// memory allocated with `allocate_pool` or `allocate_pages`, and it is the caller's
138+
/// responsibility to free it with the appropriate function based on `must_align()`.
139+
pub fn read_bytes(&self, start: u64, num_bytes: usize) -> Result<&mut [u8], Status> {
140+
let bs = ::get_system_table().boot_services();
141+
let mut read_size = num_bytes;
142+
let buffer: Result<*mut u8, Status>;
143+
144+
// Reads can only be performed in multiples of the block size, so round up to the nearest
145+
// block.
146+
let block_size = unsafe { (*self.media).block_size } as usize;
147+
if num_bytes % block_size != 0 {
148+
read_size = block_size * ((num_bytes / block_size) + 1);
149+
}
150+
151+
// The read buffer must be aligned to the value of `media.io_align`. UEFI doesn't provide
152+
// any sort of memalign, so in order to be safe, if we need to be aligned on any boundary
153+
// greater than 1, use `allocate_pages` to obtain a 4K-aligned address instead of
154+
// `allocate_pool`. This isn't an ideal solution, but it does work in lieu of implementing
155+
// memalign and keeping track of the original allocation.
156+
if self.must_align() {
157+
buffer = bs.allocate_pages(self.required_pages(read_size)).map(|buf| buf as *mut u8);
158+
} else {
159+
buffer = bs.allocate_pool::<u8>(read_size);
160+
}
161+
162+
buffer.and_then(|buffer| unsafe {
163+
match (self.read_blocks)(
164+
self,
165+
(*self.media).media_id,
166+
start,
167+
num_bytes,
168+
buffer as *mut CVoid,
169+
) {
170+
Status::Success => Ok(slice::from_raw_parts_mut(buffer, num_bytes)),
171+
e => {
172+
if self.must_align() {
173+
bs.free_pages(buffer, self.required_pages(read_size));
174+
} else {
175+
bs.free_pool(buffer);
176+
}
177+
Err(e)
178+
}
179+
}
180+
})
181+
}
182+
183+
/// Read `num_blocks` blocks from the disk starting at block `start`. The returned slice
184+
/// includes memory allocated with `allocate_pool`, and it is the caller's responsibility to
185+
/// free it.
186+
pub fn read_blocks(&self, start: u64, num_blocks: usize) -> Result<&mut [u8], Status> {
187+
let block_size = unsafe { (*self.media).block_size };
188+
let read_size_bytes = num_blocks * block_size as usize;
189+
self.read_bytes(start, read_size_bytes)
190+
}
191+
192+
/// Write `buffer` to the disk starting at block `start`. `buffer.len()` must be a multiple of
193+
/// the disks's block size, or else this call will fail.
194+
pub fn write_bytes(&self, start: u64, buffer: &[u8]) -> Result<(), Status> {
195+
match unsafe {
196+
(self.write_blocks)(
197+
self,
198+
(*self.media).media_id,
199+
start,
200+
buffer.len(),
201+
buffer.as_ptr() as *const CVoid,
202+
)
203+
} {
204+
Status::Success => Ok(()),
205+
e => Err(e),
206+
}
207+
}
208+
209+
/// Flush any pending writes to this disk.
210+
pub fn flush_blocks(&self) -> Result<(), Status> {
211+
match unsafe { (self.flush_blocks)(self) } {
212+
Status::Success => Ok(()),
213+
e => Err(e),
214+
}
215+
}
216+
}

src/protocol/disk.rs

Lines changed: 66 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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)