diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-02-09 19:21:15 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-09 19:21:15 +0100 |
| commit | 1e3d2fb417b84a7dfec0e3b53d991cb66fc65e84 (patch) | |
| tree | 50f4343c7a4339560a65bcc5ccc901c91bc18c32 | |
| parent | e28fae52d99d7c14bf0890d1f2b13c2c34fa8932 (diff) | |
| parent | 92d4060176bfb4a5751e17269f993dc7268fbf80 (diff) | |
| download | rust-1e3d2fb417b84a7dfec0e3b53d991cb66fc65e84.tar.gz rust-1e3d2fb417b84a7dfec0e3b53d991cb66fc65e84.zip | |
Rollup merge of #120351 - Ayush1325:uefi-time, r=m-ou-se
Implement SystemTime for UEFI - Uses SystemTable->RuntimeServices->GetTime() - Uses the algorithm described [here](https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html) for conversion to UNIX time
| -rw-r--r-- | library/std/src/sys/pal/uefi/helpers.rs | 8 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/mod.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/tests.rs | 20 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/time.rs | 105 |
4 files changed, 133 insertions, 1 deletions
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 9837cc89f2d..ba53ed88f37 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -146,3 +146,11 @@ pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T> let system_handle = uefi::env::try_image_handle()?; open_protocol(system_handle, protocol_guid).ok() } + +/// Get RuntimeServices +pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> { + let system_table: NonNull<r_efi::efi::SystemTable> = + crate::os::uefi::env::try_system_table()?.cast(); + let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services }; + NonNull::new(runtime_services) +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 9ee753aa1a0..687e7d99d13 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -38,7 +38,6 @@ pub mod thread; pub mod thread_local_key; #[path = "../unsupported/thread_parking.rs"] pub mod thread_parking; -#[path = "../unsupported/time.rs"] pub mod time; mod helpers; diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 8806eda3ac0..5eb36da922b 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,4 +1,6 @@ use super::alloc::*; +use super::time::*; +use crate::time::Duration; #[test] fn align() { @@ -19,3 +21,21 @@ fn align() { } } } + +#[test] +fn epoch() { + let t = r_efi::system::Time { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + nanosecond: 0, + timezone: r_efi::efi::UNSPECIFIED_TIMEZONE, + daylight: 0, + pad1: 0, + pad2: 0, + }; + assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0)); +} diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs new file mode 100644 index 00000000000..68f428c38fb --- /dev/null +++ b/library/std/src/sys/pal/uefi/time.rs @@ -0,0 +1,105 @@ +use crate::time::Duration; + +const SECS_IN_MINUTE: u64 = 60; +const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; +const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + panic!("time not implemented on this platform") + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + system_time_internal::now() + .unwrap_or_else(|| panic!("time not implemented on this platform")) + } + + pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} + +pub(crate) mod system_time_internal { + use super::super::helpers; + use super::*; + use crate::mem::MaybeUninit; + use crate::ptr::NonNull; + use r_efi::efi::{RuntimeServices, Time}; + + pub fn now() -> Option<SystemTime> { + let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?; + let mut t: MaybeUninit<Time> = MaybeUninit::uninit(); + let r = unsafe { + ((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut()) + }; + + if r.is_error() { + return None; + } + + let t = unsafe { t.assume_init() }; + + Some(SystemTime(uefi_time_to_duration(t))) + } + + // This algorithm is based on the one described in the post + // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html + pub const fn uefi_time_to_duration(t: r_efi::system::Time) -> Duration { + assert!(t.month <= 12); + assert!(t.month != 0); + + const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */ + + // Calculate the number of days since 1/1/1970 + // Use 1 March as the start + let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3); + let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) }; + let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry; + let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048; + let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400; + let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2472632; + + let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY + + (t.second as u64) + + (t.minute as u64) * SECS_IN_MINUTE + + (t.hour as u64) * SECS_IN_HOUR; + + let utc_epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { + localtime_epoch + } else { + (localtime_epoch as i64 + (t.timezone as i64) * SECS_IN_MINUTE as i64) as u64 + }; + + Duration::new(utc_epoch, t.nanosecond) + } +} |
