mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-10-05 13:05:45 +00:00
devices, vmm: Add legacy CMOS device
Based off of crosvm revision b5237bbcf074eb30cf368a138c0835081e747d71 add a CMOS device. This environments that can't use KVM clock to get the current time (e.g. Windows and EFI.) Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
47367eb61b
commit
8ea4145f98
@ -31,10 +31,11 @@ path = "vhost_rs"
|
|||||||
features = ["vhost-user-slave"]
|
features = ["vhost-user-slave"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["acpi", "pci"]
|
default = ["acpi", "pci", "cmos"]
|
||||||
acpi = ["vmm/acpi"]
|
acpi = ["vmm/acpi"]
|
||||||
pci = ["vmm/pci_support"]
|
pci = ["vmm/pci_support"]
|
||||||
mmio = ["vmm/mmio_support"]
|
mmio = ["vmm/mmio_support"]
|
||||||
|
cmos = ["vmm/cmos"]
|
||||||
|
|
||||||
# Integration tests require a special environment to run in
|
# Integration tests require a special environment to run in
|
||||||
integration_tests = []
|
integration_tests = []
|
||||||
|
@ -19,3 +19,4 @@ tempfile = "3.1.0"
|
|||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
acpi = []
|
acpi = []
|
||||||
|
cmos = []
|
||||||
|
115
devices/src/legacy/cmos.rs
Normal file
115
devices/src/legacy/cmos.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
use libc::{gmtime_r, time, time_t, tm};
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use crate::BusDevice;
|
||||||
|
|
||||||
|
const INDEX_MASK: u8 = 0x7f;
|
||||||
|
const INDEX_OFFSET: u64 = 0x0;
|
||||||
|
const DATA_OFFSET: u64 = 0x1;
|
||||||
|
const DATA_LEN: usize = 128;
|
||||||
|
|
||||||
|
/// A CMOS/RTC device commonly seen on x86 I/O port 0x70/0x71.
|
||||||
|
pub struct Cmos {
|
||||||
|
index: u8,
|
||||||
|
data: [u8; DATA_LEN],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cmos {
|
||||||
|
/// Constructs a CMOS/RTC device with initial data.
|
||||||
|
/// `mem_below_4g` is the size of memory in bytes below the 32-bit gap.
|
||||||
|
/// `mem_above_4g` is the size of memory in bytes above the 32-bit gap.
|
||||||
|
pub fn new(mem_below_4g: u64, mem_above_4g: u64) -> Cmos {
|
||||||
|
let mut data = [0u8; DATA_LEN];
|
||||||
|
|
||||||
|
// Extended memory from 16 MB to 4 GB in units of 64 KB
|
||||||
|
let ext_mem = min(
|
||||||
|
0xFFFF,
|
||||||
|
mem_below_4g.saturating_sub(16 * 1024 * 1024) / (64 * 1024),
|
||||||
|
);
|
||||||
|
data[0x34] = ext_mem as u8;
|
||||||
|
data[0x35] = (ext_mem >> 8) as u8;
|
||||||
|
|
||||||
|
// High memory (> 4GB) in units of 64 KB
|
||||||
|
let high_mem = min(0x00FF_FFFF, mem_above_4g / (64 * 1024));
|
||||||
|
data[0x5b] = high_mem as u8;
|
||||||
|
data[0x5c] = (high_mem >> 8) as u8;
|
||||||
|
data[0x5d] = (high_mem >> 16) as u8;
|
||||||
|
|
||||||
|
Cmos { index: 0, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusDevice for Cmos {
|
||||||
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||||
|
if data.len() != 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match offset {
|
||||||
|
INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
|
||||||
|
DATA_OFFSET => self.data[self.index as usize] = data[0],
|
||||||
|
o => panic!("bad write offset on CMOS device: {}", o),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
|
||||||
|
fn to_bcd(v: u8) -> u8 {
|
||||||
|
assert!(v < 100);
|
||||||
|
((v / 10) << 4) | (v % 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.len() != 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = match offset {
|
||||||
|
INDEX_OFFSET => self.index,
|
||||||
|
DATA_OFFSET => {
|
||||||
|
let seconds;
|
||||||
|
let minutes;
|
||||||
|
let hours;
|
||||||
|
let week_day;
|
||||||
|
let day;
|
||||||
|
let month;
|
||||||
|
let year;
|
||||||
|
// The time and gmtime_r calls are safe as long as the structs they are given are
|
||||||
|
// large enough, and neither of them fail. It is safe to zero initialize the tm
|
||||||
|
// struct because it contains only plain data.
|
||||||
|
unsafe {
|
||||||
|
let mut tm: tm = mem::zeroed();
|
||||||
|
let mut now: time_t = 0;
|
||||||
|
time(&mut now as *mut _);
|
||||||
|
gmtime_r(&now, &mut tm as *mut _);
|
||||||
|
// The following lines of code are safe but depend on tm being in scope.
|
||||||
|
seconds = tm.tm_sec;
|
||||||
|
minutes = tm.tm_min;
|
||||||
|
hours = tm.tm_hour;
|
||||||
|
week_day = tm.tm_wday + 1;
|
||||||
|
day = tm.tm_mday;
|
||||||
|
month = tm.tm_mon + 1;
|
||||||
|
year = tm.tm_year;
|
||||||
|
};
|
||||||
|
match self.index {
|
||||||
|
0x00 => to_bcd(seconds as u8),
|
||||||
|
0x02 => to_bcd(minutes as u8),
|
||||||
|
0x04 => to_bcd(hours as u8),
|
||||||
|
0x06 => to_bcd(week_day as u8),
|
||||||
|
0x07 => to_bcd(day as u8),
|
||||||
|
0x08 => to_bcd(month as u8),
|
||||||
|
0x09 => to_bcd((year % 100) as u8),
|
||||||
|
0x32 => to_bcd(((year + 1900) / 100) as u8),
|
||||||
|
_ => {
|
||||||
|
// self.index is always guaranteed to be in range via INDEX_MASK.
|
||||||
|
self.data[(self.index & INDEX_MASK) as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o => panic!("bad read offset on CMOS device: {}", o),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE-BSD-3-Clause file.
|
// found in the LICENSE-BSD-3-Clause file.
|
||||||
|
|
||||||
|
#[cfg(feature = "cmos")]
|
||||||
|
mod cmos;
|
||||||
mod i8042;
|
mod i8042;
|
||||||
mod serial;
|
mod serial;
|
||||||
|
|
||||||
|
#[cfg(feature = "cmos")]
|
||||||
|
pub use self::cmos::Cmos;
|
||||||
pub use self::i8042::I8042Device;
|
pub use self::i8042::I8042Device;
|
||||||
pub use self::serial::Serial;
|
pub use self::serial::Serial;
|
||||||
|
@ -9,6 +9,7 @@ default = []
|
|||||||
acpi = ["acpi_tables","devices/acpi", "arch/acpi"]
|
acpi = ["acpi_tables","devices/acpi", "arch/acpi"]
|
||||||
pci_support = ["pci", "vfio", "vm-virtio/pci_support"]
|
pci_support = ["pci", "vfio", "vm-virtio/pci_support"]
|
||||||
mmio_support = ["vm-virtio/mmio_support"]
|
mmio_support = ["vm-virtio/mmio_support"]
|
||||||
|
cmos = ["devices/cmos"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acpi_tables = { path = "../acpi_tables", optional = true }
|
acpi_tables = { path = "../acpi_tables", optional = true }
|
||||||
|
@ -370,7 +370,22 @@ impl DeviceManager {
|
|||||||
.io
|
.io
|
||||||
.insert(i8042.clone(), 0x61, 0x4)
|
.insert(i8042.clone(), 0x61, 0x4)
|
||||||
.map_err(DeviceManagerError::BusError)?;
|
.map_err(DeviceManagerError::BusError)?;
|
||||||
|
#[cfg(feature = "cmos")]
|
||||||
|
{
|
||||||
|
use vm_memory::GuestMemory;
|
||||||
|
let mem_size = vm_info.memory.as_ref().read().unwrap().end_addr().0 + 1;
|
||||||
|
let mem_below_4g = std::cmp::min(arch::layout::MEM_32BIT_RESERVED_START.0, mem_size);
|
||||||
|
let mem_above_4g = mem_size.saturating_sub(arch::layout::RAM_64BIT_START.0);
|
||||||
|
|
||||||
|
let cmos = Arc::new(Mutex::new(devices::legacy::Cmos::new(
|
||||||
|
mem_below_4g,
|
||||||
|
mem_above_4g,
|
||||||
|
)));
|
||||||
|
buses
|
||||||
|
.io
|
||||||
|
.insert(cmos.clone(), 0x70, 0x2)
|
||||||
|
.map_err(DeviceManagerError::BusError)?;
|
||||||
|
}
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
{
|
{
|
||||||
let acpi_device = Arc::new(Mutex::new(devices::AcpiShutdownDevice::new(
|
let acpi_device = Arc::new(Mutex::new(devices::AcpiShutdownDevice::new(
|
||||||
|
Loading…
Reference in New Issue
Block a user