// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; use anyhow::Result; use log::{debug, error, warn}; use vmm_sys_util::eventfd::EventFd; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::{ AmlBuilder, AmlDevice, AmlEisaId, AmlIoDecode, AmlIoResource, AmlIrqNoFlags, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, }; use address_space::GuestAddress; use util::time::{mktime64, NANOSECONDS_PER_SECOND}; /// IO port of RTC device to select Register to read/write. pub const RTC_PORT_INDEX: u64 = 0x70; /// Index of register of time in RTC static RAM. const RTC_SECONDS: u8 = 0x00; const RTC_MINUTES: u8 = 0x02; const RTC_HOURS: u8 = 0x04; const RTC_DAY_OF_WEEK: u8 = 0x06; const RTC_DAY_OF_MONTH: u8 = 0x07; const RTC_MONTH: u8 = 0x08; const RTC_YEAR: u8 = 0x09; const RTC_REG_A: u8 = 0x0A; const RTC_REG_B: u8 = 0x0B; const RTC_REG_C: u8 = 0x0C; const RTC_REG_D: u8 = 0x0D; const RTC_CENTURY_BCD: u8 = 0x32; // Update in progress (UIP) bit. const REG_A_UIP: u8 = 0x80; // UIP bit held for last 244 us of every second. const UIP_HOLD_LENGTH: u64 = 8 * NANOSECONDS_PER_SECOND / 32768; // Index of memory data in RTC static RAM. // 0x15/0x16 stores low/high byte below 1MB, range is [0, 640KB]. const CMOS_BASE_MEM: (u8, u8) = (0x15, 0x16); // 0x17/0x18 stores low/high byte of memory between [1MB, 64MB], unit is KB. const CMOS_EXT_MEM: (u8, u8) = (0x17, 0x18); // 0x30/0x31 stores low/high byte of memory between [1MB, 64MB], unit is KB. const CMOS_ACTUAL_EXT_MEM: (u8, u8) = (0x30, 0x31); // 0x34/0x35 stores low/high byte of memory between [16MB, 4GB], unit is 64KB. const CMOS_MEM_BELOW_4GB: (u8, u8) = (0x34, 0x35); // 0x5B/0x5C/0x5D stores low/middle/high byte of memory above 4GB, unit is 64KB. const CMOS_MEM_ABOVE_4GB: (u8, u8, u8) = (0x5B, 0x5C, 0x5D); fn rtc_time_to_tm(time_val: i64) -> libc::tm { let mut dest_tm = libc::tm { tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 0, tm_mon: 0, tm_year: 0, tm_wday: 0, tm_yday: 0, tm_isdst: 0, tm_gmtoff: 0, tm_zone: std::ptr::null_mut(), }; // SAFETY: `libc::gmtime_r` just convert calendar time to // broken-down format, and saved to `dest_tm`. unsafe { libc::gmtime_r(&time_val, &mut dest_tm) }; dest_tm } /// Transfer binary coded decimal to BCD coded decimal. fn bin_to_bcd(src: u8) -> u8 { ((src / 10) << 4) + (src % 10) } /// Transfer BCD coded decimal to binary coded decimal. fn bcd_to_bin(src: u8) -> u64 { if (src >> 4) > 9 || (src & 0x0f) > 9 { warn!("RTC: The BCD coded format is wrong."); return 0_u64; } (((src >> 4) * 10) + (src & 0x0f)) as u64 } #[allow(clippy::upper_case_acronyms)] /// RTC device. pub struct RTC { base: SysBusDevBase, /// Static CMOS RAM. cmos_data: [u8; 128], /// Index of Selected register. cur_index: u8, /// Guest memory size. mem_size: u64, /// The start address of gap. gap_start: u64, /// The tick offset. tick_offset: u64, /// Record the real time. base_time: Instant, } impl RTC { /// Construct function of RTC device. pub fn new() -> Result<RTC> { let mut rtc = RTC { base: SysBusDevBase { dev_type: SysBusDevType::Rtc, res: SysRes { region_base: RTC_PORT_INDEX, region_size: 8, irq: -1, }, interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), ..Default::default() }, cmos_data: [0_u8; 128], cur_index: 0_u8, mem_size: 0, gap_start: 0, // Since 1970-01-01 00:00:00, it never cause overflow. tick_offset: SystemTime::now() .duration_since(UNIX_EPOCH) .expect("time wrong") .as_secs(), base_time: Instant::now(), }; let tm = rtc_time_to_tm(rtc.get_current_value()); rtc.set_rtc_cmos(tm); rtc.init_rtc_reg(); Ok(rtc) } /// Set memory info stored in RTC static RAM. /// /// # Arguments /// /// * `mem_size` - Guest memory size. /// * `gap_start` - The start address of gap on x86_64 platform. This value can be found in /// memory layout. pub fn set_memory(&mut self, mem_size: u64, gap_start: u64) { self.mem_size = mem_size; self.gap_start = gap_start; let (mem_below_4g, mem_above_4g) = if mem_size > gap_start { (gap_start, mem_size - gap_start) } else { (mem_size, 0) }; let kb = 1024_u64; let base_mem_kb = 640; self.cmos_data[CMOS_BASE_MEM.0 as usize] = base_mem_kb as u8; self.cmos_data[CMOS_BASE_MEM.1 as usize] = (base_mem_kb >> 8) as u8; let ext_mem_kb = 63_u64 * kb; self.cmos_data[CMOS_EXT_MEM.0 as usize] = ext_mem_kb as u8; self.cmos_data[CMOS_EXT_MEM.1 as usize] = (ext_mem_kb >> 8) as u8; self.cmos_data[CMOS_ACTUAL_EXT_MEM.0 as usize] = ext_mem_kb as u8; self.cmos_data[CMOS_ACTUAL_EXT_MEM.1 as usize] = (ext_mem_kb >> 8) as u8; let mem_data = (mem_below_4g - 16 * kb * kb) / (64 * kb); self.cmos_data[CMOS_MEM_BELOW_4GB.0 as usize] = mem_data as u8; self.cmos_data[CMOS_MEM_BELOW_4GB.1 as usize] = (mem_data >> 8) as u8; if mem_above_4g > 0 { let mem_data = mem_above_4g / (64 * kb); self.cmos_data[CMOS_MEM_ABOVE_4GB.0 as usize] = mem_data as u8; self.cmos_data[CMOS_MEM_ABOVE_4GB.1 as usize] = (mem_data >> 8) as u8; self.cmos_data[CMOS_MEM_ABOVE_4GB.2 as usize] = (mem_data >> 16) as u8; } } fn init_rtc_reg(&mut self) { // Set Time frequency divider and Rate selection frequency in Register-A. // Bits 6-4 = Time frequency divider (010 = 32.768KHz). // Bits 3-0 = Rate selection frequency (110 = 1.024KHz, 976.562s). self.cmos_data[RTC_REG_A as usize] = 0x26; // Set 24 hour mode in Register-B. self.cmos_data[RTC_REG_B as usize] = 0x02; // Set VRT bit in Register-D, indicates that RAM and time are valid. self.cmos_data[RTC_REG_D as usize] = 0x80; } fn read_data(&mut self, data: &mut [u8]) -> bool { if data.len() != 1 { error!("RTC only supports reading data byte by byte."); return false; } let tm = rtc_time_to_tm(self.get_current_value()); self.set_rtc_cmos(tm); match self.cur_index { RTC_REG_A => { data[0] = self.cmos_data[RTC_REG_A as usize]; // UIP(update in progress) bit will be set at last 244us of every second. if self.update_in_progress() { data[0] |= REG_A_UIP; self.inject_interrupt(); trace::rtc_inject_interrupt(); } } RTC_REG_C => { // The interrupt request flag (IRQF), alarm interrupt flag (AF). data[0] = 1 << 7 | 1 << 5; } _ => { data[0] = self.cmos_data[self.cur_index as usize]; } } trace::rtc_read(self.cur_index, data[0]); true } fn write_data(&mut self, data: &[u8]) -> bool { if data.len() != 1 { error!("RTC only supports writing data byte by byte."); return false; } trace::rtc_write(self.cur_index, data[0]); match self.cur_index { RTC_SECONDS | RTC_MINUTES | RTC_HOURS | RTC_DAY_OF_WEEK | RTC_DAY_OF_MONTH | RTC_MONTH | RTC_YEAR | RTC_CENTURY_BCD => { if self.rtc_valid_check(data[0]) { self.cmos_data[self.cur_index as usize] = data[0]; self.update_rtc_time(); } else { warn!( "Set invalid RTC time, index {}, data {}", self.cur_index, data[0] ); } } RTC_REG_C | RTC_REG_D => { warn!( "Failed to write: read-only register, index {}, data {}", self.cur_index, data[0] ); return false; } _ => { self.cmos_data[self.cur_index as usize] = data[0]; } } true } pub fn realize(mut self, sysbus: &mut SysBus) -> Result<()> { let region_base = self.base.res.region_base; let region_size = self.base.res.region_size; self.set_sys_resource(sysbus, region_base, region_size)?; let dev = Arc::new(Mutex::new(self)); sysbus.attach_device(&dev, region_base, region_size, "RTC")?; Ok(()) } /// Get current clock value. fn get_current_value(&self) -> i64 { (self.base_time.elapsed().as_secs() as i128 + self.tick_offset as i128) as i64 } fn set_rtc_cmos(&mut self, tm: libc::tm) { self.cmos_data[RTC_SECONDS as usize] = bin_to_bcd(tm.tm_sec as u8); self.cmos_data[RTC_MINUTES as usize] = bin_to_bcd(tm.tm_min as u8); self.cmos_data[RTC_HOURS as usize] = bin_to_bcd(tm.tm_hour as u8); self.cmos_data[RTC_DAY_OF_WEEK as usize] = bin_to_bcd((tm.tm_wday + 1) as u8); self.cmos_data[RTC_DAY_OF_MONTH as usize] = bin_to_bcd(tm.tm_mday as u8); self.cmos_data[RTC_MONTH as usize] = bin_to_bcd((tm.tm_mon + 1) as u8); self.cmos_data[RTC_YEAR as usize] = bin_to_bcd(((tm.tm_year + 1900) % 100) as u8); self.cmos_data[RTC_CENTURY_BCD as usize] = bin_to_bcd(((tm.tm_year + 1900) / 100) as u8); } fn rtc_valid_check(&self, val: u8) -> bool { let range = [ [0, 59], // Seconds [0, 59], // Seconds Alarm [0, 59], // Minutes [0, 59], // Minutes Alarm [0, 23], // Hours [0, 23], // Hours Alarm [1, 7], // Day of the Week [1, 31], // Day of the Month [1, 12], // Month [0, 99], // Year ]; if (val >> 4) > 9 || (val & 0x0f) > 9 { return false; } let value = bcd_to_bin(val); if self.cur_index <= 9 && (value < range[self.cur_index as usize][0] || value > range[self.cur_index as usize][1]) { return false; } true } fn update_rtc_time(&mut self) { let sec = bcd_to_bin(self.cmos_data[RTC_SECONDS as usize]); let min = bcd_to_bin(self.cmos_data[RTC_MINUTES as usize]); let hour = bcd_to_bin(self.cmos_data[RTC_HOURS as usize]); let day = bcd_to_bin(self.cmos_data[RTC_DAY_OF_MONTH as usize]); let mon = bcd_to_bin(self.cmos_data[RTC_MONTH as usize]); let year = bcd_to_bin(self.cmos_data[RTC_YEAR as usize]) + bcd_to_bin(self.cmos_data[RTC_CENTURY_BCD as usize]) * 100; // Check rtc time is valid to prevent tick_offset overflow. if year < 1970 || !(1..=12).contains(&mon) || !(1..=31).contains(&day) { warn!( "RTC: the updated rtc time {}-{}-{} may be invalid.", year, mon, day ); return; } self.tick_offset = mktime64(year, mon, day, hour, min, sec); self.base_time = Instant::now(); } fn update_in_progress(&self) -> bool { self.base_time.elapsed().subsec_nanos() >= (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH) as u32 } } impl Device for RTC { fn device_base(&self) -> &DeviceBase { &self.base.base } fn device_base_mut(&mut self) -> &mut DeviceBase { &mut self.base.base } } impl SysBusDevOps for RTC { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base } fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { &mut self.base } fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { if offset == 0 { debug!( "Reading from ioport 0x{:x} is not supported yet", base.0 + offset ); data[0] = 0xFF; false } else { self.read_data(data) } } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { if offset == 0 { self.cur_index = data[0] & 0x7F; true } else { self.write_data(data) } } fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } fn reset(&mut self) -> Result<()> { self.cmos_data.fill(0); self.init_rtc_reg(); self.set_memory(self.mem_size, self.gap_start); Ok(()) } } impl AmlBuilder for RTC { fn aml_bytes(&self) -> Vec<u8> { let mut acpi_dev = AmlDevice::new("RTC"); acpi_dev.append_child(AmlNameDecl::new("_HID", AmlEisaId::new("PNP0B00"))); let mut res = AmlResTemplate::new(); res.append_child(AmlIoResource::new( AmlIoDecode::Decode16, self.base.res.region_base as u16, self.base.res.region_base as u16, 0x01, self.base.res.region_size as u8, )); res.append_child(AmlIrqNoFlags::new(self.base.res.irq as u8)); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); acpi_dev.aml_bytes() } } #[cfg(test)] mod test { use anyhow::Context; use super::*; use address_space::GuestAddress; const WIGGLE: u8 = 2; fn cmos_read(rtc: &mut RTC, index: u8) -> u8 { let mut data: [u8; 1] = [index; 1]; RTC::write(rtc, &mut data, GuestAddress(0), 0); RTC::read(rtc, &mut data, GuestAddress(0), 1); data[0] } fn cmos_write(rtc: &mut RTC, index: u8, val: u8) { let mut data: [u8; 1] = [index; 1]; RTC::write(rtc, &mut data, GuestAddress(0), 0); data[0] = val; RTC::write(rtc, &mut data, GuestAddress(0), 1); } #[test] fn test_set_year_20xx() -> Result<()> { let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; // Set rtc time: 2013-11-13 02:04:56 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x20); cmos_write(&mut rtc, RTC_YEAR, 0x13); cmos_write(&mut rtc, RTC_MONTH, 0x11); cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x13); cmos_write(&mut rtc, RTC_HOURS, 0x02); cmos_write(&mut rtc, RTC_MINUTES, 0x04); cmos_write(&mut rtc, RTC_SECONDS, 0x56); assert!((cmos_read(&mut rtc, RTC_SECONDS) - 0x56) <= WIGGLE); assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x04); assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x02); assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x13); assert_eq!(cmos_read(&mut rtc, RTC_MONTH), 0x11); assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x13); assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x20); // Set rtc time: 2080-11-13 02:04:56, ensure there is no year-2080 overflow. cmos_write(&mut rtc, RTC_YEAR, 0x80); assert!((cmos_read(&mut rtc, RTC_SECONDS) - 0x56) <= WIGGLE); assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x04); assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x02); assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x13); assert_eq!(cmos_read(&mut rtc, RTC_MONTH), 0x11); assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x80); assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x20); Ok(()) } #[test] fn test_set_year_1970() -> Result<()> { let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; // Set rtc time (min): 1970-01-01 00:00:00 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); cmos_write(&mut rtc, RTC_YEAR, 0x70); cmos_write(&mut rtc, RTC_MONTH, 0x01); cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x01); cmos_write(&mut rtc, RTC_HOURS, 0x00); cmos_write(&mut rtc, RTC_MINUTES, 0x00); cmos_write(&mut rtc, RTC_SECONDS, 0x00); assert!((cmos_read(&mut rtc, RTC_SECONDS) - 0x00) <= WIGGLE); assert_eq!(cmos_read(&mut rtc, RTC_MINUTES), 0x00); assert_eq!(cmos_read(&mut rtc, RTC_HOURS), 0x00); assert_eq!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x01); assert_eq!(cmos_read(&mut rtc, RTC_MONTH), 0x01); assert_eq!(cmos_read(&mut rtc, RTC_YEAR), 0x70); assert_eq!(cmos_read(&mut rtc, RTC_CENTURY_BCD), 0x19); Ok(()) } #[test] fn test_invalid_rtc_time() -> Result<()> { let mut rtc = RTC::new().with_context(|| "Failed to create RTC device")?; // Set rtc year: 1969 cmos_write(&mut rtc, RTC_CENTURY_BCD, 0x19); cmos_write(&mut rtc, RTC_YEAR, 0x69); assert_ne!(cmos_read(&mut rtc, RTC_YEAR), 0x69); // Set rtc month: 13 cmos_write(&mut rtc, RTC_MONTH, 0x13); assert_ne!(cmos_read(&mut rtc, RTC_MONTH), 0x13); // Set rtc day: 32 cmos_write(&mut rtc, RTC_DAY_OF_MONTH, 0x32); assert_ne!(cmos_read(&mut rtc, RTC_DAY_OF_MONTH), 0x32); // Set rtc hour: 25 cmos_write(&mut rtc, RTC_HOURS, 0x25); assert_ne!(cmos_read(&mut rtc, RTC_HOURS), 0x25); // Set rtc minute: 60 cmos_write(&mut rtc, RTC_MINUTES, 0x60); assert_ne!(cmos_read(&mut rtc, RTC_MINUTES), 0x60); // Set rtc second: 60 cmos_write(&mut rtc, RTC_SECONDS, 0x60); assert_ne!(cmos_read(&mut rtc, RTC_SECONDS), 0x60); Ok(()) } }