1 //! Partition information protocol.
2 
3 use crate::proto::unsafe_protocol;
4 use crate::{guid, Char16, Guid};
5 
6 newtype_enum! {
7     /// MBR OS type.
8     ///
9     /// Only two values are defined in the UEFI specification, other
10     /// values are used by legacy operating systems.
11     pub enum MbrOsType: u8 => {
12         /// A fake partition covering the entire disk.
13         GPT_PROTECTIVE = 0xee,
14 
15         /// UEFI system partition.
16         UEFI_SYSTEM_PARTITION = 0xef,
17     }
18 }
19 
20 /// Legacy MBR Partition Record.
21 #[repr(C)]
22 #[repr(packed)]
23 #[derive(Clone, Copy, Debug)]
24 pub struct MbrPartitionRecord {
25     /// If 0x80, this is the bootable legacy partition.
26     pub boot_indicator: u8,
27 
28     /// Start of the partition in CHS address format.
29     pub starting_chs: [u8; 3],
30 
31     /// Type of partition.
32     pub os_type: MbrOsType,
33 
34     /// End of the partition in CHS address format.
35     pub ending_chs: [u8; 3],
36 
37     /// Starting LBA of the partition on the disk.
38     pub starting_lba: u32,
39 
40     /// Size of the partition in LBA units of logical blocks.
41     pub size_in_lba: u32,
42 }
43 
44 impl MbrPartitionRecord {
45     /// True if the partition is a bootable legacy partition.
46     #[must_use]
is_bootable(&self) -> bool47     pub const fn is_bootable(&self) -> bool {
48         self.boot_indicator == 0x80
49     }
50 }
51 
52 newtype_enum! {
53     /// GUID that defines the type of partition. Only three values are
54     /// defined in the UEFI specification, OS vendors define their own
55     /// Partition Type GUIDs.
56     pub enum GptPartitionType: Guid => {
57         /// Indicates a partition entry is unused.
58         UNUSED_ENTRY = guid!("00000000-0000-0000-0000-000000000000"),
59 
60         /// EFI System Partition.
61         EFI_SYSTEM_PARTITION = guid!("c12a7328-f81f-11d2-ba4b-00a0c93ec93b"),
62 
63         /// Partition containing a legacy MBR.
64         LEGACY_MBR = guid!("024dee41-33e7-11d3-9d69-0008c781f39f"),
65     }
66 }
67 
68 bitflags::bitflags! {
69 
70     /// Attributes describing a GPT partition.
71     ///
72     /// * Bit 0: [`REQUIRED_PARTITION`][Self::REQUIRED_PARTITION]
73     /// * Bit 1: [`NO_BLOCK_IO_PROTOCOL`][Self::NO_BLOCK_IO_PROTOCOL]
74     /// * Bit 2: [`LEGACY_BIOS_BOOTABLE`][Self::LEGACY_BIOS_BOOTABLE]
75     /// * Bits `3..=47`: reserved for future use and must be zero.
76     /// * Bits `48..=63`: See
77     /// [`type_specific_bits`][Self::type_specific_bits] and
78     /// [`RESERVED_FOR_PARTITION_TYPE`][Self::RESERVED_FOR_PARTITION_TYPE].
79     #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
80     #[repr(transparent)]
81     pub struct GptPartitionAttributes: u64 {
82         /// Bit: Partition is required for the platform to function.
83         const REQUIRED_PARTITION = 1;
84         /// Bit: No [`BlockIO`] protocol will be created for this partition.
85         ///
86         /// [`BlockIO`]: uefi::proto::media::block::BlockIO
87         const NO_BLOCK_IO_PROTOCOL = 1 << 1;
88 
89         /// Bit: Indicates that special software on a legacy BIOS system may
90         /// treat this partition as bootable. UEFI boot managers must
91         /// ignore the partition.
92         const LEGACY_BIOS_BOOTABLE = 1 << 2;
93 
94         /// Mask for bits `48..=63`. The meaning of these bits depends
95         /// on the partition type.
96         const RESERVED_FOR_PARTITION_TYPE = 0xffff_0000_0000_0000;
97 
98         /// The meaning of this bit depends on the partition type.
99         const TYPE_SPECIFIC_BIT_0 = 1 << 47;
100 
101         /// The meaning of this bit depends on the partition type.
102         const TYPE_SPECIFIC_BIT_1 = 1 << 48;
103 
104         /// The meaning of this bit depends on the partition type.
105         const TYPE_SPECIFIC_BIT_2 = 1 << 49;
106 
107         /// The meaning of this bit depends on the partition type.
108         const TYPE_SPECIFIC_BIT_3 = 1 << 50;
109 
110         /// The meaning of this bit depends on the partition type.
111         const TYPE_SPECIFIC_BIT_4 = 1 << 51;
112 
113         /// The meaning of this bit depends on the partition type.
114         const TYPE_SPECIFIC_BIT_5 = 1 << 52;
115 
116         /// The meaning of this bit depends on the partition type.
117         const TYPE_SPECIFIC_BIT_6 = 1 << 53;
118 
119         /// The meaning of this bit depends on the partition type.
120         const TYPE_SPECIFIC_BIT_7 = 1 << 54;
121 
122         /// The meaning of this bit depends on the partition type.
123         const TYPE_SPECIFIC_BIT_8 = 1 << 55;
124 
125         /// The meaning of this bit depends on the partition type.
126         const TYPE_SPECIFIC_BIT_9 = 1 << 56;
127 
128         /// The meaning of this bit depends on the partition type.
129         const TYPE_SPECIFIC_BIT_10 = 1 << 57;
130 
131         /// The meaning of this bit depends on the partition type.
132         const TYPE_SPECIFIC_BIT_11 = 1 << 58;
133 
134         /// The meaning of this bit depends on the partition type.
135         const TYPE_SPECIFIC_BIT_12 = 1 << 59;
136 
137         /// The meaning of this bit depends on the partition type.
138         const TYPE_SPECIFIC_BIT_13 = 1 << 60;
139 
140         /// The meaning of this bit depends on the partition type.
141         const TYPE_SPECIFIC_BIT_14 = 1 << 61;
142 
143         /// The meaning of this bit depends on the partition type.
144         const TYPE_SPECIFIC_BIT_15 = 1 << 62;
145     }
146 }
147 
148 impl GptPartitionAttributes {
149     /// Get bits `48..=63` as a [`u16`]. The meaning of these bits depends
150     /// on the partition's type (see [`GptPartitionEntry::partition_type_guid`]).
151     #[must_use]
type_specific_bits(&self) -> u16152     pub const fn type_specific_bits(&self) -> u16 {
153         (self.0.bits() >> 48) as u16
154     }
155 }
156 
157 /// GPT/EFI Partition Entry.
158 #[repr(C)]
159 #[repr(packed)]
160 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
161 pub struct GptPartitionEntry {
162     /// GUID that defines the type of this Partition. A value of zero
163     /// indicates that this partition entry is unused.
164     pub partition_type_guid: GptPartitionType,
165 
166     /// GUID that is unique for every partition entry.
167     pub unique_partition_guid: Guid,
168 
169     /// Starting LBA of the partition.
170     pub starting_lba: u64,
171 
172     /// Ending LBA of the partition.
173     pub ending_lba: u64,
174 
175     /// All attribute bits of the partition.
176     pub attributes: GptPartitionAttributes,
177 
178     /// Null-terminated string containing a human-readable name of the
179     /// partition.
180     pub partition_name: [Char16; 36],
181 }
182 
183 impl GptPartitionEntry {
184     /// Get the number of blocks in the partition. Returns `None` if the
185     /// end block is before the start block, or if the number doesn't
186     /// fit in a `u64`.
187     #[must_use]
num_blocks(&self) -> Option<u64>188     pub fn num_blocks(&self) -> Option<u64> {
189         self.ending_lba
190             .checked_sub(self.starting_lba)?
191             .checked_add(1)
192     }
193 }
194 
195 newtype_enum! {
196     /// Partition type.
197     pub enum PartitionType: u32 => {
198         /// Partition is not MBR or GPT.
199         OTHER = 0x00,
200         /// MBR partition.
201         MBR = 0x01,
202         /// GPT partition.
203         GPT = 0x02,
204     }
205 }
206 
207 #[repr(C)]
208 #[derive(Clone, Copy)]
209 union PartitionInfoRecord {
210     mbr: MbrPartitionRecord,
211     gpt: GptPartitionEntry,
212 }
213 
214 newtype_enum! {
215     /// Partition info protocol revision.
216     pub enum PartitionInfoRevision: u32 => {
217         /// Revision of EFI_PARTITION_INFO_PROTOCOL_REVISION.
218         PROTOCOL_REVISION = 0x0001000,
219     }
220 }
221 
222 /// Protocol for accessing partition information.
223 #[allow(missing_debug_implementations)]
224 #[repr(C)]
225 #[repr(packed)]
226 #[unsafe_protocol("8cf2f62c-bc9b-4821-808d-ec9ec421a1a0")]
227 pub struct PartitionInfo {
228     /// Revision of the partition info protocol.
229     pub revision: PartitionInfoRevision,
230 
231     /// Type of partition.
232     pub partition_type: PartitionType,
233 
234     system: u8,
235     reserved: [u8; 7],
236     record: PartitionInfoRecord,
237 }
238 
239 impl PartitionInfo {
240     /// True if the partition is an EFI system partition.
241     #[must_use]
is_system(&self) -> bool242     pub const fn is_system(&self) -> bool {
243         self.system == 1
244     }
245 
246     /// Get the MBR partition record. Returns None if the partition
247     /// type is not MBR.
248     #[must_use]
mbr_partition_record(&self) -> Option<&MbrPartitionRecord>249     pub fn mbr_partition_record(&self) -> Option<&MbrPartitionRecord> {
250         if { self.revision } != PartitionInfoRevision::PROTOCOL_REVISION {
251             return None;
252         }
253 
254         if { self.partition_type } == PartitionType::MBR {
255             Some(unsafe { &self.record.mbr })
256         } else {
257             None
258         }
259     }
260 
261     /// Get the GPT partition entry. Returns None if the partition
262     /// type is not GPT.
263     #[must_use]
gpt_partition_entry(&self) -> Option<&GptPartitionEntry>264     pub fn gpt_partition_entry(&self) -> Option<&GptPartitionEntry> {
265         if { self.revision } != PartitionInfoRevision::PROTOCOL_REVISION {
266             return None;
267         }
268 
269         if { self.partition_type } == PartitionType::GPT {
270             Some(unsafe { &self.record.gpt })
271         } else {
272             None
273         }
274     }
275 }
276 
277 #[cfg(test)]
278 mod tests {
279     use super::*;
280 
281     #[test]
test_partition_attributes()282     fn test_partition_attributes() {
283         let attr: GptPartitionAttributes =
284             GptPartitionAttributes::from_bits_retain(0xabcd_0000_0000_0007);
285         assert_eq!(attr.type_specific_bits(), 0xabcd);
286     }
287 }
288