use crate::proto::unsafe_protocol;
use crate::{Char16, Guid, guid};
newtype_enum! {
pub enum MbrOsType: u8 => {
GPT_PROTECTIVE = 0xee,
UEFI_SYSTEM_PARTITION = 0xef,
}
}
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
pub struct MbrPartitionRecord {
pub boot_indicator: u8,
pub starting_chs: [u8; 3],
pub os_type: MbrOsType,
pub ending_chs: [u8; 3],
pub starting_lba: u32,
pub size_in_lba: u32,
}
impl MbrPartitionRecord {
#[must_use]
pub const fn is_bootable(&self) -> bool {
self.boot_indicator == 0x80
}
}
newtype_enum! {
pub enum GptPartitionType: Guid => {
UNUSED_ENTRY = guid!("00000000-0000-0000-0000-000000000000"),
EFI_SYSTEM_PARTITION = guid!("c12a7328-f81f-11d2-ba4b-00a0c93ec93b"),
LEGACY_MBR = guid!("024dee41-33e7-11d3-9d69-0008c781f39f"),
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct GptPartitionAttributes: u64 {
const REQUIRED_PARTITION = 1;
const NO_BLOCK_IO_PROTOCOL = 1 << 1;
const LEGACY_BIOS_BOOTABLE = 1 << 2;
const RESERVED_FOR_PARTITION_TYPE = 0xffff_0000_0000_0000;
const TYPE_SPECIFIC_BIT_0 = 1 << 47;
const TYPE_SPECIFIC_BIT_1 = 1 << 48;
const TYPE_SPECIFIC_BIT_2 = 1 << 49;
const TYPE_SPECIFIC_BIT_3 = 1 << 50;
const TYPE_SPECIFIC_BIT_4 = 1 << 51;
const TYPE_SPECIFIC_BIT_5 = 1 << 52;
const TYPE_SPECIFIC_BIT_6 = 1 << 53;
const TYPE_SPECIFIC_BIT_7 = 1 << 54;
const TYPE_SPECIFIC_BIT_8 = 1 << 55;
const TYPE_SPECIFIC_BIT_9 = 1 << 56;
const TYPE_SPECIFIC_BIT_10 = 1 << 57;
const TYPE_SPECIFIC_BIT_11 = 1 << 58;
const TYPE_SPECIFIC_BIT_12 = 1 << 59;
const TYPE_SPECIFIC_BIT_13 = 1 << 60;
const TYPE_SPECIFIC_BIT_14 = 1 << 61;
const TYPE_SPECIFIC_BIT_15 = 1 << 62;
}
}
impl GptPartitionAttributes {
#[must_use]
pub const fn type_specific_bits(&self) -> u16 {
(self.0.bits() >> 48) as u16
}
}
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GptPartitionEntry {
pub partition_type_guid: GptPartitionType,
pub unique_partition_guid: Guid,
pub starting_lba: u64,
pub ending_lba: u64,
pub attributes: GptPartitionAttributes,
pub partition_name: [Char16; 36],
}
impl GptPartitionEntry {
#[must_use]
pub fn num_blocks(&self) -> Option<u64> {
self.ending_lba
.checked_sub(self.starting_lba)?
.checked_add(1)
}
}
newtype_enum! {
pub enum PartitionType: u32 => {
OTHER = 0x00,
MBR = 0x01,
GPT = 0x02,
}
}
#[repr(C)]
#[derive(Clone, Copy)]
union PartitionInfoRecord {
mbr: MbrPartitionRecord,
gpt: GptPartitionEntry,
}
newtype_enum! {
pub enum PartitionInfoRevision: u32 => {
PROTOCOL_REVISION = 0x0001000,
}
}
#[allow(missing_debug_implementations)]
#[repr(C)]
#[repr(packed)]
#[unsafe_protocol("8cf2f62c-bc9b-4821-808d-ec9ec421a1a0")]
pub struct PartitionInfo {
pub revision: PartitionInfoRevision,
pub partition_type: PartitionType,
system: u8,
reserved: [u8; 7],
record: PartitionInfoRecord,
}
impl PartitionInfo {
#[must_use]
pub const fn is_system(&self) -> bool {
self.system == 1
}
#[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
}
}
#[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);
}
}