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