Skip to content

Commit c257c00

Browse files
committed
Initial implementation of Instant using TSC
Signed-off-by: Ayush <ayushsingh1325@gmail.com>
1 parent 4bc53e3 commit c257c00

File tree

3 files changed

+83
-12
lines changed

3 files changed

+83
-12
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2946,7 +2946,7 @@ dependencies = [
29462946
[[package]]
29472947
name = "r-efi"
29482948
version = "4.0.0"
2949-
source = "git+https://github.com/r-efi/r-efi#30e0e8b8552179e1cc7f054a8b64f243db7c9bee"
2949+
source = "git+https://github.com/r-efi/r-efi#2c5125e697c68010813c1dfbc68de4ba573f277d"
29502950
dependencies = [
29512951
"compiler_builtins",
29522952
"rustc-std-workspace-core",

library/std/src/os/uefi/env.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ pub fn get_runtime_services() -> Option<NonNull<RuntimeServices>> {
5050

5151
/// Open Protocol on a handle
5252
/// Implemented using `EFI_BOOT_SERVICES.OpenProtocol()`
53-
#[unstable(feature = "uefi_std", issue = "100499")]
54-
pub fn open_protocol<T>(
53+
pub(crate) fn open_protocol<T>(
5554
handle: NonNull<c_void>,
5655
mut protocol_guid: Guid,
5756
) -> io::Result<NonNull<T>> {
@@ -82,8 +81,7 @@ pub fn open_protocol<T>(
8281

8382
// Locate handles with a particular protocol GUID
8483
/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()`
85-
#[unstable(feature = "uefi_std", issue = "100499")]
86-
pub fn locate_handles(mut guid: Guid) -> io::Result<Vec<NonNull<c_void>>> {
84+
pub(crate) fn locate_handles(mut guid: Guid) -> io::Result<Vec<NonNull<c_void>>> {
8785
fn inner(
8886
guid: &mut Guid,
8987
boot_services: NonNull<BootServices>,

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

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,66 @@
1-
//! Time is implemeted using `EFI_RUNTIME_SERVICES.GetTime()`
2-
//! While this is not technically monotonic, the single-threaded nature of UEFI might make it fine
3-
//! to use for Instant. Still, maybe revisit this in future.
4-
1+
use crate::mem::MaybeUninit;
52
use crate::os::uefi;
3+
use crate::ptr::NonNull;
4+
use crate::sys_common::mul_div_u64;
65
use crate::time::Duration;
76

7+
use r_efi::protocols::timestamp;
8+
89
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
910
pub struct Instant(Duration);
1011

1112
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1213
pub struct SystemTime(Duration);
1314

1415
pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::ZERO);
16+
const NS_PER_SEC: u64 = 1_000_000_000;
1517

1618
impl Instant {
1719
pub fn now() -> Instant {
20+
if let Ok(handles) = crate::os::uefi::env::locate_handles(timestamp::PROTOCOL_GUID) {
21+
// First try using `EFI_TIMESTAMP_PROTOCOL` if present
22+
for handle in handles {
23+
let protocol: NonNull<timestamp::Protocol> =
24+
match uefi::env::open_protocol(handle, timestamp::PROTOCOL_GUID) {
25+
Ok(x) => x,
26+
Err(_) => continue,
27+
};
28+
let mut properties: MaybeUninit<timestamp::Properties> = MaybeUninit::uninit();
29+
let r = unsafe { ((*protocol.as_ptr()).get_properties)(properties.as_mut_ptr()) };
30+
if r.is_error() {
31+
continue;
32+
} else {
33+
let properties = unsafe { properties.assume_init() };
34+
let ts = unsafe { ((*protocol.as_ptr()).get_timestamp)() };
35+
let frequency = properties.frequency;
36+
let ns = mul_div_u64(ts, NS_PER_SEC, frequency);
37+
return Instant(Duration::from_nanos(ns));
38+
}
39+
}
40+
}
41+
42+
// Try using raw CPU Registers
43+
// Currently only implemeted for x86_64 using CPUID (0x15) and TSC register
44+
#[cfg(target_arch = "x86_64")]
45+
if let Some(ns) = get_timestamp() {
46+
return Instant(Duration::from_nanos(ns));
47+
}
48+
1849
if let Some(runtime_services) = uefi::env::get_runtime_services() {
50+
// Finally just use `EFI_RUNTIME_SERVICES.GetTime()`
1951
let mut t = r_efi::efi::Time::default();
2052
let r =
2153
unsafe { ((*runtime_services.as_ptr()).get_time)(&mut t, crate::ptr::null_mut()) };
2254

2355
if r.is_error() {
2456
panic!("time not implemented on this platform")
2557
} else {
26-
Instant(uefi_time_to_duration(t))
58+
return Instant(uefi_time_to_duration(t));
2759
}
28-
} else {
29-
panic!("Runtime Services are needed for Time to work")
3060
}
61+
62+
// Panic if nothing works
63+
panic!("Failed to create Instant")
3164
}
3265

3366
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
@@ -100,3 +133,43 @@ fn uefi_time_to_duration(t: r_efi::system::Time) -> Duration {
100133

101134
Duration::new(utc_epoch, t.nanosecond)
102135
}
136+
137+
// Returns the Frequency in Mhz
138+
// Mostly based on [`edk2/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c`](https://github.com/tianocore/edk2/blob/master/UefiCpuPkg/Library/CpuTimerLib/CpuTimerLib.c)
139+
// Currently implemented only for x86_64 but can be extended for other arch if they ever support
140+
// std.
141+
#[cfg(target_arch = "x86_64")]
142+
fn frequency() -> Option<u64> {
143+
use crate::sync::atomic::{AtomicU64, Ordering};
144+
145+
static FREQUENCY: AtomicU64 = AtomicU64::new(0);
146+
147+
let cached = FREQUENCY.load(Ordering::Relaxed);
148+
if cached != 0 {
149+
return Some(cached);
150+
}
151+
152+
if crate::arch::x86_64::has_cpuid() {
153+
let cpuid = unsafe { crate::arch::x86_64::__cpuid(0x15) };
154+
155+
if cpuid.eax == 0 || cpuid.ebx == 0 || cpuid.ecx == 0 {
156+
return None;
157+
}
158+
159+
let freq = mul_div_u64(cpuid.ecx as u64, cpuid.ebx as u64, cpuid.eax as u64);
160+
FREQUENCY.store(freq, Ordering::Relaxed);
161+
return Some(freq);
162+
}
163+
164+
None
165+
}
166+
167+
// Currently implemented only for x86_64 but can be extended for other arch if they ever support
168+
// std.
169+
#[cfg(target_arch = "x86_64")]
170+
fn get_timestamp() -> Option<u64> {
171+
let freq = frequency()?;
172+
let ts = unsafe { crate::arch::x86_64::_rdtsc() };
173+
let ns = mul_div_u64(ts, 1000, freq);
174+
Some(ns)
175+
}

0 commit comments

Comments
 (0)