devices: use PCI bar configuration when adding regions

This removes add_memory_region and add_io_region, and replaces
it with the add_pci_bar function.

BUG=chromium:924405
TEST=boot VM

Change-Id: Ifc637d174d3f8b1255cf13725a1a224b4cdf0a30
Reviewed-on: https://chromium-review.googlesource.com/1480741
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Tested-by: Gurchetan Singh <gurchetansingh@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
diff --git a/devices/src/pci/ac97.rs b/devices/src/pci/ac97.rs
index c8eb8ca..68dfe3c 100644
--- a/devices/src/pci/ac97.rs
+++ b/devices/src/pci/ac97.rs
@@ -9,7 +9,7 @@
 use pci::ac97_mixer::Ac97Mixer;
 use pci::ac97_regs::*;
 use pci::pci_configuration::{
-    PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass,
+    PciBarConfiguration, PciClassCode, PciConfiguration, PciHeaderType, PciMultimediaSubclass,
 };
 use pci::pci_device::{self, PciDevice, Result};
 use pci::PciInterruptPin;
@@ -141,15 +141,24 @@
         let mixer_regs_addr = resources
             .allocate_mmio_addresses(MIXER_REGS_SIZE)
             .ok_or(pci_device::Error::IoAllocationFailed(MIXER_REGS_SIZE))?;
+        let config: PciBarConfiguration = PciBarConfiguration::default()
+            .set_register_index(0)
+            .set_address(mixer_regs_addr)
+            .set_size(MIXER_REGS_SIZE);
         self.config_regs
-            .add_memory_region(mixer_regs_addr, MIXER_REGS_SIZE)
+            .add_pci_bar(&config)
             .ok_or_else(|| pci_device::Error::IoRegistrationFailed(mixer_regs_addr))?;
         ranges.push((mixer_regs_addr, MIXER_REGS_SIZE));
+
         let master_regs_addr = resources
             .allocate_mmio_addresses(MASTER_REGS_SIZE)
             .ok_or_else(|| pci_device::Error::IoAllocationFailed(MASTER_REGS_SIZE))?;
+        config
+            .set_register_index(1)
+            .set_address(master_regs_addr)
+            .set_size(MASTER_REGS_SIZE);
         self.config_regs
-            .add_memory_region(master_regs_addr, MASTER_REGS_SIZE)
+            .add_pci_bar(&config)
             .ok_or_else(|| pci_device::Error::IoRegistrationFailed(master_regs_addr))?;
         ranges.push((master_regs_addr, MASTER_REGS_SIZE));
         Ok(ranges)
diff --git a/devices/src/pci/pci_configuration.rs b/devices/src/pci/pci_configuration.rs
index b2bf90d..d93ae03 100644
--- a/devices/src/pci/pci_configuration.rs
+++ b/devices/src/pci/pci_configuration.rs
@@ -11,7 +11,6 @@
 const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000;
 const BAR0_REG: usize = 4;
 const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc;
-const BAR_IO_BIT: u32 = 0x0000_0001;
 const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0;
 const NUM_BAR_REGS: usize = 6;
 const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34;
@@ -167,13 +166,12 @@
 pub struct PciConfiguration {
     registers: [u32; NUM_CONFIGURATION_REGISTERS],
     writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
-    num_bars: usize,
+    bar_used: [bool; NUM_BAR_REGS],
     // Contains the byte offset and size of the last capability.
     last_capability: Option<(usize, usize)>,
 }
 
 /// See pci_regs.h in kernel
-#[allow(dead_code)]
 #[derive(Copy, Clone)]
 pub enum PciBarRegionType {
     Memory32BitRegion = 0,
@@ -181,14 +179,12 @@
     Memory64BitRegion = 0x04,
 }
 
-#[allow(dead_code)]
 #[derive(Copy, Clone)]
 pub enum PciBarPrefetchable {
     NotPrefetchable = 0,
     Prefetchable = 0x08,
 }
 
-#[allow(dead_code)]
 #[derive(Copy, Clone)]
 pub struct PciBarConfiguration {
     addr: u64,
@@ -239,7 +235,7 @@
         PciConfiguration {
             registers,
             writable_bits,
-            num_bars: 0,
+            bar_used: [false; NUM_BAR_REGS],
             last_capability: None,
         }
     }
@@ -304,44 +300,61 @@
         }
     }
 
