1//! Partition information protocol.
2
3use crate::proto::unsafe_protocol;
4use crate::{guid, Char16, Guid};
5use bitflags::bitflags;
6
7newtype_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)]
25pub 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
45impl 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
53newtype_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
69bitflags! {
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
101impl 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)]
114pub 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
136impl 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
148newtype_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)]
162union PartitionInfoRecord {
163 mbr: MbrPartitionRecord,
164 gpt: GptPartitionEntry,
165}
166
167newtype_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")]
179pub 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
191impl 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)]
230mod 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