• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use base::{warn, Event};
6 use std::convert::TryFrom;
7 use std::time::{SystemTime, UNIX_EPOCH};
8 
9 use crate::{BusAccessInfo, BusDevice};
10 
11 // Register offsets
12 // Data register
13 const RTCDR: u64 = 0x0;
14 // Match register
15 const RTCMR: u64 = 0x4;
16 // Interrupt status register
17 const RTCSTAT: u64 = 0x8;
18 // Interrupt clear register
19 const RTCEOI: u64 = 0x8;
20 // Counter load register
21 const RTCLR: u64 = 0xC;
22 // Counter register
23 const RTCCR: u64 = 0x10;
24 
25 // A single 4K page is mapped for this device
26 pub const PL030_AMBA_IOMEM_SIZE: u64 = 0x1000;
27 
28 // AMBA id registers are at the end of the allocated memory space
29 const AMBA_ID_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x20;
30 const AMBA_MASK_OFFSET: u64 = PL030_AMBA_IOMEM_SIZE - 0x28;
31 
32 // This is the AMBA id for this device
33 pub const PL030_AMBA_ID: u32 = 0x00041030;
34 pub const PL030_AMBA_MASK: u32 = 0x000FFFFF;
35 
36 /// An emulated ARM pl030 RTC
37 pub struct Pl030 {
38     // Event to be used to interrupt the guest for an alarm event
39     alarm_evt: Event,
40 
41     // This is the delta we subtract from current time to get the
42     // counter value
43     counter_delta_time: u32,
44 
45     // This is the value that triggers an alarm interrupt when it
46     // matches with the rtc time
47     match_value: u32,
48 
49     // status flag to keep track of whether the interrupt is cleared
50     // or not
51     interrupt_active: bool,
52 }
53 
get_epoch_time() -> u3254 fn get_epoch_time() -> u32 {
55     let epoch_time = SystemTime::now()
56         .duration_since(UNIX_EPOCH)
57         .expect("SystemTime::duration_since failed");
58     epoch_time.as_secs() as u32
59 }
60 
61 impl Pl030 {
62     /// Constructs a Pl030 device
new(evt: Event) -> Pl03063     pub fn new(evt: Event) -> Pl030 {
64         Pl030 {
65             alarm_evt: evt,
66             counter_delta_time: get_epoch_time(),
67             match_value: 0,
68             interrupt_active: false,
69         }
70     }
71 }
72 
73 impl BusDevice for Pl030 {
debug_label(&self) -> String74     fn debug_label(&self) -> String {
75         "Pl030".to_owned()
76     }
77 
write(&mut self, info: BusAccessInfo, data: &[u8])78     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
79         let data_array = match <&[u8; 4]>::try_from(data) {
80             Ok(array) => array,
81             _ => {
82                 warn!("bad write size: {} for pl030", data.len());
83                 return;
84             }
85         };
86 
87         let reg_val = u32::from_ne_bytes(*data_array);
88         match info.offset {
89             RTCDR => {
90                 warn!("invalid write to read-only RTCDR register");
91             }
92             RTCMR => {
93                 self.match_value = reg_val;
94                 // TODO(sonnyrao): here we need to set up a timer for
95                 // when host time equals the value written here and
96                 // fire the interrupt
97                 warn!("Not implemented: VM tried to set an RTC alarm");
98             }
99             RTCEOI => {
100                 if reg_val == 0 {
101                     self.interrupt_active = false;
102                 } else {
103                     self.alarm_evt.write(1).unwrap();
104                     self.interrupt_active = true;
105                 }
106             }
107             RTCLR => {
108                 // TODO(sonnyrao): if we ever need to let the VM set it's own time
109                 // then we'll need to keep track of the delta between
110                 // the rtc time it sets and the host's rtc time and
111                 // record that here
112                 warn!("Not implemented: VM tried to set the RTC");
113             }
114             RTCCR => {
115                 self.counter_delta_time = get_epoch_time();
116             }
117             o => panic!("pl030: bad write {}", o),
118         }
119     }
120 
read(&mut self, info: BusAccessInfo, data: &mut [u8])121     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
122         let data_array = match <&mut [u8; 4]>::try_from(data) {
123             Ok(array) => array,
124             _ => {
125                 warn!("bad write size for pl030");
126                 return;
127             }
128         };
129 
130         let reg_content: u32 = match info.offset {
131             RTCDR => get_epoch_time(),
132             RTCMR => self.match_value,
133             RTCSTAT => self.interrupt_active as u32,
134             RTCLR => {
135                 warn!("invalid read of RTCLR register");
136                 0
137             }
138             RTCCR => get_epoch_time() - self.counter_delta_time,
139             AMBA_ID_OFFSET => PL030_AMBA_ID,
140             AMBA_MASK_OFFSET => PL030_AMBA_MASK,
141 
142             o => panic!("pl030: bad read {}", o),
143         };
144         *data_array = reg_content.to_ne_bytes();
145     }
146 }
147 #[cfg(test)]
148 mod tests {
149     use super::*;
150 
151     // The RTC device is placed at page 2 in the mmio bus
152     const AARCH64_RTC_ADDR: u64 = 0x2000;
153 
pl030_bus_address(offset: u64) -> BusAccessInfo154     fn pl030_bus_address(offset: u64) -> BusAccessInfo {
155         BusAccessInfo {
156             address: AARCH64_RTC_ADDR + offset,
157             offset,
158             id: 0,
159         }
160     }
161 
162     #[test]
test_interrupt_status_register()163     fn test_interrupt_status_register() {
164         let event = Event::new().unwrap();
165         let mut device = Pl030::new(event.try_clone().unwrap());
166         let mut register = [0, 0, 0, 0];
167 
168         // set interrupt
169         device.write(pl030_bus_address(RTCEOI), &[1, 0, 0, 0]);
170         device.read(pl030_bus_address(RTCSTAT), &mut register);
171         assert_eq!(register, [1, 0, 0, 0]);
172         assert_eq!(event.read().unwrap(), 1);
173 
174         // clear interrupt
175         device.write(pl030_bus_address(RTCEOI), &[0, 0, 0, 0]);
176         device.read(pl030_bus_address(RTCSTAT), &mut register);
177         assert_eq!(register, [0, 0, 0, 0]);
178     }
179 
180     #[test]
test_match_register()181     fn test_match_register() {
182         let mut device = Pl030::new(Event::new().unwrap());
183         let mut register = [0, 0, 0, 0];
184 
185         device.write(pl030_bus_address(RTCMR), &[1, 2, 3, 4]);
186         device.read(pl030_bus_address(RTCMR), &mut register);
187         assert_eq!(register, [1, 2, 3, 4]);
188     }
189 }
190