-    // Add either an IO or memory region, depending on `mem_type` mask, which is ORed in to the
-    // value before saving it.
-    fn add_bar(&mut self, addr: u64, size: u64, addr_mask: u32, mem_type: u32) -> Option<usize> {
-        if self.num_bars >= NUM_BAR_REGS {
-            return None;
-        }
-        if size.count_ones() != 1 {
+    /// Adds a region specified by `config`.  Configures the specified BAR(s) to
+    /// report this region and size to the guest kernel.  Enforces a few constraints
+    /// (i.e, region size must be power of two, register not already used). Returns 'None' on
+    /// failure all, `Some(BarIndex)` on success.
+    pub fn add_pci_bar(&mut self, config: &PciBarConfiguration) -> Option<usize> {
+        if self.bar_used[config.reg_idx] {
             return None;
         }
 
-        // TODO(dgreid) Allow 64 bit address and size.
-        if addr.checked_add(size)? > u64::from(u32::max_value()) {
+        if config.size.count_ones() != 1 {
             return None;
         }
 
-        let this_bar = self.num_bars;
-        let bar_idx = BAR0_REG + this_bar;
+        if config.reg_idx >= NUM_BAR_REGS {
+            return None;
+        }
 
-        self.registers[bar_idx] = addr as u32 & addr_mask | mem_type;
-        // The first writable bit represents the size of the region.
-        self.writable_bits[bar_idx] = !(size - 1) as u32;
+        let bar_idx = BAR0_REG + config.reg_idx;
+        match config.region_type {
+            PciBarRegionType::Memory32BitRegion | PciBarRegionType::IORegion => {
+                if config.addr.checked_add(config.size)? > u64::from(u32::max_value()) {
+                    return None;
+                }
+            }
+            PciBarRegionType::Memory64BitRegion => {
+                if config.reg_idx + 1 >= NUM_BAR_REGS {
+                    return None;
+                }
 
-        self.num_bars += 1;
-        Some(this_bar)
-    }
+                if config.addr.checked_add(config.size)? > u64::max_value() {
+                    return None;
+                }
 
-    /// Adds a memory region of `size` at `addr`. Configures the next available BAR register to
-    /// report this region and size to the guest kernel. Returns 'None' if all BARs are full, or
-    /// `Some(BarIndex)` on success. `size` must be a power of 2.
-    pub fn add_memory_region(&mut self, addr: u64, size: u64) -> Option<usize> {
-        self.add_bar(addr, size, BAR_MEM_ADDR_MASK, 0)
-    }
+                if self.bar_used[config.reg_idx + 1] {
+                    return None;
+                }
 
-    /// Adds an IO region of `size` at `addr`. Configures the next available BAR register to
-    /// report this region and size to the guest kernel. Returns 'None' if all BARs are full, or
-    /// `Some(BarIndex)` on success. `size` must be a power of 2.
-    pub fn add_io_region(&mut self, addr: u64, size: u64) -> Option<usize> {
-        self.add_bar(addr, size, BAR_IO_ADDR_MASK, BAR_IO_BIT)
+                self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
+                self.writable_bits[bar_idx + 1] = !((config.size >> 32) - 1) as u32;
+                self.bar_used[config.reg_idx + 1] = true;
+            }
+        }
+
+        let (mask, lower_bits) = match config.region_type {
+            PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => (
+                BAR_MEM_ADDR_MASK,
+                config.prefetchable as u32 | config.region_type as u32,
+            ),
+            PciBarRegionType::IORegion => (BAR_IO_ADDR_MASK, config.region_type as u32),
+        };
+
+        self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
+        self.writable_bits[bar_idx] = !(config.size - 1) as u32;
+        self.bar_used[config.reg_idx] = true;
+        Some(config.reg_idx)
     }
 
     /// Returns the address of the given BAR region.
@@ -407,7 +420,6 @@
     }
 }
 
-#[allow(dead_code)]
 impl PciBarConfiguration {
     pub fn new(
         reg_idx: usize,
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index f9cb948..bdd10c2 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -11,8 +11,8 @@
 use data_model::{DataInit, Le32};
 use kvm::Datamatch;
 use pci::{
-    PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciDevice, PciDeviceError,
-    PciHeaderType, PciInterruptPin, PciSubclass,
+    PciBarConfiguration, PciCapability, PciCapabilityID, PciClassCode, PciConfiguration, PciDevice,
+    PciDeviceError, PciHeaderType, PciInterruptPin, PciSubclass,
 };
 use resources::SystemAllocator;
 use sys_util::{EventFd, GuestMemory, Result};
@@ -312,9 +312,13 @@
         let settings_config_addr = resources
             .allocate_mmio_addresses(CAPABILITY_BAR_SIZE)
             .ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?;
+        let config = PciBarConfiguration::default()
+            .set_register_index(0)
+            .set_address(settings_config_addr)
+            .set_size(CAPABILITY_BAR_SIZE);
         let settings_bar = self
             .config_regs
-            .add_memory_region(settings_config_addr, CAPABILITY_BAR_SIZE)
+            .add_pci_bar(&config)
             .ok_or(PciDeviceError::IoRegistrationFailed(settings_config_addr))?
             as u8;
         ranges.push((settings_config_addr, CAPABILITY_BAR_SIZE));