18ea4145fSRob Bradford // Copyright 2017 The Chromium OS Authors. All rights reserved. 28ea4145fSRob Bradford // Use of this source code is governed by a BSD-style license that can be 38ea4145fSRob Bradford // found in the LICENSE file. 45e9886bbSRuslan Mstoi // 55e9886bbSRuslan Mstoi // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 68ea4145fSRob Bradford 78ea4145fSRob Bradford use std::cmp::min; 806dc7085SRob Bradford use std::sync::atomic::{AtomicBool, Ordering}; 91fc6d50fSRob Bradford use std::sync::{Arc, Barrier}; 1061e57e1cSRuoqing He use std::{mem, thread}; 118ea4145fSRob Bradford 1216f52914SWei Liu // https://github.com/rust-lang/libc/issues/1848 1316f52914SWei Liu #[cfg_attr(target_env = "musl", allow(deprecated))] 1416f52914SWei Liu use libc::time_t; 1588a9f799SRob Bradford use libc::{clock_gettime, gmtime_r, timespec, tm, CLOCK_REALTIME}; 1688a9f799SRob Bradford use vm_device::BusDevice; 1788a9f799SRob Bradford use vmm_sys_util::eventfd::EventFd; 1816f52914SWei Liu 198ea4145fSRob Bradford const INDEX_MASK: u8 = 0x7f; 208ea4145fSRob Bradford const INDEX_OFFSET: u64 = 0x0; 218ea4145fSRob Bradford const DATA_OFFSET: u64 = 0x1; 228ea4145fSRob Bradford const DATA_LEN: usize = 128; 238ea4145fSRob Bradford 248ea4145fSRob Bradford /// A CMOS/RTC device commonly seen on x86 I/O port 0x70/0x71. 258ea4145fSRob Bradford pub struct Cmos { 268ea4145fSRob Bradford index: u8, 278ea4145fSRob Bradford data: [u8; DATA_LEN], 28e0d3efecSRob Bradford reset_evt: EventFd, 29a00d2986SRob Bradford vcpus_kill_signalled: Option<Arc<AtomicBool>>, 308ea4145fSRob Bradford } 318ea4145fSRob Bradford 328ea4145fSRob Bradford impl Cmos { 338ea4145fSRob Bradford /// Constructs a CMOS/RTC device with initial data. 348ea4145fSRob Bradford /// `mem_below_4g` is the size of memory in bytes below the 32-bit gap. 358ea4145fSRob Bradford /// `mem_above_4g` is the size of memory in bytes above the 32-bit gap. new( mem_below_4g: u64, mem_above_4g: u64, reset_evt: EventFd, vcpus_kill_signalled: Option<Arc<AtomicBool>>, ) -> Cmos3606dc7085SRob Bradford pub fn new( 3706dc7085SRob Bradford mem_below_4g: u64, 3806dc7085SRob Bradford mem_above_4g: u64, 3906dc7085SRob Bradford reset_evt: EventFd, 40a00d2986SRob Bradford vcpus_kill_signalled: Option<Arc<AtomicBool>>, 4106dc7085SRob Bradford ) -> Cmos { 428ea4145fSRob Bradford let mut data = [0u8; DATA_LEN]; 438ea4145fSRob Bradford 448ea4145fSRob Bradford // Extended memory from 16 MB to 4 GB in units of 64 KB 458ea4145fSRob Bradford let ext_mem = min( 468ea4145fSRob Bradford 0xFFFF, 478ea4145fSRob Bradford mem_below_4g.saturating_sub(16 * 1024 * 1024) / (64 * 1024), 488ea4145fSRob Bradford ); 498ea4145fSRob Bradford data[0x34] = ext_mem as u8; 508ea4145fSRob Bradford data[0x35] = (ext_mem >> 8) as u8; 518ea4145fSRob Bradford 528ea4145fSRob Bradford // High memory (> 4GB) in units of 64 KB 538ea4145fSRob Bradford let high_mem = min(0x00FF_FFFF, mem_above_4g / (64 * 1024)); 548ea4145fSRob Bradford data[0x5b] = high_mem as u8; 558ea4145fSRob Bradford data[0x5c] = (high_mem >> 8) as u8; 568ea4145fSRob Bradford data[0x5d] = (high_mem >> 16) as u8; 578ea4145fSRob Bradford 58e0d3efecSRob Bradford Cmos { 59e0d3efecSRob Bradford index: 0, 60e0d3efecSRob Bradford data, 61e0d3efecSRob Bradford reset_evt, 6206dc7085SRob Bradford vcpus_kill_signalled, 63e0d3efecSRob Bradford } 648ea4145fSRob Bradford } 658ea4145fSRob Bradford } 668ea4145fSRob Bradford 678ea4145fSRob Bradford impl BusDevice for Cmos { write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>>681fc6d50fSRob Bradford fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> { 698ea4145fSRob Bradford if data.len() != 1 { 70ccda1a00SRob Bradford warn!("Invalid write size on CMOS device: {}", data.len()); 711fc6d50fSRob Bradford return None; 728ea4145fSRob Bradford } 738ea4145fSRob Bradford 748ea4145fSRob Bradford match offset { 75e0d3efecSRob Bradford INDEX_OFFSET => self.index = data[0], 76e0d3efecSRob Bradford DATA_OFFSET => { 77e0d3efecSRob Bradford if self.index == 0x8f && data[0] == 0 { 78e0d3efecSRob Bradford info!("CMOS reset"); 79e0d3efecSRob Bradford self.reset_evt.write(1).unwrap(); 80a00d2986SRob Bradford if let Some(vcpus_kill_signalled) = self.vcpus_kill_signalled.take() { 8106dc7085SRob Bradford // Spin until we are sure the reset_evt has been handled and that when 8206dc7085SRob Bradford // we return from the KVM_RUN we will exit rather than re-enter the guest. 83a00d2986SRob Bradford while !vcpus_kill_signalled.load(Ordering::SeqCst) { 8406dc7085SRob Bradford // This is more effective than thread::yield_now() at 8506dc7085SRob Bradford // avoiding a priority inversion with the VMM thread 8606dc7085SRob Bradford thread::sleep(std::time::Duration::from_millis(1)); 8706dc7085SRob Bradford } 88a00d2986SRob Bradford } 89e0d3efecSRob Bradford } else { 90e0d3efecSRob Bradford self.data[(self.index & INDEX_MASK) as usize] = data[0] 91e0d3efecSRob Bradford } 92e0d3efecSRob Bradford } 93ccda1a00SRob Bradford o => warn!("bad write offset on CMOS device: {}", o), 941fc6d50fSRob Bradford }; 951fc6d50fSRob Bradford None 968ea4145fSRob Bradford } 978ea4145fSRob Bradford read(&mut self, _base: u64, offset: u64, data: &mut [u8])988ea4145fSRob Bradford fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) { 998ea4145fSRob Bradford fn to_bcd(v: u8) -> u8 { 1008ea4145fSRob Bradford assert!(v < 100); 1018ea4145fSRob Bradford ((v / 10) << 4) | (v % 10) 1028ea4145fSRob Bradford } 1038ea4145fSRob Bradford 1048ea4145fSRob Bradford if data.len() != 1 { 105ccda1a00SRob Bradford warn!("Invalid read size on CMOS device: {}", data.len()); 1068ea4145fSRob Bradford return; 1078ea4145fSRob Bradford } 1088ea4145fSRob Bradford 1098ea4145fSRob Bradford data[0] = match offset { 1108ea4145fSRob Bradford INDEX_OFFSET => self.index, 1118ea4145fSRob Bradford DATA_OFFSET => { 1128ea4145fSRob Bradford let seconds; 1138ea4145fSRob Bradford let minutes; 1148ea4145fSRob Bradford let hours; 1158ea4145fSRob Bradford let week_day; 1168ea4145fSRob Bradford let day; 1178ea4145fSRob Bradford let month; 1188ea4145fSRob Bradford let year; 1199806d83eSWei Liu // SAFETY: The clock_gettime and gmtime_r calls are safe as long as the structs they are 12008288305SRob Bradford // given are large enough, and neither of them fail. It is safe to zero initialize 12108288305SRob Bradford // the tm and timespec struct because it contains only plain data. 12208288305SRob Bradford let update_in_progress = unsafe { 12308288305SRob Bradford let mut timespec: timespec = mem::zeroed(); 12408288305SRob Bradford clock_gettime(CLOCK_REALTIME, &mut timespec as *mut _); 12508288305SRob Bradford 12616f52914SWei Liu // https://github.com/rust-lang/libc/issues/1848 12716f52914SWei Liu #[cfg_attr(target_env = "musl", allow(deprecated))] 12808288305SRob Bradford let now: time_t = timespec.tv_sec; 1298ea4145fSRob Bradford let mut tm: tm = mem::zeroed(); 1308ea4145fSRob Bradford gmtime_r(&now, &mut tm as *mut _); 13108288305SRob Bradford 1328ea4145fSRob Bradford // The following lines of code are safe but depend on tm being in scope. 1338ea4145fSRob Bradford seconds = tm.tm_sec; 1348ea4145fSRob Bradford minutes = tm.tm_min; 1358ea4145fSRob Bradford hours = tm.tm_hour; 1368ea4145fSRob Bradford week_day = tm.tm_wday + 1; 1378ea4145fSRob Bradford day = tm.tm_mday; 1388ea4145fSRob Bradford month = tm.tm_mon + 1; 1398ea4145fSRob Bradford year = tm.tm_year; 14008288305SRob Bradford 14108288305SRob Bradford // Update in Progress bit held for last 224us of each second 14208288305SRob Bradford const NANOSECONDS_PER_SECOND: i64 = 1_000_000_000; 14308288305SRob Bradford const UIP_HOLD_LENGTH: i64 = 8 * NANOSECONDS_PER_SECOND / 32768; 14408288305SRob Bradford timespec.tv_nsec >= (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH) 1458ea4145fSRob Bradford }; 1468ea4145fSRob Bradford match self.index { 1478ea4145fSRob Bradford 0x00 => to_bcd(seconds as u8), 1488ea4145fSRob Bradford 0x02 => to_bcd(minutes as u8), 1498ea4145fSRob Bradford 0x04 => to_bcd(hours as u8), 1508ea4145fSRob Bradford 0x06 => to_bcd(week_day as u8), 1518ea4145fSRob Bradford 0x07 => to_bcd(day as u8), 1528ea4145fSRob Bradford 0x08 => to_bcd(month as u8), 1538ea4145fSRob Bradford 0x09 => to_bcd((year % 100) as u8), 15408288305SRob Bradford // Bit 5 for 32kHz clock. Bit 7 for Update in Progress 155*dd0b95baSRob Bradford 0x0a => (1 << 5) | ((update_in_progress as u8) << 7), 156c7725f92SSebastien Boeuf // Bit 0-6 are reserved and must be 0. 157c7725f92SSebastien Boeuf // Bit 7 must be 1 (CMOS has power) 158c7725f92SSebastien Boeuf 0x0d => 1 << 7, 1598ea4145fSRob Bradford 0x32 => to_bcd(((year + 1900) / 100) as u8), 1608ea4145fSRob Bradford _ => { 1618ea4145fSRob Bradford // self.index is always guaranteed to be in range via INDEX_MASK. 1628ea4145fSRob Bradford self.data[(self.index & INDEX_MASK) as usize] 1638ea4145fSRob Bradford } 1648ea4145fSRob Bradford } 1658ea4145fSRob Bradford } 166ccda1a00SRob Bradford o => { 167ccda1a00SRob Bradford warn!("bad read offset on CMOS device: {}", o); 168ccda1a00SRob Bradford 0 169ccda1a00SRob Bradford } 1708ea4145fSRob Bradford } 1718ea4145fSRob Bradford } 1728ea4145fSRob Bradford } 173