diff options
| author | Ayush Singh <ayush@beagleboard.org> | 2025-04-14 18:55:04 +0530 |
|---|---|---|
| committer | Ayush Singh <ayush@beagleboard.org> | 2025-08-11 18:58:34 +0530 |
| commit | 0ccbe8fc71173bf5d2e348e5b3ed6cb344db913b (patch) | |
| tree | ea71e1f25ea36d9b8f897e55cbc9026e15716116 /library/std/src | |
| parent | 577166503aee7290e09374da21f4045c455acfd5 (diff) | |
| download | rust-0ccbe8fc71173bf5d2e348e5b3ed6cb344db913b.tar.gz rust-0ccbe8fc71173bf5d2e348e5b3ed6cb344db913b.zip | |
std: sys: pal: uefi: Overhaul Time
Use a time representation with 1900-01-01-00:00:00 at timezone -1440 min as anchor. This is the earliest time supported in UEFI. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/sys/pal/uefi/tests.rs | 3 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/time.rs | 125 |
2 files changed, 110 insertions, 18 deletions
diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 49e75a1a70d..6a15c03184c 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,3 +1,6 @@ +//! These tests are not run automatically right now. Please run these tests manually by copying them +//! to a separate project when modifying any related code. + use super::alloc::*; use super::time::*; use crate::io::{IoSlice, IoSliceMut}; diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index eeb2c35ffbb..32c3cfd143c 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -1,16 +1,28 @@ 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); +/// When a Timezone is specified, the stored Duration is in UTC. If timezone is unspecified, then +/// the timezone is assumed to be in UTC. +/// +/// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct SystemTime(Duration); -pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); +pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + nanosecond: 0, + timezone: 0, + daylight: 0, + pad1: 0, + pad2: 0, +}); impl Instant { pub fn now() -> Instant { @@ -40,6 +52,14 @@ impl Instant { } impl SystemTime { + pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self { + Self(system_time_internal::from_uefi(&t)) + } + + pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option<r_efi::efi::Time> { + system_time_internal::to_uefi(&self.0, timezone, daylight) + } + pub fn now() -> SystemTime { system_time_internal::now() .unwrap_or_else(|| panic!("time not implemented on this platform")) @@ -50,11 +70,17 @@ impl SystemTime { } pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime(self.0.checked_add(*other)?)) + let temp = Self(self.0.checked_add(*other)?); + + // Check if can be represented in UEFI + if temp.to_uefi(0, 0).is_some() { Some(temp) } else { None } } pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime(self.0.checked_sub(*other)?)) + let temp = Self(self.0.checked_sub(*other)?); + + // Check if can be represented in UEFI + if temp.to_uefi(0, 0).is_some() { Some(temp) } else { None } } } @@ -66,51 +92,114 @@ pub(crate) mod system_time_internal { use crate::mem::MaybeUninit; use crate::ptr::NonNull; + const SECS_IN_MINUTE: u64 = 60; + const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; + const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; + const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE; + 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))) + Some(SystemTime::from_uefi(t)) } - // This algorithm is based on the one described in the post - // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html - pub(crate) const fn uefi_time_to_duration(t: r_efi::system::Time) -> Duration { + pub(crate) const fn from_uefi(t: &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 + // Calculate the number of days since 1/1/1900 // 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 days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065; 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 + // Calculate the offset from 1/1/1900 at timezone -1440 min + let adjusted_localtime_epoc: u64 = localtime_epoch + TIMEZONE_DELTA; + + let epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { + adjusted_localtime_epoc + } else { + adjusted_localtime_epoc + .checked_add_signed((t.timezone as i64) * SECS_IN_MINUTE as i64) + .unwrap() + }; + + Duration::new(epoch, t.nanosecond) + } + + pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Option<Time> { + // Check timzone validity + assert!(timezone <= 1440); + assert!(timezone >= -1440); + + let secs: u64 = if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { + dur.as_secs() } else { - (localtime_epoch as i64 + (t.timezone as i64) * SECS_IN_MINUTE as i64) as u64 + // FIXME: use checked_sub_signed once stablized + dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap() }; - Duration::new(utc_epoch, t.nanosecond) + // Convert to UTC + let Some(secs) = secs.checked_sub(TIMEZONE_DELTA) else { return None }; + + let days = secs / SECS_IN_DAY; + let remaining_secs = secs % SECS_IN_DAY; + + let z = days + 693901; + let era = z / 146097; + let doe = z - (era * 146097); + let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + let mut y = yoe + era * 400; + let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + let mp = (5 * doy + 2) / 153; + let d = doy - (153 * mp + 2) / 5 + 1; + let m = if mp < 10 { mp + 3 } else { mp - 9 }; + + if m <= 2 { + y += 1; + } + + let hour = (remaining_secs / SECS_IN_HOUR) as u8; + let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8; + let second = (remaining_secs % SECS_IN_MINUTE) as u8; + + // Check Bounds + if y >= 1900 && y <= 9999 { + Some(Time { + year: y as u16, + month: m as u8, + day: d as u8, + hour, + minute, + second, + nanosecond: dur.subsec_nanos(), + timezone, + daylight, + pad1: 0, + pad2: 0, + }) + } else { + None + } } } |
