/src/crosvm/devices/src/bus.rs
Line | Count | Source |
1 | | // Copyright 2017 The ChromiumOS Authors |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | //! Handles routing to devices in an address space. |
6 | | |
7 | | use std::cmp::Ord; |
8 | | use std::cmp::Ordering; |
9 | | use std::cmp::PartialEq; |
10 | | use std::cmp::PartialOrd; |
11 | | use std::collections::BTreeMap; |
12 | | use std::collections::BTreeSet; |
13 | | use std::fmt; |
14 | | use std::result; |
15 | | use std::sync::Arc; |
16 | | |
17 | | use anyhow::anyhow; |
18 | | use anyhow::Context; |
19 | | use base::debug; |
20 | | use base::error; |
21 | | use base::Event; |
22 | | use base::SharedMemory; |
23 | | use hypervisor::HypercallAbi; |
24 | | use remain::sorted; |
25 | | use serde::Deserialize; |
26 | | use serde::Serialize; |
27 | | use snapshot::AnySnapshot; |
28 | | use sync::Mutex; |
29 | | use thiserror::Error; |
30 | | use vm_control::DeviceId; |
31 | | |
32 | | #[cfg(feature = "stats")] |
33 | | use crate::bus_stats::BusOperation; |
34 | | #[cfg(feature = "stats")] |
35 | | use crate::BusStatistics; |
36 | | use crate::PciAddress; |
37 | | use crate::PciDevice; |
38 | | use crate::Suspendable; |
39 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
40 | | use crate::VfioPlatformDevice; |
41 | | |
42 | | /// Information about how a device was accessed. |
43 | | #[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] |
44 | | pub struct BusAccessInfo { |
45 | | /// Offset from base address that the device was accessed at. |
46 | | pub offset: u64, |
47 | | /// Absolute address of the device's access in its address space. |
48 | | pub address: u64, |
49 | | /// ID of the entity requesting a device access, usually the VCPU id. |
50 | | pub id: usize, |
51 | | } |
52 | | |
53 | | // Implement `Display` for `MinMax`. |
54 | | impl std::fmt::Display for BusAccessInfo { |
55 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
56 | 0 | write!(f, "{self:?}") |
57 | 0 | } |
58 | | } |
59 | | |
60 | | /// Result of a write to a device's PCI configuration space. |
61 | | /// This value represents the state change(s) that occurred due to the write. |
62 | | #[derive(Clone, Debug, Default, PartialEq, Eq)] |
63 | | pub struct ConfigWriteResult { |
64 | | /// The BusRange in the vector will be removed from mmio_bus |
65 | | pub mmio_remove: Vec<BusRange>, |
66 | | |
67 | | /// The BusRange in the vector will be added into mmio_bus |
68 | | pub mmio_add: Vec<BusRange>, |
69 | | |
70 | | /// The BusRange in the vector will be removed from io_bus |
71 | | pub io_remove: Vec<BusRange>, |
72 | | |
73 | | /// The BusRange in the vector will be added into io_bus |
74 | | pub io_add: Vec<BusRange>, |
75 | | |
76 | | /// Device specified at PciAddress will be removed after this config write |
77 | | /// - `Vec<PciAddress>>`: specified device will be removed after this config write |
78 | | pub removed_pci_devices: Vec<PciAddress>, |
79 | | } |
80 | | |
81 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] |
82 | | pub enum BusType { |
83 | | Mmio, |
84 | | Io, |
85 | | Hypercall, |
86 | | } |
87 | | |
88 | | /// Trait for devices that respond to reads or writes in an arbitrary address space. |
89 | | #[allow(unused_variables)] |
90 | | pub trait BusDevice: Send + Suspendable { |
91 | | /// Returns a label suitable for debug output. |
92 | | fn debug_label(&self) -> String; |
93 | | /// Returns a unique id per device type suitable for metrics gathering. |
94 | | fn device_id(&self) -> DeviceId; |
95 | | /// Performs a read access to this device, based on `info`. |
96 | 0 | fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {} |
97 | | /// Performs a write access to this device, based on `info`. |
98 | 0 | fn write(&mut self, info: BusAccessInfo, data: &[u8]) {} |
99 | | /// Sets a register in the configuration space. Only used by PCI. |
100 | | /// * `reg_idx` - The index of the config register to modify. |
101 | | /// * `offset` - Offset in to the register. |
102 | 0 | fn config_register_write( |
103 | 0 | &mut self, |
104 | 0 | reg_idx: usize, |
105 | 0 | offset: u64, |
106 | 0 | data: &[u8], |
107 | 0 | ) -> ConfigWriteResult { |
108 | 0 | ConfigWriteResult { |
109 | 0 | ..Default::default() |
110 | 0 | } |
111 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::config_register_write Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::config_register_write Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::config_register_write Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::config_register_write |
112 | | /// Gets a register from the configuration space. Only used by PCI. |
113 | | /// * `reg_idx` - The index of the config register to read. |
114 | 0 | fn config_register_read(&self, reg_idx: usize) -> u32 { |
115 | 0 | 0 |
116 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::config_register_read Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::config_register_read Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::config_register_read Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::config_register_read |
117 | | /// Provides a memory region to back MMIO access to the configuration |
118 | | /// space. If the device can keep the memory region up to date, then it |
119 | | /// should return true, after which no more calls to config_register_read |
120 | | /// will be made. Otherwise the device should return false. |
121 | | /// |
122 | | /// The device must set the header type register (0x0E) before returning |
123 | | /// from this function, and must make no further modifications to it |
124 | | /// after returning. This is to allow the caller to manage the multi- |
125 | | /// function device bit without worrying about race conditions. |
126 | | /// |
127 | | /// * `shmem` - The shared memory to use for the configuration space. |
128 | | /// * `base` - The base address of the memory region in shmem. |
129 | | /// * `len` - The length of the memory region. |
130 | 0 | fn init_pci_config_mapping(&mut self, shmem: &SharedMemory, base: usize, len: usize) -> bool { |
131 | 0 | false |
132 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::init_pci_config_mapping Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::init_pci_config_mapping Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::init_pci_config_mapping Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::init_pci_config_mapping |
133 | | /// Sets a register in the virtual config space. Only used by PCI. |
134 | | /// * `reg_idx` - The index of the config register to modify. |
135 | | /// * `value` - The value to be written. |
136 | 0 | fn virtual_config_register_write(&mut self, reg_idx: usize, value: u32) {}Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::virtual_config_register_write Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::virtual_config_register_write Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::virtual_config_register_write Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::virtual_config_register_write |
137 | | /// Gets a register from the virtual config space. Only used by PCI. |
138 | | /// * `reg_idx` - The index of the config register to read. |
139 | 0 | fn virtual_config_register_read(&self, reg_idx: usize) -> u32 { |
140 | 0 | 0 |
141 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::virtual_config_register_read Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::virtual_config_register_read Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::virtual_config_register_read Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::virtual_config_register_read |
142 | | /// Invoked when the device is sandboxed. |
143 | 0 | fn on_sandboxed(&mut self) {}Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::on_sandboxed Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::on_sandboxed Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::on_sandboxed Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::on_sandboxed |
144 | | |
145 | | /// Gets a list of all ranges registered by this BusDevice. |
146 | 0 | fn get_ranges(&self) -> Vec<(BusRange, BusType)> { |
147 | 0 | Vec::new() |
148 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::get_ranges Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::get_ranges Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::get_ranges Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::get_ranges |
149 | | |
150 | | /// Invoked when the device is destroyed |
151 | 0 | fn destroy_device(&mut self) {}Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::destroy_device Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::destroy_device Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::destroy_device Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::destroy_device |
152 | | |
153 | | /// Supports runtime power_on()/power_off() |
154 | 0 | fn supports_power_management(&self) -> anyhow::Result<bool> { |
155 | 0 | Ok(false) |
156 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::supports_power_management Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::supports_power_management Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::supports_power_management Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::supports_power_management Unexecuted instantiation: <devices::pci::pci_root::PciRootConfiguration as devices::bus::BusDevice>::supports_power_management |
157 | | |
158 | | /// Returns whether the device starts powered on. |
159 | 0 | fn initial_power_state(&self) -> bool { |
160 | | // Most devices should start on, in particular if they don't support PM. |
161 | 0 | true |
162 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::initial_power_state Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::initial_power_state Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::initial_power_state Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::initial_power_state Unexecuted instantiation: <devices::pci::pci_root::PciRootConfiguration as devices::bus::BusDevice>::initial_power_state |
163 | | |
164 | | /// Powers the device on. |
165 | 0 | fn power_on(&mut self) -> anyhow::Result<()> { |
166 | 0 | Err(anyhow!( |
167 | 0 | "power_on not implemented for {}", |
168 | 0 | std::any::type_name::<Self>() |
169 | 0 | )) |
170 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::power_on Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::power_on Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::power_on Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::power_on Unexecuted instantiation: <devices::pci::pci_root::PciRootConfiguration as devices::bus::BusDevice>::power_on |
171 | | |
172 | | /// Powers the device off. |
173 | 0 | fn power_off(&mut self) -> anyhow::Result<()> { |
174 | 0 | Err(anyhow!( |
175 | 0 | "power_off not implemented for {}", |
176 | 0 | std::any::type_name::<Self>() |
177 | 0 | )) |
178 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::power_off Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::power_off Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::power_off Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::power_off Unexecuted instantiation: <devices::pci::pci_root::PciRootConfiguration as devices::bus::BusDevice>::power_off |
179 | | |
180 | | /// Returns the secondary bus number if this bus device is pci bridge |
181 | 0 | fn is_bridge(&self) -> Option<u8> { |
182 | 0 | None |
183 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::is_bridge Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::is_bridge Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::is_bridge Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::is_bridge |
184 | | |
185 | | /// Handles a hypercall. Only used by hypercall-based devices. |
186 | | /// |
187 | | /// # Returns |
188 | | /// Ok(()) or Err(_) depending on whether an error occurred. Either way, the ABI-specific |
189 | | /// result for the guest is stored in `abi`, even on failure. |
190 | 0 | fn handle_hypercall(&self, abi: &mut HypercallAbi) -> anyhow::Result<()> { |
191 | 0 | Err(anyhow!( |
192 | 0 | "handle_hypercall not implemented for {}", |
193 | 0 | std::any::type_name::<Self>() |
194 | 0 | )) |
195 | 0 | } Unexecuted instantiation: <devices::irqchip::ioapic::Ioapic as devices::bus::BusDevice>::handle_hypercall Unexecuted instantiation: <devices::irqchip::pic::Pic as devices::bus::BusDevice>::handle_hypercall Unexecuted instantiation: <devices::irqchip::userspace::UserspaceIrqChip as devices::bus::BusDevice>::handle_hypercall Unexecuted instantiation: <devices::pit::Pit as devices::bus::BusDevice>::handle_hypercall Unexecuted instantiation: <devices::pci::pci_root::PciRootConfiguration as devices::bus::BusDevice>::handle_hypercall |
196 | | } |
197 | | |
198 | | pub trait BusDeviceSync: BusDevice + Sync { |
199 | | fn read(&self, offset: BusAccessInfo, data: &mut [u8]); |
200 | | fn write(&self, offset: BusAccessInfo, data: &[u8]); |
201 | 0 | fn snapshot_sync(&self) -> anyhow::Result<AnySnapshot> { |
202 | 0 | Err(anyhow!( |
203 | 0 | "snapshot_sync not implemented for {}", |
204 | 0 | std::any::type_name::<Self>() |
205 | 0 | )) |
206 | 0 | } |
207 | | /// Load a saved snapshot of an image. |
208 | 0 | fn restore_sync(&self, _data: AnySnapshot) -> anyhow::Result<()> { |
209 | 0 | Err(anyhow!( |
210 | 0 | "restore_sync not implemented for {}", |
211 | 0 | std::any::type_name::<Self>() |
212 | 0 | )) |
213 | 0 | } |
214 | | /// Stop all threads related to the device. |
215 | | /// Sleep should be idempotent. |
216 | 0 | fn sleep_sync(&self) -> anyhow::Result<()> { |
217 | 0 | Err(anyhow!( |
218 | 0 | "sleep_sync not implemented for {}", |
219 | 0 | std::any::type_name::<Self>() |
220 | 0 | )) |
221 | 0 | } |
222 | | /// Create/Resume all threads related to the device. |
223 | | /// Wake should be idempotent. |
224 | 0 | fn wake_sync(&self) -> anyhow::Result<()> { |
225 | 0 | Err(anyhow!( |
226 | 0 | "wake_sync not implemented for {}", |
227 | 0 | std::any::type_name::<Self>() |
228 | 0 | )) |
229 | 0 | } |
230 | | } |
231 | | |
232 | | pub trait BusResumeDevice: Send { |
233 | | /// notify the devices which are invoked |
234 | | /// before the VM resumes form suspend. |
235 | 0 | fn resume_imminent(&mut self) {} |
236 | | } |
237 | | |
238 | | /// The key to identify hotplug device from host view. |
239 | | /// like host sysfs path for vfio pci device, host disk file |
240 | | /// path for virtio block device |
241 | | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] |
242 | | pub enum HotPlugKey { |
243 | | HostUpstreamPort { host_addr: PciAddress }, |
244 | | HostDownstreamPort { host_addr: PciAddress }, |
245 | | HostVfio { host_addr: PciAddress }, |
246 | | GuestDevice { guest_addr: PciAddress }, |
247 | | } |
248 | | |
249 | | /// Trait for devices that notify hotplug event into guest |
250 | | pub trait HotPlugBus: Send { |
251 | | /// Request hot plug event. Returns error if the request is not sent. Upon success, optionally |
252 | | /// returns an event, which is triggerred once when the guest OS completes the request (by |
253 | | /// sending PCI_EXP_SLTCTL_CCIE). Returns None if no such mechanism is provided. |
254 | | /// * 'addr' - the guest pci address for hotplug in device |
255 | | fn hot_plug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>>; |
256 | | /// Request hot unplug event. Returns error if the request is not sent. Upon success, optionally |
257 | | /// returns an event, which is triggerred once when the guest OS completes the request (by |
258 | | /// sending PCI_EXP_SLTCTL_CCIE). Returns None if no such mechanism is provided. |
259 | | /// * 'addr' - the guest pci address for hotplug out device |
260 | | fn hot_unplug(&mut self, addr: PciAddress) -> anyhow::Result<Option<Event>>; |
261 | | /// Get a notification event when the HotPlugBus is ready for hot plug commands. If the port is |
262 | | /// already ready, then the notification event is triggerred immediately. |
263 | | fn get_ready_notification(&mut self) -> anyhow::Result<Event>; |
264 | | /// Check whether the hotplug bus is available to add the new device |
265 | | /// |
266 | | /// - 'None': hotplug bus isn't match with host pci device |
267 | | /// - 'Some(bus_num)': hotplug bus is match and put the device at bus_num |
268 | | fn is_match(&self, host_addr: PciAddress) -> Option<u8>; |
269 | | /// Gets the upstream PCI Address of the hotplug bus |
270 | | fn get_address(&self) -> Option<PciAddress>; |
271 | | /// Gets the secondary bus number of this bus |
272 | | fn get_secondary_bus_number(&self) -> Option<u8>; |
273 | | /// Add hotplug device into this bus |
274 | | /// * 'hotplug_key' - the key to identify hotplug device from host view |
275 | | /// * 'guest_addr' - the guest pci address for hotplug device |
276 | | fn add_hotplug_device(&mut self, hotplug_key: HotPlugKey, guest_addr: PciAddress); |
277 | | /// get guest pci address from the specified hotplug_key |
278 | | fn get_hotplug_device(&self, hotplug_key: HotPlugKey) -> Option<PciAddress>; |
279 | | /// Check whether this hotplug bus is empty |
280 | | fn is_empty(&self) -> bool; |
281 | | /// Get hotplug key of this hotplug bus |
282 | | fn get_hotplug_key(&self) -> Option<HotPlugKey>; |
283 | | } |
284 | | |
285 | | /// Trait for generic device abstraction, that is, all devices that reside on BusDevice and want |
286 | | /// to be converted back to its original type. Each new foo device must provide |
287 | | /// as_foo_device() + as_foo_device_mut() + into_foo_device(), default impl methods return None. |
288 | | pub trait BusDeviceObj { |
289 | 0 | fn as_pci_device(&self) -> Option<&dyn PciDevice> { |
290 | 0 | None |
291 | 0 | } |
292 | 0 | fn as_pci_device_mut(&mut self) -> Option<&mut dyn PciDevice> { |
293 | 0 | None |
294 | 0 | } |
295 | 0 | fn into_pci_device(self: Box<Self>) -> Option<Box<dyn PciDevice>> { |
296 | 0 | None |
297 | 0 | } |
298 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
299 | 0 | fn as_platform_device(&self) -> Option<&VfioPlatformDevice> { |
300 | 0 | None |
301 | 0 | } |
302 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
303 | 0 | fn as_platform_device_mut(&mut self) -> Option<&mut VfioPlatformDevice> { |
304 | 0 | None |
305 | 0 | } |
306 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
307 | 0 | fn into_platform_device(self: Box<Self>) -> Option<Box<VfioPlatformDevice>> { |
308 | 0 | None |
309 | 0 | } |
310 | | } |
311 | | |
312 | | #[sorted] |
313 | | #[derive(Error, Debug, PartialEq)] |
314 | | pub enum Error { |
315 | | #[error("Bus Range not found")] |
316 | | Empty, |
317 | | /// The insertion failed because the new device overlapped with an old device. |
318 | | #[error("new device {base},{len} overlaps with an old device {other_base},{other_len}")] |
319 | | Overlap { |
320 | | base: u64, |
321 | | len: u64, |
322 | | other_base: u64, |
323 | | other_len: u64, |
324 | | }, |
325 | | } |
326 | | |
327 | | pub type Result<T> = result::Result<T, Error>; |
328 | | |
329 | | /// Holds a base and length representing the address space occupied by a `BusDevice`. |
330 | | /// |
331 | | /// * base - The address at which the range start. |
332 | | /// * len - The length of the range in bytes. |
333 | | #[derive(Copy, Clone, Serialize, Deserialize)] |
334 | | pub struct BusRange { |
335 | | pub base: u64, |
336 | | pub len: u64, |
337 | | } |
338 | | |
339 | | impl BusRange { |
340 | | /// Returns true if `addr` is within the range. |
341 | 0 | pub fn contains(&self, addr: u64) -> bool { |
342 | 0 | self.base <= addr && addr < self.base.saturating_add(self.len) |
343 | 0 | } |
344 | | |
345 | | /// Returns true if there is overlap with the given range. |
346 | 0 | pub fn overlaps(&self, base: u64, len: u64) -> bool { |
347 | 0 | self.base < base.saturating_add(len) && base < self.base.saturating_add(self.len) |
348 | 0 | } |
349 | | } |
350 | | |
351 | | impl Eq for BusRange {} |
352 | | |
353 | | impl PartialEq for BusRange { |
354 | 0 | fn eq(&self, other: &BusRange) -> bool { |
355 | 0 | self.base == other.base |
356 | 0 | } |
357 | | } |
358 | | |
359 | | impl Ord for BusRange { |
360 | 0 | fn cmp(&self, other: &BusRange) -> Ordering { |
361 | 0 | self.base.cmp(&other.base) |
362 | 0 | } |
363 | | } |
364 | | |
365 | | impl PartialOrd for BusRange { |
366 | 0 | fn partial_cmp(&self, other: &BusRange) -> Option<Ordering> { |
367 | 0 | Some(self.cmp(other)) |
368 | 0 | } |
369 | | } |
370 | | |
371 | | impl std::fmt::Debug for BusRange { |
372 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
373 | 0 | write!(f, "{:#x}..+{:#x}", self.base, self.len) |
374 | 0 | } |
375 | | } |
376 | | |
377 | | #[derive(Clone)] |
378 | | struct BusEntry { |
379 | | #[cfg(feature = "stats")] |
380 | | index: usize, |
381 | | device: BusDeviceEntry, |
382 | | } |
383 | | |
384 | | #[derive(Clone)] |
385 | | enum BusDeviceEntry { |
386 | | OuterSync(Arc<Mutex<dyn BusDevice>>), |
387 | | InnerSync(Arc<dyn BusDeviceSync>), |
388 | | } |
389 | | |
390 | | /// A device container for routing reads and writes over some address space. |
391 | | /// |
392 | | /// This doesn't have any restrictions on what kind of device or address space this applies to. The |
393 | | /// only restriction is that no two devices can overlap in this address space. |
394 | | #[derive(Clone)] |
395 | | pub struct Bus { |
396 | | devices: Arc<Mutex<BTreeMap<BusRange, BusEntry>>>, |
397 | | access_id: usize, |
398 | | #[cfg(feature = "stats")] |
399 | | pub stats: Arc<Mutex<BusStatistics>>, |
400 | | bus_type: BusType, |
401 | | } |
402 | | |
403 | | impl Bus { |
404 | | /// Constructs an a bus with an empty address space. |
405 | 0 | pub fn new(bus_type: BusType) -> Bus { |
406 | 0 | Bus { |
407 | 0 | devices: Arc::new(Mutex::new(BTreeMap::new())), |
408 | 0 | access_id: 0, |
409 | 0 | #[cfg(feature = "stats")] |
410 | 0 | stats: Arc::new(Mutex::new(BusStatistics::new())), |
411 | 0 | bus_type, |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | /// Gets the bus type |
416 | 0 | pub fn get_bus_type(&self) -> BusType { |
417 | 0 | self.bus_type |
418 | 0 | } |
419 | | |
420 | | /// Sets the id that will be used for BusAccessInfo. |
421 | 0 | pub fn set_access_id(&mut self, id: usize) { |
422 | 0 | self.access_id = id; |
423 | 0 | } |
424 | | |
425 | 0 | fn first_before(&self, addr: u64) -> Option<(BusRange, BusEntry)> { |
426 | 0 | let devices = self.devices.lock(); |
427 | 0 | let (range, entry) = devices |
428 | 0 | .range(..=BusRange { base: addr, len: 1 }) |
429 | 0 | .next_back()?; |
430 | 0 | Some((*range, entry.clone())) |
431 | 0 | } |
432 | | |
433 | 0 | fn get_device(&self, addr: u64) -> Option<(u64, u64, BusEntry)> { |
434 | 0 | if let Some((range, entry)) = self.first_before(addr) { |
435 | 0 | let offset = addr - range.base; |
436 | 0 | if offset < range.len { |
437 | 0 | return Some((offset, addr, entry)); |
438 | 0 | } |
439 | 0 | } |
440 | 0 | None |
441 | 0 | } |
442 | | |
443 | | /// There is no unique ID for device instances. For now we use the Arc pointers to dedup them. |
444 | | /// |
445 | | /// See virtio-gpu for an example of a single device instance with multiple bus entries. |
446 | | /// |
447 | | /// TODO: Add a unique ID to BusDevice and use that instead of pointers. |
448 | 0 | fn unique_devices(&self) -> Vec<BusDeviceEntry> { |
449 | 0 | let mut seen_ptrs = BTreeSet::new(); |
450 | 0 | self.devices |
451 | 0 | .lock() |
452 | 0 | .values() |
453 | 0 | .map(|bus_entry| bus_entry.device.clone()) |
454 | 0 | .filter(|dev| match dev { |
455 | 0 | BusDeviceEntry::OuterSync(dev) => seen_ptrs.insert(Arc::as_ptr(dev) as *const u8), |
456 | 0 | BusDeviceEntry::InnerSync(dev) => seen_ptrs.insert(Arc::as_ptr(dev) as *const u8), |
457 | 0 | }) |
458 | 0 | .collect() |
459 | 0 | } |
460 | | |
461 | | /// Same as `unique_devices`, but also calculates the "snapshot key" for each device. |
462 | | /// |
463 | | /// The keys are used to associate a particular device with data in a serialized snapshot. The |
464 | | /// keys need to be stable across multiple runs of the same crosvm binary. |
465 | | /// |
466 | | /// It is most convienent to calculate all the snapshot keys at once, because the keys are |
467 | | /// dependant on the order of devices on the bus. |
468 | 0 | fn unique_devices_with_snapshot_key(&self) -> Vec<(String, BusDeviceEntry)> { |
469 | 0 | let mut next_ids = BTreeMap::<String, usize>::new(); |
470 | 0 | let mut choose_key = |debug_label: String| -> String { |
471 | 0 | let label = debug_label.replace(char::is_whitespace, "-"); |
472 | 0 | let id = next_ids.entry(label.clone()).or_default(); |
473 | 0 | let key = format!("{label}-{id}"); |
474 | 0 | *id += 1; |
475 | 0 | key |
476 | 0 | }; |
477 | | |
478 | 0 | let mut result = Vec::new(); |
479 | 0 | for device_entry in self.unique_devices() { |
480 | 0 | let key = match &device_entry { |
481 | 0 | BusDeviceEntry::OuterSync(d) => choose_key(d.lock().debug_label()), |
482 | 0 | BusDeviceEntry::InnerSync(d) => choose_key(d.debug_label()), |
483 | | }; |
484 | 0 | result.push((key, device_entry)); |
485 | | } |
486 | 0 | result |
487 | 0 | } |
488 | | |
489 | 0 | pub fn sleep_devices(&self) -> anyhow::Result<()> { |
490 | 0 | for device_entry in self.unique_devices() { |
491 | 0 | match device_entry { |
492 | 0 | BusDeviceEntry::OuterSync(dev) => { |
493 | 0 | let mut dev = (*dev).lock(); |
494 | 0 | debug!("Sleep on device: {}", dev.debug_label()); |
495 | 0 | dev.sleep() |
496 | 0 | .with_context(|| format!("failed to sleep {}", dev.debug_label()))?; |
497 | | } |
498 | 0 | BusDeviceEntry::InnerSync(dev) => { |
499 | 0 | debug!("Sleep on device: {}", dev.debug_label()); |
500 | 0 | dev.sleep_sync() |
501 | 0 | .with_context(|| format!("failed to sleep {}", dev.debug_label()))?; |
502 | | } |
503 | | } |
504 | | } |
505 | 0 | Ok(()) |
506 | 0 | } |
507 | | |
508 | 0 | pub fn wake_devices(&self) -> anyhow::Result<()> { |
509 | 0 | for device_entry in self.unique_devices() { |
510 | 0 | match device_entry { |
511 | 0 | BusDeviceEntry::OuterSync(dev) => { |
512 | 0 | let mut dev = dev.lock(); |
513 | 0 | debug!("Wake on device: {}", dev.debug_label()); |
514 | 0 | dev.wake() |
515 | 0 | .with_context(|| format!("failed to wake {}", dev.debug_label()))?; |
516 | | } |
517 | 0 | BusDeviceEntry::InnerSync(dev) => { |
518 | 0 | debug!("Wake on device: {}", dev.debug_label()); |
519 | 0 | dev.wake_sync() |
520 | 0 | .with_context(|| format!("failed to wake {}", dev.debug_label()))?; |
521 | | } |
522 | | } |
523 | | } |
524 | 0 | Ok(()) |
525 | 0 | } |
526 | | |
527 | 0 | pub fn snapshot_devices( |
528 | 0 | &self, |
529 | 0 | snapshot_writer: &snapshot::SnapshotWriter, |
530 | 0 | ) -> anyhow::Result<()> { |
531 | 0 | for (snapshot_key, device_entry) in self.unique_devices_with_snapshot_key() { |
532 | 0 | match device_entry { |
533 | 0 | BusDeviceEntry::OuterSync(dev) => { |
534 | 0 | let mut dev = dev.lock(); |
535 | 0 | debug!("Snapshot on device: {}", dev.debug_label()); |
536 | 0 | snapshot_writer.write_fragment( |
537 | 0 | &snapshot_key, |
538 | 0 | &(*dev) |
539 | 0 | .snapshot() |
540 | 0 | .with_context(|| format!("failed to snapshot {}", dev.debug_label()))?, |
541 | 0 | )?; |
542 | | } |
543 | 0 | BusDeviceEntry::InnerSync(dev) => { |
544 | 0 | debug!("Snapshot on device: {}", dev.debug_label()); |
545 | 0 | snapshot_writer.write_fragment( |
546 | 0 | &snapshot_key, |
547 | 0 | &dev.snapshot_sync() |
548 | 0 | .with_context(|| format!("failed to snapshot {}", dev.debug_label()))?, |
549 | 0 | )?; |
550 | | } |
551 | | } |
552 | | } |
553 | 0 | Ok(()) |
554 | 0 | } |
555 | | |
556 | 0 | pub fn restore_devices( |
557 | 0 | &self, |
558 | 0 | snapshot_reader: &snapshot::SnapshotReader, |
559 | 0 | ) -> anyhow::Result<()> { |
560 | 0 | let mut unused_keys: BTreeSet<String> = |
561 | 0 | snapshot_reader.list_fragments()?.into_iter().collect(); |
562 | 0 | for (snapshot_key, device_entry) in self.unique_devices_with_snapshot_key() { |
563 | 0 | unused_keys.remove(&snapshot_key); |
564 | 0 | match device_entry { |
565 | 0 | BusDeviceEntry::OuterSync(dev) => { |
566 | 0 | let mut dev = dev.lock(); |
567 | 0 | debug!("Restore on device: {}", dev.debug_label()); |
568 | 0 | dev.restore(snapshot_reader.read_fragment(&snapshot_key)?) |
569 | 0 | .with_context(|| { |
570 | 0 | format!("restore failed for device {}", dev.debug_label()) |
571 | 0 | })?; |
572 | | } |
573 | 0 | BusDeviceEntry::InnerSync(dev) => { |
574 | 0 | debug!("Restore on device: {}", dev.debug_label()); |
575 | 0 | dev.restore_sync(snapshot_reader.read_fragment(&snapshot_key)?) |
576 | 0 | .with_context(|| { |
577 | 0 | format!("restore failed for device {}", dev.debug_label()) |
578 | 0 | })?; |
579 | | } |
580 | | } |
581 | | } |
582 | | |
583 | 0 | if !unused_keys.is_empty() { |
584 | 0 | error!( |
585 | 0 | "unused restore data in bus, devices might be missing: {:?}", |
586 | | unused_keys |
587 | | ); |
588 | 0 | } |
589 | | |
590 | 0 | Ok(()) |
591 | 0 | } |
592 | | |
593 | | /// Puts the given device at the given address space. |
594 | 0 | pub fn insert(&self, device: Arc<Mutex<dyn BusDevice>>, base: u64, len: u64) -> Result<()> { |
595 | 0 | if len == 0 { |
596 | 0 | return Err(Error::Overlap { |
597 | 0 | base, |
598 | 0 | len, |
599 | 0 | other_base: 0, |
600 | 0 | other_len: 0, |
601 | 0 | }); |
602 | 0 | } |
603 | | |
604 | | // Reject all cases where the new device's range overlaps with an existing device. |
605 | 0 | let mut devices = self.devices.lock(); |
606 | 0 | devices.iter().try_for_each(|(range, _dev)| { |
607 | 0 | if range.overlaps(base, len) { |
608 | 0 | Err(Error::Overlap { |
609 | 0 | base, |
610 | 0 | len, |
611 | 0 | other_base: range.base, |
612 | 0 | other_len: range.len, |
613 | 0 | }) |
614 | | } else { |
615 | 0 | Ok(()) |
616 | | } |
617 | 0 | })?; |
618 | | |
619 | | #[cfg(feature = "stats")] |
620 | | let name = device.lock().debug_label(); |
621 | | #[cfg(feature = "stats")] |
622 | | let device_id = device.lock().device_id(); |
623 | 0 | if devices |
624 | 0 | .insert( |
625 | 0 | BusRange { base, len }, |
626 | 0 | BusEntry { |
627 | 0 | #[cfg(feature = "stats")] |
628 | 0 | index: self.stats.lock().next_device_index( |
629 | 0 | name, |
630 | 0 | device_id.metrics_id(), |
631 | 0 | base, |
632 | 0 | len, |
633 | 0 | ), |
634 | 0 | device: BusDeviceEntry::OuterSync(device), |
635 | 0 | }, |
636 | 0 | ) |
637 | 0 | .is_some() |
638 | | { |
639 | 0 | return Err(Error::Overlap { |
640 | 0 | base, |
641 | 0 | len, |
642 | 0 | other_base: base, |
643 | 0 | other_len: len, |
644 | 0 | }); |
645 | 0 | } |
646 | | |
647 | 0 | Ok(()) |
648 | 0 | } |
649 | | |
650 | | /// Puts the given device that implements BusDeviceSync at the given address space. Devices |
651 | | /// that implement BusDeviceSync manage thread safety internally, and thus can be written to |
652 | | /// by multiple threads simultaneously. |
653 | 0 | pub fn insert_sync(&self, device: Arc<dyn BusDeviceSync>, base: u64, len: u64) -> Result<()> { |
654 | 0 | if len == 0 { |
655 | 0 | return Err(Error::Overlap { |
656 | 0 | base, |
657 | 0 | len, |
658 | 0 | other_base: 0, |
659 | 0 | other_len: 0, |
660 | 0 | }); |
661 | 0 | } |
662 | | |
663 | | // Reject all cases where the new device's range overlaps with an existing device. |
664 | 0 | let mut devices = self.devices.lock(); |
665 | 0 | devices.iter().try_for_each(|(range, _dev)| { |
666 | 0 | if range.overlaps(base, len) { |
667 | 0 | Err(Error::Overlap { |
668 | 0 | base, |
669 | 0 | len, |
670 | 0 | other_base: range.base, |
671 | 0 | other_len: range.len, |
672 | 0 | }) |
673 | | } else { |
674 | 0 | Ok(()) |
675 | | } |
676 | 0 | })?; |
677 | | |
678 | 0 | if devices |
679 | 0 | .insert( |
680 | 0 | BusRange { base, len }, |
681 | 0 | BusEntry { |
682 | 0 | #[cfg(feature = "stats")] |
683 | 0 | index: self.stats.lock().next_device_index( |
684 | 0 | device.debug_label(), |
685 | 0 | device.device_id().metrics_id(), |
686 | 0 | base, |
687 | 0 | len, |
688 | 0 | ), |
689 | 0 | device: BusDeviceEntry::InnerSync(device), |
690 | 0 | }, |
691 | 0 | ) |
692 | 0 | .is_some() |
693 | | { |
694 | 0 | return Err(Error::Overlap { |
695 | 0 | base, |
696 | 0 | len, |
697 | 0 | other_base: base, |
698 | 0 | other_len: len, |
699 | 0 | }); |
700 | 0 | } |
701 | | |
702 | 0 | Ok(()) |
703 | 0 | } |
704 | | |
705 | | /// Remove the given device at the given address space. |
706 | 0 | pub fn remove(&self, base: u64, len: u64) -> Result<()> { |
707 | 0 | if len == 0 { |
708 | 0 | return Err(Error::Overlap { |
709 | 0 | base, |
710 | 0 | len, |
711 | 0 | other_base: 0, |
712 | 0 | other_len: 0, |
713 | 0 | }); |
714 | 0 | } |
715 | | |
716 | 0 | let mut devices = self.devices.lock(); |
717 | 0 | if devices |
718 | 0 | .iter() |
719 | 0 | .any(|(range, _dev)| range.base == base && range.len == len) |
720 | | { |
721 | 0 | let ret = devices.remove(&BusRange { base, len }); |
722 | 0 | if ret.is_some() { |
723 | 0 | Ok(()) |
724 | | } else { |
725 | 0 | Err(Error::Empty) |
726 | | } |
727 | | } else { |
728 | 0 | Err(Error::Empty) |
729 | | } |
730 | 0 | } |
731 | | |
732 | | /// Reads data from the device that owns the range containing `addr` and puts it into `data`. |
733 | | /// |
734 | | /// Returns true on success, otherwise `data` is filled with zeroes. |
735 | 0 | pub fn read(&self, addr: u64, data: &mut [u8]) -> bool { |
736 | | #[cfg(feature = "stats")] |
737 | | let start = self.stats.lock().start_stat(); |
738 | | |
739 | | // Initialize `data` with all zeroes to ensure consistent results even if device `read()` |
740 | | // implementations don't always fill every byte. |
741 | 0 | data.fill(0); |
742 | | |
743 | 0 | let device_index = if let Some((offset, address, entry)) = self.get_device(addr) { |
744 | 0 | let io = BusAccessInfo { |
745 | 0 | address, |
746 | 0 | offset, |
747 | 0 | id: self.access_id, |
748 | 0 | }; |
749 | | |
750 | 0 | match &entry.device { |
751 | 0 | BusDeviceEntry::OuterSync(dev) => dev.lock().read(io, data), |
752 | 0 | BusDeviceEntry::InnerSync(dev) => dev.read(io, data), |
753 | | } |
754 | | #[cfg(feature = "stats")] |
755 | | let index = Some(entry.index); |
756 | | #[cfg(not(feature = "stats"))] |
757 | 0 | let index = Some(()); |
758 | 0 | index |
759 | | } else { |
760 | 0 | None |
761 | | }; |
762 | | |
763 | | #[cfg(feature = "stats")] |
764 | | if let Some(device_index) = device_index { |
765 | | self.stats |
766 | | .lock() |
767 | | .end_stat(BusOperation::Write, start, device_index); |
768 | | return true; |
769 | | } |
770 | | |
771 | 0 | device_index.is_some() |
772 | 0 | } |
773 | | |
774 | | /// Writes `data` to the device that owns the range containing `addr`. |
775 | | /// |
776 | | /// Returns true on success, otherwise `data` is untouched. |
777 | 0 | pub fn write(&self, addr: u64, data: &[u8]) -> bool { |
778 | | #[cfg(feature = "stats")] |
779 | | let start = self.stats.lock().start_stat(); |
780 | | |
781 | 0 | let device_index = if let Some((offset, address, entry)) = self.get_device(addr) { |
782 | 0 | let io = BusAccessInfo { |
783 | 0 | address, |
784 | 0 | offset, |
785 | 0 | id: self.access_id, |
786 | 0 | }; |
787 | | |
788 | 0 | match &entry.device { |
789 | 0 | BusDeviceEntry::OuterSync(dev) => dev.lock().write(io, data), |
790 | 0 | BusDeviceEntry::InnerSync(dev) => dev.write(io, data), |
791 | | } |
792 | | |
793 | | #[cfg(feature = "stats")] |
794 | | let index = Some(entry.index); |
795 | | #[cfg(not(feature = "stats"))] |
796 | 0 | let index = Some(()); |
797 | 0 | index |
798 | | } else { |
799 | 0 | None |
800 | | }; |
801 | | |
802 | | #[cfg(feature = "stats")] |
803 | | if let Some(device_index) = device_index { |
804 | | self.stats |
805 | | .lock() |
806 | | .end_stat(BusOperation::Write, start, device_index); |
807 | | } |
808 | 0 | device_index.is_some() |
809 | 0 | } |
810 | | |
811 | | /// Handles a guest hypercall. |
812 | | /// |
813 | | /// # Returns |
814 | | /// Ok(()) or Err(_) depending on whether an error occurred. Either way, the ABI-specific |
815 | | /// result for the guest is stored in `abi`, even on failure. |
816 | 0 | pub fn handle_hypercall(&self, abi: &mut HypercallAbi) -> anyhow::Result<()> { |
817 | 0 | let id = abi.hypercall_id().try_into().unwrap(); |
818 | 0 | let (_, _, entry) = self |
819 | 0 | .get_device(id) |
820 | 0 | .with_context(|| format!("Unknown hypercall {id:#x}"))?; |
821 | 0 | match &entry.device { |
822 | 0 | BusDeviceEntry::OuterSync(dev) => dev.lock().handle_hypercall(abi), |
823 | 0 | BusDeviceEntry::InnerSync(dev) => dev.handle_hypercall(abi), |
824 | | } |
825 | 0 | } |
826 | | } |
827 | | |
828 | | impl Default for Bus { |
829 | 0 | fn default() -> Self { |
830 | 0 | Self::new(BusType::Io) |
831 | 0 | } |
832 | | } |
833 | | |
834 | | #[cfg(test)] |
835 | | mod tests { |
836 | | use anyhow::Result as AnyhowResult; |
837 | | use vm_control::PlatformDeviceId; |
838 | | |
839 | | use super::*; |
840 | | use crate::suspendable::Suspendable; |
841 | | use crate::suspendable_tests; |
842 | | use crate::MockDevice; |
843 | | |
844 | | #[derive(Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Debug)] |
845 | | struct ConstantDevice { |
846 | | uses_full_addr: bool, |
847 | | } |
848 | | |
849 | | impl BusDevice for ConstantDevice { |
850 | | fn device_id(&self) -> DeviceId { |
851 | | PlatformDeviceId::Cmos.into() |
852 | | } |
853 | | |
854 | | fn debug_label(&self) -> String { |
855 | | "constant device".to_owned() |
856 | | } |
857 | | |
858 | | fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) { |
859 | | let addr = if self.uses_full_addr { |
860 | | info.address |
861 | | } else { |
862 | | info.offset |
863 | | }; |
864 | | for (i, v) in data.iter_mut().enumerate() { |
865 | | *v = (addr as u8) + (i as u8); |
866 | | } |
867 | | } |
868 | | |
869 | | fn write(&mut self, info: BusAccessInfo, data: &[u8]) { |
870 | | let addr = if self.uses_full_addr { |
871 | | info.address |
872 | | } else { |
873 | | info.offset |
874 | | }; |
875 | | for (i, v) in data.iter().enumerate() { |
876 | | assert_eq!(*v, (addr as u8) + (i as u8)) |
877 | | } |
878 | | } |
879 | | } |
880 | | |
881 | | impl Suspendable for ConstantDevice { |
882 | | fn snapshot(&mut self) -> AnyhowResult<AnySnapshot> { |
883 | | AnySnapshot::to_any(self).context("error serializing") |
884 | | } |
885 | | |
886 | | fn restore(&mut self, data: AnySnapshot) -> AnyhowResult<()> { |
887 | | *self = AnySnapshot::from_any(data).context("error deserializing")?; |
888 | | Ok(()) |
889 | | } |
890 | | |
891 | | fn sleep(&mut self) -> AnyhowResult<()> { |
892 | | Ok(()) |
893 | | } |
894 | | |
895 | | fn wake(&mut self) -> AnyhowResult<()> { |
896 | | Ok(()) |
897 | | } |
898 | | } |
899 | | |
900 | | fn modify_constant_device(constant: &mut ConstantDevice) { |
901 | | constant.uses_full_addr = !constant.uses_full_addr; |
902 | | } |
903 | | |
904 | | #[test] |
905 | | fn bus_insert() { |
906 | | let bus = Bus::new(BusType::Io); |
907 | | let dev = Arc::new(Mutex::new(MockDevice::new())); |
908 | | assert_eq!( |
909 | | bus.insert(dev.clone(), 0x10, 0), |
910 | | Err(Error::Overlap { |
911 | | base: 0x10, |
912 | | len: 0, |
913 | | other_base: 0, |
914 | | other_len: 0 |
915 | | }) |
916 | | ); |
917 | | assert_eq!(bus.insert(dev.clone(), 0x10, 0x10), Ok(())); |
918 | | assert_eq!( |
919 | | bus.insert(dev.clone(), 0x0f, 0x10), |
920 | | Err(Error::Overlap { |
921 | | base: 0x0f, |
922 | | len: 0x10, |
923 | | other_base: 0x10, |
924 | | other_len: 0x10 |
925 | | }) |
926 | | ); |
927 | | assert_eq!( |
928 | | bus.insert(dev.clone(), 0x10, 0x10), |
929 | | Err(Error::Overlap { |
930 | | base: 0x10, |
931 | | len: 0x10, |
932 | | other_base: 0x10, |
933 | | other_len: 0x10 |
934 | | }) |
935 | | ); |
936 | | assert_eq!( |
937 | | bus.insert(dev.clone(), 0x10, 0x15), |
938 | | Err(Error::Overlap { |
939 | | base: 0x10, |
940 | | len: 0x15, |
941 | | other_base: 0x10, |
942 | | other_len: 0x10 |
943 | | }) |
944 | | ); |
945 | | assert_eq!( |
946 | | bus.insert(dev.clone(), 0x12, 0x15), |
947 | | Err(Error::Overlap { |
948 | | base: 0x12, |
949 | | len: 0x15, |
950 | | other_base: 0x10, |
951 | | other_len: 0x10 |
952 | | }) |
953 | | ); |
954 | | assert_eq!( |
955 | | bus.insert(dev.clone(), 0x12, 0x01), |
956 | | Err(Error::Overlap { |
957 | | base: 0x12, |
958 | | len: 0x01, |
959 | | other_base: 0x10, |
960 | | other_len: 0x10 |
961 | | }) |
962 | | ); |
963 | | assert_eq!( |
964 | | bus.insert(dev.clone(), 0x0, 0x20), |
965 | | Err(Error::Overlap { |
966 | | base: 0x0, |
967 | | len: 0x20, |
968 | | other_base: 0x10, |
969 | | other_len: 0x10 |
970 | | }) |
971 | | ); |
972 | | assert_eq!(bus.insert(dev.clone(), 0x20, 0x05), Ok(())); |
973 | | assert_eq!(bus.insert(dev.clone(), 0x25, 0x05), Ok(())); |
974 | | assert_eq!(bus.insert(dev, 0x0, 0x10), Ok(())); |
975 | | } |
976 | | |
977 | | #[test] |
978 | | fn bus_insert_full_addr() { |
979 | | let bus = Bus::new(BusType::Io); |
980 | | let dev = Arc::new(Mutex::new(MockDevice::new())); |
981 | | assert_eq!( |
982 | | bus.insert(dev.clone(), 0x10, 0), |
983 | | Err(Error::Overlap { |
984 | | base: 0x10, |
985 | | len: 0, |
986 | | other_base: 0, |
987 | | other_len: 0 |
988 | | }) |
989 | | ); |
990 | | assert_eq!(bus.insert(dev.clone(), 0x10, 0x10), Ok(())); |
991 | | assert_eq!( |
992 | | bus.insert(dev.clone(), 0x0f, 0x10), |
993 | | Err(Error::Overlap { |
994 | | base: 0x0f, |
995 | | len: 0x10, |
996 | | other_base: 0x10, |
997 | | other_len: 0x10 |
998 | | }) |
999 | | ); |
1000 | | assert_eq!( |
1001 | | bus.insert(dev.clone(), 0x10, 0x10), |
1002 | | Err(Error::Overlap { |
1003 | | base: 0x10, |
1004 | | len: 0x10, |
1005 | | other_base: 0x10, |
1006 | | other_len: 0x10 |
1007 | | }) |
1008 | | ); |
1009 | | assert_eq!( |
1010 | | bus.insert(dev.clone(), 0x10, 0x15), |
1011 | | Err(Error::Overlap { |
1012 | | base: 0x10, |
1013 | | len: 0x15, |
1014 | | other_base: 0x10, |
1015 | | other_len: 0x10 |
1016 | | }) |
1017 | | ); |
1018 | | assert_eq!( |
1019 | | bus.insert(dev.clone(), 0x12, 0x15), |
1020 | | Err(Error::Overlap { |
1021 | | base: 0x12, |
1022 | | len: 0x15, |
1023 | | other_base: 0x10, |
1024 | | other_len: 0x10 |
1025 | | }) |
1026 | | ); |
1027 | | assert_eq!( |
1028 | | bus.insert(dev.clone(), 0x12, 0x01), |
1029 | | Err(Error::Overlap { |
1030 | | base: 0x12, |
1031 | | len: 0x01, |
1032 | | other_base: 0x10, |
1033 | | other_len: 0x10 |
1034 | | }) |
1035 | | ); |
1036 | | assert_eq!( |
1037 | | bus.insert(dev.clone(), 0x0, 0x20), |
1038 | | Err(Error::Overlap { |
1039 | | base: 0x0, |
1040 | | len: 0x20, |
1041 | | other_base: 0x10, |
1042 | | other_len: 0x10 |
1043 | | }) |
1044 | | ); |
1045 | | assert_eq!(bus.insert(dev.clone(), 0x20, 0x05), Ok(())); |
1046 | | assert_eq!(bus.insert(dev.clone(), 0x25, 0x05), Ok(())); |
1047 | | assert_eq!(bus.insert(dev, 0x0, 0x10), Ok(())); |
1048 | | } |
1049 | | |
1050 | | #[test] |
1051 | | fn bus_read_write() { |
1052 | | let bus = Bus::new(BusType::Io); |
1053 | | let dev = Arc::new(Mutex::new(MockDevice::new())); |
1054 | | assert_eq!(bus.insert(dev, 0x10, 0x10), Ok(())); |
1055 | | assert!(bus.read(0x10, &mut [0, 0, 0, 0])); |
1056 | | assert!(bus.write(0x10, &[0, 0, 0, 0])); |
1057 | | assert!(bus.read(0x11, &mut [0, 0, 0, 0])); |
1058 | | assert!(bus.write(0x11, &[0, 0, 0, 0])); |
1059 | | assert!(bus.read(0x16, &mut [0, 0, 0, 0])); |
1060 | | assert!(bus.write(0x16, &[0, 0, 0, 0])); |
1061 | | assert!(!bus.read(0x20, &mut [0, 0, 0, 0])); |
1062 | | assert!(!bus.write(0x20, &[0, 0, 0, 0])); |
1063 | | assert!(!bus.read(0x06, &mut [0, 0, 0, 0])); |
1064 | | assert!(!bus.write(0x06, &[0, 0, 0, 0])); |
1065 | | } |
1066 | | |
1067 | | #[test] |
1068 | | fn bus_read_write_values() { |
1069 | | let bus = Bus::new(BusType::Io); |
1070 | | let dev = Arc::new(Mutex::new(ConstantDevice { |
1071 | | uses_full_addr: false, |
1072 | | })); |
1073 | | assert_eq!(bus.insert(dev, 0x10, 0x10), Ok(())); |
1074 | | |
1075 | | let mut values = [0, 1, 2, 3]; |
1076 | | assert!(bus.read(0x10, &mut values)); |
1077 | | assert_eq!(values, [0, 1, 2, 3]); |
1078 | | assert!(bus.write(0x10, &values)); |
1079 | | assert!(bus.read(0x15, &mut values)); |
1080 | | assert_eq!(values, [5, 6, 7, 8]); |
1081 | | assert!(bus.write(0x15, &values)); |
1082 | | } |
1083 | | |
1084 | | #[test] |
1085 | | fn bus_read_write_full_addr_values() { |
1086 | | let bus = Bus::new(BusType::Io); |
1087 | | let dev = Arc::new(Mutex::new(ConstantDevice { |
1088 | | uses_full_addr: true, |
1089 | | })); |
1090 | | assert_eq!(bus.insert(dev, 0x10, 0x10), Ok(())); |
1091 | | |
1092 | | let mut values = [0u8; 4]; |
1093 | | assert!(bus.read(0x10, &mut values)); |
1094 | | assert_eq!(values, [0x10, 0x11, 0x12, 0x13]); |
1095 | | assert!(bus.write(0x10, &values)); |
1096 | | assert!(bus.read(0x15, &mut values)); |
1097 | | assert_eq!(values, [0x15, 0x16, 0x17, 0x18]); |
1098 | | assert!(bus.write(0x15, &values)); |
1099 | | } |
1100 | | |
1101 | | #[test] |
1102 | | fn bus_read_no_device() { |
1103 | | let bus = Bus::new(BusType::Io); |
1104 | | |
1105 | | // read() should return false, since there is no device at address 0x10, but it should |
1106 | | // also fill the data with 0s. |
1107 | | let mut values = [1, 2, 3, 4]; |
1108 | | assert!(!bus.read(0x10, &mut values)); |
1109 | | assert_eq!(values, [0, 0, 0, 0]); |
1110 | | } |
1111 | | |
1112 | | suspendable_tests!( |
1113 | | constant_device_true, |
1114 | | ConstantDevice { |
1115 | | uses_full_addr: true, |
1116 | | }, |
1117 | | modify_constant_device |
1118 | | ); |
1119 | | |
1120 | | suspendable_tests!( |
1121 | | constant_device_false, |
1122 | | ConstantDevice { |
1123 | | uses_full_addr: false, |
1124 | | }, |
1125 | | modify_constant_device |
1126 | | ); |
1127 | | |
1128 | | #[test] |
1129 | | fn bus_range_contains() { |
1130 | | let a = BusRange { |
1131 | | base: 0x1000, |
1132 | | len: 0x400, |
1133 | | }; |
1134 | | assert!(a.contains(0x1000)); |
1135 | | assert!(a.contains(0x13ff)); |
1136 | | assert!(!a.contains(0xfff)); |
1137 | | assert!(!a.contains(0x1400)); |
1138 | | assert!(a.contains(0x1200)); |
1139 | | } |
1140 | | |
1141 | | #[test] |
1142 | | fn bus_range_overlap() { |
1143 | | let a = BusRange { |
1144 | | base: 0x1000, |
1145 | | len: 0x400, |
1146 | | }; |
1147 | | assert!(a.overlaps(0x1000, 0x400)); |
1148 | | assert!(a.overlaps(0xf00, 0x400)); |
1149 | | assert!(a.overlaps(0x1000, 0x01)); |
1150 | | assert!(a.overlaps(0xfff, 0x02)); |
1151 | | assert!(a.overlaps(0x1100, 0x100)); |
1152 | | assert!(a.overlaps(0x13ff, 0x100)); |
1153 | | assert!(!a.overlaps(0x1400, 0x100)); |
1154 | | assert!(!a.overlaps(0xf00, 0x100)); |
1155 | | } |
1156 | | } |