devices: implement msix capability structure

The MSI-X feature is ported from Cloud-hypervisor commit 69e27288a2e.
(https://github.com/intel/cloud-hypervisor.git)

In this commit:

- add a new "msix" module to the pci crate.
- implement the MSI-X Capability Structure.
- implement per virtio device msix_vectors() function which represents the
  supported MSI-X vector for this device.

BUG=chromium:854765
TEST=launch Crosvm on eve and Linux
TEST=cargo test -p devices
TEST=./bin/clippy
TEST=./build_test.py --x86_64-sysroot /build/eve

Change-Id: I5498b15a3bf115e34764e6610407b3ba204dae7f
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.corp-partner.google.com>
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.corp-partner.google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1873356
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Stephen Barber <smbarber@chromium.org>
diff --git a/devices/src/pci/msix.rs b/devices/src/pci/msix.rs
new file mode 100644
index 0000000..55e71ba
--- /dev/null
+++ b/devices/src/pci/msix.rs
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::pci::{PciCapability, PciCapabilityID};
+
+use data_model::DataInit;
+
+const MAX_MSIX_VECTORS_PER_DEVICE: u16 = 2048;
+
+// It is safe to implement DataInit; all members are simple numbers and any value is valid.
+unsafe impl DataInit for MsixCap {}
+
+#[allow(dead_code)]
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+/// MSI-X Capability Structure
+pub struct MsixCap {
+    // To make add_capability() happy
+    _cap_vndr: u8,
+    _cap_next: u8,
+    // Message Control Register
+    //   10-0:  MSI-X Table size
+    //   13-11: Reserved
+    //   14:    Mask. Mask all MSI-X when set.
+    //   15:    Enable. Enable all MSI-X when set.
+    msg_ctl: u16,
+    // Table. Contains the offset and the BAR indicator (BIR)
+    //   2-0:  Table BAR indicator (BIR). Can be 0 to 5.
+    //   31-3: Table offset in the BAR pointed by the BIR.
+    table: u32,
+    // Pending Bit Array. Contains the offset and the BAR indicator (BIR)
+    //   2-0:  PBA BAR indicator (BIR). Can be 0 to 5.
+    //   31-3: PBA offset in the BAR pointed by the BIR.
+    pba: u32,
+}
+
+impl PciCapability for MsixCap {
+    fn bytes(&self) -> &[u8] {
+        self.as_slice()
+    }
+
+    fn id(&self) -> PciCapabilityID {
+        PciCapabilityID::MSIX
+    }
+}
+
+impl MsixCap {
+    pub fn new(
+        table_pci_bar: u8,
+        table_size: u16,
+        table_off: u32,
+        pba_pci_bar: u8,
+        pba_off: u32,
+    ) -> Self {
+        assert!(table_size < MAX_MSIX_VECTORS_PER_DEVICE);
+
+        // Set the table size and enable MSI-X.
+        let msg_ctl: u16 = 0x8000u16 + table_size - 1;
+
+        MsixCap {
+            _cap_vndr: 0,
+            _cap_next: 0,
+            msg_ctl,
+            table: (table_off & 0xffff_fff8u32) | u32::from(table_pci_bar & 0x7u8),
+            pba: (pba_off & 0xffff_fff8u32) | u32::from(pba_pci_bar & 0x7u8),
+        }
+    }
+}