1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use core::fmt;

/// A globally unique identifier
///
/// GUIDs are used by UEFI to identify protocols and other objects. They are
/// mostly like variant 2 UUIDs as specified by RFC 4122, but differ from them
/// in that the first 3 fields are little endian instead of big endian.
///
/// The `Display` formatter prints GUIDs in the canonical format defined by
/// RFC 4122, which is also used by UEFI.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[repr(C)]
pub struct Guid {
    /// The low field of the timestamp.
    a: u32,
    /// The middle field of the timestamp.
    b: u16,
    /// The high field of the timestamp multiplexed with the version number.
    c: u16,
    /// Contains, in this order:
    /// - The high field of the clock sequence multiplexed with the variant.
    /// - The low field of the clock sequence.
    /// - The spatially unique node identifier.
    d: [u8; 8],
}

impl Guid {
    /// Creates a new GUID from its canonical representation
    pub const fn from_values(
        time_low: u32,
        time_mid: u16,
        time_high_and_version: u16,
        clock_seq_and_variant: u16,
        node: u64,
    ) -> Self {
        assert!(node.leading_zeros() >= 16, "node must be a 48-bit integer");
        // intentional shadowing
        let node = node.to_be_bytes();

        Guid {
            a: time_low,
            b: time_mid,
            c: time_high_and_version,
            d: [
                (clock_seq_and_variant / 0x100) as u8,
                (clock_seq_and_variant % 0x100) as u8,
                // first two elements of node are ignored, we only want the low 48 bits
                node[2],
                node[3],
                node[4],
                node[5],
                node[6],
                node[7],
            ],
        }
    }
}

impl fmt::Display for Guid {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let d = {
            let mut buf = [0u8; 2];
            buf[..].copy_from_slice(&self.d[0..2]);
            u16::from_be_bytes(buf)
        };

        let e = {
            let mut buf = [0u8; 8];
            // first two elements of node are ignored, we only want the low 48 bits
            buf[2..].copy_from_slice(&self.d[2..8]);
            u64::from_be_bytes(buf)
        };

        write!(
            fmt,
            "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
            self.a, self.b, self.c, d, e
        )
    }
}

/// Several entities in the UEFI specification can be referred to by their GUID,
/// this trait is a building block to interface them in uefi-rs.
///
/// You should never need to use the `Identify` trait directly, but instead go
/// for more specific traits such as `Protocol` or `FileProtocolInfo`, which
/// indicate in which circumstances an `Identify`-tagged type should be used.
///
/// # Safety
///
/// Implementing `Identify` is unsafe because attaching an incorrect GUID to a
/// type can lead to type unsafety on both the Rust and UEFI side.
///
/// You can derive `Identify` for a type using the `unsafe_guid` procedural
/// macro, which is exported by this module. This macro mostly works like a
/// custom derive, but also supports type aliases. It takes a GUID in canonical
/// textual format as an argument, and is used in the following way:
///
/// ```
/// use uefi::unsafe_guid;
/// #[unsafe_guid("12345678-9abc-def0-1234-56789abcdef0")]
/// struct Emptiness;
/// ```
pub unsafe trait Identify {
    /// Unique protocol identifier.
    const GUID: Guid;
}

pub use uefi_macros::unsafe_guid;

#[cfg(test)]
mod tests {
    use uefi::unsafe_guid;
    extern crate alloc;
    use super::*;

    #[test]
    fn test_guid_display() {
        assert_eq!(
            alloc::format!(
                "{}",
                Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0)
            ),
            "12345678-9abc-def0-1234-56789abcdef0"
        );
    }

    #[test]
    fn test_unsafe_guid() {
        #[unsafe_guid("12345678-9abc-def0-1234-56789abcdef0")]
        struct X;

        assert_eq!(
            X::GUID,
            Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0)
        );
    }
}