1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
rtc.rs 18 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
Jinhao Gao Отправлено год назад 969efb0
// 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(())
}
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
v2.4.0