@@ -14,6 +14,15 @@ pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
1414
1515impl Instant {
1616 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+
1726 panic ! ( "time not implemented on this platform" )
1827 }
1928
@@ -103,3 +112,110 @@ pub(crate) mod system_time_internal {
103112 Duration :: new ( utc_epoch, t. nanosecond )
104113 }
105114}
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