1use core::fmt;
2
3/// A globally unique identifier
4///
5/// GUIDs are used by UEFI to identify protocols and other objects. They are
6/// mostly like variant 2 UUIDs as specified by RFC 4122, but differ from them
7/// in that the first 3 fields are little endian instead of big endian.
8///
9/// The `Display` formatter prints GUIDs in the canonical format defined by
10/// RFC 4122, which is also used by UEFI.
11#[derive(Debug, Default, Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
12#[repr(C)]
13pub struct Guid {
14 /// The low field of the timestamp.
15 a: u32,
16 /// The middle field of the timestamp.
17 b: u16,
18 /// The high field of the timestamp multiplexed with the version number.
19 c: u16,
20 /// Contains, in this order:
21 /// - The high field of the clock sequence multiplexed with the variant.
22 /// - The low field of the clock sequence.
23 /// - The spatially unique node identifier.
24 d: [u8; 8],
25}
26
27impl Guid {
28 /// Creates a new GUID from its canonical representation
29 #[must_use]
30 pub const fn from_values(
31 time_low: u32,
32 time_mid: u16,
33 time_high_and_version: u16,
34 clock_seq_and_variant: u16,
35 node: u64,
36 ) -> Self {
37 assert!(node.leading_zeros() >= 16, "node must be a 48-bit integer");
38 // intentional shadowing
39 let node = node.to_be_bytes();
40
41 Guid {
42 a: time_low,
43 b: time_mid,
44 c: time_high_and_version,
45 d: [
46 (clock_seq_and_variant / 0x100) as u8,
47 (clock_seq_and_variant % 0x100) as u8,
48 // first two elements of node are ignored, we only want the low 48 bits
49 node[2],
50 node[3],
51 node[4],
52 node[5],
53 node[6],
54 node[7],
55 ],
56 }
57 }
58
59 /// Create a GUID from a 16-byte array. No changes to byte order are made.
60 #[must_use]
61 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
62 let a = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
63 let b = u16::from_le_bytes([bytes[4], bytes[5]]);
64 let c = u16::from_le_bytes([bytes[6], bytes[7]]);
65 let d = [
66 bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
67 ];
68
69 Self { a, b, c, d }
70 }
71
72 /// Convert to a 16-byte array.
73 #[must_use]
74 #[rustfmt::skip]
75 pub const fn to_bytes(self) -> [u8; 16] {
76 let a = self.a.to_le_bytes();
77 let b = self.b.to_le_bytes();
78 let c = self.c.to_le_bytes();
79 let d = self.d;
80
81 [
82 a[0], a[1], a[2], a[3],
83 b[0], b[1], c[0], c[1],
84 d[0], d[1], d[2], d[3],
85 d[4], d[5], d[6], d[7],
86 ]
87 }
88}
89
90impl fmt::Display for Guid {
91 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
92 let a: u32 = self.a;
93 let b: u16 = self.b;
94 let c: u16 = self.c;
95
96 let d: u16 = {
97 let mut buf: [u8; 2] = [0u8; 2];
98 buf[..].copy_from_slice(&self.d[0..2]);
99 u16::from_be_bytes(buf)
100 };
101
102 let e: u64 = {
103 let mut buf: [u8; 8] = [0u8; 8];
104 // first two elements of node are ignored, we only want the low 48 bits
105 buf[2..].copy_from_slice(&self.d[2..8]);
106 u64::from_be_bytes(buf)
107 };
108
109 write!(fmt, "{a:08x}-{b:04x}-{c:04x}-{d:04x}-{e:012x}",)
110 }
111}
112
113/// Several entities in the UEFI specification can be referred to by their GUID,
114/// this trait is a building block to interface them in uefi-rs.
115///
116/// You should never need to use the `Identify` trait directly, but instead go
117/// for more specific traits such as [`Protocol`] or [`FileProtocolInfo`], which
118/// indicate in which circumstances an `Identify`-tagged type should be used.
119///
120/// For the common case of implementing this trait for a protocol, use
121/// the [`unsafe_protocol`] macro.
122///
123/// # Safety
124///
125/// Implementing `Identify` is unsafe because attaching an incorrect GUID to a
126/// type can lead to type unsafety on both the Rust and UEFI side.
127///
128/// [`Protocol`]: crate::proto::Protocol
129/// [`FileProtocolInfo`]: crate::proto::media::file::FileProtocolInfo
130/// [`unsafe_protocol`]: crate::proto::unsafe_protocol
131pub unsafe trait Identify {
132 /// Unique protocol identifier.
133 const GUID: Guid;
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use uefi::guid;
140
141 #[test]
142 fn test_guid_display() {
143 assert_eq!(
144 alloc::format!(
145 "{}",
146 Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0)
147 ),
148 "12345678-9abc-def0-1234-56789abcdef0"
149 );
150 }
151
152 #[test]
153 fn test_guid_macro() {
154 assert_eq!(
155 guid!("12345678-9abc-def0-1234-56789abcdef0"),
156 Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0)
157 );
158 }
159
160 #[test]
161 fn test_to_from_bytes() {
162 #[rustfmt::skip]
163 let bytes = [
164 0x78, 0x56, 0x34, 0x12,
165 0xbc, 0x9a,
166 0xf0, 0xde,
167 0x12, 0x34,
168 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
169 ];
170 assert_eq!(
171 Guid::from_bytes(bytes),
172 Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0)
173 );
174 assert_eq!(Guid::from_bytes(bytes).to_bytes(), bytes);
175 }
176}
177