blob: c2b368a9cf1918c81068c4b8e8045db59a7d694c [file] [log] [blame]
//! Partition information protocol.
use crate::proto::unsafe_protocol;
use crate::{guid, Char16, Guid};
newtype_enum! {
/// MBR OS type.
///
/// Only two values are defined in the UEFI specification, other
/// values are used by legacy operating systems.
pub enum MbrOsType: u8 => {
/// A fake partition covering the entire disk.
GPT_PROTECTIVE = 0xee,
/// UEFI system partition.
UEFI_SYSTEM_PARTITION = 0xef,
}
}
/// Legacy MBR Partition Record.
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
pub struct MbrPartitionRecord {
/// If 0x80, this is the bootable legacy partition.
pub boot_indicator: u8,
/// Start of the partition in CHS address format.
pub starting_chs: [u8; 3],
/// Type of partition.
pub os_type: MbrOsType,
/// End of the partition in CHS address format.
pub ending_chs: [u8; 3],
/// Starting LBA of the partition on the disk.
pub starting_lba: u32,
/// Size of the partition in LBA units of logical blocks.
pub size_in_lba: u32,
}
impl MbrPartitionRecord {
/// True if the partition is a bootable legacy partition.
#[must_use]
pub const fn is_bootable(&self) -> bool {
self.boot_indicator == 0x80
}
}
newtype_enum! {
/// GUID that defines the type of partition. Only three values are
/// defined in the UEFI specification, OS vendors define their own
/// Partition Type GUIDs.
pub enum GptPartitionType: Guid => {
/// Indicates a partition entry is unused.
UNUSED_ENTRY = guid!("00000000-0000-0000-0000-000000000000"),
/// EFI System Partition.
EFI_SYSTEM_PARTITION = guid!("c12a7328-f81f-11d2-ba4b-00a0c93ec93b"),
/// Partition containing a legacy MBR.
LEGACY_MBR = guid!("024dee41-33e7-11d3-9d69-0008c781f39f"),
}
}
bitflags::bitflags! {
/// Attributes describing a GPT partition.
///
/// * Bit 0: [`REQUIRED_PARTITION`][Self::REQUIRED_PARTITION]
/// * Bit 1: [`NO_BLOCK_IO_PROTOCOL`][Self::NO_BLOCK_IO_PROTOCOL]
/// * Bit 2: [`LEGACY_BIOS_BOOTABLE`][Self::LEGACY_BIOS_BOOTABLE]
/// * Bits `3..=47`: reserved for future use and must be zero.
/// * Bits `48..=63`: See
/// [`type_specific_bits`][Self::type_specific_bits] and
/// [`RESERVED_FOR_PARTITION_TYPE`][Self::RESERVED_FOR_PARTITION_TYPE].
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct GptPartitionAttributes: u64 {
/// Bit: Partition is required for the platform to function.
const REQUIRED_PARTITION = 1;
/// Bit: No [`BlockIO`] protocol will be created for this partition.
///
/// [`BlockIO`]: uefi::proto::media::block::BlockIO
const NO_BLOCK_IO_PROTOCOL = 1 << 1;
/// Bit: Indicates that special software on a legacy BIOS system may
/// treat this partition as bootable. UEFI boot managers must
/// ignore the partition.
const LEGACY_BIOS_BOOTABLE = 1 << 2;
/// Mask for bits `48..=63`. The meaning of these bits depends
/// on the partition type.
const RESERVED_FOR_PARTITION_TYPE = 0xffff_0000_0000_0000;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_0 = 1 << 47;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_1 = 1 << 48;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_2 = 1 << 49;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_3 = 1 << 50;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_4 = 1 << 51;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_5 = 1 << 52;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_6 = 1 << 53;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_7 = 1 << 54;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_8 = 1 << 55;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_9 = 1 << 56;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_10 = 1 << 57;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_11 = 1 << 58;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_12 = 1 << 59;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_13 = 1 << 60;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_14 = 1 << 61;
/// The meaning of this bit depends on the partition type.
const TYPE_SPECIFIC_BIT_15 = 1 << 62;
}
}
impl GptPartitionAttributes {
/// Get bits `48..=63` as a [`u16`]. The meaning of these bits depends
/// on the partition's type (see [`GptPartitionEntry::partition_type_guid`]).
#[must_use]
pub const fn type_specific_bits(&self) -> u16 {
(self.0.bits() >> 48) as u16
}
}
/// GPT/EFI Partition Entry.
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GptPartitionEntry {
/// GUID that defines the type of this Partition. A value of zero
/// indicates that this partition entry is unused.
pub partition_type_guid: GptPartitionType,
/// GUID that is unique for every partition entry.
pub unique_partition_guid: Guid,
/// Starting LBA of the partition.
pub starting_lba: u64,
/// Ending LBA of the partition.
pub ending_lba: u64,
/// All attribute bits of the partition.
pub attributes: GptPartitionAttributes,
/// Null-terminated string containing a human-readable name of the
/// partition.
pub partition_name: [Char16; 36],
}
impl GptPartitionEntry {
/// Get the number of blocks in the partition. Returns `None` if the
/// end block is before the start block, or if the number doesn't
/// fit in a `u64`.
#[must_use]
pub fn num_blocks(&self) -> Option<u64> {
self.ending_lba
.checked_sub(self.starting_lba)?
.checked_add(1)
}
}
newtype_enum! {
/// Partition type.
pub enum PartitionType: u32 => {
/// Partition is not MBR or GPT.
OTHER = 0x00,
/// MBR partition.
MBR = 0x01,
/// GPT partition.
GPT = 0x02,
}
}
#[repr(C)]
#[derive(Clone, Copy)]
union PartitionInfoRecord {
mbr: MbrPartitionRecord,
gpt: GptPartitionEntry,
}
newtype_enum! {
/// Partition info protocol revision.
pub enum PartitionInfoRevision: u32 => {
/// Revision of EFI_PARTITION_INFO_PROTOCOL_REVISION.
PROTOCOL_REVISION = 0x0001000,
}
}
/// Protocol for accessing partition information.
#[allow(missing_debug_implementations)]
#[repr(C)]
#[repr(packed)]
#[unsafe_protocol("8cf2f62c-bc9b-4821-808d-ec9ec421a1a0")]
pub struct PartitionInfo {
/// Revision of the partition info protocol.
pub revision: PartitionInfoRevision,
/// Type of partition.
pub partition_type: PartitionType,
system: u8,
reserved: [u8; 7],
record: PartitionInfoRecord,
}
impl PartitionInfo {
/// True if the partition is an EFI system partition.
#[must_use]
pub const fn is_system(&self) -> bool {
self.system == 1
}
/// Get the MBR partition record. Returns None if the partition
/// type is not MBR.
#[must_use]
pub fn mbr_partition_record(&self) -> Option<&MbrPartitionRecord> {
if { self.revision } != PartitionInfoRevision::PROTOCOL_REVISION {
return None;
}
if { self.partition_type } == PartitionType::MBR {
Some(unsafe { &self.record.mbr })
} else {
None
}
}
/// Get the GPT partition entry. Returns None if the partition
/// type is not GPT.
#[must_use]
pub fn gpt_partition_entry(&self) -> Option<&GptPartitionEntry> {
if { self.revision } != PartitionInfoRevision::PROTOCOL_REVISION {
return None;
}
if { self.partition_type } == PartitionType::GPT {
Some(unsafe { &self.record.gpt })
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partition_attributes() {
let attr: GptPartitionAttributes =
GptPartitionAttributes::from_bits_retain(0xabcd_0000_0000_0007);
assert_eq!(attr.type_specific_bits(), 0xabcd);
}
}