mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-10 20:19:07 +00:00
devices: add un-mutex-ed BusDevice in Bus
Allow devices to be added to a Bus without a mutex. If a device implements BusDeviceSync and is inserted into the Bus via the new insert_sync function, the Bus will not lock the device before write and read operations. This feature will allow IrqChip implementations to use the mmio bus for APIC mmio, and allow each vcpu to write to their respective APICs simultaneously. This also changes the BusDevice trait so read and write functions take a new BusAccessInfo struct. The BusAccessInfo conveys the full address of the read/write operation, the offset of the address relative to the device start address, and an id that in practice will hold the vcpu id for the vcpu thread perforing the read/write. As a result, inserts into the Bus are no longer distinguished between full_addr and non full_addr inserts. Instead, each device's BusDevice implementation must decide whether they use the absolute read/write address or the relative read/write offset. BUG=chromium:1077058 TEST=ran build_test TEST=ran simple debian image Change-Id: I9125aaa69869c1004b6c6a099b50f5c58038d4ab Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2514662 Reviewed-by: Zach Reizner <zachr@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Colin Downs-Razouk <colindr@google.com>
This commit is contained in:
parent
11dc671da4
commit
62e2e2e8de
21 changed files with 459 additions and 275 deletions
|
@ -296,12 +296,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
.map_err(Error::RegisterIrqfd)?;
|
||||
|
||||
mmio_bus
|
||||
.insert(
|
||||
pci_bus.clone(),
|
||||
AARCH64_PCI_CFG_BASE,
|
||||
AARCH64_PCI_CFG_SIZE,
|
||||
false,
|
||||
)
|
||||
.insert(pci_bus.clone(), AARCH64_PCI_CFG_BASE, AARCH64_PCI_CFG_SIZE)
|
||||
.map_err(Error::RegisterPci)?;
|
||||
|
||||
let mut cmdline = Self::get_base_linux_cmdline();
|
||||
|
@ -462,7 +457,7 @@ impl AArch64 {
|
|||
.map_err(Error::RegisterIrqfd)?;
|
||||
|
||||
let rtc = Arc::new(Mutex::new(devices::pl030::Pl030::new(rtc_evt)));
|
||||
bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE, false)
|
||||
bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE)
|
||||
.expect("failed to add rtc device");
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -408,13 +408,13 @@ pub fn generate_pci_root(
|
|||
root.add_device(address, arced_dev.clone());
|
||||
for range in &ranges {
|
||||
mmio_bus
|
||||
.insert(arced_dev.clone(), range.0, range.1, true)
|
||||
.insert(arced_dev.clone(), range.0, range.1)
|
||||
.map_err(DeviceRegistrationError::MmioInsert)?;
|
||||
}
|
||||
|
||||
for range in &device_ranges {
|
||||
mmio_bus
|
||||
.insert(arced_dev.clone(), range.0, range.1, true)
|
||||
.insert(arced_dev.clone(), range.0, range.1)
|
||||
.map_err(DeviceRegistrationError::MmioInsert)?;
|
||||
}
|
||||
}
|
||||
|
@ -484,7 +484,6 @@ pub fn add_goldfish_battery(
|
|||
)),
|
||||
mmio_base,
|
||||
devices::bat::GOLDFISHBAT_MMIO_LEN,
|
||||
false,
|
||||
)
|
||||
.map_err(DeviceRegistrationError::MmioInsert)?;
|
||||
}
|
||||
|
@ -494,7 +493,6 @@ pub fn add_goldfish_battery(
|
|||
Arc::new(Mutex::new(goldfish_bat)),
|
||||
mmio_base,
|
||||
devices::bat::GOLDFISHBAT_MMIO_LEN,
|
||||
false,
|
||||
)
|
||||
.map_err(DeviceRegistrationError::MmioInsert)?;
|
||||
}
|
||||
|
|
|
@ -445,13 +445,13 @@ pub fn add_serial_devices(
|
|||
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?,
|
||||
));
|
||||
io_bus
|
||||
.insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
|
||||
.insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
|
||||
.unwrap();
|
||||
}
|
||||
None => {
|
||||
let com = Arc::new(Mutex::new(com));
|
||||
io_bus
|
||||
.insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
|
||||
.insert(com.clone(), SERIAL_ADDR[x as usize], 0x8)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use crate::{BusDevice, BusResumeDevice};
|
||||
use crate::{BusAccessInfo, BusDevice, BusResumeDevice};
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use base::{error, warn, Event};
|
||||
|
||||
|
@ -56,15 +56,15 @@ impl BusDevice for ACPIPMResource {
|
|||
"ACPIPMResource".to_owned()
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
let val = match offset as u16 {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
let val = match info.offset as u16 {
|
||||
PM1_STATUS => self.pm1_status,
|
||||
PM1_ENABLE => self.pm1_enable,
|
||||
PM1_CONTROL => self.pm1_control,
|
||||
SLEEP_CONTROL => self.sleep_control as u16,
|
||||
SLEEP_STATUS => self.sleep_status as u16,
|
||||
_ => {
|
||||
warn!("ACPIPM: Bad read from offset {}", offset);
|
||||
warn!("ACPIPM: Bad read from {}", info);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -77,7 +77,7 @@ impl BusDevice for ACPIPMResource {
|
|||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
let max_bytes = std::mem::size_of::<u16>();
|
||||
|
||||
// only allow maximum max_bytes to write
|
||||
|
@ -94,7 +94,7 @@ impl BusDevice for ACPIPMResource {
|
|||
}
|
||||
let val = u16::from_ne_bytes(val_arr);
|
||||
|
||||
match offset as u16 {
|
||||
match info.offset as u16 {
|
||||
PM1_STATUS => self.pm1_status &= !val,
|
||||
PM1_ENABLE => self.pm1_enable = val,
|
||||
PM1_CONTROL => {
|
||||
|
@ -123,7 +123,7 @@ impl BusDevice for ACPIPMResource {
|
|||
}
|
||||
SLEEP_STATUS => self.sleep_status &= !val as u8,
|
||||
_ => {
|
||||
warn!("ACPIPM: Bad write to offset {}", offset);
|
||||
warn!("ACPIPM: Bad write to {}", info);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use base::{error, warn, AsRawDescriptor, Descriptor, Event, PollContext, PollToken};
|
||||
use msg_socket::{MsgReceiver, MsgSender};
|
||||
|
@ -324,7 +324,7 @@ impl BusDevice for GoldfishBattery {
|
|||
"GoldfishBattery".to_owned()
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
if data.len() != std::mem::size_of::<u32>() {
|
||||
warn!(
|
||||
"{}: unsupported read length {}, only support 4bytes read",
|
||||
|
@ -334,7 +334,7 @@ impl BusDevice for GoldfishBattery {
|
|||
return;
|
||||
}
|
||||
|
||||
let val = match offset as u32 {
|
||||
let val = match info.offset as u32 {
|
||||
BATTERY_INT_STATUS => {
|
||||
// read to clear the interrupt status
|
||||
std::mem::replace(&mut self.state.lock().int_status, 0)
|
||||
|
@ -355,7 +355,7 @@ impl BusDevice for GoldfishBattery {
|
|||
BATTERY_CHARGE_FULL_UAH => 0,
|
||||
BATTERY_CYCLE_COUNT => 0,
|
||||
_ => {
|
||||
warn!("{}: unsupported read offset {}", self.debug_label(), offset);
|
||||
warn!("{}: unsupported read address {}", self.debug_label(), info);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -364,7 +364,7 @@ impl BusDevice for GoldfishBattery {
|
|||
data.copy_from_slice(&val_arr);
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() != std::mem::size_of::<u32>() {
|
||||
warn!(
|
||||
"{}: unsupported write length {}, only support 4bytes write",
|
||||
|
@ -378,7 +378,7 @@ impl BusDevice for GoldfishBattery {
|
|||
val_arr.copy_from_slice(data);
|
||||
let val = u32::from_ne_bytes(val_arr);
|
||||
|
||||
match offset as u32 {
|
||||
match info.offset as u32 {
|
||||
BATTERY_INT_ENABLE => {
|
||||
self.state.lock().int_enable = val;
|
||||
if (val & BATTERY_INT_MASK) != 0 && !self.activated {
|
||||
|
@ -386,7 +386,7 @@ impl BusDevice for GoldfishBattery {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
warn!("{}: Bad write to offset {}", self.debug_label(), offset);
|
||||
warn!("{}: Bad write to address {}", self.debug_label(), info);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,8 +10,27 @@ use std::fmt::{self, Display};
|
|||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use base::RawDescriptor;
|
||||
use msg_socket::MsgOnSocket;
|
||||
use sync::Mutex;
|
||||
|
||||
/// Information about how a device was accessed.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, MsgOnSocket)]
|
||||
pub struct BusAccessInfo {
|
||||
/// Offset from base address that the device was accessed at.
|
||||
pub offset: u64,
|
||||
/// Absolute address of the device's access in its address space.
|
||||
pub address: u64,
|
||||
/// ID of the entity requesting a device access, usually the VCPU id.
|
||||
pub id: usize,
|
||||
}
|
||||
|
||||
// Implement `Display` for `MinMax`.
|
||||
impl std::fmt::Display for BusAccessInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
/// Trait for devices that respond to reads or writes in an arbitrary address space.
|
||||
///
|
||||
/// The device does not care where it exists in address space as each method is only given an offset
|
||||
|
@ -21,9 +40,9 @@ pub trait BusDevice: Send {
|
|||
/// Returns a label suitable for debug output.
|
||||
fn debug_label(&self) -> String;
|
||||
/// Reads at `offset` from this device
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {}
|
||||
fn read(&mut self, offset: BusAccessInfo, data: &mut [u8]) {}
|
||||
/// Writes at `offset` into this device
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {}
|
||||
fn write(&mut self, offset: BusAccessInfo, data: &[u8]) {}
|
||||
/// Sets a register in the configuration space. Only used by PCI.
|
||||
/// * `reg_idx` - The index of the config register to modify.
|
||||
/// * `offset` - Offset in to the register.
|
||||
|
@ -37,6 +56,11 @@ pub trait BusDevice: Send {
|
|||
fn on_sandboxed(&mut self) {}
|
||||
}
|
||||
|
||||
pub trait BusDeviceSync: BusDevice + Sync {
|
||||
fn read(&self, offset: BusAccessInfo, data: &mut [u8]);
|
||||
fn write(&self, offset: BusAccessInfo, data: &[u8]);
|
||||
}
|
||||
|
||||
pub trait BusResumeDevice: Send {
|
||||
/// notify the devices which are invoked
|
||||
/// before the VM resumes form suspend.
|
||||
|
@ -65,13 +89,10 @@ pub type Result<T> = result::Result<T, Error>;
|
|||
///
|
||||
/// * base - The address at which the range start.
|
||||
/// * len - The length of the range in bytes.
|
||||
/// * full_addr - If true, return the full address from `get_device`, otherwise return the offset
|
||||
/// from `base`
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct BusRange {
|
||||
pub base: u64,
|
||||
pub len: u64,
|
||||
pub full_addr: bool,
|
||||
}
|
||||
|
||||
impl BusRange {
|
||||
|
@ -106,6 +127,12 @@ impl PartialOrd for BusRange {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum BusDeviceEntry {
|
||||
OuterSync(Arc<Mutex<dyn BusDevice>>),
|
||||
InnerSync(Arc<dyn BusDeviceSync>),
|
||||
}
|
||||
|
||||
/// A device container for routing reads and writes over some address space.
|
||||
///
|
||||
/// This doesn't have any restrictions on what kind of device or address space this applies to. The
|
||||
|
@ -115,8 +142,9 @@ impl PartialOrd for BusRange {
|
|||
/// resume back from S3 suspended state.
|
||||
#[derive(Clone)]
|
||||
pub struct Bus {
|
||||
devices: BTreeMap<BusRange, Arc<Mutex<dyn BusDevice>>>,
|
||||
devices: BTreeMap<BusRange, BusDeviceEntry>,
|
||||
resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
|
||||
access_id: usize,
|
||||
}
|
||||
|
||||
impl Bus {
|
||||
|
@ -125,45 +153,68 @@ impl Bus {
|
|||
Bus {
|
||||
devices: BTreeMap::new(),
|
||||
resume_notify_devices: Vec::new(),
|
||||
access_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn first_before(&self, addr: u64) -> Option<(BusRange, &Mutex<dyn BusDevice>)> {
|
||||
/// Sets the id that will be used for BusAccessInfo.
|
||||
pub fn set_access_id(&mut self, id: usize) {
|
||||
self.access_id = id;
|
||||
}
|
||||
|
||||
fn first_before(&self, addr: u64) -> Option<(BusRange, &BusDeviceEntry)> {
|
||||
let (range, dev) = self
|
||||
.devices
|
||||
.range(
|
||||
..=BusRange {
|
||||
base: addr,
|
||||
len: 1,
|
||||
full_addr: false,
|
||||
},
|
||||
)
|
||||
.range(..=BusRange { base: addr, len: 1 })
|
||||
.rev()
|
||||
.next()?;
|
||||
Some((*range, dev))
|
||||
}
|
||||
|
||||
fn get_device(&self, addr: u64) -> Option<(u64, &Mutex<dyn BusDevice>)> {
|
||||
fn get_device(&self, addr: u64) -> Option<(u64, u64, &BusDeviceEntry)> {
|
||||
if let Some((range, dev)) = self.first_before(addr) {
|
||||
let offset = addr - range.base;
|
||||
if offset < range.len {
|
||||
if range.full_addr {
|
||||
return Some((addr, dev));
|
||||
} else {
|
||||
return Some((offset, dev));
|
||||
}
|
||||
return Some((offset, addr, dev));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Puts the given device at the given address space.
|
||||
pub fn insert(
|
||||
pub fn insert(&mut self, device: Arc<Mutex<dyn BusDevice>>, base: u64, len: u64) -> Result<()> {
|
||||
if len == 0 {
|
||||
return Err(Error::Overlap);
|
||||
}
|
||||
|
||||
// Reject all cases where the new device's range overlaps with an existing device.
|
||||
if self
|
||||
.devices
|
||||
.iter()
|
||||
.any(|(range, _dev)| range.overlaps(base, len))
|
||||
{
|
||||
return Err(Error::Overlap);
|
||||
}
|
||||
|
||||
if self
|
||||
.devices
|
||||
.insert(BusRange { base, len }, BusDeviceEntry::OuterSync(device))
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::Overlap);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Puts the given device that implements BusDeviceSync at the given address space. Devices
|
||||
/// that implement BusDeviceSync manage thread safety internally, and thus can be written to
|
||||
/// by multiple threads simultaneously.
|
||||
pub fn insert_sync(
|
||||
&mut self,
|
||||
device: Arc<Mutex<dyn BusDevice>>,
|
||||
device: Arc<dyn BusDeviceSync>,
|
||||
base: u64,
|
||||
len: u64,
|
||||
full_addr: bool,
|
||||
) -> Result<()> {
|
||||
if len == 0 {
|
||||
return Err(Error::Overlap);
|
||||
|
@ -180,14 +231,7 @@ impl Bus {
|
|||
|
||||
if self
|
||||
.devices
|
||||
.insert(
|
||||
BusRange {
|
||||
base,
|
||||
len,
|
||||
full_addr,
|
||||
},
|
||||
device,
|
||||
)
|
||||
.insert(BusRange { base, len }, BusDeviceEntry::InnerSync(device))
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::Overlap);
|
||||
|
@ -200,8 +244,16 @@ impl Bus {
|
|||
///
|
||||
/// Returns true on success, otherwise `data` is untouched.
|
||||
pub fn read(&self, addr: u64, data: &mut [u8]) -> bool {
|
||||
if let Some((offset, dev)) = self.get_device(addr) {
|
||||
dev.lock().read(offset, data);
|
||||
if let Some((offset, address, dev)) = self.get_device(addr) {
|
||||
let io = BusAccessInfo {
|
||||
address,
|
||||
offset,
|
||||
id: self.access_id,
|
||||
};
|
||||
match dev {
|
||||
BusDeviceEntry::OuterSync(dev) => dev.lock().read(io, data),
|
||||
BusDeviceEntry::InnerSync(dev) => dev.read(io, data),
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -212,8 +264,16 @@ impl Bus {
|
|||
///
|
||||
/// Returns true on success, otherwise `data` is untouched.
|
||||
pub fn write(&self, addr: u64, data: &[u8]) -> bool {
|
||||
if let Some((offset, dev)) = self.get_device(addr) {
|
||||
dev.lock().write(offset, data);
|
||||
if let Some((offset, address, dev)) = self.get_device(addr) {
|
||||
let io = BusAccessInfo {
|
||||
address,
|
||||
offset,
|
||||
id: self.access_id,
|
||||
};
|
||||
match dev {
|
||||
BusDeviceEntry::OuterSync(dev) => dev.lock().write(io, data),
|
||||
BusDeviceEntry::InnerSync(dev) => dev.write(io, data),
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -245,21 +305,34 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
struct ConstantDevice;
|
||||
struct ConstantDevice {
|
||||
uses_full_addr: bool,
|
||||
}
|
||||
|
||||
impl BusDevice for ConstantDevice {
|
||||
fn debug_label(&self) -> String {
|
||||
"constant device".to_owned()
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
let addr = if self.uses_full_addr {
|
||||
info.address
|
||||
} else {
|
||||
info.offset
|
||||
};
|
||||
for (i, v) in data.iter_mut().enumerate() {
|
||||
*v = (offset as u8) + (i as u8);
|
||||
*v = (addr as u8) + (i as u8);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
let addr = if self.uses_full_addr {
|
||||
info.address
|
||||
} else {
|
||||
info.offset
|
||||
};
|
||||
for (i, v) in data.iter().enumerate() {
|
||||
assert_eq!(*v, (offset as u8) + (i as u8))
|
||||
assert_eq!(*v, (addr as u8) + (i as u8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -268,41 +341,41 @@ mod tests {
|
|||
fn bus_insert() {
|
||||
let mut bus = Bus::new();
|
||||
let dummy = Arc::new(Mutex::new(DummyDevice));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0f, 0x10, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x15, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x15, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x01, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x20, false).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x20, 0x05, false).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x25, 0x05, false).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x10, false).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0f, 0x10).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x15).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x15).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x01).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x20).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x20, 0x05).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x25, 0x05).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x10).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bus_insert_full_addr() {
|
||||
let mut bus = Bus::new();
|
||||
let dummy = Arc::new(Mutex::new(DummyDevice));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, true).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0f, 0x10, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x15, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x15, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x01, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x20, true).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x20, 0x05, true).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x25, 0x05, true).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x10, true).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0f, 0x10).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x15).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x15).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x12, 0x01).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x20).is_err());
|
||||
assert!(bus.insert(dummy.clone(), 0x20, 0x05).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x25, 0x05).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x0, 0x10).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bus_read_write() {
|
||||
let mut bus = Bus::new();
|
||||
let dummy = Arc::new(Mutex::new(DummyDevice));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_ok());
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
|
||||
assert!(bus.read(0x10, &mut [0, 0, 0, 0]));
|
||||
assert!(bus.write(0x10, &[0, 0, 0, 0]));
|
||||
assert!(bus.read(0x11, &mut [0, 0, 0, 0]));
|
||||
|
@ -318,8 +391,10 @@ mod tests {
|
|||
#[test]
|
||||
fn bus_read_write_values() {
|
||||
let mut bus = Bus::new();
|
||||
let dummy = Arc::new(Mutex::new(ConstantDevice));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, false).is_ok());
|
||||
let dummy = Arc::new(Mutex::new(ConstantDevice {
|
||||
uses_full_addr: false,
|
||||
}));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
|
||||
|
||||
let mut values = [0, 1, 2, 3];
|
||||
assert!(bus.read(0x10, &mut values));
|
||||
|
@ -333,8 +408,10 @@ mod tests {
|
|||
#[test]
|
||||
fn bus_read_write_full_addr_values() {
|
||||
let mut bus = Bus::new();
|
||||
let dummy = Arc::new(Mutex::new(ConstantDevice));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10, true).is_ok());
|
||||
let dummy = Arc::new(Mutex::new(ConstantDevice {
|
||||
uses_full_addr: true,
|
||||
}));
|
||||
assert!(bus.insert(dummy.clone(), 0x10, 0x10).is_ok());
|
||||
|
||||
let mut values = [0u8; 4];
|
||||
assert!(bus.read(0x10, &mut values));
|
||||
|
@ -350,7 +427,6 @@ mod tests {
|
|||
let a = BusRange {
|
||||
base: 0x1000,
|
||||
len: 0x400,
|
||||
full_addr: false,
|
||||
};
|
||||
assert!(a.contains(0x1000));
|
||||
assert!(a.contains(0x13ff));
|
||||
|
@ -364,7 +440,6 @@ mod tests {
|
|||
let a = BusRange {
|
||||
base: 0x1000,
|
||||
len: 0x400,
|
||||
full_addr: false,
|
||||
};
|
||||
assert!(a.overlaps(0x1000, 0x400));
|
||||
assert!(a.overlaps(0xf00, 0x400));
|
||||
|
|
|
@ -6,7 +6,7 @@ use libc::{gmtime_r, time, time_t, tm};
|
|||
use std::cmp::min;
|
||||
use std::mem;
|
||||
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
|
||||
const INDEX_MASK: u8 = 0x7f;
|
||||
const INDEX_OFFSET: u64 = 0x0;
|
||||
|
@ -51,19 +51,19 @@ impl BusDevice for Cmos {
|
|||
"cmos".to_owned()
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
match offset {
|
||||
match info.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 read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
fn to_bcd(v: u8) -> u8 {
|
||||
assert!(v < 100);
|
||||
((v / 10) << 4) | (v % 10)
|
||||
|
@ -73,7 +73,7 @@ impl BusDevice for Cmos {
|
|||
return;
|
||||
}
|
||||
|
||||
data[0] = match offset {
|
||||
data[0] = match info.offset {
|
||||
INDEX_OFFSET => self.index,
|
||||
DATA_OFFSET => {
|
||||
let seconds;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use base::{error, Event};
|
||||
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
|
||||
/// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
|
||||
pub struct I8042Device {
|
||||
|
@ -25,18 +25,18 @@ impl BusDevice for I8042Device {
|
|||
"i8042".to_owned()
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
if data.len() == 1 && offset == 0x64 {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
if data.len() == 1 && info.offset == 0x64 {
|
||||
data[0] = 0x0;
|
||||
} else if data.len() == 1 && offset == 0x61 {
|
||||
} else if data.len() == 1 && info.offset == 0x61 {
|
||||
// Like kvmtool, we return bit 5 set in I8042_PORT_B_REG to
|
||||
// avoid hang in pit_calibrate_tsc() in Linux kernel.
|
||||
data[0] = 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
if data.len() == 1 && data[0] == 0xfe && offset == 0x64 {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() == 1 && data[0] == 0xfe && info.offset == 0x64 {
|
||||
if let Err(e) = self.reset_evt.write(1) {
|
||||
error!("failed to trigger i8042 reset event: {}", e);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use super::IrqEvent;
|
||||
use crate::bus::BusAccessInfo;
|
||||
use crate::BusDevice;
|
||||
use base::{error, warn, AsRawDescriptor, Error, Event, Result};
|
||||
use hypervisor::{IoapicState, MsiAddressMessage, MsiDataMessage, TriggerMode, NUM_IOAPIC_PINS};
|
||||
|
@ -75,20 +76,20 @@ impl BusDevice for Ioapic {
|
|||
"userspace IOAPIC".to_string()
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
if data.len() > 8 || data.len() == 0 {
|
||||
warn!("IOAPIC: Bad read size: {}", data.len());
|
||||
return;
|
||||
}
|
||||
if offset >= IOAPIC_MEM_LENGTH_BYTES {
|
||||
warn!("IOAPIC: Bad read from offset {}", offset);
|
||||
if info.offset >= IOAPIC_MEM_LENGTH_BYTES {
|
||||
warn!("IOAPIC: Bad read from {}", info);
|
||||
}
|
||||
let out = match offset as u8 {
|
||||
let out = match info.offset as u8 {
|
||||
IOREGSEL_OFF => self.state.ioregsel.into(),
|
||||
IOREGSEL_DUMMY_UPPER_32_BITS_OFF => 0,
|
||||
IOWIN_OFF => self.ioapic_read(),
|
||||
_ => {
|
||||
warn!("IOAPIC: Bad read from offset {}", offset);
|
||||
warn!("IOAPIC: Bad read from {}", info);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -100,15 +101,15 @@ impl BusDevice for Ioapic {
|
|||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() > 8 || data.len() == 0 {
|
||||
warn!("IOAPIC: Bad write size: {}", data.len());
|
||||
return;
|
||||
}
|
||||
if offset >= IOAPIC_MEM_LENGTH_BYTES {
|
||||
warn!("IOAPIC: Bad write to offset {}", offset);
|
||||
if info.offset >= IOAPIC_MEM_LENGTH_BYTES {
|
||||
warn!("IOAPIC: Bad write to {}", info);
|
||||
}
|
||||
match offset as u8 {
|
||||
match info.offset as u8 {
|
||||
IOREGSEL_OFF => self.state.ioregsel = data[0],
|
||||
IOREGSEL_DUMMY_UPPER_32_BITS_OFF => {} // Ignored.
|
||||
IOWIN_OFF => {
|
||||
|
@ -121,7 +122,7 @@ impl BusDevice for Ioapic {
|
|||
self.ioapic_write(val);
|
||||
}
|
||||
_ => {
|
||||
warn!("IOAPIC: Bad write to offset {}", offset);
|
||||
warn!("IOAPIC: Bad write to {}", info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -437,6 +438,15 @@ mod tests {
|
|||
Ioapic::new(device_socket).unwrap()
|
||||
}
|
||||
|
||||
fn ioapic_bus_address(offset: u8) -> BusAccessInfo {
|
||||
let offset = offset as u64;
|
||||
BusAccessInfo {
|
||||
offset,
|
||||
address: IOAPIC_BASE_ADDRESS + offset,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_up(trigger: TriggerMode) -> (Ioapic, usize) {
|
||||
let irq = NUM_IOAPIC_PINS - 1;
|
||||
let ioapic = set_up_with_irq(irq, trigger);
|
||||
|
@ -456,14 +466,14 @@ mod tests {
|
|||
|
||||
fn read_reg(ioapic: &mut Ioapic, selector: u8) -> u32 {
|
||||
let mut data = [0; 4];
|
||||
ioapic.write(IOREGSEL_OFF.into(), &[selector]);
|
||||
ioapic.read(IOWIN_OFF.into(), &mut data);
|
||||
ioapic.write(ioapic_bus_address(IOREGSEL_OFF), &[selector]);
|
||||
ioapic.read(ioapic_bus_address(IOWIN_OFF), &mut data);
|
||||
u32::from_ne_bytes(data)
|
||||
}
|
||||
|
||||
fn write_reg(ioapic: &mut Ioapic, selector: u8, value: u32) {
|
||||
ioapic.write(IOREGSEL_OFF.into(), &[selector]);
|
||||
ioapic.write(IOWIN_OFF.into(), &value.to_ne_bytes());
|
||||
ioapic.write(ioapic_bus_address(IOREGSEL_OFF), &[selector]);
|
||||
ioapic.write(ioapic_bus_address(IOWIN_OFF), &value.to_ne_bytes());
|
||||
}
|
||||
|
||||
fn read_entry(ioapic: &mut Ioapic, irq: usize) -> IoapicRedirectionTableEntry {
|
||||
|
@ -517,8 +527,8 @@ mod tests {
|
|||
let mut data_read = [0; 4];
|
||||
|
||||
for i in 0..data_write.len() {
|
||||
ioapic.write(IOREGSEL_OFF.into(), &data_write[i..i + 1]);
|
||||
ioapic.read(IOREGSEL_OFF.into(), &mut data_read[i..i + 1]);
|
||||
ioapic.write(ioapic_bus_address(IOREGSEL_OFF), &data_write[i..i + 1]);
|
||||
ioapic.read(ioapic_bus_address(IOREGSEL_OFF), &mut data_read[i..i + 1]);
|
||||
assert_eq!(data_write[i], data_read[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ impl IrqChip for KvmKernelIrqChip {
|
|||
/// Broadcast an end of interrupt.
|
||||
/// This should never be called on a KvmKernelIrqChip because a KVM vcpu should never exit
|
||||
/// with the KVM_EXIT_EOI_BROADCAST reason when an in-kernel irqchip exists.
|
||||
fn broadcast_eoi(&mut self, _vector: u8) -> Result<()> {
|
||||
fn broadcast_eoi(&self, _vector: u8) -> Result<()> {
|
||||
error!("broadcast_eoi should never be called for KvmKernelIrqChip");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -453,7 +453,7 @@ impl IrqChip for KvmSplitIrqChip {
|
|||
}
|
||||
|
||||
/// Broadcast an end of interrupt. For KvmSplitIrqChip this sends the EOI to the ioapic
|
||||
fn broadcast_eoi(&mut self, vector: u8) -> Result<()> {
|
||||
fn broadcast_eoi(&self, vector: u8) -> Result<()> {
|
||||
self.ioapic.lock().end_of_interrupt(vector);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -538,13 +538,13 @@ impl IrqChip for KvmSplitIrqChip {
|
|||
mmio_bus: &mut Bus,
|
||||
) -> Result<()> {
|
||||
// Insert pit into io_bus
|
||||
io_bus.insert(self.pit.clone(), 0x040, 0x8, true).unwrap();
|
||||
io_bus.insert(self.pit.clone(), 0x061, 0x1, true).unwrap();
|
||||
io_bus.insert(self.pit.clone(), 0x040, 0x8).unwrap();
|
||||
io_bus.insert(self.pit.clone(), 0x061, 0x1).unwrap();
|
||||
|
||||
// Insert pic into io_bus
|
||||
io_bus.insert(self.pic.clone(), 0x20, 0x2, true).unwrap();
|
||||
io_bus.insert(self.pic.clone(), 0xa0, 0x2, true).unwrap();
|
||||
io_bus.insert(self.pic.clone(), 0x4d0, 0x2, true).unwrap();
|
||||
io_bus.insert(self.pic.clone(), 0x20, 0x2).unwrap();
|
||||
io_bus.insert(self.pic.clone(), 0xa0, 0x2).unwrap();
|
||||
io_bus.insert(self.pic.clone(), 0x4d0, 0x2).unwrap();
|
||||
|
||||
// Insert ioapic into mmio_bus
|
||||
mmio_bus
|
||||
|
@ -552,7 +552,6 @@ impl IrqChip for KvmSplitIrqChip {
|
|||
self.ioapic.clone(),
|
||||
IOAPIC_BASE_ADDRESS,
|
||||
IOAPIC_MEM_LENGTH_BYTES,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ pub trait IrqChip: Send {
|
|||
fn service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()>;
|
||||
|
||||
/// Broadcast an end of interrupt.
|
||||
fn broadcast_eoi(&mut self, vector: u8) -> Result<()>;
|
||||
fn broadcast_eoi(&self, vector: u8) -> Result<()>;
|
||||
|
||||
/// Injects any pending interrupts for `vcpu`.
|
||||
fn inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()>;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// For the purposes of both using more descriptive terms and avoiding terms with lots of charged
|
||||
// emotional context, this file refers to them instead as "primary" and "secondary" PICs.
|
||||
|
||||
use crate::bus::BusAccessInfo;
|
||||
use crate::BusDevice;
|
||||
use base::{debug, warn, Event};
|
||||
use hypervisor::{PicInitState, PicSelect, PicState};
|
||||
|
@ -87,28 +88,28 @@ impl BusDevice for Pic {
|
|||
"userspace PIC".to_string()
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() != 1 {
|
||||
warn!("PIC: Bad write size: {}", data.len());
|
||||
return;
|
||||
}
|
||||
match offset {
|
||||
match info.address {
|
||||
PIC_PRIMARY_COMMAND => self.pic_write_command(PicSelect::Primary, data[0]),
|
||||
PIC_PRIMARY_DATA => self.pic_write_data(PicSelect::Primary, data[0]),
|
||||
PIC_PRIMARY_ELCR => self.pic_write_elcr(PicSelect::Primary, data[0]),
|
||||
PIC_SECONDARY_COMMAND => self.pic_write_command(PicSelect::Secondary, data[0]),
|
||||
PIC_SECONDARY_DATA => self.pic_write_data(PicSelect::Secondary, data[0]),
|
||||
PIC_SECONDARY_ELCR => self.pic_write_elcr(PicSelect::Secondary, data[0]),
|
||||
_ => warn!("PIC: Invalid write to offset {}", offset),
|
||||
_ => warn!("PIC: Invalid write to {}", info),
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
if data.len() != 1 {
|
||||
warn!("PIC: Bad read size: {}", data.len());
|
||||
return;
|
||||
}
|
||||
data[0] = match offset {
|
||||
data[0] = match info.address {
|
||||
PIC_PRIMARY_COMMAND => self.pic_read_command(PicSelect::Primary),
|
||||
PIC_PRIMARY_DATA => self.pic_read_data(PicSelect::Primary),
|
||||
PIC_PRIMARY_ELCR => self.pic_read_elcr(PicSelect::Primary),
|
||||
|
@ -116,7 +117,7 @@ impl BusDevice for Pic {
|
|||
PIC_SECONDARY_DATA => self.pic_read_data(PicSelect::Secondary),
|
||||
PIC_SECONDARY_ELCR => self.pic_read_elcr(PicSelect::Secondary),
|
||||
_ => {
|
||||
warn!("PIC: Invalid read from offset {}", offset);
|
||||
warn!("PIC: Invalid read from {}", info);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -543,11 +544,29 @@ mod tests {
|
|||
pic: Pic,
|
||||
}
|
||||
|
||||
fn pic_bus_address(address: u64) -> BusAccessInfo {
|
||||
// The PIC is added to the io_bus in three locations, so the offset depends on which
|
||||
// address range the address is in. The PIC implementation currently does not use the
|
||||
// offset, but we're setting it accurately here in case it does in the future.
|
||||
let offset = match address {
|
||||
x if x >= PIC_PRIMARY && x < PIC_PRIMARY + 0x2 => address - PIC_PRIMARY,
|
||||
x if x >= PIC_SECONDARY && x < PIC_SECONDARY + 0x2 => address - PIC_SECONDARY,
|
||||
x if x >= PIC_PRIMARY_ELCR && x < PIC_PRIMARY_ELCR + 0x2 => address - PIC_PRIMARY_ELCR,
|
||||
_ => panic!("invalid PIC address: {:#x}", address),
|
||||
};
|
||||
|
||||
BusAccessInfo {
|
||||
offset,
|
||||
address,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_up() -> TestData {
|
||||
let mut pic = Pic::new();
|
||||
// Use edge-triggered mode.
|
||||
pic.write(PIC_PRIMARY_ELCR, &[0]);
|
||||
pic.write(PIC_SECONDARY_ELCR, &[0]);
|
||||
pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0]);
|
||||
pic.write(pic_bus_address(PIC_SECONDARY_ELCR), &[0]);
|
||||
TestData { pic }
|
||||
}
|
||||
|
||||
|
@ -562,10 +581,10 @@ mod tests {
|
|||
PicSelect::Secondary => PIC_SECONDARY_DATA,
|
||||
};
|
||||
|
||||
pic.write(command_offset, &[icw1]);
|
||||
pic.write(data_offset, &[icw2]);
|
||||
pic.write(data_offset, &[icw3]);
|
||||
pic.write(data_offset, &[icw4]);
|
||||
pic.write(pic_bus_address(command_offset), &[icw1]);
|
||||
pic.write(pic_bus_address(data_offset), &[icw2]);
|
||||
pic.write(pic_bus_address(data_offset), &[icw3]);
|
||||
pic.write(pic_bus_address(data_offset), &[icw4]);
|
||||
}
|
||||
|
||||
/// Convenience function for primary ICW init.
|
||||
|
@ -610,12 +629,16 @@ mod tests {
|
|||
let data_write = [0x5f];
|
||||
let mut data_read = [0];
|
||||
|
||||
data.pic.write(PIC_PRIMARY_ELCR, &data_write);
|
||||
data.pic.read(PIC_PRIMARY_ELCR, &mut data_read);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_ELCR), &data_write);
|
||||
data.pic
|
||||
.read(pic_bus_address(PIC_PRIMARY_ELCR), &mut data_read);
|
||||
assert_eq!(data_read, data_write);
|
||||
|
||||
data.pic.write(PIC_SECONDARY_ELCR, &data_write);
|
||||
data.pic.read(PIC_SECONDARY_ELCR, &mut data_read);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_ELCR), &data_write);
|
||||
data.pic
|
||||
.read(pic_bus_address(PIC_SECONDARY_ELCR), &mut data_read);
|
||||
assert_eq!(data_read, data_write);
|
||||
}
|
||||
|
||||
|
@ -626,13 +649,16 @@ mod tests {
|
|||
|
||||
// ICW1
|
||||
let mut data_write = [0x10];
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &data_write);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &data_write);
|
||||
|
||||
data_write[0] = 0x08;
|
||||
data.pic.write(PIC_PRIMARY_DATA, &data_write);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
|
||||
|
||||
data_write[0] = 0xff;
|
||||
data.pic.write(PIC_PRIMARY_DATA, &data_write);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_DATA), &data_write);
|
||||
|
||||
assert_eq!(
|
||||
data.pic.pics[PicSelect::Primary as usize].init_state,
|
||||
|
@ -678,19 +704,23 @@ mod tests {
|
|||
icw_init_secondary(&mut data.pic);
|
||||
|
||||
// OCW1: Write to IMR.
|
||||
data.pic.write(PIC_SECONDARY_DATA, &[0x5f]);
|
||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x5f]);
|
||||
|
||||
// OCW2: Set rotate on auto EOI.
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0x80]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
|
||||
|
||||
// OCW2: Set priority.
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0xc0]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xc0]);
|
||||
|
||||
// OCW3: Change flags.
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0x6b]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x6b]);
|
||||
|
||||
let mut data_read = [0];
|
||||
data.pic.read(PIC_SECONDARY_DATA, &mut data_read);
|
||||
data.pic
|
||||
.read(pic_bus_address(PIC_SECONDARY_DATA), &mut data_read);
|
||||
assert_eq!(data_read, [0x5f]);
|
||||
|
||||
let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
|
||||
|
@ -716,13 +746,15 @@ mod tests {
|
|||
icw_init_secondary(&mut data.pic);
|
||||
|
||||
// OCW2: Set rotate on auto EOI.
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0x80]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x80]);
|
||||
|
||||
let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
|
||||
assert!(secondary_pic.rotate_on_auto_eoi);
|
||||
|
||||
// OCW2: Clear rotate on auto EOI.
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0x00]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x00]);
|
||||
|
||||
let secondary_pic = &data.pic.pics[PicSelect::Secondary as usize];
|
||||
assert!(!secondary_pic.rotate_on_auto_eoi);
|
||||
|
@ -810,8 +842,10 @@ mod tests {
|
|||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
|
||||
// OCW2: Non-specific EOI, one for primary and one for secondary.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0x20]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0x20]);
|
||||
|
||||
// Now that the first IRQ is no longer in service, the second IRQ can be ack'd.
|
||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
|
||||
|
@ -830,7 +864,7 @@ mod tests {
|
|||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||
|
||||
// OCW2: Mask IRQ line 6 on secondary (IRQ 14).
|
||||
data.pic.write(PIC_SECONDARY_DATA, &[0x40]);
|
||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
|
||||
|
||||
data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
|
||||
assert_eq!(data.pic.get_external_interrupt(), None);
|
||||
|
@ -842,7 +876,7 @@ mod tests {
|
|||
|
||||
// OCW2: Unmask IRQ line 6 on secondary (IRQ 14)
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.write(PIC_SECONDARY_DATA, &[0x00]);
|
||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
|
||||
|
||||
// Previously-masked interrupt can now be served.
|
||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 6));
|
||||
|
@ -862,8 +896,8 @@ mod tests {
|
|||
icw_init_both(&mut data.pic);
|
||||
|
||||
// OCW2: Mask *all* IRQ lines on primary and secondary.
|
||||
data.pic.write(PIC_PRIMARY_DATA, &[0xff]);
|
||||
data.pic.write(PIC_SECONDARY_DATA, &[0xff]);
|
||||
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
|
||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
|
||||
|
||||
data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
|
||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
||||
|
@ -873,14 +907,14 @@ mod tests {
|
|||
assert_eq!(data.pic.get_external_interrupt(), None);
|
||||
|
||||
// OCW2: Unmask IRQ lines on secondary.
|
||||
data.pic.write(PIC_SECONDARY_DATA, &[0x00]);
|
||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x00]);
|
||||
|
||||
// Cascade line is masked, so the primary *still* cannot get any IRQs.
|
||||
assert_eq!(data.pic.get_external_interrupt(), None);
|
||||
|
||||
// Unmask cascade line on primary.
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.write(PIC_PRIMARY_DATA, &[0xfb]);
|
||||
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xfb]);
|
||||
|
||||
// Previously-masked IRQs should now be served in order of priority.
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
|
@ -889,7 +923,7 @@ mod tests {
|
|||
|
||||
// Unmask all other IRQ lines on primary.
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.write(PIC_PRIMARY_DATA, &[0x00]);
|
||||
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0x00]);
|
||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
||||
}
|
||||
|
||||
|
@ -906,25 +940,32 @@ mod tests {
|
|||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
||||
|
||||
// Read primary IRR.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x0a]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0a]);
|
||||
let mut data_read = [0];
|
||||
data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
|
||||
data.pic
|
||||
.read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
|
||||
assert_eq!(data_read[0], 1 << 5);
|
||||
|
||||
// Read primary ISR.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x0b]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0b]);
|
||||
data_read = [0];
|
||||
data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
|
||||
data.pic
|
||||
.read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
|
||||
assert_eq!(data_read[0], 1 << 4);
|
||||
|
||||
// Non-sepcific EOI to end IRQ4. Then, PIC should signal CPU about IRQ5.
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
|
||||
|
||||
// Poll command on primary.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x0c]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x0c]);
|
||||
data_read = [0];
|
||||
data.pic.read(PIC_PRIMARY_COMMAND, &mut data_read);
|
||||
data.pic
|
||||
.read(pic_bus_address(PIC_PRIMARY_COMMAND), &mut data_read);
|
||||
assert_eq!(data_read[0], 5);
|
||||
}
|
||||
|
||||
|
@ -956,7 +997,8 @@ mod tests {
|
|||
|
||||
// In edge triggered mode, there should be no IRQ after this EOI.
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
|
||||
}
|
||||
|
||||
/// Raising the same IRQ line twice in level-triggered mode should send two IRQ requests out.
|
||||
|
@ -966,7 +1008,7 @@ mod tests {
|
|||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||
|
||||
// Turn IRQ4 to level-triggered mode.
|
||||
data.pic.write(PIC_PRIMARY_ELCR, &[0x10]);
|
||||
data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
|
||||
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
||||
|
@ -977,7 +1019,8 @@ mod tests {
|
|||
|
||||
// In level-triggered mode, there should be another IRQ request after this EOI.
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
|
||||
}
|
||||
|
||||
/// Specific EOI command in OCW2.
|
||||
|
@ -992,11 +1035,13 @@ mod tests {
|
|||
|
||||
// Specific EOI command on IRQ3. Primary PIC's ISR should be unaffected since it's targeted
|
||||
// at the wrong IRQ number.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x63]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x63]);
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 4);
|
||||
|
||||
// Specific EOI command on IRQ4. Primary PIC's ISR should now be cleared.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x64]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x64]);
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
||||
}
|
||||
|
||||
|
@ -1007,7 +1052,8 @@ mod tests {
|
|||
icw_init_both(&mut data.pic);
|
||||
|
||||
// OCW3: Clear rotate on auto EOI mode.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x00]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
|
||||
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
|
||||
|
@ -1021,7 +1067,8 @@ mod tests {
|
|||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 0);
|
||||
|
||||
// OCW2: Set rotate on auto EOI mode.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x80]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
|
||||
|
||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||
data.pic.service_irq(/*irq=*/ 5, /*level*/ true);
|
||||
|
@ -1046,12 +1093,14 @@ mod tests {
|
|||
|
||||
// Rotate on specific EOI IRQ4. Since this is a different IRQ number, Should not have an
|
||||
// effect on isr.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0xe4]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe4]);
|
||||
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 1 << 5);
|
||||
|
||||
// Rotate on specific EOI IRQ5. This should clear the isr.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0xe5]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xe5]);
|
||||
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].priority_add, 6);
|
||||
|
@ -1069,7 +1118,8 @@ mod tests {
|
|||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
|
||||
|
||||
// Rotate on non-specific EOI.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0xa0]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0xa0]);
|
||||
|
||||
// The EOI should have cleared isr.
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
||||
|
@ -1099,9 +1149,11 @@ mod tests {
|
|||
// - The first resets bit 2 in the primary isr (the highest-priority bit that was set
|
||||
// before the EOI)
|
||||
// - The second resets the secondary PIC's highest-priority isr bit.
|
||||
data.pic.write(PIC_PRIMARY_COMMAND, &[0x20]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x20]);
|
||||
// Rotate non-specific EOI.
|
||||
data.pic.write(PIC_SECONDARY_COMMAND, &[0xa0]);
|
||||
data.pic
|
||||
.write(pic_bus_address(PIC_SECONDARY_COMMAND), &[0xa0]);
|
||||
|
||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
||||
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].isr, 0);
|
||||
|
|
|
@ -27,7 +27,7 @@ pub mod virtio;
|
|||
pub use self::acpi::ACPIPMResource;
|
||||
pub use self::bat::{BatteryError, GoldfishBattery};
|
||||
pub use self::bus::Error as BusError;
|
||||
pub use self::bus::{Bus, BusDevice, BusRange, BusResumeDevice};
|
||||
pub use self::bus::{Bus, BusAccessInfo, BusDevice, BusRange, BusResumeDevice};
|
||||
pub use self::cmos::Cmos;
|
||||
pub use self::i8042::I8042Device;
|
||||
pub use self::irqchip::*;
|
||||
|
|
|
@ -10,7 +10,7 @@ use resources::{Error as SystemAllocatorFaliure, SystemAllocator};
|
|||
|
||||
use crate::pci::pci_configuration;
|
||||
use crate::pci::{PciAddress, PciInterruptPin};
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
@ -119,12 +119,12 @@ impl<T: PciDevice> BusDevice for T {
|
|||
PciDevice::debug_label(self)
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
self.read_bar(offset, data)
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
self.read_bar(info.address, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
self.write_bar(offset, data)
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
self.write_bar(info.address, data)
|
||||
}
|
||||
|
||||
fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::pci::pci_configuration::{
|
|||
PciBridgeSubclass, PciClassCode, PciConfiguration, PciHeaderType,
|
||||
};
|
||||
use crate::pci::pci_device::PciDevice;
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
|
||||
// A PciDevice that holds the root hub's configuration.
|
||||
struct PciRootConfiguration {
|
||||
|
@ -226,16 +226,16 @@ impl BusDevice for PciConfigIo {
|
|||
format!("pci config io-port 0x{:03x}", self.config_address)
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
// `offset` is relative to 0xcf8
|
||||
let value = match offset {
|
||||
let value = match info.offset {
|
||||
0..=3 => self.config_address,
|
||||
4..=7 => self.config_space_read(),
|
||||
_ => 0xffff_ffff,
|
||||
};
|
||||
|
||||
// Only allow reads to the register boundary.
|
||||
let start = offset as usize % 4;
|
||||
let start = info.offset as usize % 4;
|
||||
let end = start + data.len();
|
||||
if end <= 4 {
|
||||
for i in start..end {
|
||||
|
@ -248,9 +248,9 @@ impl BusDevice for PciConfigIo {
|
|||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
// `offset` is relative to 0xcf8
|
||||
match offset {
|
||||
match info.offset {
|
||||
o @ 0..=3 => self.set_config_address(o, data),
|
||||
o @ 4..=7 => self.config_space_write(o - 4, data),
|
||||
_ => (),
|
||||
|
@ -286,27 +286,27 @@ impl BusDevice for PciConfigMmio {
|
|||
"pci config mmio".to_owned()
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
// Only allow reads to the register boundary.
|
||||
let start = offset as usize % 4;
|
||||
let start = info.offset as usize % 4;
|
||||
let end = start + data.len();
|
||||
if end > 4 || offset > u32::max_value() as u64 {
|
||||
if end > 4 || info.offset > u32::max_value() as u64 {
|
||||
for d in data {
|
||||
*d = 0xff;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let value = self.config_space_read(offset as u32);
|
||||
let value = self.config_space_read(info.offset as u32);
|
||||
for i in start..end {
|
||||
data[i - start] = (value >> (i * 8)) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
if offset > u32::max_value() as u64 {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if info.offset > u32::max_value() as u64 {
|
||||
return;
|
||||
}
|
||||
self.config_space_write(offset as u32, offset % 4, data)
|
||||
self.config_space_write(info.offset as u32, info.offset % 4, data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ use base::FakeTimer as Timer;
|
|||
#[cfg(not(test))]
|
||||
use base::Timer;
|
||||
|
||||
use crate::bus::BusAccessInfo;
|
||||
use crate::BusDevice;
|
||||
|
||||
// Bitmask for areas of standard (non-ReadBack) Control Word Format. Constant
|
||||
|
@ -214,31 +215,31 @@ impl BusDevice for Pit {
|
|||
"userspace PIT".to_string()
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
self.ensure_started();
|
||||
|
||||
if data.len() != 1 {
|
||||
warn!("Bad write size for Pit: {}", data.len());
|
||||
return;
|
||||
}
|
||||
match PortIOSpace::n(offset as i64) {
|
||||
match PortIOSpace::n(info.address as i64) {
|
||||
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().write_counter(data[0]),
|
||||
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().write_counter(data[0]),
|
||||
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().write_counter(data[0]),
|
||||
Some(PortIOSpace::PortCommand) => self.command_write(data[0]),
|
||||
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().write_speaker(data[0]),
|
||||
None => warn!("PIT: bad write to offset {}", offset),
|
||||
None => warn!("PIT: bad write to {}", info),
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
self.ensure_started();
|
||||
|
||||
if data.len() != 1 {
|
||||
warn!("Bad read size for Pit: {}", data.len());
|
||||
return;
|
||||
}
|
||||
data[0] = match PortIOSpace::n(offset as i64) {
|
||||
data[0] = match PortIOSpace::n(info.address as i64) {
|
||||
Some(PortIOSpace::PortCounter0Data) => self.counters[0].lock().read_counter(),
|
||||
Some(PortIOSpace::PortCounter1Data) => self.counters[1].lock().read_counter(),
|
||||
Some(PortIOSpace::PortCounter2Data) => self.counters[2].lock().read_counter(),
|
||||
|
@ -251,7 +252,7 @@ impl BusDevice for Pit {
|
|||
}
|
||||
Some(PortIOSpace::PortSpeaker) => self.counters[2].lock().read_speaker(),
|
||||
None => {
|
||||
warn!("PIT: bad read from offset {}", offset);
|
||||
warn!("PIT: bad read from {}", info);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
@ -926,14 +927,35 @@ mod tests {
|
|||
clock: Arc<Mutex<Clock>>,
|
||||
}
|
||||
|
||||
fn pit_bus_address(address: PortIOSpace) -> BusAccessInfo {
|
||||
// The PIT is added to the io_bus in two locations, so the offset depends on which
|
||||
// address range the address is in. The PIT implementation currently does not use the
|
||||
// offset, but we're setting it accurately here in case it does in the future.
|
||||
let offset = match address as u64 {
|
||||
x if x >= PortIOSpace::PortCounter0Data as u64
|
||||
&& x < PortIOSpace::PortCounter0Data as u64 + 0x8 =>
|
||||
{
|
||||
address as u64 - PortIOSpace::PortCounter0Data as u64
|
||||
}
|
||||
x if x == PortIOSpace::PortSpeaker as u64 => 0,
|
||||
_ => panic!("invalid PIT address: {:#x}", address as u64),
|
||||
};
|
||||
|
||||
BusAccessInfo {
|
||||
offset,
|
||||
address: address as u64,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility method for writing a command word to a command register.
|
||||
fn write_command(pit: &mut Pit, command: u8) {
|
||||
pit.write(PortIOSpace::PortCommand as u64, &[command])
|
||||
pit.write(pit_bus_address(PortIOSpace::PortCommand), &[command])
|
||||
}
|
||||
|
||||
/// Utility method for writing a command word to the speaker register.
|
||||
fn write_speaker(pit: &mut Pit, command: u8) {
|
||||
pit.write(PortIOSpace::PortSpeaker as u64, &[command])
|
||||
pit.write(pit_bus_address(PortIOSpace::PortSpeaker), &[command])
|
||||
}
|
||||
|
||||
/// Utility method for writing to a counter.
|
||||
|
@ -943,17 +965,17 @@ mod tests {
|
|||
1 => PortIOSpace::PortCounter1Data,
|
||||
2 => PortIOSpace::PortCounter2Data,
|
||||
_ => panic!("Invalid counter_idx: {}", counter_idx),
|
||||
} as u64;
|
||||
};
|
||||
// Write the least, then the most, significant byte.
|
||||
if access_mode == CommandAccess::CommandRWLeast
|
||||
|| access_mode == CommandAccess::CommandRWBoth
|
||||
{
|
||||
pit.write(port, &[(data & 0xff) as u8]);
|
||||
pit.write(pit_bus_address(port), &[(data & 0xff) as u8]);
|
||||
}
|
||||
if access_mode == CommandAccess::CommandRWMost
|
||||
|| access_mode == CommandAccess::CommandRWBoth
|
||||
{
|
||||
pit.write(port, &[(data >> 8) as u8]);
|
||||
pit.write(pit_bus_address(port), &[(data >> 8) as u8]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -964,20 +986,20 @@ mod tests {
|
|||
1 => PortIOSpace::PortCounter1Data,
|
||||
2 => PortIOSpace::PortCounter2Data,
|
||||
_ => panic!("Invalid counter_idx: {}", counter_idx),
|
||||
} as u64;
|
||||
};
|
||||
let mut result: u16 = 0;
|
||||
if access_mode == CommandAccess::CommandRWLeast
|
||||
|| access_mode == CommandAccess::CommandRWBoth
|
||||
{
|
||||
let mut buffer = [0];
|
||||
pit.read(port, &mut buffer);
|
||||
pit.read(pit_bus_address(port), &mut buffer);
|
||||
result = buffer[0].into();
|
||||
}
|
||||
if access_mode == CommandAccess::CommandRWMost
|
||||
|| access_mode == CommandAccess::CommandRWBoth
|
||||
{
|
||||
let mut buffer = [0];
|
||||
pit.read(port, &mut buffer);
|
||||
pit.read(pit_bus_address(port), &mut buffer);
|
||||
result |= u16::from(buffer[0]) << 8;
|
||||
}
|
||||
assert_eq!(result, expected);
|
||||
|
@ -1105,7 +1127,8 @@ mod tests {
|
|||
fn read_command() {
|
||||
let mut data = set_up();
|
||||
let mut buf = [0];
|
||||
data.pit.read(PortIOSpace::PortCommand as u64, &mut buf);
|
||||
data.pit
|
||||
.read(pit_bus_address(PortIOSpace::PortCommand), &mut buf);
|
||||
assert_eq!(buf, [0]);
|
||||
}
|
||||
|
||||
|
@ -1426,7 +1449,21 @@ mod tests {
|
|||
#[test]
|
||||
fn invalid_write_and_read() {
|
||||
let mut data = set_up();
|
||||
data.pit.write(0x44, &[0]);
|
||||
data.pit.read(0x55, &mut [0]);
|
||||
data.pit.write(
|
||||
BusAccessInfo {
|
||||
address: 0x44,
|
||||
offset: 0x4,
|
||||
id: 0,
|
||||
},
|
||||
&[0],
|
||||
);
|
||||
data.pit.read(
|
||||
BusAccessInfo {
|
||||
address: 0x55,
|
||||
offset: 0x15,
|
||||
id: 0,
|
||||
},
|
||||
&mut [0],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use base::{warn, Event};
|
|||
use std::convert::TryFrom;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
|
||||
// Register offsets
|
||||
// Data register
|
||||
|
@ -75,7 +75,7 @@ impl BusDevice for Pl030 {
|
|||
"Pl030".to_owned()
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
let data_array = match <&[u8; 4]>::try_from(data) {
|
||||
Ok(array) => array,
|
||||
_ => {
|
||||
|
@ -85,7 +85,7 @@ impl BusDevice for Pl030 {
|
|||
};
|
||||
|
||||
let reg_val = u32::from_ne_bytes(*data_array);
|
||||
match offset {
|
||||
match info.offset {
|
||||
RTCDR => {
|
||||
warn!("invalid write to read-only RTCDR register");
|
||||
}
|
||||
|
@ -114,11 +114,11 @@ impl BusDevice for Pl030 {
|
|||
RTCCR => {
|
||||
self.counter_delta_time = get_epoch_time();
|
||||
}
|
||||
o => panic!("pl030: bad write offset {}", o),
|
||||
o => panic!("pl030: bad write {}", o),
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
let data_array = match <&mut [u8; 4]>::try_from(data) {
|
||||
Ok(array) => array,
|
||||
_ => {
|
||||
|
@ -127,7 +127,7 @@ impl BusDevice for Pl030 {
|
|||
}
|
||||
};
|
||||
|
||||
let reg_content: u32 = match offset {
|
||||
let reg_content: u32 = match info.offset {
|
||||
RTCDR => get_epoch_time(),
|
||||
RTCMR => self.match_value,
|
||||
RTCSTAT => self.interrupt_active as u32,
|
||||
|
@ -139,7 +139,7 @@ impl BusDevice for Pl030 {
|
|||
AMBA_ID_OFFSET => PL030_AMBA_ID,
|
||||
AMBA_MASK_OFFSET => PL030_AMBA_MASK,
|
||||
|
||||
o => panic!("pl030: bad read offset {}", o),
|
||||
o => panic!("pl030: bad read {}", o),
|
||||
};
|
||||
*data_array = reg_content.to_ne_bytes();
|
||||
}
|
||||
|
@ -148,6 +148,17 @@ impl BusDevice for Pl030 {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// The RTC device is placed at page 2 in the mmio bus
|
||||
const AARCH64_RTC_ADDR: u64 = 0x2000;
|
||||
|
||||
fn pl030_bus_address(offset: u64) -> BusAccessInfo {
|
||||
BusAccessInfo {
|
||||
address: AARCH64_RTC_ADDR + offset,
|
||||
offset,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interrupt_status_register() {
|
||||
let event = Event::new().unwrap();
|
||||
|
@ -155,14 +166,14 @@ mod tests {
|
|||
let mut register = [0, 0, 0, 0];
|
||||
|
||||
// set interrupt
|
||||
device.write(RTCEOI, &[1, 0, 0, 0]);
|
||||
device.read(RTCSTAT, &mut register);
|
||||
device.write(pl030_bus_address(RTCEOI), &[1, 0, 0, 0]);
|
||||
device.read(pl030_bus_address(RTCSTAT), &mut register);
|
||||
assert_eq!(register, [1, 0, 0, 0]);
|
||||
assert_eq!(event.read().unwrap(), 1);
|
||||
|
||||
// clear interrupt
|
||||
device.write(RTCEOI, &[0, 0, 0, 0]);
|
||||
device.read(RTCSTAT, &mut register);
|
||||
device.write(pl030_bus_address(RTCEOI), &[0, 0, 0, 0]);
|
||||
device.read(pl030_bus_address(RTCSTAT), &mut register);
|
||||
assert_eq!(register, [0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
|
@ -171,8 +182,8 @@ mod tests {
|
|||
let mut device = Pl030::new(Event::new().unwrap());
|
||||
let mut register = [0, 0, 0, 0];
|
||||
|
||||
device.write(RTCMR, &[1, 2, 3, 4]);
|
||||
device.read(RTCMR, &mut register);
|
||||
device.write(pl030_bus_address(RTCMR), &[1, 2, 3, 4]);
|
||||
device.read(pl030_bus_address(RTCMR), &mut register);
|
||||
assert_eq!(register, [1, 2, 3, 4]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use libc::{self, pid_t};
|
|||
use minijail::{self, Minijail};
|
||||
use msg_socket::{MsgOnSocket, MsgReceiver, MsgSender, MsgSocket};
|
||||
|
||||
use crate::BusDevice;
|
||||
use crate::{BusAccessInfo, BusDevice};
|
||||
|
||||
/// Errors for proxy devices.
|
||||
#[derive(Debug)]
|
||||
|
@ -40,11 +40,11 @@ const SOCKET_TIMEOUT_MS: u64 = 2000;
|
|||
enum Command {
|
||||
Read {
|
||||
len: u32,
|
||||
offset: u64,
|
||||
info: BusAccessInfo,
|
||||
},
|
||||
Write {
|
||||
len: u32,
|
||||
offset: u64,
|
||||
info: BusAccessInfo,
|
||||
data: [u8; 8],
|
||||
},
|
||||
ReadConfig(u32),
|
||||
|
@ -78,14 +78,14 @@ fn child_proc<D: BusDevice>(sock: UnixSeqpacket, device: &mut D) {
|
|||
};
|
||||
|
||||
let res = match cmd {
|
||||
Command::Read { len, offset } => {
|
||||
Command::Read { len, info } => {
|
||||
let mut buffer = [0u8; 8];
|
||||
device.read(offset, &mut buffer[0..len as usize]);
|
||||
device.read(info, &mut buffer[0..len as usize]);
|
||||
sock.send(&CommandResult::ReadResult(buffer))
|
||||
}
|
||||
Command::Write { len, offset, data } => {
|
||||
Command::Write { len, info, data } => {
|
||||
let len = len as usize;
|
||||
device.write(offset, &data[0..len]);
|
||||
device.write(info, &data[0..len]);
|
||||
// Command::Write does not have a result.
|
||||
Ok(())
|
||||
}
|
||||
|
@ -237,23 +237,23 @@ impl BusDevice for ProxyDevice {
|
|||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
let len = data.len() as u32;
|
||||
if let Some(CommandResult::ReadResult(buffer)) =
|
||||
self.sync_send(&Command::Read { len, offset })
|
||||
self.sync_send(&Command::Read { len, info })
|
||||
{
|
||||
let len = data.len();
|
||||
data.clone_from_slice(&buffer[0..len]);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
let mut buffer = [0u8; 8];
|
||||
let len = data.len() as u32;
|
||||
buffer[0..data.len()].clone_from_slice(data);
|
||||
self.send_no_result(&Command::Write {
|
||||
len,
|
||||
offset,
|
||||
info,
|
||||
data: buffer,
|
||||
});
|
||||
}
|
||||
|
@ -286,12 +286,12 @@ mod tests {
|
|||
"EchoDevice".to_owned()
|
||||
}
|
||||
|
||||
fn write(&mut self, _offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
|
||||
assert!(data.len() == 1);
|
||||
self.data = data[0];
|
||||
}
|
||||
|
||||
fn read(&mut self, _offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
|
||||
assert!(data.len() == 1);
|
||||
data[0] = self.data;
|
||||
}
|
||||
|
@ -322,9 +322,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_proxied_read_write() {
|
||||
let mut proxy_device = new_proxied_echo_device();
|
||||
proxy_device.write(0, &[42]);
|
||||
let address = BusAccessInfo {
|
||||
offset: 0,
|
||||
address: 0,
|
||||
id: 0,
|
||||
};
|
||||
proxy_device.write(address, &[42]);
|
||||
let mut read_buffer = [0];
|
||||
proxy_device.read(0, &mut read_buffer);
|
||||
proxy_device.read(address, &mut read_buffer);
|
||||
assert_eq!(read_buffer, [42]);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::thread::{self};
|
|||
|
||||
use base::{error, Event, RawDescriptor, Result};
|
||||
|
||||
use crate::bus::BusAccessInfo;
|
||||
use crate::{BusDevice, SerialDevice};
|
||||
|
||||
const LOOP_SIZE: usize = 0x40;
|
||||
|
@ -310,24 +311,24 @@ impl BusDevice for Serial {
|
|||
"serial".to_owned()
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u64, data: &[u8]) {
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(e) = self.handle_write(offset as u8, data[0]) {
|
||||
if let Err(e) = self.handle_write(info.offset as u8, data[0]) {
|
||||
error!("serial failed write: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, offset: u64, data: &mut [u8]) {
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
if data.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.handle_input_thread();
|
||||
|
||||
data[0] = match offset as u8 {
|
||||
data[0] = match info.offset as u8 {
|
||||
DLAB_LOW if self.is_dlab_set() => self.baud_divisor as u8,
|
||||
DLAB_HIGH if self.is_dlab_set() => (self.baud_divisor >> 8) as u8,
|
||||
DATA => {
|
||||
|
@ -403,6 +404,15 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn serial_bus_address(offset: u8) -> BusAccessInfo {
|
||||
// Serial devices only use the offset of the BusAccessInfo
|
||||
BusAccessInfo {
|
||||
offset: offset as u64,
|
||||
address: 0,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serial_output() {
|
||||
let intr_evt = Event::new().unwrap();
|
||||
|
@ -416,9 +426,9 @@ mod tests {
|
|||
Vec::new(),
|
||||
);
|
||||
|
||||
serial.write(DATA as u64, &['a' as u8]);
|
||||
serial.write(DATA as u64, &['b' as u8]);
|
||||
serial.write(DATA as u64, &['c' as u8]);
|
||||
serial.write(serial_bus_address(DATA), &['a' as u8]);
|
||||
serial.write(serial_bus_address(DATA), &['b' as u8]);
|
||||
serial.write(serial_bus_address(DATA), &['c' as u8]);
|
||||
assert_eq!(
|
||||
serial_out.buf.lock().as_slice(),
|
||||
&['a' as u8, 'b' as u8, 'c' as u8]
|
||||
|
@ -438,18 +448,18 @@ mod tests {
|
|||
Vec::new(),
|
||||
);
|
||||
|
||||
serial.write(IER as u64, &[IER_RECV_BIT]);
|
||||
serial.write(serial_bus_address(IER), &[IER_RECV_BIT]);
|
||||
serial
|
||||
.queue_input_bytes(&['a' as u8, 'b' as u8, 'c' as u8])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(intr_evt.read(), Ok(1));
|
||||
let mut data = [0u8; 1];
|
||||
serial.read(DATA as u64, &mut data[..]);
|
||||
serial.read(serial_bus_address(DATA), &mut data[..]);
|
||||
assert_eq!(data[0], 'a' as u8);
|
||||
serial.read(DATA as u64, &mut data[..]);
|
||||
serial.read(serial_bus_address(DATA), &mut data[..]);
|
||||
assert_eq!(data[0], 'b' as u8);
|
||||
serial.read(DATA as u64, &mut data[..]);
|
||||
serial.read(serial_bus_address(DATA), &mut data[..]);
|
||||
assert_eq!(data[0], 'c' as u8);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -993,7 +993,6 @@ impl X8664arch {
|
|||
Arc::new(Mutex::new(devices::Cmos::new(mem_below_4g, mem_above_4g))),
|
||||
0x70,
|
||||
0x2,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -1003,25 +1002,19 @@ impl X8664arch {
|
|||
)));
|
||||
|
||||
if pit_uses_speaker_port {
|
||||
io_bus.insert(i8042, 0x062, 0x3, true).unwrap();
|
||||
io_bus.insert(i8042, 0x062, 0x3).unwrap();
|
||||
} else {
|
||||
io_bus.insert(i8042, 0x061, 0x4, true).unwrap();
|
||||
io_bus.insert(i8042, 0x061, 0x4).unwrap();
|
||||
}
|
||||
|
||||
io_bus
|
||||
.insert(nul_device.clone(), 0x0ed, 0x1, false)
|
||||
.unwrap(); // most likely this one does nothing
|
||||
io_bus
|
||||
.insert(nul_device.clone(), 0x0f0, 0x2, false)
|
||||
.unwrap(); // ignore fpu
|
||||
io_bus.insert(nul_device.clone(), 0x0ed, 0x1).unwrap(); // most likely this one does nothing
|
||||
io_bus.insert(nul_device.clone(), 0x0f0, 0x2).unwrap(); // ignore fpu
|
||||
|
||||
if let Some(pci_root) = pci {
|
||||
io_bus.insert(pci_root, 0xcf8, 0x8, false).unwrap();
|
||||
io_bus.insert(pci_root, 0xcf8, 0x8).unwrap();
|
||||
} else {
|
||||
// ignore pci.
|
||||
io_bus
|
||||
.insert(nul_device.clone(), 0xcf8, 0x8, false)
|
||||
.unwrap();
|
||||
io_bus.insert(nul_device.clone(), 0xcf8, 0x8).unwrap();
|
||||
}
|
||||
|
||||
Ok(io_bus)
|
||||
|
@ -1074,7 +1067,6 @@ impl X8664arch {
|
|||
pm.clone(),
|
||||
pm_iobase as u64,
|
||||
devices::acpi::ACPIPM_RESOURCE_LEN as u64,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
io_bus.notify_on_resume(pm);
|
||||
|
|
Loading…
Reference in a new issue