Skip to content

Commit 582ef06

Browse files
committed
protocol: implement block and disk IO protocols
1 parent 05cc323 commit 582ef06

File tree

3 files changed

+267
-0
lines changed

3 files changed

+267
-0
lines changed

src/protocol/block.rs

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

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)