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