mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-11 12:35:26 +00:00
5306418bd8
The emulated CMOS RTC device always returns the hour field in 24-hour format; initialize the Status Register B bit that indicates this to the guest. This also lets Linux register the CMOS RTC as an rtc-class device. It was previously ignoring it since it was assuming the device was in 12-hour mode. Also bump the minimum IRQ for the system allocator up to 9 to avoid sharing IRQ 8, which becomes an issue once Linux tries to enable the RTC device: snd_intel8x0 0000:00:0b.0: PCI->APIC IRQ transform: INT C -> IRQ 8 genirq: Flags mismatch irq 8. 00000080 (snd_intel8x0) vs. 00000000 (rtc0) snd_intel8x0 0000:00:0b.0: unable to grab IRQ 8 snd_intel8x0: probe of 0000:00:0b.0 failed with error -16 On x86_64, only 4 device IRQs are used (for PCI pin interrupts), so this does not reduce the number of available IRQs for devices. Most PCI devices in crosvm also support MSI-X, which this change also does not affect. BUG=b:162789858 TEST=Boot Linux 5.8; verify existence of /sys/class/rtc/rtc0 TEST=Boot on x86_64 Chromebook; verify IRQ 8 is not used by PCI devices Change-Id: I39ca6a823914d2d27caec7812b54bf754ac3db9a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2337370 Reviewed-by: Dylan Reid <dgreid@chromium.org> Reviewed-by: Tomasz Jeznach <tjeznach@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
121 lines
4 KiB
Rust
121 lines
4 KiB
Rust
// 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];
|
|
|
|
data[0x0B] = 0x02; // Status Register B: 24-hour mode
|
|
|
|
// 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(0xFFFFFF, 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 debug_label(&self) -> String {
|
|
"cmos".to_owned()
|
|
}
|
|
|
|
fn write(&mut self, 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, 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),
|
|
}
|
|
}
|
|
}
|