• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
6 use acpi_tables::sdt::SDT;
7 use anyhow::bail;
8 use base::{error, Event, RawDescriptor};
9 use hypervisor::Datamatch;
10 use remain::sorted;
11 use resources::{Error as SystemAllocatorFaliure, SystemAllocator};
12 use thiserror::Error;
13 
14 use crate::bus::{BusDeviceObj, BusRange, BusType, ConfigWriteResult};
15 use crate::pci::pci_configuration::{
16     self, PciBarConfiguration, BAR0_REG, COMMAND_REG, COMMAND_REG_IO_SPACE_MASK,
17     COMMAND_REG_MEMORY_SPACE_MASK, NUM_BAR_REGS, PCI_ID_REG, ROM_BAR_REG,
18 };
19 use crate::pci::{PciAddress, PciAddressError, PciInterruptPin};
20 use crate::virtio::ipc_memory_mapper::IpcMemoryMapper;
21 #[cfg(feature = "audio")]
22 use crate::virtio::snd::vios_backend::Error as VioSError;
23 use crate::{BusAccessInfo, BusDevice, IrqLevelEvent};
24 
25 #[sorted]
26 #[derive(Error, Debug)]
27 pub enum Error {
28     /// Invalid alignment encountered.
29     #[error("Alignment must be a power of 2")]
30     BadAlignment,
31     /// Setup of the device capabilities failed.
32     #[error("failed to add capability {0}")]
33     CapabilitiesSetup(pci_configuration::Error),
34     /// Create cras client failed.
35     #[cfg(all(feature = "audio", feature = "audio_cras"))]
36     #[error("failed to create CRAS Client: {0}")]
37     CreateCrasClientFailed(libcras::Error),
38     /// Create VioS client failed.
39     #[cfg(feature = "audio")]
40     #[error("failed to create VioS Client: {0}")]
41     CreateViosClientFailed(VioSError),
42     /// Allocating space for an IO BAR failed.
43     #[error("failed to allocate space for an IO BAR, size={0}: {1}")]
44     IoAllocationFailed(u64, SystemAllocatorFaliure),
45     /// supports_iommu is false.
46     #[error("Iommu is not supported")]
47     IommuNotSupported,
48     /// Registering an IO BAR failed.
49     #[error("failed to register an IO BAR, addr={0} err={1}")]
50     IoRegistrationFailed(u64, pci_configuration::Error),
51     /// Out-of-space encountered
52     #[error("Out-of-space detected")]
53     OutOfSpace,
54     /// Overflow encountered
55     #[error("base={0} + size={1} overflows")]
56     Overflow(u64, u64),
57     /// PCI Address is not allocated.
58     #[error("PCI address is not allocated")]
59     PciAddressMissing,
60     /// PCI Address parsing failure.
61     #[error("PCI address '{0}' could not be parsed: {1}")]
62     PciAddressParseFailure(String, PciAddressError),
63     /// PCI Address allocation failure.
64     #[error("failed to allocate PCI address")]
65     PciAllocationFailed,
66     /// Size of zero encountered
67     #[error("Size of zero detected")]
68     SizeZero,
69 }
70 
71 pub type Result<T> = std::result::Result<T, Error>;
72 
73 /// Pci Bar Range information
74 #[derive(Clone)]
75 pub struct BarRange {
76     /// pci bar start address
77     pub addr: u64,
78     /// pci bar size
79     pub size: u64,
80     /// pci bar is prefetchable or not, it used to set parent's bridge window
81     pub prefetchable: bool,
82 }
83 
84 pub trait PciDevice: Send {
85     /// Returns a label suitable for debug output.
debug_label(&self) -> String86     fn debug_label(&self) -> String;
87     /// Allocate and return an unique bus, device and function number for this device.
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>88     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>;
89     /// A vector of device-specific file descriptors that must be kept open
90     /// after jailing. Must be called before the process is jailed.
keep_rds(&self) -> Vec<RawDescriptor>91     fn keep_rds(&self) -> Vec<RawDescriptor>;
92     /// Assign a legacy PCI IRQ to this device.
93     /// The device may write to `irq_evt` to trigger an interrupt.
94     /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary.
95     /// Optional irq_num can be used for default INTx allocation, device can overwrite it.
96     /// If legacy INTx is used, function shall return requested IRQ number and PCI INTx pin.
assign_irq( &mut self, _irq_evt: &IrqLevelEvent, _irq_num: Option<u32>, ) -> Option<(u32, PciInterruptPin)>97     fn assign_irq(
98         &mut self,
99         _irq_evt: &IrqLevelEvent,
100         _irq_num: Option<u32>,
101     ) -> Option<(u32, PciInterruptPin)> {
102         None
103     }
104     /// Allocates the needed IO BAR space using the `allocate` function which takes a size and
105     /// returns an address. Returns a Vec of BarRange{addr, size, prefetchable}.
allocate_io_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<BarRange>>106     fn allocate_io_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
107         Ok(Vec::new())
108     }
109 
110     /// Allocates the needed device BAR space. Returns a Vec of BarRange{addr, size, prefetchable}.
111     /// Unlike MMIO BARs (see allocate_io_bars), device BARs are not expected to incur VM exits
112     /// - these BARs represent normal memory.
allocate_device_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<BarRange>>113     fn allocate_device_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
114         Ok(Vec::new())
115     }
116 
117     /// Returns the configuration of a base address register, if present.
get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>118     fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>;
119 
120     /// Register any capabilties specified by the device.
register_device_capabilities(&mut self) -> Result<()>121     fn register_device_capabilities(&mut self) -> Result<()> {
122         Ok(())
123     }
124 
125     /// Gets a list of ioevents that should be registered with the running VM. The list is
126     /// returned as a Vec of (event, addr, datamatch) tuples.
ioevents(&self) -> Vec<(&Event, u64, Datamatch)>127     fn ioevents(&self) -> Vec<(&Event, u64, Datamatch)> {
128         Vec::new()
129     }
130 
131     /// Reads from a PCI configuration register.
132     /// * `reg_idx` - PCI register index (in units of 4 bytes).
read_config_register(&self, reg_idx: usize) -> u32133     fn read_config_register(&self, reg_idx: usize) -> u32;
134 
135     /// Writes to a PCI configuration register.
136     /// * `reg_idx` - PCI register index (in units of 4 bytes).
137     /// * `offset`  - byte offset within 4-byte register.
138     /// * `data`    - The data to write.
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])139     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]);
140 
141     /// Reads from a virtual config register.
142     /// * `reg_idx` - virtual config register index (in units of 4 bytes).
read_virtual_config_register(&self, _reg_idx: usize) -> u32143     fn read_virtual_config_register(&self, _reg_idx: usize) -> u32 {
144         0
145     }
146 
147     /// Writes to a virtual config register.
148     /// * `reg_idx` - virtual config register index (in units of 4 bytes).
149     /// * `value`   - the value to be written.
write_virtual_config_register(&mut self, _reg_idx: usize, _value: u32)150     fn write_virtual_config_register(&mut self, _reg_idx: usize, _value: u32) {}
151 
152     /// Reads from a BAR region mapped in to the device.
153     /// * `addr` - The guest address inside the BAR.
154     /// * `data` - Filled with the data from `addr`.
read_bar(&mut self, addr: u64, data: &mut [u8])155     fn read_bar(&mut self, addr: u64, data: &mut [u8]);
156     /// Writes to a BAR region mapped in to the device.
157     /// * `addr` - The guest address inside the BAR.
158     /// * `data` - The data to write.
write_bar(&mut self, addr: u64, data: &[u8])159     fn write_bar(&mut self, addr: u64, data: &[u8]);
160     /// Invoked when the device is sandboxed.
on_device_sandboxed(&mut self)161     fn on_device_sandboxed(&mut self) {}
162 
163     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
generate_acpi(&mut self, sdts: Vec<SDT>) -> Option<Vec<SDT>>164     fn generate_acpi(&mut self, sdts: Vec<SDT>) -> Option<Vec<SDT>> {
165         Some(sdts)
166     }
167 
168     /// Invoked when the device is destroyed
destroy_device(&mut self)169     fn destroy_device(&mut self) {}
170 
171     /// Get the removed children devices under pci bridge
get_removed_children_devices(&self) -> Vec<PciAddress>172     fn get_removed_children_devices(&self) -> Vec<PciAddress> {
173         Vec::new()
174     }
175 
176     /// if device is a pci brdige, configure pci bridge window
configure_bridge_window( &mut self, _resources: &mut SystemAllocator, _bar_ranges: &[BarRange], ) -> Result<()>177     fn configure_bridge_window(
178         &mut self,
179         _resources: &mut SystemAllocator,
180         _bar_ranges: &[BarRange],
181     ) -> Result<()> {
182         Ok(())
183     }
184 
185     /// Indicates whether the device supports IOMMU
supports_iommu(&self) -> bool186     fn supports_iommu(&self) -> bool {
187         false
188     }
189 
190     /// Sets the IOMMU for the device if `supports_iommu()`
set_iommu(&mut self, _iommu: IpcMemoryMapper) -> anyhow::Result<()>191     fn set_iommu(&mut self, _iommu: IpcMemoryMapper) -> anyhow::Result<()> {
192         bail!("Iommu not supported.");
193     }
194 }
195 
196 impl<T: PciDevice> BusDevice for T {
debug_label(&self) -> String197     fn debug_label(&self) -> String {
198         PciDevice::debug_label(self)
199     }
200 
device_id(&self) -> u32201     fn device_id(&self) -> u32 {
202         // Use the PCI ID for PCI devices, which contains the PCI vendor ID and the PCI device ID
203         PciDevice::read_config_register(self, PCI_ID_REG)
204     }
205 
read(&mut self, info: BusAccessInfo, data: &mut [u8])206     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
207         self.read_bar(info.address, data)
208     }
209 
write(&mut self, info: BusAccessInfo, data: &[u8])210     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
211         self.write_bar(info.address, data)
212     }
213 
config_register_write( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> ConfigWriteResult214     fn config_register_write(
215         &mut self,
216         reg_idx: usize,
217         offset: u64,
218         data: &[u8],
219     ) -> ConfigWriteResult {
220         let mut result = ConfigWriteResult {
221             ..Default::default()
222         };
223         if offset as usize + data.len() > 4 {
224             return result;
225         }
226 
227         if reg_idx == COMMAND_REG {
228             let old_command_reg = self.read_config_register(COMMAND_REG);
229             let old_ranges = self.get_ranges();
230             self.write_config_register(reg_idx, offset, data);
231             let new_command_reg = self.read_config_register(COMMAND_REG);
232             let new_ranges = self.get_ranges();
233 
234             // Inform the caller of state changes.
235             if (old_command_reg ^ new_command_reg) & COMMAND_REG_MEMORY_SPACE_MASK != 0 {
236                 // Enable memory, add new_mmio into mmio_bus
237                 if new_command_reg & COMMAND_REG_MEMORY_SPACE_MASK != 0 {
238                     for (range, bus_type) in new_ranges.iter() {
239                         if *bus_type == BusType::Mmio && range.base != 0 {
240                             result.mmio_add.push(*range);
241                         }
242                     }
243                 } else {
244                     // Disable memory, remove old_mmio from mmio_bus
245                     for (range, bus_type) in old_ranges.iter() {
246                         if *bus_type == BusType::Mmio && range.base != 0 {
247                             result.mmio_remove.push(*range);
248                         }
249                     }
250                 }
251             }
252             if (old_command_reg ^ new_command_reg) & COMMAND_REG_IO_SPACE_MASK != 0 {
253                 // Enable IO, add new_io into io_bus
254                 if new_command_reg & COMMAND_REG_IO_SPACE_MASK != 0 {
255                     for (range, bus_type) in new_ranges.iter() {
256                         if *bus_type == BusType::Io && range.base != 0 {
257                             result.io_add.push(*range);
258                         }
259                     }
260                 } else {
261                     // Disable IO, remove old_io from io_bus
262                     for (range, bus_type) in old_ranges.iter() {
263                         if *bus_type == BusType::Io && range.base != 0 {
264                             result.io_remove.push(*range);
265                         }
266                     }
267                 }
268             }
269         } else if (BAR0_REG..=BAR0_REG + 5).contains(&reg_idx) || reg_idx == ROM_BAR_REG {
270             let old_ranges = self.get_ranges();
271             self.write_config_register(reg_idx, offset, data);
272             let new_ranges = self.get_ranges();
273 
274             for ((old_range, old_type), (new_range, new_type)) in
275                 old_ranges.iter().zip(new_ranges.iter())
276             {
277                 if *old_type != *new_type {
278                     error!(
279                         "{}: bar {:x} type changed after a bar write",
280                         self.debug_label(),
281                         reg_idx
282                     );
283                     continue;
284                 }
285                 if old_range.base != new_range.base {
286                     if *new_type == BusType::Mmio {
287                         if old_range.base != 0 {
288                             result.mmio_remove.push(*old_range);
289                         }
290                         if new_range.base != 0 {
291                             result.mmio_add.push(*new_range);
292                         }
293                     } else {
294                         if old_range.base != 0 {
295                             result.io_remove.push(*old_range);
296                         }
297                         if new_range.base != 0 {
298                             result.io_add.push(*new_range);
299                         }
300                     }
301                 }
302             }
303         } else {
304             self.write_config_register(reg_idx, offset, data);
305             let children_pci_addr = self.get_removed_children_devices();
306             if !children_pci_addr.is_empty() {
307                 result.removed_pci_devices = children_pci_addr;
308             }
309         }
310 
311         result
312     }
313 
config_register_read(&self, reg_idx: usize) -> u32314     fn config_register_read(&self, reg_idx: usize) -> u32 {
315         self.read_config_register(reg_idx)
316     }
317 
virtual_config_register_write(&mut self, reg_idx: usize, value: u32)318     fn virtual_config_register_write(&mut self, reg_idx: usize, value: u32) {
319         self.write_virtual_config_register(reg_idx, value);
320     }
321 
virtual_config_register_read(&self, reg_idx: usize) -> u32322     fn virtual_config_register_read(&self, reg_idx: usize) -> u32 {
323         self.read_virtual_config_register(reg_idx)
324     }
325 
on_sandboxed(&mut self)326     fn on_sandboxed(&mut self) {
327         self.on_device_sandboxed();
328     }
329 
get_ranges(&self) -> Vec<(BusRange, BusType)>330     fn get_ranges(&self) -> Vec<(BusRange, BusType)> {
331         let mut ranges = Vec::new();
332         for bar_num in 0..NUM_BAR_REGS {
333             if let Some(bar) = self.get_bar_configuration(bar_num) {
334                 let bus_type = if bar.is_memory() {
335                     BusType::Mmio
336                 } else {
337                     BusType::Io
338                 };
339                 ranges.push((
340                     BusRange {
341                         base: bar.address(),
342                         len: bar.size(),
343                     },
344                     bus_type,
345                 ));
346             }
347         }
348         ranges
349     }
350 
351     // Invoked when the device is destroyed
destroy_device(&mut self)352     fn destroy_device(&mut self) {
353         self.destroy_device()
354     }
355 }
356 
357 impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
358     /// Returns a label suitable for debug output.
debug_label(&self) -> String359     fn debug_label(&self) -> String {
360         (**self).debug_label()
361     }
allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress>362     fn allocate_address(&mut self, resources: &mut SystemAllocator) -> Result<PciAddress> {
363         (**self).allocate_address(resources)
364     }
keep_rds(&self) -> Vec<RawDescriptor>365     fn keep_rds(&self) -> Vec<RawDescriptor> {
366         (**self).keep_rds()
367     }
assign_irq( &mut self, irq_evt: &IrqLevelEvent, irq_num: Option<u32>, ) -> Option<(u32, PciInterruptPin)>368     fn assign_irq(
369         &mut self,
370         irq_evt: &IrqLevelEvent,
371         irq_num: Option<u32>,
372     ) -> Option<(u32, PciInterruptPin)> {
373         (**self).assign_irq(irq_evt, irq_num)
374     }
allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>>375     fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
376         (**self).allocate_io_bars(resources)
377     }
allocate_device_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>>378     fn allocate_device_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<BarRange>> {
379         (**self).allocate_device_bars(resources)
380     }
get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>381     fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
382         (**self).get_bar_configuration(bar_num)
383     }
register_device_capabilities(&mut self) -> Result<()>384     fn register_device_capabilities(&mut self) -> Result<()> {
385         (**self).register_device_capabilities()
386     }
ioevents(&self) -> Vec<(&Event, u64, Datamatch)>387     fn ioevents(&self) -> Vec<(&Event, u64, Datamatch)> {
388         (**self).ioevents()
389     }
read_virtual_config_register(&self, reg_idx: usize) -> u32390     fn read_virtual_config_register(&self, reg_idx: usize) -> u32 {
391         (**self).read_virtual_config_register(reg_idx)
392     }
write_virtual_config_register(&mut self, reg_idx: usize, value: u32)393     fn write_virtual_config_register(&mut self, reg_idx: usize, value: u32) {
394         (**self).write_virtual_config_register(reg_idx, value)
395     }
read_config_register(&self, reg_idx: usize) -> u32396     fn read_config_register(&self, reg_idx: usize) -> u32 {
397         (**self).read_config_register(reg_idx)
398     }
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])399     fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
400         (**self).write_config_register(reg_idx, offset, data)
401     }
read_bar(&mut self, addr: u64, data: &mut [u8])402     fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
403         (**self).read_bar(addr, data)
404     }
write_bar(&mut self, addr: u64, data: &[u8])405     fn write_bar(&mut self, addr: u64, data: &[u8]) {
406         (**self).write_bar(addr, data)
407     }
408     /// Invoked when the device is sandboxed.
on_device_sandboxed(&mut self)409     fn on_device_sandboxed(&mut self) {
410         (**self).on_device_sandboxed()
411     }
412 
413     #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
generate_acpi(&mut self, sdts: Vec<SDT>) -> Option<Vec<SDT>>414     fn generate_acpi(&mut self, sdts: Vec<SDT>) -> Option<Vec<SDT>> {
415         (**self).generate_acpi(sdts)
416     }
417 
destroy_device(&mut self)418     fn destroy_device(&mut self) {
419         (**self).destroy_device();
420     }
get_removed_children_devices(&self) -> Vec<PciAddress>421     fn get_removed_children_devices(&self) -> Vec<PciAddress> {
422         (**self).get_removed_children_devices()
423     }
424 
configure_bridge_window( &mut self, resources: &mut SystemAllocator, bar_ranges: &[BarRange], ) -> Result<()>425     fn configure_bridge_window(
426         &mut self,
427         resources: &mut SystemAllocator,
428         bar_ranges: &[BarRange],
429     ) -> Result<()> {
430         (**self).configure_bridge_window(resources, bar_ranges)
431     }
432 }
433 
434 impl<T: 'static + PciDevice> BusDeviceObj for T {
as_pci_device(&self) -> Option<&dyn PciDevice>435     fn as_pci_device(&self) -> Option<&dyn PciDevice> {
436         Some(self)
437     }
as_pci_device_mut(&mut self) -> Option<&mut dyn PciDevice>438     fn as_pci_device_mut(&mut self) -> Option<&mut dyn PciDevice> {
439         Some(self)
440     }
into_pci_device(self: Box<Self>) -> Option<Box<dyn PciDevice>>441     fn into_pci_device(self: Box<Self>) -> Option<Box<dyn PciDevice>> {
442         Some(self)
443     }
444 }
445 
446 #[cfg(test)]
447 mod tests {
448     use super::*;
449     use pci_configuration::{
450         PciBarPrefetchable, PciBarRegionType, PciClassCode, PciConfiguration, PciHeaderType,
451         PciMultimediaSubclass,
452     };
453 
454     const BAR0_SIZE: u64 = 0x1000;
455     const BAR2_SIZE: u64 = 0x20;
456     const BAR0_ADDR: u64 = 0xc0000000;
457     const BAR2_ADDR: u64 = 0x800;
458 
459     struct TestDev {
460         pub config_regs: PciConfiguration,
461     }
462 
463     impl PciDevice for TestDev {
debug_label(&self) -> String464         fn debug_label(&self) -> String {
465             "test".to_owned()
466         }
467 
keep_rds(&self) -> Vec<RawDescriptor>468         fn keep_rds(&self) -> Vec<RawDescriptor> {
469             Vec::new()
470         }
471 
read_config_register(&self, reg_idx: usize) -> u32472         fn read_config_register(&self, reg_idx: usize) -> u32 {
473             self.config_regs.read_reg(reg_idx)
474         }
475 
write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8])476         fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
477             (&mut self.config_regs).write_reg(reg_idx, offset, data)
478         }
479 
read_bar(&mut self, _addr: u64, _data: &mut [u8])480         fn read_bar(&mut self, _addr: u64, _data: &mut [u8]) {}
481 
write_bar(&mut self, _addr: u64, _data: &[u8])482         fn write_bar(&mut self, _addr: u64, _data: &[u8]) {}
483 
allocate_address(&mut self, _resources: &mut SystemAllocator) -> Result<PciAddress>484         fn allocate_address(&mut self, _resources: &mut SystemAllocator) -> Result<PciAddress> {
485             Err(Error::PciAllocationFailed)
486         }
487 
get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration>488         fn get_bar_configuration(&self, bar_num: usize) -> Option<PciBarConfiguration> {
489             self.config_regs.get_bar_configuration(bar_num)
490         }
491     }
492 
493     #[test]
config_write_result()494     fn config_write_result() {
495         let mut test_dev = TestDev {
496             config_regs: PciConfiguration::new(
497                 0x1234,
498                 0xABCD,
499                 PciClassCode::MultimediaController,
500                 &PciMultimediaSubclass::AudioDevice,
501                 None,
502                 PciHeaderType::Device,
503                 0x5678,
504                 0xEF01,
505                 0,
506             ),
507         };
508 
509         let _ = test_dev.config_regs.add_pci_bar(
510             PciBarConfiguration::new(
511                 0,
512                 BAR0_SIZE,
513                 PciBarRegionType::Memory64BitRegion,
514                 PciBarPrefetchable::Prefetchable,
515             )
516             .set_address(BAR0_ADDR),
517         );
518         let _ = test_dev.config_regs.add_pci_bar(
519             PciBarConfiguration::new(
520                 2,
521                 BAR2_SIZE,
522                 PciBarRegionType::IoRegion,
523                 PciBarPrefetchable::NotPrefetchable,
524             )
525             .set_address(BAR2_ADDR),
526         );
527         let bar0_range = BusRange {
528             base: BAR0_ADDR,
529             len: BAR0_SIZE,
530         };
531         let bar2_range = BusRange {
532             base: BAR2_ADDR,
533             len: BAR2_SIZE,
534         };
535 
536         // Initialize command register to an all-zeroes value.
537         test_dev.config_register_write(COMMAND_REG, 0, &0u32.to_le_bytes());
538 
539         // Enable IO space access (bit 0 of command register).
540         assert_eq!(
541             test_dev.config_register_write(COMMAND_REG, 0, &1u32.to_le_bytes()),
542             ConfigWriteResult {
543                 mmio_remove: Vec::new(),
544                 mmio_add: Vec::new(),
545                 io_remove: Vec::new(),
546                 io_add: vec![bar2_range],
547                 removed_pci_devices: Vec::new(),
548             }
549         );
550 
551         // Enable memory space access (bit 1 of command register).
552         assert_eq!(
553             test_dev.config_register_write(COMMAND_REG, 0, &3u32.to_le_bytes()),
554             ConfigWriteResult {
555                 mmio_remove: Vec::new(),
556                 mmio_add: vec![bar0_range],
557                 io_remove: Vec::new(),
558                 io_add: Vec::new(),
559                 removed_pci_devices: Vec::new(),
560             }
561         );
562 
563         // Rewrite the same IO + mem value again (result should be no change).
564         assert_eq!(
565             test_dev.config_register_write(COMMAND_REG, 0, &3u32.to_le_bytes()),
566             ConfigWriteResult {
567                 mmio_remove: Vec::new(),
568                 mmio_add: Vec::new(),
569                 io_remove: Vec::new(),
570                 io_add: Vec::new(),
571                 removed_pci_devices: Vec::new(),
572             }
573         );
574 
575         // Disable IO space access, leaving mem enabled.
576         assert_eq!(
577             test_dev.config_register_write(COMMAND_REG, 0, &2u32.to_le_bytes()),
578             ConfigWriteResult {
579                 mmio_remove: Vec::new(),
580                 mmio_add: Vec::new(),
581                 io_remove: vec![bar2_range],
582                 io_add: Vec::new(),
583                 removed_pci_devices: Vec::new(),
584             }
585         );
586 
587         // Disable mem space access.
588         assert_eq!(
589             test_dev.config_register_write(COMMAND_REG, 0, &0u32.to_le_bytes()),
590             ConfigWriteResult {
591                 mmio_remove: vec![bar0_range],
592                 mmio_add: Vec::new(),
593                 io_remove: Vec::new(),
594                 io_add: Vec::new(),
595                 removed_pci_devices: Vec::new(),
596             }
597         );
598 
599         assert_eq!(test_dev.get_ranges(), Vec::new());
600 
601         // Re-enable mem and IO space.
602         assert_eq!(
603             test_dev.config_register_write(COMMAND_REG, 0, &3u32.to_le_bytes()),
604             ConfigWriteResult {
605                 mmio_remove: Vec::new(),
606                 mmio_add: vec![bar0_range],
607                 io_remove: Vec::new(),
608                 io_add: vec![bar2_range],
609                 removed_pci_devices: Vec::new(),
610             }
611         );
612 
613         // Change Bar0's address
614         assert_eq!(
615             test_dev.config_register_write(BAR0_REG, 0, &0xD0000000u32.to_le_bytes()),
616             ConfigWriteResult {
617                 mmio_remove: vec!(bar0_range),
618                 mmio_add: vec![BusRange {
619                     base: 0xD0000000,
620                     len: BAR0_SIZE
621                 }],
622                 io_remove: Vec::new(),
623                 io_add: Vec::new(),
624                 removed_pci_devices: Vec::new(),
625             }
626         );
627     }
628 }
629