1 | use 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)] |
13 | pub 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 | |
27 | impl 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 | |
90 | impl 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 |
131 | pub unsafe trait Identify { |
132 | /// Unique protocol identifier. |
133 | const GUID: Guid; |
134 | } |
135 | |
136 | #[cfg (test)] |
137 | mod 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 | |