• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023, The Android Open Source Project
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 //! Support for parsing GUID partition tables.
16 
17 use core::cmp::min;
18 use core::fmt;
19 use core::mem::size_of;
20 use core::ops::RangeInclusive;
21 use core::slice;
22 use static_assertions::const_assert;
23 use static_assertions::const_assert_eq;
24 use uuid::Uuid;
25 use virtio_drivers::device::blk::SECTOR_SIZE;
26 use vmbase::util::ceiling_div;
27 use vmbase::virtio::{pci, HalImpl};
28 use zerocopy::FromBytes;
29 
30 type VirtIOBlk = pci::VirtIOBlk<HalImpl>;
31 
32 pub enum Error {
33     /// VirtIO error during read operation.
34     FailedRead(virtio_drivers::Error),
35     /// VirtIO error during write operation.
36     FailedWrite(virtio_drivers::Error),
37     /// Invalid GPT header.
38     InvalidHeader,
39     /// Invalid partition block index.
40     BlockOutsidePartition(usize),
41 }
42 
43 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result44     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45         match self {
46             Self::FailedRead(e) => write!(f, "Failed to read from disk: {e}"),
47             Self::FailedWrite(e) => write!(f, "Failed to write to disk: {e}"),
48             Self::InvalidHeader => write!(f, "Found invalid GPT header"),
49             Self::BlockOutsidePartition(i) => write!(f, "Accessed invalid block index {i}"),
50         }
51     }
52 }
53 
54 pub type Result<T> = core::result::Result<T, Error>;
55 
56 pub struct Partition {
57     partitions: Partitions,
58     indices: RangeInclusive<usize>,
59 }
60 
61 impl Partition {
get_by_name(device: VirtIOBlk, name: &str) -> Result<Option<Self>>62     pub fn get_by_name(device: VirtIOBlk, name: &str) -> Result<Option<Self>> {
63         Partitions::new(device)?.get_partition_by_name(name)
64     }
65 
new(partitions: Partitions, entry: &Entry) -> Self66     fn new(partitions: Partitions, entry: &Entry) -> Self {
67         let first = entry.first_lba().try_into().unwrap();
68         let last = entry.last_lba().try_into().unwrap();
69 
70         Self { partitions, indices: first..=last }
71     }
72 
indices(&self) -> RangeInclusive<usize>73     pub fn indices(&self) -> RangeInclusive<usize> {
74         self.indices.clone()
75     }
76 
read_block(&mut self, index: usize, blk: &mut [u8]) -> Result<()>77     pub fn read_block(&mut self, index: usize, blk: &mut [u8]) -> Result<()> {
78         let index = self.block_index(index).ok_or(Error::BlockOutsidePartition(index))?;
79         self.partitions.read_block(index, blk)
80     }
81 
write_block(&mut self, index: usize, blk: &[u8]) -> Result<()>82     pub fn write_block(&mut self, index: usize, blk: &[u8]) -> Result<()> {
83         let index = self.block_index(index).ok_or(Error::BlockOutsidePartition(index))?;
84         self.partitions.write_block(index, blk)
85     }
86 
block_index(&self, index: usize) -> Option<usize>87     fn block_index(&self, index: usize) -> Option<usize> {
88         if self.indices.contains(&index) {
89             Some(index)
90         } else {
91             None
92         }
93     }
94 }
95 
96 pub struct Partitions {
97     device: VirtIOBlk,
98     entries_count: usize,
99 }
100 
101 impl Partitions {
102     pub const LBA_SIZE: usize = SECTOR_SIZE;
103 
new(mut device: VirtIOBlk) -> Result<Self>104     fn new(mut device: VirtIOBlk) -> Result<Self> {
105         let mut blk = [0; Self::LBA_SIZE];
106         device.read_blocks(Header::LBA, &mut blk).map_err(Error::FailedRead)?;
107         let header = Header::read_from_prefix(blk.as_slice()).unwrap().0;
108         if !header.is_valid() {
109             return Err(Error::InvalidHeader);
110         }
111         let entries_count = usize::try_from(header.entries_count()).unwrap();
112 
113         Ok(Self { device, entries_count })
114     }
115 
get_partition_by_name(mut self, name: &str) -> Result<Option<Partition>>116     fn get_partition_by_name(mut self, name: &str) -> Result<Option<Partition>> {
117         const_assert_eq!(Partitions::LBA_SIZE.rem_euclid(size_of::<Entry>()), 0);
118         let entries_per_blk = Partitions::LBA_SIZE.checked_div(size_of::<Entry>()).unwrap();
119 
120         // Create a UTF-16 reference against which we'll compare partition names. Note that unlike
121         // the C99 wcslen(), this comparison will cover bytes past the first L'\0' character.
122         let mut needle = [0; Entry::NAME_SIZE / size_of::<u16>()];
123         for (dest, src) in needle.iter_mut().zip(name.encode_utf16()) {
124             *dest = src;
125         }
126 
127         let mut blk = [0; Self::LBA_SIZE];
128         let mut rem = self.entries_count;
129         let num_blocks = ceiling_div(self.entries_count, entries_per_blk).unwrap();
130         for i in Header::ENTRIES_LBA..Header::ENTRIES_LBA.checked_add(num_blocks).unwrap() {
131             self.read_block(i, &mut blk)?;
132             let entries = blk.as_ptr().cast::<Entry>();
133             // SAFETY: blk is assumed to be properly aligned for Entry and its size is assert-ed
134             // above. All potential values of the slice will produce valid Entry values.
135             let entries = unsafe { slice::from_raw_parts(entries, min(rem, entries_per_blk)) };
136             for entry in entries {
137                 let entry_name = entry.name;
138                 if entry_name == needle {
139                     return Ok(Some(Partition::new(self, entry)));
140                 }
141                 rem -= 1;
142             }
143         }
144         Ok(None)
145     }
146 
read_block(&mut self, index: usize, blk: &mut [u8]) -> Result<()>147     fn read_block(&mut self, index: usize, blk: &mut [u8]) -> Result<()> {
148         self.device.read_blocks(index, blk).map_err(Error::FailedRead)
149     }
150 
write_block(&mut self, index: usize, blk: &[u8]) -> Result<()>151     fn write_block(&mut self, index: usize, blk: &[u8]) -> Result<()> {
152         self.device.write_blocks(index, blk).map_err(Error::FailedWrite)
153     }
154 }
155 
156 type Lba = u64;
157 
158 /// Structure as defined in release 2.10 of the UEFI Specification (5.3.2 GPT Header).
159 #[derive(FromBytes)]
160 #[repr(C, packed)]
161 struct Header {
162     signature: u64,
163     revision: u32,
164     header_size: u32,
165     header_crc32: u32,
166     reserved0: u32,
167     current_lba: Lba,
168     backup_lba: Lba,
169     first_lba: Lba,
170     last_lba: Lba,
171     disk_guid: u128,
172     entries_lba: Lba,
173     entries_count: u32,
174     entry_size: u32,
175     entries_crc32: u32,
176 }
177 const_assert!(size_of::<Header>() < Partitions::LBA_SIZE);
178 
179 impl Header {
180     const SIGNATURE: u64 = u64::from_le_bytes(*b"EFI PART");
181     const REVISION_1_0: u32 = 1 << 16;
182     const LBA: usize = 1;
183     const ENTRIES_LBA: usize = 2;
184 
is_valid(&self) -> bool185     fn is_valid(&self) -> bool {
186         self.signature() == Self::SIGNATURE
187             && self.header_size() == size_of::<Self>().try_into().unwrap()
188             && self.revision() == Self::REVISION_1_0
189             && self.entry_size() == size_of::<Entry>().try_into().unwrap()
190             && self.current_lba() == Self::LBA.try_into().unwrap()
191             && self.entries_lba() == Self::ENTRIES_LBA.try_into().unwrap()
192     }
193 
signature(&self) -> u64194     fn signature(&self) -> u64 {
195         u64::from_le(self.signature)
196     }
197 
entries_count(&self) -> u32198     fn entries_count(&self) -> u32 {
199         u32::from_le(self.entries_count)
200     }
201 
header_size(&self) -> u32202     fn header_size(&self) -> u32 {
203         u32::from_le(self.header_size)
204     }
205 
revision(&self) -> u32206     fn revision(&self) -> u32 {
207         u32::from_le(self.revision)
208     }
209 
entry_size(&self) -> u32210     fn entry_size(&self) -> u32 {
211         u32::from_le(self.entry_size)
212     }
213 
entries_lba(&self) -> Lba214     fn entries_lba(&self) -> Lba {
215         Lba::from_le(self.entries_lba)
216     }
217 
current_lba(&self) -> Lba218     fn current_lba(&self) -> Lba {
219         Lba::from_le(self.current_lba)
220     }
221 }
222 
223 /// Structure as defined in release 2.10 of the UEFI Specification (5.3.3 GPT Partition Entry
224 /// Array).
225 #[repr(C, packed)]
226 struct Entry {
227     type_guid: Uuid,
228     guid: Uuid,
229     first_lba: Lba,
230     last_lba: Lba,
231     flags: u64,
232     name: [u16; Entry::NAME_SIZE / size_of::<u16>()], // UTF-16
233 }
234 
235 impl Entry {
236     const NAME_SIZE: usize = 72;
237 
first_lba(&self) -> Lba238     fn first_lba(&self) -> Lba {
239         Lba::from_le(self.first_lba)
240     }
241 
last_lba(&self) -> Lba242     fn last_lba(&self) -> Lba {
243         Lba::from_le(self.last_lba)
244     }
245 }
246