use std::cmp::Ord;
use std::cmp::Ordering;
use std::cmp::PartialEq;
use std::cmp::PartialOrd;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::fmt;
use std::result;
use std::sync::Arc;
use anyhow::anyhow;
use anyhow::Context;
use base::debug;
use base::error;
use base::Event;
use base::SharedMemory;
use remain::sorted;
use serde::Deserialize;
use serde::Serialize;
use sync::Mutex;
use thiserror::Error;
#[cfg(feature = "stats")]
use crate::bus_stats::BusOperation;
#[cfg(feature = "stats")]
use crate::BusStatistics;
use crate::DeviceId;
use crate::PciAddress;
use crate::PciDevice;
use crate::Suspendable;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::VfioPlatformDevice;
use crate::VirtioMmioDevice;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub struct BusAccessInfo {
pub offset: u64,
pub address: u64,
pub id: usize,
}
impl std::fmt::Display for BusAccessInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ConfigWriteResult {
pub mmio_remove: Vec<BusRange>,
pub mmio_add: Vec<BusRange>,
pub io_remove: Vec<BusRange>,
pub io_add: Vec<BusRange>,
pub removed_pci_devices: Vec<PciAddress>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
pub enum BusType {
Mmio,
Io,
}
#[allow(unused_variables)]
pub trait BusDevice: Send + Suspendable {
fn debug_label(&self) -> String;
fn device_id(&self) -> DeviceId;
fn read(&mut self, offset: BusAccessInfo, data: &mut [u8]) {}
fn write(&mut self, offset: BusAccessInfo, data: &[u8]) {}
fn config_register_write(
&mut self,
reg_idx: usize,
offset: u64,
data: &[u8],
) -> ConfigWriteResult {
ConfigWriteResult {
..Default::default()
}
}
fn config_register_read(&self, reg_idx: usize) -> u32 {
0
}
fn init_pci_config_mapping(&mut self, shmem: &SharedMemory, base: usize, len: usize) -> bool {
false
}
fn virtual_config_register_write(&mut self, reg_idx: usize, value: u32) {}
fn virtual_config_register_read(&self, reg_idx: usize) -> u32 {
0
}
fn on_sandboxed(&mut self) {}
fn get_ranges(&self) -> Vec<(BusRange, BusType)> {
Vec::new()
}
fn destroy_device(&mut self) {}
fn is_bridge(&self) -> Option<u8> {
None
}
}
pub trait BusDeviceSync: BusDevice + Sync {
fn read(&self, offset: BusAccessInfo, data: &mut [u8]);
fn write(&self, offset: BusAccessInfo, data: &[u8]);
fn snapshot_sync(&self) -> anyhow::Result<serde_json::Value> {
Err(anyhow!(
"snapshot_sync not implemented for {}",
std::any::type_name::<Self>()
))
}
fn restore_sync(&self, _data: serde_json::Value) -> anyhow::Result<()> {
Err(anyhow!(
"restore_sync not implemented for {}",
std::any::type_name::<Self>()
))
}
fn sleep_sync(&self) -> anyhow::Result<()> {
Err(anyhow!(
"sleep_sync not implemented for {}",
std::any::type_name::<Self>()
))
}
fn wake_sync(&self) -> anyhow::Result<()> {
Err(anyhow!(
"wake_sync not implemented for {}",
std::any::type_name::<Self>()
))
}
}
pub trait BusResumeDevice: Send {
fn resume_imminent(&mut self) {}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum HotPlugKey {
HostUpstreamPort { host_addr: PciAddress },
HostDownstreamPort { host_addr: PciAddress },
HostVfio { host_addr: PciAddress },
GuestDevice { guest_addr: PciAddress },
}
pub trait HotPlugBus: Send {
fn hot_plug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>>;
fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>>;
fn get_ready_notification(&mut self) -> anyhow::Result<Event>;
fn is_match(&self, host_addr: PciAddress) -> Option<u8>;
fn get_address(&self) -> Option<PciAddress>;
fn get_secondary_bus_number(&self) -> Option<u8>;
fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress);
fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>;
fn is_empty(&self) -> bool;
fn get_hotplug_key(&self) -> Option<HotPlugKey>;
}
pub trait BusDeviceObj {
fn as_pci_device(&self) -> Option<&dyn PciDevice> {
None
}
fn as_pci_device_mut(&mut self) -> Option<&mut dyn PciDevice> {
None
}
fn into_pci_device(self: Box<Self>) -> Option<Box<dyn PciDevice>> {
None
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn as_platform_device(&self) -> Option<&VfioPlatformDevice> {
None
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn as_platform_device_mut(&mut self) -> Option<&mut VfioPlatformDevice> {
None
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn into_platform_device(self: Box<Self>) -> Option<Box<VfioPlatformDevice>> {
None
}
fn as_virtio_mmio_device(&self) -> Option<&VirtioMmioDevice> {
None
}
fn as_virtio_mmio_device_mut(&mut self) -> Option<&mut VirtioMmioDevice> {
None
}
fn into_virtio_mmio_device(self: Box<Self>) -> Option<Box<VirtioMmioDevice>> {
None
}
}
#[sorted]
#[derive(Error, Debug)]
pub enum Error {
#[error("Bus Range not found")]
Empty,
#[error("new device {base},{len} overlaps with an old device {other_base},{other_len}")]
Overlap {
base: u64,
len: u64,
other_base: u64,
other_len: u64,
},
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct BusRange {
pub base: u64,
pub len: u64,
}
impl BusRange {
pub fn contains(&self, addr: u64) -> bool {
self.base <= addr && addr < self.base.saturating_add(self.len)
}
pub fn overlaps(&self, base: u64, len: u64) -> bool {
self.base < base.saturating_add(len) && base < self.base.saturating_add(self.len)
}
}
impl Eq for BusRange {}
impl PartialEq for BusRange {
fn eq(&self, other: &BusRange) -> bool {
self.base == other.base
}
}
impl Ord for BusRange {
fn cmp(&self, other: &BusRange) -> Ordering {
self.base.cmp(&other.base)
}
}
impl PartialOrd for BusRange {
fn partial_cmp(&self, other: &BusRange) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl std::fmt::Debug for BusRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#x}..+{:#x}", self.base, self.len)
}
}
#[derive(Clone)]
struct BusEntry {
#[cfg(feature = "stats")]
index: usize,
device: BusDeviceEntry,
}
#[derive(Clone)]
enum BusDeviceEntry {
OuterSync(Arc<Mutex<dyn BusDevice>>),
InnerSync(Arc<dyn BusDeviceSync>),
}
#[derive(Clone)]
pub struct Bus {
devices: Arc<Mutex<BTreeMap<BusRange, BusEntry>>>,
access_id: usize,
#[cfg(feature = "stats")]
pub stats: Arc<Mutex<BusStatistics>>,
bus_type: BusType,
}
impl Bus {
pub fn new(bus_type: BusType) -> Bus {
Bus {
devices: Arc::new(Mutex::new(BTreeMap::new())),
access_id: 0,
#[cfg(feature = "stats")]
stats: Arc::new(Mutex::new(BusStatistics::new())),
bus_type,
}
}
pub fn get_bus_type(&self) -> BusType {
self.bus_type
}
pub fn set_access_id(&mut self, id: usize) {
self.access_id = id;
}
fn first_before(&self, addr: u64) -> Option<(BusRange, BusEntry)> {
let devices = self.devices.lock();
let (range, entry) = devices
.range(..=BusRange { base: addr, len: 1 })
.next_back()?;
Some((*range, entry.clone()))
}
fn get_device(&self, addr: u64) -> Option<(u64, u64, BusEntry)> {
if let Some((range, entry)) = self.first_before(addr) {
let offset = addr - range.base;
if offset < range.len {
return Some((offset, addr, entry));
}
}
None
}
fn unique_devices(&self) -> Vec<BusDeviceEntry> {
let mut seen_ptrs = BTreeSet::new();
self.devices
.lock()
.iter()
.map(|(_, bus_entry)| bus_entry.device.clone())
.filter(|dev| match dev {
BusDeviceEntry::OuterSync(dev) => seen_ptrs.insert(Arc::as_ptr(dev) as *const u8),
BusDeviceEntry::InnerSync(dev) => seen_ptrs.insert(Arc::as_ptr(dev) as *const u8),
})
.collect()
}
fn unique_devices_with_snapshot_key(&self) -> Vec<(String, BusDeviceEntry)> {
let mut next_ids = BTreeMap::<String, usize>::new();
let mut choose_key = |debug_label: String| -> String {
let label = debug_label.replace(char::is_whitespace, "-");
let id = next_ids.entry(label.clone()).or_default();
let key = format!("{}-{}", label, id);
*id += 1;
key
};
let mut result = Vec::new();
for device_entry in self.unique_devices() {
let key = match &device_entry {
BusDeviceEntry::OuterSync(d) => choose_key(d.lock().debug_label()),
BusDeviceEntry::InnerSync(d) => choose_key(d.debug_label()),
};
result.push((key, device_entry));
}
result
}
pub fn sleep_devices(&self) -> anyhow::Result<()> {
for device_entry in self.unique_devices() {
match device_entry {
BusDeviceEntry::OuterSync(dev) => {
let mut dev = (*dev).lock();
debug!("Sleep on device: {}", dev.debug_label());
dev.sleep()
.with_context(|| format!("failed to sleep {}", dev.debug_label()))?;
}
BusDeviceEntry::InnerSync(dev) => {
debug!("Sleep on device: {}", dev.debug_label());
dev.sleep_sync()
.with_context(|| format!("failed to sleep {}", dev.debug_label()))?;
}
}
}
Ok(())
}
pub fn wake_devices(&self) -> anyhow::Result<()> {
for device_entry in self.unique_devices() {
match device_entry {
BusDeviceEntry::OuterSync(dev) => {
let mut dev = dev.lock();
debug!("Wake on device: {}", dev.debug_label());
dev.wake()
.with_context(|| format!("failed to wake {}", dev.debug_label()))?;
}
BusDeviceEntry::InnerSync(dev) => {
debug!("Wake on device: {}", dev.debug_label());
dev.wake_sync()
.with_context(|| format!("failed to wake {}", dev.debug_label()))?;
}
}
}
Ok(())
}
pub fn snapshot_devices(
&self,
snapshot_writer: &vm_control::SnapshotWriter,
) -> anyhow::Result<()> {
for (snapshot_key, device_entry) in self.unique_devices_with_snapshot_key() {
match device_entry {
BusDeviceEntry::OuterSync(dev) => {
let mut dev = dev.lock();
debug!("Snapshot on device: {}", dev.debug_label());
snapshot_writer.write_fragment(
&snapshot_key,
&(*dev)
.snapshot()
.with_context(|| format!("failed to snapshot {}", dev.debug_label()))?,
)?;
}
BusDeviceEntry::InnerSync(dev) => {
debug!("Snapshot on device: {}", dev.debug_label());
snapshot_writer.write_fragment(
&snapshot_key,
&dev.snapshot_sync()
.with_context(|| format!("failed to snapshot {}", dev.debug_label()))?,
)?;
}
}
}
Ok(())
}
pub fn restore_devices(
&self,
snapshot_reader: &vm_control::SnapshotReader,
) -> anyhow::Result<()> {
let mut unused_keys: BTreeSet<String> =
snapshot_reader.list_fragments()?.into_iter().collect();
for (snapshot_key, device_entry) in self.unique_devices_with_snapshot_key() {
unused_keys.remove(&snapshot_key);
match device_entry {
BusDeviceEntry::OuterSync(dev) => {
let mut dev = dev.lock();
debug!("Restore on device: {}", dev.debug_label());
dev.restore(snapshot_reader.read_fragment(&snapshot_key)?)
.with_context(|| {
format!("restore failed for device {}", dev.debug_label())
})?;
}
BusDeviceEntry::InnerSync(dev) => {
debug!("Restore on device: {}", dev.debug_label());
dev.restore_sync(snapshot_reader.read_fragment(&snapshot_key)?)
.with_context(|| {
format!("restore failed for device {}", dev.debug_label())
})?;
}
}
}
if !unused_keys.is_empty() {
error!(
"unused restore data in bus, devices might be missing: {:?}",
unused_keys
);
}
Ok(())
}
pub fn insert(&self, device: Arc<Mutex<dyn BusDevice>>, base: u64, len: u64) -> Result<()> {
if len == 0 {
return Err(Error::Overlap {
base,
len,
other_base: 0,
other_len: 0,
});
}
let mut devices = self.devices.lock();
devices.iter().try_for_each(|(range, _dev)| {
if range.overlaps(base, len) {
Err(Error::Overlap {
base,
len,
other_base: range.base,
other_len: range.len,
})
} else {
Ok(())
}
})?;
#[cfg(feature = "stats")]
let name = device.lock().debug_label();
#[cfg(feature = "stats")]
let device_id = device.lock().device_id();
if devices
.insert(
BusRange { base, len },
BusEntry {
#[cfg(feature = "stats")]
index: self
.stats
.lock()
.next_device_index(name, device_id.into(), base, len),
device: BusDeviceEntry::OuterSync(device),
},
)
.is_some()
{
return Err(Error::Overlap {
base,
len,
other_base: base,
other_len: len,
});
}
Ok(())
}
pub fn insert_sync(&self, device: Arc<dyn BusDeviceSync>, base: u64, len: u64) -> Result<()> {
if len == 0 {
return Err(Error::Overlap {
base,
len,
other_base: 0,
other_len: 0,
});
}
let mut devices = self.devices.lock();
devices.iter().try_for_each(|(range, _dev)| {
if range.overlaps(base, len) {
Err(Error::Overlap {
base,
len,
other_base: range.base,
other_len: range.len,
})
} else {
Ok(())
}
})?;
if devices
.insert(
BusRange { base, len },
BusEntry {
#[cfg(feature = "stats")]
index: self.stats.lock().next_device_index(
device.debug_label(),
device.device_id().into(),
base,
len,
),
device: BusDeviceEntry::InnerSync(device),
},
)
.is_some()
{
return Err(Error::Overlap {
base,
len,
other_base: base,
other_len: len,
});
}
Ok(())
}
pub fn remove(&self, base: u64, len: u64) -> Result<()> {
if len == 0 {
return Err(Error::Overlap {
base,
len,
other_base: 0,
other_len: 0,
});
}
let mut devices = self.devices.lock();
if devices
.iter()
.any(|(range, _dev)| range.base == base && range.len == len)
{
let ret = devices.remove(&BusRange { base, len });
if ret.is_some() {
Ok(())
} else {
Err(Error::Empty)
}
} else {
Err(Error::Empty)
}
}
pub fn read(&self, addr: u64, data: &mut [u8]) -> bool {
#[cfg(feature = "stats")]
let start = self.stats.lock().start_stat();
data.fill(0);
let device_index = if let Some((offset, address, entry)) = self.get_device(addr) {
let io = BusAccessInfo {
address,
offset,
id: self.access_id,
};
match &entry.device {
BusDeviceEntry::OuterSync(dev) => dev.lock().read(io, data),
BusDeviceEntry::InnerSync(dev) => dev.read(io, data),
}
#[cfg(feature = "stats")]
let index = Some(entry.index);
#[cfg(not(feature = "stats"))]
let index = Some(());
index
} else {
None
};
#[cfg(feature = "stats")]
if let Some(device_index) = device_index {
self.stats
.lock()
.end_stat(BusOperation::Write, start, device_index);
return true;
}
device_index.is_some()
}
pub fn write(&self, addr: u64, data: &[u8]) -> bool {
#[cfg(feature = "stats")]
let start = self.stats.lock().start_stat();
let device_index = if let Some((offset, address, entry)) = self.get_device(addr) {
let io = BusAccessInfo {
address,
offset,
id: self.access_id,
};
match &entry.device {
BusDeviceEntry::OuterSync(dev) => dev.lock().write(io, data),
BusDeviceEntry::InnerSync(dev) => dev.write(io, data),
}
#[cfg(feature = "stats")]
let index = Some(entry.index);
#[cfg(not(feature = "stats"))]
let index = Some(());
index
} else {
None
};
#[cfg(feature = "stats")]
if let Some(device_index) = device_index {
self.stats
.lock()
.end_stat(BusOperation::Write, start, device_index);
}
device_index.is_some()
}
}
impl Default for Bus {
fn default() -> Self {
Self::new(BusType::Io)
}
}
#[cfg(test)]
mod tests {
use anyhow::Result as AnyhowResult;
use super::*;
use crate::pci::CrosvmDeviceId;
use crate::suspendable::Suspendable;
use crate::suspendable_tests;
#[derive(Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
struct DummyDevice;
impl BusDevice for DummyDevice {
fn device_id(&self) -> DeviceId {
CrosvmDeviceId::Cmos.into()
}
fn debug_label(&self) -> String {
"dummy device".to_owned()
}
}
impl Suspendable for DummyDevice {
fn snapshot(&mut self) -> AnyhowResult<serde_json::Value> {
serde_json::to_value(self).context("error serializing")
}
fn restore(&mut self, data: serde_json::Value) -> AnyhowResult<()> {
*self = serde_json::from_value(data).context("error deserializing")?;
Ok(())
}
fn sleep(&mut self) -> AnyhowResult<()> {
Ok(())
}
fn wake(&mut self) -> AnyhowResult<()> {
Ok(())
}
}
#[derive(Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
struct ConstantDevice {
uses_full_addr: bool,
}
impl BusDevice for ConstantDevice {
fn device_id(&self) -> DeviceId {
CrosvmDeviceId::Cmos.into()
}
fn debug_label(&self) -> String {
"constant device".to_owned()
}
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 = (addr as u8) + (i as 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, (addr as u8) + (i as u8))
}
}
}
impl Suspendable for ConstantDevice {
fn snapshot(&mut self) -> AnyhowResult<serde_json::Value> {
serde_json::to_value(self).context("error serializing")
}
fn restore(&mut self, data: serde_json::Value) -> AnyhowResult<()> {
*self = serde_json::from_value(data).context("error deserializing")?;
Ok(())
}
fn sleep(&mut self) -> AnyhowResult<()> {
Ok(())
}
fn wake(&mut self) -> AnyhowResult<()> {
Ok(())
}
}
fn modify_constant_device(constant: &mut ConstantDevice) {
constant.uses_full_addr = !constant.uses_full_addr;
}
#[test]
fn bus_insert() {
let bus = Bus::new(BusType::Io);
let dummy = Arc::new(Mutex::new(DummyDevice));
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, 0x0, 0x10).is_ok());
}
#[test]
fn bus_insert_full_addr() {
let bus = Bus::new(BusType::Io);
let dummy = Arc::new(Mutex::new(DummyDevice));
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, 0x0, 0x10).is_ok());
}
#[test]
fn bus_read_write() {
let bus = Bus::new(BusType::Io);
let dummy = Arc::new(Mutex::new(DummyDevice));
assert!(bus.insert(dummy, 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]));
assert!(bus.write(0x11, &[0, 0, 0, 0]));
assert!(bus.read(0x16, &mut [0, 0, 0, 0]));
assert!(bus.write(0x16, &[0, 0, 0, 0]));
assert!(!bus.read(0x20, &mut [0, 0, 0, 0]));
assert!(!bus.write(0x20, &[0, 0, 0, 0]));
assert!(!bus.read(0x06, &mut [0, 0, 0, 0]));
assert!(!bus.write(0x06, &[0, 0, 0, 0]));
}
#[test]
fn bus_read_write_values() {
let bus = Bus::new(BusType::Io);
let dummy = Arc::new(Mutex::new(ConstantDevice {
uses_full_addr: false,
}));
assert!(bus.insert(dummy, 0x10, 0x10).is_ok());
let mut values = [0, 1, 2, 3];
assert!(bus.read(0x10, &mut values));
assert_eq!(values, [0, 1, 2, 3]);
assert!(bus.write(0x10, &values));
assert!(bus.read(0x15, &mut values));
assert_eq!(values, [5, 6, 7, 8]);
assert!(bus.write(0x15, &values));
}
#[test]
fn bus_read_write_full_addr_values() {
let bus = Bus::new(BusType::Io);
let dummy = Arc::new(Mutex::new(ConstantDevice {
uses_full_addr: true,
}));
assert!(bus.insert(dummy, 0x10, 0x10).is_ok());
let mut values = [0u8; 4];
assert!(bus.read(0x10, &mut values));
assert_eq!(values, [0x10, 0x11, 0x12, 0x13]);
assert!(bus.write(0x10, &values));
assert!(bus.read(0x15, &mut values));
assert_eq!(values, [0x15, 0x16, 0x17, 0x18]);
assert!(bus.write(0x15, &values));
}
#[test]
fn bus_read_no_device() {
let bus = Bus::new(BusType::Io);
let mut values = [1, 2, 3, 4];
assert!(!bus.read(0x10, &mut values));
assert_eq!(values, [0, 0, 0, 0]);
}
suspendable_tests!(
constant_device_true,
ConstantDevice {
uses_full_addr: true,
},
modify_constant_device
);
suspendable_tests!(
constant_device_false,
ConstantDevice {
uses_full_addr: false,
},
modify_constant_device
);
#[test]
fn bus_range_contains() {
let a = BusRange {
base: 0x1000,
len: 0x400,
};
assert!(a.contains(0x1000));
assert!(a.contains(0x13ff));
assert!(!a.contains(0xfff));
assert!(!a.contains(0x1400));
assert!(a.contains(0x1200));
}
#[test]
fn bus_range_overlap() {
let a = BusRange {
base: 0x1000,
len: 0x400,
};
assert!(a.overlaps(0x1000, 0x400));
assert!(a.overlaps(0xf00, 0x400));
assert!(a.overlaps(0x1000, 0x01));
assert!(a.overlaps(0xfff, 0x02));
assert!(a.overlaps(0x1100, 0x100));
assert!(a.overlaps(0x13ff, 0x100));
assert!(!a.overlaps(0x1400, 0x100));
assert!(!a.overlaps(0xf00, 0x100));
}
}