@@ -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,111 @@ 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 :: 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