morpheus_bootloader/uefi/
gpt_adapter.rs

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
// Adapter to use UEFI BlockIoProtocol with gpt_disk_io

use crate::uefi::block_io::BlockIoProtocol;
use core::fmt;
use gpt_disk_io::BlockIo;
use gpt_disk_types::{BlockSize, Lba};

pub struct UefiBlockIoAdapter<'a> {
    protocol: &'a mut BlockIoProtocol,
    block_size: BlockSize,
}

impl<'a> UefiBlockIoAdapter<'a> {
    pub fn new(protocol: &'a mut BlockIoProtocol) -> Result<Self, AdapterError> {
        let media = unsafe { &*protocol.media };

        let block_size = match media.block_size {
            512 => BlockSize::BS_512,
            4096 => BlockSize::BS_4096,
            _ => return Err(AdapterError::UnsupportedBlockSize(media.block_size)),
        };

        Ok(Self {
            protocol,
            block_size,
        })
    }
}

#[derive(Debug)]
pub enum AdapterError {
    UnsupportedBlockSize(u32),
    UefiError(usize),
}

impl fmt::Display for AdapterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnsupportedBlockSize(size) => {
                write!(f, "Unsupported block size: {}", size)
            }
            Self::UefiError(code) => {
                write!(f, "UEFI error: {}", code)
            }
        }
    }
}

impl BlockIo for UefiBlockIoAdapter<'_> {
    type Error = AdapterError;

    fn block_size(&self) -> BlockSize {
        self.block_size
    }

    fn num_blocks(&mut self) -> Result<u64, Self::Error> {
        let media = unsafe { &*self.protocol.media };
        Ok(media.last_block + 1)
    }

    fn read_blocks(&mut self, start_lba: Lba, dst: &mut [u8]) -> Result<(), Self::Error> {
        self.block_size.assert_valid_block_buffer(dst);

        let num_blocks = dst.len() / self.block_size.to_usize().unwrap();
        self.protocol
            .read_sectors(start_lba.to_u64(), num_blocks as u64, dst)
            .map_err(AdapterError::UefiError)
    }

    fn write_blocks(&mut self, start_lba: Lba, src: &[u8]) -> Result<(), Self::Error> {
        self.block_size.assert_valid_block_buffer(src);

        let num_blocks = src.len() / self.block_size.to_usize().unwrap();
        self.protocol
            .write_sectors(start_lba.to_u64(), num_blocks as u64, src)
            .map_err(AdapterError::UefiError)
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        // UEFI doesn't have explicit flush for BlockIO, writes are synchronous
        Ok(())
    }
}