Skip to content

Commit 0f806a9

Browse files
committed
Auto merge of #120889 - Ayush1325:uefi-instant, r=joshtriplett
Implement Instant for UEFI - Uses Timestamp Protocol if present. Else use rdtsc for x86 and x86-64
2 parents cefa14b + dee2d0f commit 0f806a9

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-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

+116
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,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

Comments
 (0)