@@ -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,110 @@ 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 :: sys_common:: mul_div_u64;
123
+ use r_efi:: protocols:: timestamp;
124
+
125
+ const NS_PER_SEC : u64 = 1_000_000_000 ;
126
+
127
+ pub fn timestamp_protocol ( ) -> Option < Instant > {
128
+ fn try_handle ( handle : NonNull < crate :: ffi:: c_void > ) -> Option < u64 > {
129
+ let protocol: NonNull < timestamp:: Protocol > =
130
+ helpers:: open_protocol ( handle, timestamp:: PROTOCOL_GUID ) . ok ( ) ?;
131
+ let mut properties: MaybeUninit < timestamp:: Properties > = MaybeUninit :: uninit ( ) ;
132
+
133
+ let r = unsafe { ( ( * protocol. as_ptr ( ) ) . get_properties ) ( properties. as_mut_ptr ( ) ) } ;
134
+ if r. is_error ( ) {
135
+ return None ;
136
+ }
137
+
138
+ let freq = unsafe { properties. assume_init ( ) . frequency } ;
139
+ let ts = unsafe { ( ( * protocol. as_ptr ( ) ) . get_timestamp ) ( ) } ;
140
+ Some ( mul_div_u64 ( ts, NS_PER_SEC , freq) )
141
+ }
142
+
143
+ static LAST_VALID_HANDLE : AtomicPtr < crate :: ffi:: c_void > =
144
+ AtomicPtr :: new ( crate :: ptr:: null_mut ( ) ) ;
145
+
146
+ if let Some ( handle) = NonNull :: new ( LAST_VALID_HANDLE . load ( Ordering :: Acquire ) ) {
147
+ if let Some ( ns) = try_handle ( handle) {
148
+ return Some ( Instant ( Duration :: from_nanos ( ns) ) ) ;
149
+ }
150
+ }
151
+
152
+ if let Ok ( handles) = helpers:: locate_handles ( timestamp:: PROTOCOL_GUID ) {
153
+ for handle in handles {
154
+ if let Some ( ns) = try_handle ( handle) {
155
+ LAST_VALID_HANDLE . store ( handle. as_ptr ( ) , Ordering :: Release ) ;
156
+ return Some ( Instant ( Duration :: from_nanos ( ns) ) ) ;
157
+ }
158
+ }
159
+ }
160
+
161
+ None
162
+ }
163
+
164
+ pub fn platform_specific ( ) -> Option < Instant > {
165
+ cfg_if:: cfg_if! {
166
+ if #[ cfg( any( target_arch = "x86_64" , target_arch = "x86" ) ) ] {
167
+ timestamp_rdtsc( ) . map( Instant )
168
+ } else {
169
+ None
170
+ }
171
+ }
172
+ }
173
+
174
+ #[ cfg( target_arch = "x86_64" ) ]
175
+ fn timestamp_rdtsc ( ) -> Option < Duration > {
176
+ if !crate :: arch:: x86_64:: has_cpuid ( ) {
177
+ return None ;
178
+ }
179
+
180
+ static FREQUENCY : crate :: sync:: OnceLock < u64 > = crate :: sync:: OnceLock :: new ( ) ;
181
+
182
+ // Get Frequency in Mhz
183
+ // Inspired by [`edk2/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c`](https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c)
184
+ let freq = FREQUENCY
185
+ . get_or_try_init ( || {
186
+ let cpuid = unsafe { crate :: arch:: x86_64:: __cpuid ( 0x15 ) } ;
187
+ if cpuid. eax == 0 || cpuid. ebx == 0 || cpuid. ecx == 0 {
188
+ return Err ( ( ) ) ;
189
+ }
190
+ Ok ( mul_div_u64 ( cpuid. ecx as u64 , cpuid. ebx as u64 , cpuid. eax as u64 ) )
191
+ } )
192
+ . ok ( ) ?;
193
+
194
+ let ts = unsafe { crate :: arch:: x86_64:: _rdtsc ( ) } ;
195
+ let ns = mul_div_u64 ( ts, 1000 , * freq) ;
196
+ Some ( Duration :: from_nanos ( ns) )
197
+ }
198
+
199
+ #[ cfg( target_arch = "x86" ) ]
200
+ fn timestamp_rdtsc ( ) -> Option < Duration > {
201
+ if !crate :: arch:: x86:: has_cpuid ( ) {
202
+ return None ;
203
+ }
204
+
205
+ static FREQUENCY : crate :: sync:: OnceLock < u64 > = crate :: sync:: OnceLock :: new ( ) ;
206
+
207
+ let freq = FREQUENCY
208
+ . get_or_try_init ( || {
209
+ let cpuid = unsafe { crate :: arch:: x86:: __cpuid ( 0x15 ) } ;
210
+ if cpuid. eax == 0 || cpuid. ebx == 0 || cpuid. ecx == 0 {
211
+ return Err ( ( ) ) ;
212
+ }
213
+ Ok ( mul_div_u64 ( cpuid. ecx as u64 , cpuid. ebx as u64 , cpuid. eax as u64 ) )
214
+ } )
215
+ . ok ( ) ?;
216
+
217
+ let ts = unsafe { crate :: arch:: x86:: _rdtsc ( ) } ;
218
+ let ns = mul_div_u64 ( ts, 1000 , * freq) ;
219
+ Some ( Duration :: from_nanos ( ns) )
220
+ }
221
+ }
0 commit comments