xref: /cloud-hypervisor/devices/src/legacy/rtc_pl031.rs (revision 1b91aa8ef3ba490a12853d41783ed558cf7b7060)
15f9e079aSHenry Wang // Copyright 2020 Arm Limited (or its affiliates). All rights reserved.
25f9e079aSHenry Wang // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
35f9e079aSHenry Wang // SPDX-License-Identifier: Apache-2.0
45f9e079aSHenry Wang 
55f9e079aSHenry Wang //! ARM PL031 Real Time Clock
65f9e079aSHenry Wang //!
75f9e079aSHenry Wang //! This module implements a PL031 Real Time Clock (RTC) that provides to provides long time base counter.
85f9e079aSHenry Wang //! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of
95f9e079aSHenry Wang //! a real-time clock input.
105f9e079aSHenry Wang //!
111fc6d50fSRob Bradford use std::sync::{Arc, Barrier};
125f9e079aSHenry Wang use std::time::Instant;
135f9e079aSHenry Wang use std::{io, result};
1488a9f799SRob Bradford 
1589b429c7SSamrutGadde use thiserror::Error;
165f9e079aSHenry Wang use vm_device::interrupt::InterruptSourceGroup;
1715025d71SRob Bradford use vm_device::BusDevice;
185f9e079aSHenry Wang 
1988a9f799SRob Bradford use crate::{read_le_u32, write_le_u32};
2088a9f799SRob Bradford 
215f9e079aSHenry Wang // As you can see in https://static.docs.arm.com/ddi0224/c/real_time_clock_pl031_r1p3_technical_reference_manual_DDI0224C.pdf
225f9e079aSHenry Wang // at section 3.2 Summary of RTC registers, the total size occupied by this device is 0x000 -> 0xFFC + 4 = 0x1000.
235f9e079aSHenry Wang // From 0x0 to 0x1C we have following registers:
245f9e079aSHenry Wang const RTCDR: u64 = 0x0; // Data Register.
255f9e079aSHenry Wang const RTCMR: u64 = 0x4; // Match Register.
265c3f4dbeSJosh Soref const RTCLR: u64 = 0x8; // Load Register.
275f9e079aSHenry Wang const RTCCR: u64 = 0xc; // Control Register.
285f9e079aSHenry Wang const RTCIMSC: u64 = 0x10; // Interrupt Mask Set or Clear Register.
295f9e079aSHenry Wang const RTCRIS: u64 = 0x14; // Raw Interrupt Status.
305f9e079aSHenry Wang const RTCMIS: u64 = 0x18; // Masked Interrupt Status.
315f9e079aSHenry Wang const RTCICR: u64 = 0x1c; // Interrupt Clear Register.
325f9e079aSHenry Wang                           // From 0x020 to 0xFDC => reserved space.
335f9e079aSHenry Wang                           // From 0xFE0 to 0x1000 => Peripheral and PrimeCell Identification Registers which are Read Only registers.
345f9e079aSHenry Wang                           // AMBA standard devices have CIDs (Cell IDs) and PIDs (Peripheral IDs). The linux kernel will look for these in order to assert the identity
355f9e079aSHenry Wang                           // of these devices (i.e look at the `amba_device_try_add` function).
365f9e079aSHenry Wang                           // We are putting the expected values (look at 'Reset value' column from above mentioned document) in an array.
375f9e079aSHenry Wang const PL031_ID: [u8; 8] = [0x31, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
385f9e079aSHenry Wang // We are only interested in the margins.
395f9e079aSHenry Wang const AMBA_ID_LOW: u64 = 0xFE0;
405f9e079aSHenry Wang const AMBA_ID_HIGH: u64 = 0x1000;
415f9e079aSHenry Wang /// Constant to convert seconds to nanoseconds.
425f9e079aSHenry Wang pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
435f9e079aSHenry Wang 
4489b429c7SSamrutGadde #[derive(Debug, Error)]
455f9e079aSHenry Wang pub enum Error {
4689b429c7SSamrutGadde     #[error("Bad Write Offset: {0}")]
475f9e079aSHenry Wang     BadWriteOffset(u64),
48*1b91aa8eSPhilipp Schuster     #[error("Failed to trigger interrupt")]
4993b599e5SPhilipp Schuster     InterruptFailure(#[source] io::Error),
505f9e079aSHenry Wang }
515f9e079aSHenry Wang 
525f9e079aSHenry Wang type Result<T> = result::Result<T, Error>;
535f9e079aSHenry Wang 
545f9e079aSHenry Wang /// Wrapper over `libc::clockid_t` to specify Linux Kernel clock source.
555f9e079aSHenry Wang pub enum ClockType {
565f9e079aSHenry Wang     /// Equivalent to `libc::CLOCK_MONOTONIC`.
575f9e079aSHenry Wang     Monotonic,
585f9e079aSHenry Wang     /// Equivalent to `libc::CLOCK_REALTIME`.
595f9e079aSHenry Wang     Real,
605f9e079aSHenry Wang     /// Equivalent to `libc::CLOCK_PROCESS_CPUTIME_ID`.
615f9e079aSHenry Wang     ProcessCpu,
625f9e079aSHenry Wang     /// Equivalent to `libc::CLOCK_THREAD_CPUTIME_ID`.
635f9e079aSHenry Wang     ThreadCpu,
645f9e079aSHenry Wang }
655f9e079aSHenry Wang 
664da6bcd5SRob Bradford impl From<ClockType> for libc::clockid_t {
from(ct: ClockType) -> libc::clockid_t674da6bcd5SRob Bradford     fn from(ct: ClockType) -> libc::clockid_t {
684da6bcd5SRob Bradford         match ct {
695f9e079aSHenry Wang             ClockType::Monotonic => libc::CLOCK_MONOTONIC,
705f9e079aSHenry Wang             ClockType::Real => libc::CLOCK_REALTIME,
715f9e079aSHenry Wang             ClockType::ProcessCpu => libc::CLOCK_PROCESS_CPUTIME_ID,
725f9e079aSHenry Wang             ClockType::ThreadCpu => libc::CLOCK_THREAD_CPUTIME_ID,
735f9e079aSHenry Wang         }
745f9e079aSHenry Wang     }
755f9e079aSHenry Wang }
765f9e079aSHenry Wang 
775f9e079aSHenry Wang /// Returns a timestamp in nanoseconds based on the provided clock type.
785f9e079aSHenry Wang ///
795f9e079aSHenry Wang /// # Arguments
805f9e079aSHenry Wang ///
815f9e079aSHenry Wang /// * `clock_type` - Identifier of the Linux Kernel clock on which to act.
get_time(clock_type: ClockType) -> u64825f9e079aSHenry Wang pub fn get_time(clock_type: ClockType) -> u64 {
835f9e079aSHenry Wang     let mut time_struct = libc::timespec {
845f9e079aSHenry Wang         tv_sec: 0,
855f9e079aSHenry Wang         tv_nsec: 0,
865f9e079aSHenry Wang     };
879806d83eSWei Liu     // SAFETY: the parameters are valid.
885f9e079aSHenry Wang     unsafe { libc::clock_gettime(clock_type.into(), &mut time_struct) };
895f9e079aSHenry Wang     seconds_to_nanoseconds(time_struct.tv_sec).unwrap() as u64 + (time_struct.tv_nsec as u64)
905f9e079aSHenry Wang }
915f9e079aSHenry Wang 
925f9e079aSHenry Wang /// Converts a timestamp in seconds to an equivalent one in nanoseconds.
935f9e079aSHenry Wang /// Returns `None` if the conversion overflows.
945f9e079aSHenry Wang ///
955f9e079aSHenry Wang /// # Arguments
965f9e079aSHenry Wang ///
975f9e079aSHenry Wang /// * `value` - Timestamp in seconds.
seconds_to_nanoseconds(value: i64) -> Option<i64>985f9e079aSHenry Wang pub fn seconds_to_nanoseconds(value: i64) -> Option<i64> {
995f9e079aSHenry Wang     value.checked_mul(NANOS_PER_SECOND as i64)
1005f9e079aSHenry Wang }
1015f9e079aSHenry Wang 
1025f9e079aSHenry Wang /// A RTC device following the PL031 specification..
10340da6210SRob Bradford pub struct Rtc {
1045f9e079aSHenry Wang     previous_now: Instant,
1055f9e079aSHenry Wang     tick_offset: i64,
1065f9e079aSHenry Wang     // This is used for implementing the RTC alarm. However, in Firecracker we do not need it.
1075f9e079aSHenry Wang     match_value: u32,
1085f9e079aSHenry Wang     // Writes to this register load an update value into the RTC.
1095f9e079aSHenry Wang     load: u32,
1105f9e079aSHenry Wang     imsc: u32,
1115f9e079aSHenry Wang     ris: u32,
112dcc646f5SSebastien Boeuf     interrupt: Arc<dyn InterruptSourceGroup>,
1135f9e079aSHenry Wang }
1145f9e079aSHenry Wang 
11540da6210SRob Bradford impl Rtc {
1165f9e079aSHenry Wang     /// Constructs an AMBA PL031 RTC device.
new(interrupt: Arc<dyn InterruptSourceGroup>) -> Self117dcc646f5SSebastien Boeuf     pub fn new(interrupt: Arc<dyn InterruptSourceGroup>) -> Self {
11840da6210SRob Bradford         Self {
1195f9e079aSHenry Wang             // This is used only for duration measuring purposes.
1205f9e079aSHenry Wang             previous_now: Instant::now(),
1215f9e079aSHenry Wang             tick_offset: get_time(ClockType::Real) as i64,
1225f9e079aSHenry Wang             match_value: 0,
1235f9e079aSHenry Wang             load: 0,
1245f9e079aSHenry Wang             imsc: 0,
1255f9e079aSHenry Wang             ris: 0,
1265f9e079aSHenry Wang             interrupt,
1275f9e079aSHenry Wang         }
1285f9e079aSHenry Wang     }
1295f9e079aSHenry Wang 
trigger_interrupt(&mut self) -> Result<()>1305f9e079aSHenry Wang     fn trigger_interrupt(&mut self) -> Result<()> {
1315f9e079aSHenry Wang         self.interrupt.trigger(0).map_err(Error::InterruptFailure)?;
1325f9e079aSHenry Wang         Ok(())
1335f9e079aSHenry Wang     }
1345f9e079aSHenry Wang 
get_time(&self) -> u321355f9e079aSHenry Wang     fn get_time(&self) -> u32 {
1365f9e079aSHenry Wang         let ts = (self.tick_offset as i128)
1375f9e079aSHenry Wang             + (Instant::now().duration_since(self.previous_now).as_nanos() as i128);
1385f9e079aSHenry Wang         (ts / NANOS_PER_SECOND as i128) as u32
1395f9e079aSHenry Wang     }
1405f9e079aSHenry Wang 
handle_write(&mut self, offset: u64, val: u32) -> Result<()>1415f9e079aSHenry Wang     fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> {
1425f9e079aSHenry Wang         match offset {
1435f9e079aSHenry Wang             RTCMR => {
1445f9e079aSHenry Wang                 // The MR register is used for implementing the RTC alarm. A real time clock alarm is
1455f9e079aSHenry Wang                 // a feature that can be used to allow a computer to 'wake up' after shut down to execute
1465f9e079aSHenry Wang                 // tasks every day or on a certain day. It can sometimes be found in the 'Power Management'
1475f9e079aSHenry Wang                 // section of a motherboard's BIOS setup. This is functionality that extends beyond
1485f9e079aSHenry Wang                 // Firecracker intended use. However, we increment a metric just in case.
1495f9e079aSHenry Wang                 self.match_value = val;
1505f9e079aSHenry Wang             }
1515f9e079aSHenry Wang             RTCLR => {
1525f9e079aSHenry Wang                 self.load = val;
1535f9e079aSHenry Wang                 self.previous_now = Instant::now();
1545f9e079aSHenry Wang                 // If the unwrap fails, then the internal value of the clock has been corrupted and
1555f9e079aSHenry Wang                 // we want to terminate the execution of the process.
1565f9e079aSHenry Wang                 self.tick_offset = seconds_to_nanoseconds(i64::from(val)).unwrap();
1575f9e079aSHenry Wang             }
1585f9e079aSHenry Wang             RTCIMSC => {
1595f9e079aSHenry Wang                 self.imsc = val & 1;
1605f9e079aSHenry Wang                 self.trigger_interrupt()?;
1615f9e079aSHenry Wang             }
1625f9e079aSHenry Wang             RTCICR => {
1635f9e079aSHenry Wang                 // As per above mentioned doc, the interrupt is cleared by writing any data value to
1645f9e079aSHenry Wang                 // the Interrupt Clear Register.
1655f9e079aSHenry Wang                 self.ris = 0;
1665f9e079aSHenry Wang                 self.trigger_interrupt()?;
1675f9e079aSHenry Wang             }
1685f9e079aSHenry Wang             RTCCR => (), // ignore attempts to turn off the timer.
1695f9e079aSHenry Wang             o => {
1705f9e079aSHenry Wang                 return Err(Error::BadWriteOffset(o));
1715f9e079aSHenry Wang             }
1725f9e079aSHenry Wang         }
1735f9e079aSHenry Wang         Ok(())
1745f9e079aSHenry Wang     }
1755f9e079aSHenry Wang }
1765f9e079aSHenry Wang 
17740da6210SRob Bradford impl BusDevice for Rtc {
read(&mut self, _base: u64, offset: u64, data: &mut [u8])1785f9e079aSHenry Wang     fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
1795f9e079aSHenry Wang         let mut read_ok = true;
1805f9e079aSHenry Wang 
1811dc91342SRob Bradford         let v = if (AMBA_ID_LOW..AMBA_ID_HIGH).contains(&offset) {
1825f9e079aSHenry Wang             let index = ((offset - AMBA_ID_LOW) >> 2) as usize;
1831dc91342SRob Bradford             u32::from(PL031_ID[index])
1845f9e079aSHenry Wang         } else {
1851dc91342SRob Bradford             match offset {
1865f9e079aSHenry Wang                 RTCDR => self.get_time(),
1875f9e079aSHenry Wang                 RTCMR => {
1885f9e079aSHenry Wang                     // Even though we are not implementing RTC alarm we return the last value
1895f9e079aSHenry Wang                     self.match_value
1905f9e079aSHenry Wang                 }
1915f9e079aSHenry Wang                 RTCLR => self.load,
1925f9e079aSHenry Wang                 RTCCR => 1, // RTC is always enabled.
1935f9e079aSHenry Wang                 RTCIMSC => self.imsc,
1945f9e079aSHenry Wang                 RTCRIS => self.ris,
1955f9e079aSHenry Wang                 RTCMIS => self.ris & self.imsc,
1965f9e079aSHenry Wang                 _ => {
1975f9e079aSHenry Wang                     read_ok = false;
1985f9e079aSHenry Wang                     0
1995f9e079aSHenry Wang                 }
2005f9e079aSHenry Wang             }
2011dc91342SRob Bradford         };
2025f9e079aSHenry Wang         if read_ok && data.len() <= 4 {
2035f9e079aSHenry Wang             write_le_u32(data, v);
2045f9e079aSHenry Wang         } else {
2055f9e079aSHenry Wang             warn!(
2065f9e079aSHenry Wang                 "Invalid RTC PL031 read: offset {}, data length {}",
2075f9e079aSHenry Wang                 offset,
2085f9e079aSHenry Wang                 data.len()
2095f9e079aSHenry Wang             );
2105f9e079aSHenry Wang         }
2115f9e079aSHenry Wang     }
2125f9e079aSHenry Wang 
write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>>2131fc6d50fSRob Bradford     fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
2145f9e079aSHenry Wang         if data.len() <= 4 {
2155825ab2dSBo Chen             let v = read_le_u32(data);
2165f9e079aSHenry Wang             if let Err(e) = self.handle_write(offset, v) {
2175f9e079aSHenry Wang                 warn!("Failed to write to RTC PL031 device: {}", e);
2185f9e079aSHenry Wang             }
2195f9e079aSHenry Wang         } else {
2205f9e079aSHenry Wang             warn!(
2215f9e079aSHenry Wang                 "Invalid RTC PL031 write: offset {}, data length {}",
2225f9e079aSHenry Wang                 offset,
2235f9e079aSHenry Wang                 data.len()
2245f9e079aSHenry Wang             );
2255f9e079aSHenry Wang         }
2261fc6d50fSRob Bradford 
2271fc6d50fSRob Bradford         None
2285f9e079aSHenry Wang     }
2295f9e079aSHenry Wang }
2305f9e079aSHenry Wang 
2315f9e079aSHenry Wang #[cfg(test)]
2325f9e079aSHenry Wang mod tests {
23388a9f799SRob Bradford     use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig};
23488a9f799SRob Bradford     use vmm_sys_util::eventfd::EventFd;
23588a9f799SRob Bradford 
2365f9e079aSHenry Wang     use super::*;
237fd95acc6SHenry Wang     use crate::{
2382f5e48d2SRob Bradford         read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u64, write_be_u16,
2392f5e48d2SRob Bradford         write_be_u32, write_le_i32, write_le_u16, write_le_u64,
240fd95acc6SHenry Wang     };
2415f9e079aSHenry Wang 
2425f9e079aSHenry Wang     const LEGACY_RTC_MAPPED_IO_START: u64 = 0x0901_0000;
2435f9e079aSHenry Wang 
2449c42d98eSWei Liu     struct LocalTime {
2459c42d98eSWei Liu         sec: i32,
2469c42d98eSWei Liu         min: i32,
2479c42d98eSWei Liu         hour: i32,
2489c42d98eSWei Liu         mday: i32,
2499c42d98eSWei Liu         mon: i32,
2509c42d98eSWei Liu         year: i32,
2519c42d98eSWei Liu         nsec: i64,
2529c42d98eSWei Liu     }
2539c42d98eSWei Liu 
2549c42d98eSWei Liu     impl LocalTime {
now() -> LocalTime2559c42d98eSWei Liu         fn now() -> LocalTime {
2569c42d98eSWei Liu             let mut timespec = libc::timespec {
2579c42d98eSWei Liu                 tv_sec: 0,
2589c42d98eSWei Liu                 tv_nsec: 0,
2599c42d98eSWei Liu             };
2609c42d98eSWei Liu             let mut tm: libc::tm = libc::tm {
2619c42d98eSWei Liu                 tm_sec: 0,
2629c42d98eSWei Liu                 tm_min: 0,
2639c42d98eSWei Liu                 tm_hour: 0,
2649c42d98eSWei Liu                 tm_mday: 0,
2659c42d98eSWei Liu                 tm_mon: 0,
2669c42d98eSWei Liu                 tm_year: 0,
2679c42d98eSWei Liu                 tm_wday: 0,
2689c42d98eSWei Liu                 tm_yday: 0,
2699c42d98eSWei Liu                 tm_isdst: 0,
2709c42d98eSWei Liu                 tm_gmtoff: 0,
2719c42d98eSWei Liu                 tm_zone: std::ptr::null(),
2729c42d98eSWei Liu             };
2739c42d98eSWei Liu 
2749c42d98eSWei Liu             // SAFETY: the parameters are valid.
2759c42d98eSWei Liu             unsafe {
2769c42d98eSWei Liu                 libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec);
2779c42d98eSWei Liu                 libc::localtime_r(&timespec.tv_sec, &mut tm);
2789c42d98eSWei Liu             }
2799c42d98eSWei Liu 
2809c42d98eSWei Liu             LocalTime {
2819c42d98eSWei Liu                 sec: tm.tm_sec,
2829c42d98eSWei Liu                 min: tm.tm_min,
2839c42d98eSWei Liu                 hour: tm.tm_hour,
2849c42d98eSWei Liu                 mday: tm.tm_mday,
2859c42d98eSWei Liu                 mon: tm.tm_mon,
2869c42d98eSWei Liu                 year: tm.tm_year,
2879c42d98eSWei Liu                 nsec: timespec.tv_nsec,
2889c42d98eSWei Liu             }
2899c42d98eSWei Liu         }
2909c42d98eSWei Liu     }
2919c42d98eSWei Liu 
2929c42d98eSWei Liu     impl std::fmt::Display for LocalTime {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result2939c42d98eSWei Liu         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2949c42d98eSWei Liu             write!(
2959c42d98eSWei Liu                 f,
2969c42d98eSWei Liu                 "{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:09}",
2979c42d98eSWei Liu                 self.year + 1900,
2989c42d98eSWei Liu                 self.mon + 1,
2999c42d98eSWei Liu                 self.mday,
3009c42d98eSWei Liu                 self.hour,
3019c42d98eSWei Liu                 self.min,
3029c42d98eSWei Liu                 self.sec,
3039c42d98eSWei Liu                 self.nsec
3049c42d98eSWei Liu             )
3059c42d98eSWei Liu         }
3069c42d98eSWei Liu     }
3079c42d98eSWei Liu 
3085f9e079aSHenry Wang     #[test]
test_get_time()3095f9e079aSHenry Wang     fn test_get_time() {
3105f9e079aSHenry Wang         for _ in 0..1000 {
3115f9e079aSHenry Wang             assert!(get_time(ClockType::Monotonic) <= get_time(ClockType::Monotonic));
3125f9e079aSHenry Wang         }
3135f9e079aSHenry Wang 
3145f9e079aSHenry Wang         for _ in 0..1000 {
3155f9e079aSHenry Wang             assert!(get_time(ClockType::ProcessCpu) <= get_time(ClockType::ProcessCpu));
3165f9e079aSHenry Wang         }
3175f9e079aSHenry Wang 
3185f9e079aSHenry Wang         for _ in 0..1000 {
3195f9e079aSHenry Wang             assert!(get_time(ClockType::ThreadCpu) <= get_time(ClockType::ThreadCpu));
3205f9e079aSHenry Wang         }
3215f9e079aSHenry Wang 
3225f9e079aSHenry Wang         assert_ne!(get_time(ClockType::Real), 0);
3235f9e079aSHenry Wang     }
3245f9e079aSHenry Wang 
3255f9e079aSHenry Wang     #[test]
test_local_time_display()3265f9e079aSHenry Wang     fn test_local_time_display() {
3275f9e079aSHenry Wang         let local_time = LocalTime {
3285f9e079aSHenry Wang             sec: 30,
3295f9e079aSHenry Wang             min: 15,
3305f9e079aSHenry Wang             hour: 10,
3315f9e079aSHenry Wang             mday: 4,
3325f9e079aSHenry Wang             mon: 6,
3335f9e079aSHenry Wang             year: 119,
3345f9e079aSHenry Wang             nsec: 123_456_789,
3355f9e079aSHenry Wang         };
3365f9e079aSHenry Wang         assert_eq!(
3375f9e079aSHenry Wang             String::from("2019-07-04T10:15:30.123456789"),
3385f9e079aSHenry Wang             local_time.to_string()
3395f9e079aSHenry Wang         );
3405f9e079aSHenry Wang 
3415f9e079aSHenry Wang         let local_time = LocalTime {
3425f9e079aSHenry Wang             sec: 5,
3435f9e079aSHenry Wang             min: 5,
3445f9e079aSHenry Wang             hour: 5,
3455f9e079aSHenry Wang             mday: 23,
3465f9e079aSHenry Wang             mon: 7,
3475f9e079aSHenry Wang             year: 44,
3485f9e079aSHenry Wang             nsec: 123,
3495f9e079aSHenry Wang         };
3505f9e079aSHenry Wang         assert_eq!(
3515f9e079aSHenry Wang             String::from("1944-08-23T05:05:05.000000123"),
3525f9e079aSHenry Wang             local_time.to_string()
3535f9e079aSHenry Wang         );
3545f9e079aSHenry Wang 
3555f9e079aSHenry Wang         let local_time = LocalTime::now();
3565f9e079aSHenry Wang         assert!(local_time.mon >= 0 && local_time.mon <= 11);
3575f9e079aSHenry Wang     }
3585f9e079aSHenry Wang 
3595f9e079aSHenry Wang     #[test]
test_seconds_to_nanoseconds()3605f9e079aSHenry Wang     fn test_seconds_to_nanoseconds() {
3615f9e079aSHenry Wang         assert_eq!(
3625f9e079aSHenry Wang             seconds_to_nanoseconds(100).unwrap() as u64,
3635f9e079aSHenry Wang             100 * NANOS_PER_SECOND
3645f9e079aSHenry Wang         );
3655f9e079aSHenry Wang 
3665f9e079aSHenry Wang         assert!(seconds_to_nanoseconds(9_223_372_037).is_none());
3675f9e079aSHenry Wang     }
3685f9e079aSHenry Wang 
3695f9e079aSHenry Wang     struct TestInterrupt {
3705f9e079aSHenry Wang         event_fd: EventFd,
3715f9e079aSHenry Wang     }
3725f9e079aSHenry Wang 
3735f9e079aSHenry Wang     impl InterruptSourceGroup for TestInterrupt {
trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error>3745f9e079aSHenry Wang         fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> {
3755f9e079aSHenry Wang             self.event_fd.write(1)
3765f9e079aSHenry Wang         }
3775f9e079aSHenry Wang 
update( &self, _index: InterruptIndex, _config: InterruptSourceConfig, _masked: bool, _set_gsi: bool, ) -> result::Result<(), std::io::Error>3785f9e079aSHenry Wang         fn update(
3795f9e079aSHenry Wang             &self,
3805f9e079aSHenry Wang             _index: InterruptIndex,
3815f9e079aSHenry Wang             _config: InterruptSourceConfig,
382ed87e42eSRob Bradford             _masked: bool,
3830149e650SYong He             _set_gsi: bool,
3845f9e079aSHenry Wang         ) -> result::Result<(), std::io::Error> {
3855f9e079aSHenry Wang             Ok(())
3865f9e079aSHenry Wang         }
3875f9e079aSHenry Wang 
set_gsi(&self) -> result::Result<(), std::io::Error>3880149e650SYong He         fn set_gsi(&self) -> result::Result<(), std::io::Error> {
3890149e650SYong He             Ok(())
3900149e650SYong He         }
3910149e650SYong He 
notifier(&self, _index: InterruptIndex) -> Option<EventFd>392acfbee5bSSebastien Boeuf         fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
393acfbee5bSSebastien Boeuf             Some(self.event_fd.try_clone().unwrap())
3945f9e079aSHenry Wang         }
3955f9e079aSHenry Wang     }
3965f9e079aSHenry Wang 
3975f9e079aSHenry Wang     impl TestInterrupt {
new(event_fd: EventFd) -> Self3985f9e079aSHenry Wang         fn new(event_fd: EventFd) -> Self {
3995f9e079aSHenry Wang             TestInterrupt { event_fd }
4005f9e079aSHenry Wang         }
4015f9e079aSHenry Wang     }
4025f9e079aSHenry Wang 
4035f9e079aSHenry Wang     #[test]
test_rtc_read_write_and_event()4045f9e079aSHenry Wang     fn test_rtc_read_write_and_event() {
4055f9e079aSHenry Wang         let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
4065f9e079aSHenry Wang 
407dcc646f5SSebastien Boeuf         let mut rtc = Rtc::new(Arc::new(TestInterrupt::new(intr_evt.try_clone().unwrap())));
4085f9e079aSHenry Wang         let mut data = [0; 4];
4095f9e079aSHenry Wang 
4105f9e079aSHenry Wang         // Read and write to the MR register.
4115f9e079aSHenry Wang         write_le_u32(&mut data, 123);
4127ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCMR, &data);
4135f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCMR, &mut data);
4143671f5e9SRob Bradford         let v = read_le_u32(&data);
4155f9e079aSHenry Wang         assert_eq!(v, 123);
4165f9e079aSHenry Wang 
4175f9e079aSHenry Wang         // Read and write to the LR register.
4185f9e079aSHenry Wang         let v = get_time(ClockType::Real);
4195f9e079aSHenry Wang         write_le_u32(&mut data, (v / NANOS_PER_SECOND) as u32);
4205f9e079aSHenry Wang         let previous_now_before = rtc.previous_now;
4217ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCLR, &data);
4225f9e079aSHenry Wang 
4235f9e079aSHenry Wang         assert!(rtc.previous_now > previous_now_before);
4245f9e079aSHenry Wang 
4255f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCLR, &mut data);
4263671f5e9SRob Bradford         let v_read = read_le_u32(&data);
4275f9e079aSHenry Wang         assert_eq!((v / NANOS_PER_SECOND) as u32, v_read);
4285f9e079aSHenry Wang 
4295f9e079aSHenry Wang         // Read and write to IMSC register.
4305f9e079aSHenry Wang         // Test with non zero value.
4315f9e079aSHenry Wang         let non_zero = 1;
4325f9e079aSHenry Wang         write_le_u32(&mut data, non_zero);
4337ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &data);
4345f9e079aSHenry Wang         // The interrupt line should be on.
4355f9e079aSHenry Wang         assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() == 1);
4365f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
4373671f5e9SRob Bradford         let v = read_le_u32(&data);
4385f9e079aSHenry Wang         assert_eq!(non_zero & 1, v);
4395f9e079aSHenry Wang 
4405f9e079aSHenry Wang         // Now test with 0.
4415f9e079aSHenry Wang         write_le_u32(&mut data, 0);
4427ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &data);
4435f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCIMSC, &mut data);
4443671f5e9SRob Bradford         let v = read_le_u32(&data);
4455f9e079aSHenry Wang         assert_eq!(0, v);
4465f9e079aSHenry Wang 
4475f9e079aSHenry Wang         // Read and write to the ICR register.
4485f9e079aSHenry Wang         write_le_u32(&mut data, 1);
4497ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCICR, &data);
4505f9e079aSHenry Wang         // The interrupt line should be on.
4515f9e079aSHenry Wang         assert!(rtc.interrupt.notifier(0).unwrap().read().unwrap() > 1);
4523671f5e9SRob Bradford         let v_before = read_le_u32(&data);
4535f9e079aSHenry Wang 
4545f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCICR, &mut data);
4553671f5e9SRob Bradford         let v = read_le_u32(&data);
4565f9e079aSHenry Wang         // ICR is a  write only register. Data received should stay equal to data sent.
4575f9e079aSHenry Wang         assert_eq!(v, v_before);
4585f9e079aSHenry Wang 
4595f9e079aSHenry Wang         // Attempts to turn off the RTC should not go through.
4605f9e079aSHenry Wang         write_le_u32(&mut data, 0);
4617ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, RTCCR, &data);
4625f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, RTCCR, &mut data);
4633671f5e9SRob Bradford         let v = read_le_u32(&data);
4645f9e079aSHenry Wang         assert_eq!(v, 1);
4655f9e079aSHenry Wang 
4665f9e079aSHenry Wang         // Attempts to write beyond the writable space. Using here the space used to read
4675f9e079aSHenry Wang         // the CID and PID from.
4685f9e079aSHenry Wang         write_le_u32(&mut data, 0);
4697ba3eedaSMichael Zhao         rtc.write(LEGACY_RTC_MAPPED_IO_START, AMBA_ID_LOW, &data);
4705f9e079aSHenry Wang         // However, reading from the AMBA_ID_LOW should succeed upon read.
4715f9e079aSHenry Wang 
4725f9e079aSHenry Wang         let mut data = [0; 4];
4735f9e079aSHenry Wang         rtc.read(LEGACY_RTC_MAPPED_IO_START, AMBA_ID_LOW, &mut data);
4745f9e079aSHenry Wang         let index = AMBA_ID_LOW + 3;
4755f9e079aSHenry Wang         assert_eq!(data[0], PL031_ID[((index - AMBA_ID_LOW) >> 2) as usize]);
4765f9e079aSHenry Wang     }
4775f9e079aSHenry Wang 
4785f9e079aSHenry Wang     macro_rules! byte_order_test_read_write {
4795f9e079aSHenry Wang         ($test_name: ident, $write_fn_name: ident, $read_fn_name: ident, $is_be: expr, $data_type: ty) => {
4805f9e079aSHenry Wang             #[test]
4815f9e079aSHenry Wang             fn $test_name() {
4825f9e079aSHenry Wang                 let test_cases = [
4835f9e079aSHenry Wang                     (
4845f9e079aSHenry Wang                         0x0123_4567_89AB_CDEF as u64,
4855f9e079aSHenry Wang                         [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
4865f9e079aSHenry Wang                     ),
4875f9e079aSHenry Wang                     (
4885f9e079aSHenry Wang                         0x0000_0000_0000_0000 as u64,
4895f9e079aSHenry Wang                         [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
4905f9e079aSHenry Wang                     ),
4915f9e079aSHenry Wang                     (
4925f9e079aSHenry Wang                         0x1923_2345_ABF3_CCD4 as u64,
4935f9e079aSHenry Wang                         [0x19, 0x23, 0x23, 0x45, 0xAB, 0xF3, 0xCC, 0xD4],
4945f9e079aSHenry Wang                     ),
4955f9e079aSHenry Wang                     (
4965f9e079aSHenry Wang                         0x0FF0_0FF0_0FF0_0FF0 as u64,
4975f9e079aSHenry Wang                         [0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0],
4985f9e079aSHenry Wang                     ),
4995f9e079aSHenry Wang                     (
5005f9e079aSHenry Wang                         0xFFFF_FFFF_FFFF_FFFF as u64,
5015f9e079aSHenry Wang                         [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
5025f9e079aSHenry Wang                     ),
5035f9e079aSHenry Wang                     (
5045f9e079aSHenry Wang                         0x89AB_12D4_C2D2_09BB as u64,
5055f9e079aSHenry Wang                         [0x89, 0xAB, 0x12, 0xD4, 0xC2, 0xD2, 0x09, 0xBB],
5065f9e079aSHenry Wang                     ),
5075f9e079aSHenry Wang                 ];
5085f9e079aSHenry Wang 
5095f9e079aSHenry Wang                 let type_size = std::mem::size_of::<$data_type>();
5105f9e079aSHenry Wang                 for (test_val, v_arr) in &test_cases {
5115f9e079aSHenry Wang                     let v = *test_val as $data_type;
5125f9e079aSHenry Wang                     let cmp_iter: Box<dyn Iterator<Item = _>> = if $is_be {
5135f9e079aSHenry Wang                         Box::new(v_arr[(8 - type_size)..].iter())
5145f9e079aSHenry Wang                     } else {
5155f9e079aSHenry Wang                         Box::new(v_arr.iter().rev())
5165f9e079aSHenry Wang                     };
5175f9e079aSHenry Wang                     // test write
5185f9e079aSHenry Wang                     let mut write_arr = vec![Default::default(); type_size];
5195f9e079aSHenry Wang                     $write_fn_name(&mut write_arr, v);
5205f9e079aSHenry Wang                     for (cmp, cur) in cmp_iter.zip(write_arr.iter()) {
5215f9e079aSHenry Wang                         assert_eq!(*cmp, *cur as u8)
5225f9e079aSHenry Wang                     }
5235f9e079aSHenry Wang                     // test read
5245f9e079aSHenry Wang                     let read_val = $read_fn_name(&write_arr);
5255f9e079aSHenry Wang                     assert_eq!(v, read_val);
5265f9e079aSHenry Wang                 }
5275f9e079aSHenry Wang             }
5285f9e079aSHenry Wang         };
5295f9e079aSHenry Wang     }
5305f9e079aSHenry Wang 
5315f9e079aSHenry Wang     byte_order_test_read_write!(test_le_u16, write_le_u16, read_le_u16, false, u16);
5325f9e079aSHenry Wang     byte_order_test_read_write!(test_le_u32, write_le_u32, read_le_u32, false, u32);
5335f9e079aSHenry Wang     byte_order_test_read_write!(test_le_u64, write_le_u64, read_le_u64, false, u64);
5345f9e079aSHenry Wang     byte_order_test_read_write!(test_le_i32, write_le_i32, read_le_i32, false, i32);
5355f9e079aSHenry Wang     byte_order_test_read_write!(test_be_u16, write_be_u16, read_be_u16, true, u16);
5365f9e079aSHenry Wang     byte_order_test_read_write!(test_be_u32, write_be_u32, read_be_u32, true, u32);
5375f9e079aSHenry Wang }
538