blob: 7cf90064af0dedbcb3b27be9b259c558de83cf6b [file] [log] [blame]
Zach Reiznerdfda1912017-05-01 18:00:12 -07001// Copyright 2017 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
Zach Reizner55a9e502018-10-03 10:22:32 -07005use libc::{gmtime_r, time, time_t, tm};
Zach Reiznerdfda1912017-05-01 18:00:12 -07006use std::mem;
Zach Reiznerdfda1912017-05-01 18:00:12 -07007
David Tolnayfe3ef7d2019-03-08 15:57:49 -08008use crate::BusDevice;
Zach Reiznerdfda1912017-05-01 18:00:12 -07009
10const INDEX_MASK: u8 = 0x7f;
11const INDEX_OFFSET: u64 = 0x0;
12const DATA_OFFSET: u64 = 0x1;
13const DATA_LEN: usize = 128;
14
15/// A CMOS/RTC device commonly seen on x86 I/O port 0x70/0x71.
16pub struct Cmos {
17 index: u8,
18 data: [u8; DATA_LEN],
19}
20
21impl Cmos {
22 /// Constructs a CMOS/RTC device with zero data.
23 pub fn new() -> Cmos {
24 Cmos {
25 index: 0,
26 data: [0; DATA_LEN],
27 }
28 }
29}
30
31impl BusDevice for Cmos {
Zach Reizner3ba00982019-01-23 19:04:43 -080032 fn debug_label(&self) -> String {
33 "cmos".to_owned()
34 }
35
Zach Reiznerdfda1912017-05-01 18:00:12 -070036 fn write(&mut self, offset: u64, data: &[u8]) {
37 if data.len() != 1 {
38 return;
39 }
40
41 match offset {
42 INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
43 DATA_OFFSET => self.data[self.index as usize] = data[0],
44 o => panic!("bad write offset on CMOS device: {}", o),
45 }
46 }
47
48 fn read(&mut self, offset: u64, data: &mut [u8]) {
49 fn to_bcd(v: u8) -> u8 {
50 assert!(v < 100);
51 ((v / 10) << 4) | (v % 10)
52 }
53
54 if data.len() != 1 {
55 return;
56 }
57
58 data[0] = match offset {
59 INDEX_OFFSET => self.index,
60 DATA_OFFSET => {
61 let seconds;
62 let minutes;
63 let hours;
64 let week_day;
65 let day;
66 let month;
67 let year;
68 // The time and gmtime_r calls are safe as long as the structs they are given are
69 // large enough, and neither of them fail. It is safe to zero initialize the tm
70 // struct because it contains only plain data.
71 unsafe {
72 let mut tm: tm = mem::zeroed();
73 let mut now: time_t = 0;
74 time(&mut now as *mut _);
75 gmtime_r(&now, &mut tm as *mut _);
76 // The following lines of code are safe but depend on tm being in scope.
77 seconds = tm.tm_sec;
78 minutes = tm.tm_min;
79 hours = tm.tm_hour;
80 week_day = tm.tm_wday + 1;
81 day = tm.tm_mday;
82 month = tm.tm_mon + 1;
83 year = tm.tm_year;
84 };
85 match self.index {
86 0x00 => to_bcd(seconds as u8),
87 0x02 => to_bcd(minutes as u8),
88 0x04 => to_bcd(hours as u8),
89 0x06 => to_bcd(week_day as u8),
90 0x07 => to_bcd(day as u8),
91 0x08 => to_bcd(month as u8),
92 0x09 => to_bcd((year % 100) as u8),
93 0x32 => to_bcd(((year + 1900) / 100) as u8),
94 _ => {
95 // self.index is always guaranteed to be in range via INDEX_MASK.
96 self.data[(self.index & INDEX_MASK) as usize]
97 }
98 }
99 }
100 o => panic!("bad read offset on CMOS device: {}", o),
101 }
102 }
103}