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(®_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