xref: /aosp_15_r20/bootable/libbootloader/gbl/efi/src/efi_blocks.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker 
15*5225e6b1SAndroid Build Coastguard Worker use alloc::vec::Vec;
16*5225e6b1SAndroid Build Coastguard Worker use core::{cmp::max, fmt::Write};
17*5225e6b1SAndroid Build Coastguard Worker use efi::{
18*5225e6b1SAndroid Build Coastguard Worker     efi_print, efi_println,
19*5225e6b1SAndroid Build Coastguard Worker     protocol::{block_io::BlockIoProtocol, block_io2::BlockIo2Protocol, Protocol},
20*5225e6b1SAndroid Build Coastguard Worker     EfiEntry,
21*5225e6b1SAndroid Build Coastguard Worker };
22*5225e6b1SAndroid Build Coastguard Worker use efi_types::EfiBlockIoMedia;
23*5225e6b1SAndroid Build Coastguard Worker use gbl_async::block_on;
24*5225e6b1SAndroid Build Coastguard Worker use gbl_storage::{gpt_buffer_size, BlockInfo, BlockIo, Disk, Gpt, SliceMaybeUninit};
25*5225e6b1SAndroid Build Coastguard Worker use liberror::Error;
26*5225e6b1SAndroid Build Coastguard Worker use libgbl::partition::GblDisk;
27*5225e6b1SAndroid Build Coastguard Worker 
28*5225e6b1SAndroid Build Coastguard Worker /// `EfiBlockDeviceIo` wraps a EFI `BlockIoProtocol` or `BlockIo2Protocol` and implements the
29*5225e6b1SAndroid Build Coastguard Worker /// `BlockIo` interface.
30*5225e6b1SAndroid Build Coastguard Worker pub enum EfiBlockDeviceIo<'a> {
31*5225e6b1SAndroid Build Coastguard Worker     Sync(Protocol<'a, BlockIoProtocol>),
32*5225e6b1SAndroid Build Coastguard Worker     Async(Protocol<'a, BlockIo2Protocol>),
33*5225e6b1SAndroid Build Coastguard Worker }
34*5225e6b1SAndroid Build Coastguard Worker 
35*5225e6b1SAndroid Build Coastguard Worker impl<'a> EfiBlockDeviceIo<'a> {
media(&self) -> EfiBlockIoMedia36*5225e6b1SAndroid Build Coastguard Worker     fn media(&self) -> EfiBlockIoMedia {
37*5225e6b1SAndroid Build Coastguard Worker         match self {
38*5225e6b1SAndroid Build Coastguard Worker             EfiBlockDeviceIo::Sync(v) => v.media(),
39*5225e6b1SAndroid Build Coastguard Worker             EfiBlockDeviceIo::Async(v) => v.media(),
40*5225e6b1SAndroid Build Coastguard Worker         }
41*5225e6b1SAndroid Build Coastguard Worker         .unwrap()
42*5225e6b1SAndroid Build Coastguard Worker     }
43*5225e6b1SAndroid Build Coastguard Worker 
info(&mut self) -> BlockInfo44*5225e6b1SAndroid Build Coastguard Worker     fn info(&mut self) -> BlockInfo {
45*5225e6b1SAndroid Build Coastguard Worker         let media = self.media();
46*5225e6b1SAndroid Build Coastguard Worker         BlockInfo {
47*5225e6b1SAndroid Build Coastguard Worker             block_size: media.block_size as u64,
48*5225e6b1SAndroid Build Coastguard Worker             num_blocks: (media.last_block + 1) as u64,
49*5225e6b1SAndroid Build Coastguard Worker             alignment: max(1, media.io_align as u64),
50*5225e6b1SAndroid Build Coastguard Worker         }
51*5225e6b1SAndroid Build Coastguard Worker     }
52*5225e6b1SAndroid Build Coastguard Worker }
53*5225e6b1SAndroid Build Coastguard Worker 
54*5225e6b1SAndroid Build Coastguard Worker // SAFETY:
55*5225e6b1SAndroid Build Coastguard Worker // `read_blocks()` usess EFI protocol that guarantees to read exact number of blocks that were
56*5225e6b1SAndroid Build Coastguard Worker // requested, or return error.
57*5225e6b1SAndroid Build Coastguard Worker // For async `read_blocks_ex()` blocking wait guarantees that read finishes.
58*5225e6b1SAndroid Build Coastguard Worker unsafe impl BlockIo for EfiBlockDeviceIo<'_> {
info(&mut self) -> BlockInfo59*5225e6b1SAndroid Build Coastguard Worker     fn info(&mut self) -> BlockInfo {
60*5225e6b1SAndroid Build Coastguard Worker         (*self).info()
61*5225e6b1SAndroid Build Coastguard Worker     }
62*5225e6b1SAndroid Build Coastguard Worker 
read_blocks( &mut self, blk_offset: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error>63*5225e6b1SAndroid Build Coastguard Worker     async fn read_blocks(
64*5225e6b1SAndroid Build Coastguard Worker         &mut self,
65*5225e6b1SAndroid Build Coastguard Worker         blk_offset: u64,
66*5225e6b1SAndroid Build Coastguard Worker         out: &mut (impl SliceMaybeUninit + ?Sized),
67*5225e6b1SAndroid Build Coastguard Worker     ) -> Result<(), Error> {
68*5225e6b1SAndroid Build Coastguard Worker         match self {
69*5225e6b1SAndroid Build Coastguard Worker             EfiBlockDeviceIo::Sync(v) => v.read_blocks(blk_offset, out),
70*5225e6b1SAndroid Build Coastguard Worker             EfiBlockDeviceIo::Async(v) => v.read_blocks_ex(blk_offset, out).await,
71*5225e6b1SAndroid Build Coastguard Worker         }
72*5225e6b1SAndroid Build Coastguard Worker         .or(Err(Error::BlockIoError))
73*5225e6b1SAndroid Build Coastguard Worker     }
74*5225e6b1SAndroid Build Coastguard Worker 
write_blocks(&mut self, blk_offset: u64, data: &mut [u8]) -> Result<(), Error>75*5225e6b1SAndroid Build Coastguard Worker     async fn write_blocks(&mut self, blk_offset: u64, data: &mut [u8]) -> Result<(), Error> {
76*5225e6b1SAndroid Build Coastguard Worker         match self {
77*5225e6b1SAndroid Build Coastguard Worker             EfiBlockDeviceIo::Sync(v) => v.write_blocks(blk_offset, data),
78*5225e6b1SAndroid Build Coastguard Worker             EfiBlockDeviceIo::Async(v) => v.write_blocks_ex(blk_offset, data).await,
79*5225e6b1SAndroid Build Coastguard Worker         }
80*5225e6b1SAndroid Build Coastguard Worker         .or(Err(Error::BlockIoError))
81*5225e6b1SAndroid Build Coastguard Worker     }
82*5225e6b1SAndroid Build Coastguard Worker }
83*5225e6b1SAndroid Build Coastguard Worker 
84*5225e6b1SAndroid Build Coastguard Worker const MAX_GPT_ENTRIES: usize = 128;
85*5225e6b1SAndroid Build Coastguard Worker 
86*5225e6b1SAndroid Build Coastguard Worker /// The [GblDisk] type in the GBL EFI context.
87*5225e6b1SAndroid Build Coastguard Worker pub type EfiGblDisk<'a> = GblDisk<Disk<EfiBlockDeviceIo<'a>, Vec<u8>>, Gpt<Vec<u8>>>;
88*5225e6b1SAndroid Build Coastguard Worker 
89*5225e6b1SAndroid Build Coastguard Worker /// Finds and returns all EFI devices supporting either EFI_BLOCK_IO or EFI_BLOCK_IO2 protocol.
find_block_devices(efi_entry: &EfiEntry) -> Result<Vec<EfiGblDisk<'_>>, Error>90*5225e6b1SAndroid Build Coastguard Worker pub fn find_block_devices(efi_entry: &EfiEntry) -> Result<Vec<EfiGblDisk<'_>>, Error> {
91*5225e6b1SAndroid Build Coastguard Worker     let bs = efi_entry.system_table().boot_services();
92*5225e6b1SAndroid Build Coastguard Worker     let block_dev_handles = bs.locate_handle_buffer_by_protocol::<BlockIoProtocol>()?;
93*5225e6b1SAndroid Build Coastguard Worker     let mut gbl_disks = vec![];
94*5225e6b1SAndroid Build Coastguard Worker     let gpt_buffer_size = gpt_buffer_size(MAX_GPT_ENTRIES)?;
95*5225e6b1SAndroid Build Coastguard Worker     for (idx, handle) in block_dev_handles.handles().iter().enumerate() {
96*5225e6b1SAndroid Build Coastguard Worker         // Prioritizes `BlockIo2Protocol`.
97*5225e6b1SAndroid Build Coastguard Worker         let blk_io = match bs.open_protocol::<BlockIo2Protocol>(*handle) {
98*5225e6b1SAndroid Build Coastguard Worker             Ok(v) => EfiBlockDeviceIo::Async(v),
99*5225e6b1SAndroid Build Coastguard Worker             _ => EfiBlockDeviceIo::Sync(bs.open_protocol::<BlockIoProtocol>(*handle)?),
100*5225e6b1SAndroid Build Coastguard Worker         };
101*5225e6b1SAndroid Build Coastguard Worker         if blk_io.media().logical_partition {
102*5225e6b1SAndroid Build Coastguard Worker             continue;
103*5225e6b1SAndroid Build Coastguard Worker         }
104*5225e6b1SAndroid Build Coastguard Worker         // TODO(b/357688291): Support raw partition based on device path info.
105*5225e6b1SAndroid Build Coastguard Worker         let disk = GblDisk::new_gpt(
106*5225e6b1SAndroid Build Coastguard Worker             Disk::new_alloc_scratch(blk_io).unwrap(),
107*5225e6b1SAndroid Build Coastguard Worker             Gpt::new(vec![0u8; gpt_buffer_size]).unwrap(),
108*5225e6b1SAndroid Build Coastguard Worker         );
109*5225e6b1SAndroid Build Coastguard Worker         match block_on(disk.sync_gpt()) {
110*5225e6b1SAndroid Build Coastguard Worker             Ok(Some(v)) => efi_println!(efi_entry, "Block #{idx} GPT sync result: {v}"),
111*5225e6b1SAndroid Build Coastguard Worker             Err(e) => efi_println!(efi_entry, "Block #{idx} error while syncing GPT: {e}"),
112*5225e6b1SAndroid Build Coastguard Worker             _ => {}
113*5225e6b1SAndroid Build Coastguard Worker         };
114*5225e6b1SAndroid Build Coastguard Worker         gbl_disks.push(disk);
115*5225e6b1SAndroid Build Coastguard Worker     }
116*5225e6b1SAndroid Build Coastguard Worker     Ok(gbl_disks)
117*5225e6b1SAndroid Build Coastguard Worker }
118