@@ -14,6 +14,15 @@ pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
14
14
15
15
impl Instant {
16
16
pub fn now ( ) -> Instant {
17
+ // If we have a timestamp protocol, use it.
18
+ if let Some ( x) = instant_internal:: timestamp_protocol ( ) {
19
+ return x;
20
+ }
21
+
22
+ if let Some ( x) = instant_internal:: platform_specific ( ) {
23
+ return x;
24
+ }
25
+
17
26
panic ! ( "time not implemented on this platform" )
18
27
}
19
28
@@ -103,3 +112,111 @@ pub(crate) mod system_time_internal {
103
112
Duration :: new ( utc_epoch, t. nanosecond )
104
113
}
105
114
}
115
+
116
+ pub ( crate ) mod instant_internal {
117
+ use super :: super :: helpers;
118
+ use super :: * ;
119
+ use crate :: mem:: MaybeUninit ;
120
+ use crate :: ptr:: NonNull ;
121
+ use crate :: sync:: atomic:: { AtomicPtr , Ordering } ;
122
+ use crate :: sync:: OnceLock ;
123
+ use crate :: sys_common:: mul_div_u64;
124
+ use r_efi:: protocols:: timestamp;
125
+
126
+ const NS_PER_SEC : u64 = 1_000_000_000 ;
127
+
128
+ pub fn timestamp_protocol ( ) -> Option < Instant > {
129
+ fn try_handle ( handle : NonNull < crate :: ffi:: c_void > ) -> Option < u64 > {
130
+ let protocol: NonNull < timestamp:: Protocol > =
131
+ helpers:: open_protocol ( handle, timestamp:: PROTOCOL_GUID ) . ok ( ) ?;
132
+ let mut properties: MaybeUninit < timestamp:: Properties > = MaybeUninit :: uninit ( ) ;
133
+
134
+ let r = unsafe { ( ( * protocol. as_ptr ( ) ) . get_properties ) ( properties. as_mut_ptr ( ) ) } ;
135
+ if r. is_error ( ) {
136
+ return None ;
137
+ }
138
+
139
+ let freq = unsafe { properties. assume_init ( ) . frequency } ;
140
+ let ts = unsafe { ( ( * protocol. as_ptr ( ) ) . get_timestamp ) ( ) } ;
141
+ Some ( mul_div_u64 ( ts, NS_PER_SEC , freq) )
142
+ }
143
+
144
+ static LAST_VALID_HANDLE : AtomicPtr < crate :: ffi:: c_void > =
145
+ AtomicPtr :: new ( crate :: ptr:: null_mut ( ) ) ;
146
+
147
+ if let Some ( handle) = NonNull :: new ( LAST_VALID_HANDLE . load ( Ordering :: Acquire ) ) {
148
+ if let Some ( ns) = try_handle ( handle) {
149
+ return Some ( Instant ( Duration :: from_nanos ( ns) ) ) ;
150
+ }
151
+ }
152
+
153
+ if let Ok ( handles) = helpers:: locate_handles ( timestamp:: PROTOCOL_GUID ) {
154
+ for handle in handles {
155
+ if let Some ( ns) = try_handle ( handle) {
156
+ LAST_VALID_HANDLE . store ( handle. as_ptr ( ) , Ordering :: Release ) ;
157
+ return Some ( Instant ( Duration :: from_nanos ( ns) ) ) ;
158
+ }
159
+ }
160
+ }
161
+
162
+ None
163
+ }
164
+
165
+ pub fn platform_specific ( ) -> Option < Instant > {
166
+ if cfg ! ( target_arch = "x86_64" ) {
167
+ timestamp_rdtsc ( ) . map ( Instant )
168
+ } else if cfg ! ( target_arch = "x86" ) {
169
+ timestamp_rdtsc ( ) . map ( Instant )
170
+ } else {
171
+ None
172
+ }
173
+ }
174
+
175
+ #[ cfg( target_arch = "x86_64" ) ]
176
+ fn timestamp_rdtsc ( ) -> Option < Duration > {
177
+ if !crate :: arch:: x86_64:: has_cpuid ( ) {
178
+ return None ;
179
+ }
180
+
181
+ static FREQUENCY : OnceLock < u64 > = OnceLock :: new ( ) ;
182
+
183
+ // Get Frequency in Mhz
184
+ // Inspired by [`edk2/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c`](https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c)
185
+ let freq = FREQUENCY
186
+ . get_or_try_init ( || {
187
+ let cpuid = unsafe { crate :: arch:: x86_64:: __cpuid ( 0x15 ) } ;
188
+ if cpuid. eax == 0 || cpuid. ebx == 0 || cpuid. ecx == 0 {
189
+ return Err ( ( ) ) ;
190
+ }
191
+ Ok ( mul_div_u64 ( cpuid. ecx as u64 , cpuid. ebx as u64 , cpuid. eax as u64 ) )
192
+ } )
193
+ . ok ( ) ?;
194
+
195
+ let ts = unsafe { crate :: arch:: x86_64:: _rdtsc ( ) } ;
196
+ let ns = mul_div_u64 ( ts, 1000 , * freq) ;
197
+ Some ( Duration :: from_nanos ( ns) )
198
+ }
199
+
200
+ #[ cfg( target_arch = "x86" ) ]
201
+ fn timestamp_rdtsc ( ) -> Option < Duration > {
202
+ if !crate :: arch:: x86:: has_cpuid ( ) {
203
+ return None ;
204
+ }
205
+
206
+ static FREQUENCY : OnceLock < u64 > = OnceLock :: new ( ) ;
207
+
208
+ let freq = FREQUENCY
209
+ . get_or_try_init ( || {
210
+ let cpuid = unsafe { crate :: arch:: x86:: __cpuid ( 0x15 ) } ;
211
+ if cpuid. eax == 0 || cpuid. ebx == 0 || cpuid. ecx == 0 {
212
+ return Err ( ( ) ) ;
213
+ }
214
+ Ok ( mul_div_u64 ( cpuid. ecx as u64 , cpuid. ebx as u64 , cpuid. eax as u64 ) )
215
+ } )
216
+ . ok ( ) ?;
217
+
218
+ let ts = unsafe { crate :: arch:: x86:: _rdtsc ( ) } ;
219
+ let ns = mul_div_u64 ( ts, 1000 , * freq) ;
220
+ Some ( Duration :: from_nanos ( ns) )
221
+ }
222
+ }
0 commit comments