about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-02-09 19:21:15 +0100
committerGitHub <noreply@github.com>2024-02-09 19:21:15 +0100
commit1e3d2fb417b84a7dfec0e3b53d991cb66fc65e84 (patch)
tree50f4343c7a4339560a65bcc5ccc901c91bc18c32
parente28fae52d99d7c14bf0890d1f2b13c2c34fa8932 (diff)
parent92d4060176bfb4a5751e17269f993dc7268fbf80 (diff)
downloadrust-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.rs8
-rw-r--r--library/std/src/sys/pal/uefi/mod.rs1
-rw-r--r--library/std/src/sys/pal/uefi/tests.rs20
-rw-r--r--library/std/src/sys/pal/uefi/time.rs105
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)
+    }
+}