Skip to content

Commit 1793bc9

Browse files
committed
Implement Instant for UEFI
- Uses Timestamp Protocol if present. Else use rdtsc for x86 and x86-64 Signed-off-by: Ayush Singh <[email protected]>
1 parent 5f40394 commit 1793bc9

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

library/std/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@
263263
#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))]
264264
#![cfg_attr(target_os = "xous", feature(slice_ptr_len))]
265265
#![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))]
266+
#![cfg_attr(
267+
all(any(target_arch = "x86_64", target_arch = "x86"), target_os = "uefi"),
268+
feature(stdarch_x86_has_cpuid)
269+
)]
266270
//
267271
// Language features:
268272
// tidy-alphabetical-start

library/std/src/sys/pal/uefi/time.rs

+117
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
1414

1515
impl 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

Comments
 (0)