dev: Add support for MSI-X and Capability Lists for ARM and PCI devices
authorGeoffrey Blake <Geoffrey.Blake@arm.com>
Thu, 31 Oct 2013 18:41:13 +0000 (13:41 -0500)
committerGeoffrey Blake <Geoffrey.Blake@arm.com>
Thu, 31 Oct 2013 18:41:13 +0000 (13:41 -0500)
This patch adds the registers and fields to the PCI device to support
Capability lists and to support MSI-X in the GIC.

src/dev/Pci.py
src/dev/arm/Gic.py
src/dev/arm/gic_pl390.cc
src/dev/arm/gic_pl390.hh
src/dev/pcidev.cc
src/dev/pcidev.hh
src/dev/pcireg.h

index fa3fc854e0e98375ddbc337c484d31ad5b69003a..79ea1f68ae59c2717c15baff1efff295a9fccc74 100644 (file)
@@ -1,3 +1,15 @@
+# Copyright (c) 2013 ARM Limited
+#  All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
 # Copyright (c) 2005-2007 The Regents of The University of Michigan
 # All rights reserved.
 #
@@ -91,9 +103,60 @@ class PciDevice(DmaDevice):
     SubsystemID = Param.UInt16(0x00, "Subsystem ID")
     SubsystemVendorID = Param.UInt16(0x00, "Subsystem Vendor ID")
     ExpansionROM = Param.UInt32(0x00, "Expansion ROM Base Address")
+    CapabilityPtr = Param.UInt8(0x00, "Capability List Pointer offset")
     InterruptLine = Param.UInt8(0x00, "Interrupt Line")
     InterruptPin = Param.UInt8(0x00, "Interrupt Pin")
     MaximumLatency = Param.UInt8(0x00, "Maximum Latency")
     MinimumGrant = Param.UInt8(0x00, "Minimum Grant")
 
+    # Capabilities List structures for PCIe devices
+    # PMCAP - PCI Power Management Capability
+    PMCAPBaseOffset = \
+        Param.UInt8(0x00, "Base offset of PMCAP in PCI Config space")
+    PMCAPNextCapability = \
+        Param.UInt8(0x00, "Pointer to next capability block")
+    PMCAPCapId = \
+        Param.UInt8(0x00, "Specifies this is the Power Management capability")
+    PMCAPCapabilities = \
+        Param.UInt16(0x0000, "PCI Power Management Capabilities Register")
+    PMCAPCtrlStatus = \
+        Param.UInt16(0x0000, "PCI Power Management Control and Status")
+
+    # MSICAP - Message Signaled Interrupt Capability
+    MSICAPBaseOffset = \
+        Param.UInt8(0x00, "Base offset of MSICAP in PCI Config space")
+    MSICAPNextCapability = \
+        Param.UInt8(0x00, "Pointer to next capability block")
+    MSICAPCapId = Param.UInt8(0x00, "Specifies this is the MSI Capability")
+    MSICAPMsgCtrl = Param.UInt16(0x0000, "MSI Message Control")
+    MSICAPMsgAddr = Param.UInt32(0x00000000, "MSI Message Address")
+    MSICAPMsgUpperAddr = Param.UInt32(0x00000000, "MSI Message Upper Address")
+    MSICAPMsgData = Param.UInt16(0x0000, "MSI Message Data")
+    MSICAPMaskBits = Param.UInt32(0x00000000, "MSI Interrupt Mask Bits")
+    MSICAPPendingBits = Param.UInt32(0x00000000, "MSI Pending Bits")
+
+    # MSIXCAP - MSI-X Capability
+    MSIXCAPBaseOffset = \
+        Param.UInt8(0x00, "Base offset of MSIXCAP in PCI Config space")
+    MSIXCAPNextCapability = \
+        Param.UInt8(0x00, "Pointer to next capability block")
+    MSIXCAPCapId = Param.UInt8(0x00, "Specifices this the MSI-X Capability")
+    MSIXMsgCtrl = Param.UInt16(0x0000, "MSI-X Message Control")
+    MSIXTableOffset = \
+        Param.UInt32(0x00000000, "MSI-X Table Offset and Table BIR")
+    MSIXPbaOffset = Param.UInt32(0x00000000, "MSI-X PBA Offset and PBA BIR")
 
+    # PXCAP - PCI Express Capability
+    PXCAPBaseOffset = \
+        Param.UInt8(0x00, "Base offset of PXCAP in PCI Config space")
+    PXCAPNextCapability = Param.UInt8(0x00, "Pointer to next capability block")
+    PXCAPCapId = Param.UInt8(0x00, "Specifies this is the PCIe Capability")
+    PXCAPCapabilities = Param.UInt16(0x0000, "PCIe Capabilities")
+    PXCAPDevCapabilities = Param.UInt32(0x00000000, "PCIe Device Capabilities")
+    PXCAPDevCtrl = Param.UInt16(0x0000, "PCIe Device Control")
+    PXCAPDevStatus = Param.UInt16(0x0000, "PCIe Device Status")
+    PXCAPLinkCap = Param.UInt32(0x00000000, "PCIe Link Capabilities")
+    PXCAPLinkCtrl = Param.UInt16(0x0000, "PCIe Link Control")
+    PXCAPLinkStatus = Param.UInt16(0x0000, "PCIe Link Status")
+    PXCAPDevCap2 = Param.UInt32(0x00000000, "PCIe Device Capabilities 2")
+    PXCAPDevCtrl2 = Param.UInt32(0x00000000, "PCIe Device Control 2")
index 0461758ed7183d48487f7cdfe54692e1707110e8..7660492961e40ff82cbb46b1829643e1e1ada9e5 100644 (file)
@@ -54,8 +54,8 @@ class Pl390(BaseGic):
 
     dist_addr = Param.Addr(0x1f001000, "Address for distributor")
     cpu_addr = Param.Addr(0x1f000100, "Address for cpu")
+    msix_addr = Param.Addr(0x0, "Address for MSI-X register")
     dist_pio_delay = Param.Latency('10ns', "Delay for PIO r/w to distributor")
     cpu_pio_delay = Param.Latency('10ns', "Delay for PIO r/w to cpu interface")
     int_latency = Param.Latency('10ns', "Delay for interrupt to get to CPU")
     it_lines = Param.UInt32(128, "Number of interrupt lines supported (max = 1020)")
-
index fc49aa63e5bfe6eeb2022849569752cbf4054a81..d2a660e88dce22fd83ee3b3f2aad1dea67849f46 100644 (file)
@@ -56,7 +56,8 @@ Pl390::Pl390(const Params *p)
     : BaseGic(p), distAddr(p->dist_addr),
       cpuAddr(p->cpu_addr), distPioDelay(p->dist_pio_delay),
       cpuPioDelay(p->cpu_pio_delay), intLatency(p->int_latency),
-      enabled(false), itLines(p->it_lines)
+      enabled(false), itLines(p->it_lines), msixRegAddr(p->msix_addr),
+      msixReg(0x0)
 {
     itLinesLog2 = ceilLog2(itLines);
 
@@ -117,6 +118,10 @@ Pl390::read(PacketPtr pkt)
         return readDistributor(pkt);
     else if (addr >= cpuAddr && addr < cpuAddr + CPU_SIZE)
         return readCpu(pkt);
+    else if (msixRegAddr != 0x0 &&
+             addr >= msixRegAddr &&
+             addr < msixRegAddr + MSIX_SIZE)
+        return readMsix(pkt);
     else
         panic("Read to unknown address %#x\n", pkt->getAddr());
 }
@@ -132,6 +137,10 @@ Pl390::write(PacketPtr pkt)
         return writeDistributor(pkt);
     else if (addr >= cpuAddr && addr < cpuAddr + CPU_SIZE)
         return writeCpu(pkt);
+    else if (msixRegAddr != 0x0 &&
+             addr >= msixRegAddr &&
+             addr < msixRegAddr + MSIX_SIZE)
+        return writeMsix(pkt);
     else
         panic("Write to unknown address %#x\n", pkt->getAddr());
 }
@@ -355,6 +364,26 @@ Pl390::readCpu(PacketPtr pkt)
     return cpuPioDelay;
 }
 
+Tick
+Pl390::readMsix(PacketPtr pkt)
+{
+    Addr daddr = pkt->getAddr() - msixRegAddr;
+    pkt->allocate();
+
+    DPRINTF(GIC, "Gic MSIX read register %#x\n", daddr);
+
+    switch (daddr) {
+        case MSIX_SR:
+            pkt->set<uint32_t>(msixReg);
+            break;
+        default:
+            panic("Tried to read Gic MSIX register at offset %#x\n", daddr);
+            break;
+    }
+
+    pkt->makeAtomicResponse();
+    return distPioDelay;
+}
 
 Tick
 Pl390::writeDistributor(PacketPtr pkt)
@@ -535,6 +564,31 @@ Pl390::writeCpu(PacketPtr pkt)
     return cpuPioDelay;
 }
 
+Tick
+Pl390::writeMsix(PacketPtr pkt)
+{
+    Addr daddr = pkt->getAddr() - msixRegAddr;
+    pkt->allocate();
+
+    DPRINTF(GIC, "Gic MSI-X write register %#x data %d\n",
+                 daddr, pkt->get<uint32_t>());
+
+    switch (daddr) {
+        case MSIX_SR:
+            // This value is little endian, just like the ARM guest
+            msixReg = pkt->get<uint32_t>();
+            pendingInt[intNumToWord(letoh(msixReg))] |= 1UL << intNumToBit(letoh(msixReg));
+            updateIntState(-1);
+            break;
+        default:
+            panic("Tried to write Gic MSI-X register at offset %#x\n", daddr);
+            break;
+    }
+
+    pkt->makeAtomicResponse();
+    return distPioDelay;
+}
+
 void
 Pl390::softInt(int ctx_id, SWI swi)
 {
@@ -726,6 +780,9 @@ Pl390::getAddrRanges() const
     AddrRangeList ranges;
     ranges.push_back(RangeSize(distAddr, DIST_SIZE));
     ranges.push_back(RangeSize(cpuAddr, CPU_SIZE));
+    if (msixRegAddr != 0) {
+        ranges.push_back(RangeSize(msixRegAddr, MSIX_SIZE));
+    }
     return ranges;
 }
 
@@ -742,6 +799,8 @@ Pl390::serialize(std::ostream &os)
     SERIALIZE_SCALAR(enabled);
     SERIALIZE_SCALAR(itLines);
     SERIALIZE_SCALAR(itLinesLog2);
+    SERIALIZE_SCALAR(msixRegAddr);
+    SERIALIZE_SCALAR(msixReg);
     SERIALIZE_ARRAY(intEnabled, INT_BITS_MAX);
     SERIALIZE_ARRAY(pendingInt, INT_BITS_MAX);
     SERIALIZE_ARRAY(activeInt, INT_BITS_MAX);
@@ -782,6 +841,8 @@ Pl390::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_SCALAR(enabled);
     UNSERIALIZE_SCALAR(itLines);
     UNSERIALIZE_SCALAR(itLinesLog2);
+    UNSERIALIZE_SCALAR(msixRegAddr);
+    UNSERIALIZE_SCALAR(msixReg);
     UNSERIALIZE_ARRAY(intEnabled, INT_BITS_MAX);
     UNSERIALIZE_ARRAY(pendingInt, INT_BITS_MAX);
     UNSERIALIZE_ARRAY(activeInt, INT_BITS_MAX);
index 2621e1a2790a19c199c0a4f4b5068d97e50175c8..5b84ea92bdf2d8644c877882694d1f1e98a0d4b8 100644 (file)
@@ -113,6 +113,10 @@ class Pl390 : public BaseGic
     static const int INT_BITS_MAX = 32;
     static const int INT_LINES_MAX = 1020;
 
+    /** MSI-X register offset and size */
+    static const int MSIX_SR = 0x0; // MSI register devices will write to
+    static const int MSIX_SIZE = 0x4; // Size of MSI-X register space
+
     BitUnion32(SWI)
         Bitfield<3,0> sgi_id;
         Bitfield<23,16> cpu_list;
@@ -207,6 +211,10 @@ class Pl390 : public BaseGic
     /** IRQ Enable Used for debug */
     bool irqEnable;
 
+    /** MSIX Register */
+    Addr msixRegAddr;
+    uint32_t msixReg;
+
     /** software generated interrupt
      * @param data data to decode that indicates which cpus to interrupt
      */
@@ -314,6 +322,11 @@ class Pl390 : public BaseGic
      */
     Tick readCpu(PacketPtr pkt);
 
+    /** Handle a read to the MSI-X register on the GIC
+     *  @param pkt packet to respond to
+     */
+    Tick readMsix(PacketPtr pkt);
+
     /** Handle a write to the distributor poriton of the GIC
      * @param pkt packet to respond to
      */
@@ -323,6 +336,11 @@ class Pl390 : public BaseGic
      * @param pkt packet to respond to
      */
     Tick writeCpu(PacketPtr pkt);
+
+    /** Handle a write to the MSI-X register on the GIC
+     *  @param pkt packet to process
+     */
+    Tick writeMsix(PacketPtr pkt);
 };
 
 #endif //__DEV_ARM_GIC_H__
index 3ca807d06672a93194d7489de66e1a71114da932..a24fa8da8f69d0c6504b96d2bbddbc24140e6f7f 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2004-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -83,7 +95,13 @@ PciDevice::PciConfigPort::getAddrRanges() const
 
 
 PciDevice::PciDevice(const Params *p)
-    : DmaDevice(p), platform(p->platform), pioDelay(p->pio_latency),
+    : DmaDevice(p),
+      PMCAP_BASE(p->PMCAPBaseOffset),
+      MSICAP_BASE(p->MSICAPBaseOffset),
+      MSIXCAP_BASE(p->MSIXCAPBaseOffset),
+      PXCAP_BASE(p->PXCAPBaseOffset),
+      platform(p->platform),
+      pioDelay(p->pio_latency),
       configDelay(p->config_latency),
       configPort(this, params()->pci_bus, params()->pci_dev,
                  params()->pci_func, params()->platform)
@@ -111,13 +129,74 @@ PciDevice::PciDevice(const Params *p)
     config.subsystemVendorID = htole(p->SubsystemVendorID);
     config.subsystemID = htole(p->SubsystemID);
     config.expansionROM = htole(p->ExpansionROM);
-    config.reserved0 = 0;
-    config.reserved1 = 0;
+    config.capabilityPtr = htole(p->CapabilityPtr);
+    // Zero out the 7 bytes of reserved space in the PCI Config space register.
+    bzero(config.reserved, 7*sizeof(uint8_t));
     config.interruptLine = htole(p->InterruptLine);
     config.interruptPin = htole(p->InterruptPin);
     config.minimumGrant = htole(p->MinimumGrant);
     config.maximumLatency = htole(p->MaximumLatency);
 
+    // Initialize the capability lists
+    // These structs are bitunions, meaning the data is stored in host
+    // endianess and must be converted to Little Endian when accessed
+    // by the guest
+    // PMCAP
+    pmcap.pid.cid = p->PMCAPCapId;
+    pmcap.pid.next = p->PMCAPNextCapability;
+    pmcap.pc = p->PMCAPCapabilities;
+    pmcap.pmcs = p->PMCAPCtrlStatus;
+
+    // MSICAP
+    msicap.mid.cid = p->MSICAPCapId;
+    msicap.mid.next = p->MSICAPNextCapability;
+    msicap.mc = p->MSICAPMsgCtrl;
+    msicap.ma = p->MSICAPMsgAddr;
+    msicap.mua = p->MSICAPMsgUpperAddr;
+    msicap.md = p->MSICAPMsgData;
+    msicap.mmask = p->MSICAPMaskBits;
+    msicap.mpend = p->MSICAPPendingBits;
+
+    // MSIXCAP
+    msixcap.mxid.cid = p->MSIXCAPCapId;
+    msixcap.mxid.next = p->MSIXCAPNextCapability;
+    msixcap.mxc = p->MSIXMsgCtrl;
+    msixcap.mtab = p->MSIXTableOffset;
+    msixcap.mpba = p->MSIXPbaOffset;
+
+    // allocate MSIX structures if MSIXCAP_BASE
+    // indicates the MSIXCAP is being used by having a
+    // non-zero base address.
+    // The MSIX tables are stored by the guest in
+    // little endian byte-order as according the
+    // PCIe specification.  Make sure to take the proper
+    // actions when manipulating these tables on the host
+    if (MSIXCAP_BASE != 0x0) {
+        int msix_vecs = msixcap.mxc.ts + 1;
+        MSIXTable tmp1 = {{0UL,0UL,0UL,0UL}};
+        msix_table.resize(msix_vecs, tmp1);
+
+        MSIXPbaEntry tmp2 = {0};
+        int pba_size = msix_vecs / MSIXVECS_PER_PBA;
+        if ((msix_vecs % MSIXVECS_PER_PBA) > 0) {
+            pba_size++;
+        }
+        msix_pba.resize(pba_size, tmp2);
+    }
+
+    // PXCAP
+    pxcap.pxid.cid = p->PXCAPCapId;
+    pxcap.pxid.next = p->PXCAPNextCapability;
+    pxcap.pxcap = p->PXCAPCapabilities;
+    pxcap.pxdcap = p->PXCAPDevCapabilities;
+    pxcap.pxdc = p->PXCAPDevCtrl;
+    pxcap.pxds = p->PXCAPDevStatus;
+    pxcap.pxlcap = p->PXCAPLinkCap;
+    pxcap.pxlc = p->PXCAPLinkCtrl;
+    pxcap.pxls = p->PXCAPLinkStatus;
+    pxcap.pxdcap2 = p->PXCAPDevCap2;
+    pxcap.pxdc2 = p->PXCAPDevCtrl2;
+
     BARSize[0] = p->BAR0Size;
     BARSize[1] = p->BAR1Size;
     BARSize[2] = p->BAR2Size;
@@ -348,6 +427,62 @@ PciDevice::serialize(std::ostream &os)
     SERIALIZE_ARRAY(BARSize, sizeof(BARSize) / sizeof(BARSize[0]));
     SERIALIZE_ARRAY(BARAddrs, sizeof(BARAddrs) / sizeof(BARAddrs[0]));
     SERIALIZE_ARRAY(config.data, sizeof(config.data) / sizeof(config.data[0]));
+
+    // serialize the capability list registers
+    paramOut(os, csprintf("pmcap.pid"), uint16_t(pmcap.pid));
+    paramOut(os, csprintf("pmcap.pc"), uint16_t(pmcap.pc));
+    paramOut(os, csprintf("pmcap.pmcs"), uint16_t(pmcap.pmcs));
+
+    paramOut(os, csprintf("msicap.mid"), uint16_t(msicap.mid));
+    paramOut(os, csprintf("msicap.mc"), uint16_t(msicap.mc));
+    paramOut(os, csprintf("msicap.ma"), uint32_t(msicap.ma));
+    SERIALIZE_SCALAR(msicap.mua);
+    paramOut(os, csprintf("msicap.md"), uint16_t(msicap.md));
+    SERIALIZE_SCALAR(msicap.mmask);
+    SERIALIZE_SCALAR(msicap.mpend);
+
+    paramOut(os, csprintf("msixcap.mxid"), uint16_t(msixcap.mxid));
+    paramOut(os, csprintf("msixcap.mxc"), uint16_t(msixcap.mxc));
+    paramOut(os, csprintf("msixcap.mtab"), uint32_t(msixcap.mtab));
+    paramOut(os, csprintf("msixcap.mpba"), uint32_t(msixcap.mpba));
+
+    // Only serialize if we have a non-zero base address
+    if (MSIXCAP_BASE != 0x0) {
+        int msix_array_size = msixcap.mxc.ts + 1;
+        int pba_array_size = msix_array_size/MSIXVECS_PER_PBA;
+        if ((msix_array_size % MSIXVECS_PER_PBA) > 0) {
+            pba_array_size++;
+        }
+
+        SERIALIZE_SCALAR(msix_array_size);
+        SERIALIZE_SCALAR(pba_array_size);
+
+        for (int i = 0; i < msix_array_size; i++) {
+            paramOut(os, csprintf("msix_table[%d].addr_lo", i),
+                     msix_table[i].fields.addr_lo);
+            paramOut(os, csprintf("msix_table[%d].addr_hi", i),
+                     msix_table[i].fields.addr_hi);
+            paramOut(os, csprintf("msix_table[%d].msg_data", i),
+                     msix_table[i].fields.msg_data);
+            paramOut(os, csprintf("msix_table[%d].vec_ctrl", i),
+                     msix_table[i].fields.vec_ctrl);
+        }
+        for (int i = 0; i < pba_array_size; i++) {
+            paramOut(os, csprintf("msix_pba[%d].bits", i),
+                     msix_pba[i].bits);
+        }
+    }
+
+    paramOut(os, csprintf("pxcap.pxid"), uint16_t(pxcap.pxid));
+    paramOut(os, csprintf("pxcap.pxcap"), uint16_t(pxcap.pxcap));
+    paramOut(os, csprintf("pxcap.pxdcap"), uint32_t(pxcap.pxdcap));
+    paramOut(os, csprintf("pxcap.pxdc"), uint16_t(pxcap.pxdc));
+    paramOut(os, csprintf("pxcap.pxds"), uint16_t(pxcap.pxds));
+    paramOut(os, csprintf("pxcap.pxlcap"), uint32_t(pxcap.pxlcap));
+    paramOut(os, csprintf("pxcap.pxlc"), uint16_t(pxcap.pxlc));
+    paramOut(os, csprintf("pxcap.pxls"), uint16_t(pxcap.pxls));
+    paramOut(os, csprintf("pxcap.pxdcap2"), uint32_t(pxcap.pxdcap2));
+    paramOut(os, csprintf("pxcap.pxdc2"), uint32_t(pxcap.pxdc2));
 }
 
 void
@@ -357,7 +492,88 @@ PciDevice::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_ARRAY(BARAddrs, sizeof(BARAddrs) / sizeof(BARAddrs[0]));
     UNSERIALIZE_ARRAY(config.data,
                       sizeof(config.data) / sizeof(config.data[0]));
-    pioPort.sendRangeChange();
 
+    // unserialize the capability list registers
+    uint16_t tmp16;
+    uint32_t tmp32;
+    paramIn(cp, section, csprintf("pmcap.pid"), tmp16);
+    pmcap.pid = tmp16;
+    paramIn(cp, section, csprintf("pmcap.pc"), tmp16);
+    pmcap.pc = tmp16;
+    paramIn(cp, section, csprintf("pmcap.pmcs"), tmp16);
+    pmcap.pmcs = tmp16;
+
+    paramIn(cp, section, csprintf("msicap.mid"), tmp16);
+    msicap.mid = tmp16;
+    paramIn(cp, section, csprintf("msicap.mc"), tmp16);
+    msicap.mc = tmp16;
+    paramIn(cp, section, csprintf("msicap.ma"), tmp32);
+    msicap.ma = tmp32;
+    UNSERIALIZE_SCALAR(msicap.mua);
+    paramIn(cp, section, csprintf("msicap.md"), tmp16);;
+    msicap.md = tmp16;
+    UNSERIALIZE_SCALAR(msicap.mmask);
+    UNSERIALIZE_SCALAR(msicap.mpend);
+
+    paramIn(cp, section, csprintf("msixcap.mxid"), tmp16);
+    msixcap.mxid = tmp16;
+    paramIn(cp, section, csprintf("msixcap.mxc"), tmp16);
+    msixcap.mxc = tmp16;
+    paramIn(cp, section, csprintf("msixcap.mtab"), tmp32);
+    msixcap.mtab = tmp32;
+    paramIn(cp, section, csprintf("msixcap.mpba"), tmp32);
+    msixcap.mpba = tmp32;
+
+    // Only allocate if MSIXCAP_BASE is not 0x0
+    if (MSIXCAP_BASE != 0x0) {
+        int msix_array_size;
+        int pba_array_size;
+
+        UNSERIALIZE_SCALAR(msix_array_size);
+        UNSERIALIZE_SCALAR(pba_array_size);
+
+        MSIXTable tmp1 = {{0UL, 0UL, 0UL, 0UL}};
+        msix_table.resize(msix_array_size, tmp1);
+
+        MSIXPbaEntry tmp2 = {0};
+        msix_pba.resize(pba_array_size, tmp2);
+
+        for (int i = 0; i < msix_array_size; i++) {
+            paramIn(cp, section, csprintf("msix_table[%d].addr_lo", i),
+                    msix_table[i].fields.addr_lo);
+            paramIn(cp, section, csprintf("msix_table[%d].addr_hi", i),
+                    msix_table[i].fields.addr_hi);
+            paramIn(cp, section, csprintf("msix_table[%d].msg_data", i),
+                    msix_table[i].fields.msg_data);
+            paramIn(cp, section, csprintf("msix_table[%d].vec_ctrl", i),
+                    msix_table[i].fields.vec_ctrl);
+        }
+        for (int i = 0; i < pba_array_size; i++) {
+            paramIn(cp, section, csprintf("msix_pba[%d].bits", i),
+                    msix_pba[i].bits);
+        }
+    }
+
+    paramIn(cp, section, csprintf("pxcap.pxid"), tmp16);
+    pxcap.pxid = tmp16;
+    paramIn(cp, section, csprintf("pxcap.pxcap"), tmp16);
+    pxcap.pxcap = tmp16;
+    paramIn(cp, section, csprintf("pxcap.pxdcap"), tmp32);
+    pxcap.pxdcap = tmp32;
+    paramIn(cp, section, csprintf("pxcap.pxdc"), tmp16);
+    pxcap.pxdc = tmp16;
+    paramIn(cp, section, csprintf("pxcap.pxds"), tmp16);
+    pxcap.pxds = tmp16;
+    paramIn(cp, section, csprintf("pxcap.pxlcap"), tmp32);
+    pxcap.pxlcap = tmp32;
+    paramIn(cp, section, csprintf("pxcap.pxlc"), tmp16);
+    pxcap.pxlc = tmp16;
+    paramIn(cp, section, csprintf("pxcap.pxls"), tmp16);
+    pxcap.pxls = tmp16;
+    paramIn(cp, section, csprintf("pxcap.pxdcap2"), tmp32);
+    pxcap.pxdcap2 = tmp32;
+    paramIn(cp, section, csprintf("pxcap.pxdc2"), tmp32);
+    pxcap.pxdc2 = tmp32;
+    pioPort.sendRangeChange();
 }
 
index d4820c0eef1f36cb99357f0098671e6bf80ca854..4f3dfd21909966070d18aa0c18feefae5e2ceb87 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2004-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -38,6 +50,7 @@
 #define __DEV_PCIDEV_HH__
 
 #include <cstring>
+#include <vector>
 
 #include "dev/dma_device.hh"
 #include "dev/pcireg.h"
@@ -91,6 +104,25 @@ class PciDevice : public DmaDevice
   protected:
     /** The current config space.  */
     PCIConfig config;
+    /** The capability list structures and base addresses
+     * @{
+     */
+    const int PMCAP_BASE;
+    PMCAP pmcap;
+
+    const int MSICAP_BASE;
+    MSICAP msicap;
+
+    const int MSIXCAP_BASE;
+    MSIXCAP msixcap;
+
+    const int PXCAP_BASE;
+    PXCAP pxcap;
+    /** @} */
+
+    /** MSIX Table and PBA Structures */
+    std::vector<MSIXTable> msix_table;
+    std::vector<MSIXPbaEntry> msix_pba;
 
     /** The size of the BARs */
     uint32_t BARSize[6];
index 5639d8e295e03e6e3b399a1ac5ecffe2673f2b1f..045b88965647686881124066fb4f76bae3335d84 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2001-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -38,6 +50,9 @@
 
 #include <sys/types.h>
 
+#include "base/bitfield.hh"
+#include "base/bitunion.hh"
+
 union PCIConfig {
     uint8_t data[64];
 
@@ -59,8 +74,11 @@ union PCIConfig {
         uint16_t subsystemVendorID;
         uint16_t subsystemID;
         uint32_t expansionROM;
-        uint32_t reserved0;
-        uint32_t reserved1;
+        uint8_t capabilityPtr;
+        // Was 8 bytes in the legacy PCI spec, but to support PCIe
+        // this field is now 7 bytes with PCIe's addition of the
+        // capability list pointer.
+        uint8_t reserved[7];
         uint8_t interruptLine;
         uint8_t interruptPin;
         uint8_t minimumGrant;
@@ -98,8 +116,8 @@ union PCIConfig {
 #define PCI0_SUB_VENDOR_ID      0x2C    // Sub-Vendor ID                ro
 #define PCI0_SUB_SYSTEM_ID      0x2E    // Sub-System ID                ro
 #define PCI0_ROM_BASE_ADDR      0x30    // Expansion ROM Base Address   rw
-#define PCI0_RESERVED0          0x34
-#define PCI0_RESERVED1          0x38
+#define PCI0_CAP_PTR            0x34    // Capability list pointer      ro
+#define PCI0_RESERVED           0x35
 #define PCI0_INTERRUPT_LINE     0x3C    // Interrupt Line               rw
 #define PCI0_INTERRUPT_PIN      0x3D    // Interrupt Pin                ro
 #define PCI0_MINIMUM_GRANT      0x3E    // Maximum Grant                ro
@@ -146,4 +164,295 @@ union PCIConfig {
 #define PCI_PRODUCT_SIMOS_SIMOS         0x1291
 #define PCI_PRODUCT_SIMOS_ETHER         0x1292
 
+/**
+ * PCIe capability list offsets internal to the entry.
+ * Actual offsets in the PCI config space are defined in
+ * the python files setting up the system.
+ */
+#define PMCAP_ID 0x00
+#define PMCAP_PC 0x02
+#define PMCAP_PMCS 0x04
+#define PMCAP_SIZE 0x06
+
+#define MSICAP_ID 0x00
+#define MSICAP_MC 0x02
+#define MSICAP_MA 0x04
+#define MSICAP_MUA 0x08
+#define MSICAP_MD 0x0C
+#define MSICAP_MMASK 0x10
+#define MSICAP_MPEND 0x14
+#define MSICAP_SIZE 0x18
+
+#define MSIXCAP_ID 0x00
+#define MSIXCAP_MXC 0x02
+#define MSIXCAP_MTAB 0x04
+#define MSIXCAP_MPBA 0x08
+#define MSIXCAP_SIZE 0x0C
+
+#define PXCAP_ID 0x00
+#define PXCAP_PXCAP 0x02
+#define PXCAP_PXDCAP 0x04
+#define PXCAP_PXDC 0x08
+#define PXCAP_PXDS 0x0A
+#define PXCAP_PXLCAP 0x0C
+#define PXCAP_PXLC 0x10
+#define PXCAP_PXLS 0x12
+#define PXCAP_PXDCAP2 0x24
+#define PXCAP_PXDC2 0x28
+#define PXCAP_SIZE 0x30
+
+/** @struct PMCAP
+ *  Defines the Power Management capability register and all its associated
+ *  bitfields for a PCIe device.
+ */
+struct PMCAP {
+    BitUnion16(PID)
+        Bitfield<7,0>   cid;
+        Bitfield<15,8>  next;
+    EndBitUnion(PID)
+    PID pid;
+
+    BitUnion16(PC)
+        Bitfield<2,0>   vs;
+        Bitfield<3>     pmec;
+        Bitfield<4>     reserved;
+        Bitfield<5>     dsi;
+        Bitfield<8,6>   auxc;
+        Bitfield<9>     d1s;
+        Bitfield<10>    d2s;
+        Bitfield<15,11> psup;
+    EndBitUnion(PC)
+    PC pc;
+
+    BitUnion16(PMCS)
+        Bitfield<1,0>   ps;
+        Bitfield<2>     reserved0;
+        Bitfield<3>     nsfrst;
+        Bitfield<7,4>   reserved1;
+        Bitfield<8>     pmee;
+        Bitfield<12,9>  dse;
+        Bitfield<14,13> dsc;
+        Bitfield<15>    pmes;
+    EndBitUnion(PMCS)
+    PMCS pmcs;
+};
+
+/** @struct MSICAP
+ *  Defines the MSI Capability register and its associated bitfields for
+ *  the a PCI/PCIe device.  Both the MSI capability and the MSIX capability
+ *  can be filled in if a device model supports both, but only 1 of
+ *  MSI/MSIX/INTx interrupt mode can be selected at a given time.
+ */
+struct MSICAP {
+    BitUnion16(MID)
+        Bitfield<7,0>   cid;
+        Bitfield<15,8>  next;
+    EndBitUnion(MID)
+    MID mid;
+
+    BitUnion16(MC)
+        Bitfield<0>     msie;
+        Bitfield<3,1>   mmc;
+        Bitfield<6,4>   mme;
+        Bitfield<7>     c64;
+        Bitfield<8>     pvm;
+        Bitfield<15,9>  reserved;
+    EndBitUnion(MC)
+    MC mc;
+
+    BitUnion32(MA)
+        Bitfield<1,0>   reserved;
+        Bitfield<31,2>  addr;
+    EndBitUnion(MA)
+    MA ma;
+
+    uint32_t mua;
+
+    BitUnion16(MD)
+        Bitfield<15,0> data;
+    EndBitUnion(MD)
+    MD md;
+
+    uint32_t mmask;
+    uint32_t mpend;
+};
+
+/** @struct MSIX
+ *  Defines the MSI-X Capability register and its associated bitfields for
+ *  a PCIe device.
+ */
+struct MSIXCAP {
+    BitUnion16(MXID)
+        Bitfield<7,0>   cid;
+        Bitfield<15,8>  next;
+    EndBitUnion(MXID)
+    MXID mxid;
+
+    BitUnion16(MXC)
+        Bitfield<10,0>  ts;
+        Bitfield<13,11> reserved;
+        Bitfield<14>    fm;
+        Bitfield<15>    mxe;
+    EndBitUnion(MXC)
+    MXC mxc;
+
+    BitUnion32(MTAB)
+        Bitfield<31,3>  to;
+        Bitfield<2,0>   tbir;
+    EndBitUnion(MTAB)
+    MTAB mtab;
+
+    BitUnion32(MPBA)
+        Bitfield<2,0>   pbir;
+        Bitfield<31,3>  pbao;
+    EndBitUnion(MPBA)
+    MPBA mpba;
+};
+
+union MSIXTable {
+    struct {
+        uint32_t addr_lo;
+        uint32_t addr_hi;
+        uint32_t msg_data;
+        uint32_t vec_ctrl;
+    } fields;
+    uint32_t data[4];
+};
+
+#define MSIXVECS_PER_PBA 64
+struct MSIXPbaEntry {
+    uint64_t bits;
+};
+
+/** @struct PXCAP
+ *  Defines the PCI Express capability register and its associated bitfields
+ *  for a PCIe device.
+ */
+struct PXCAP {
+    BitUnion16(PXID)
+        Bitfield<7,0>   cid;
+        Bitfield<15,8>  next;
+    EndBitUnion(PXID)
+    PXID pxid;
+
+    BitUnion16(_PXCAP)
+        Bitfield<3,0>   ver;
+        Bitfield<7,4>   dpt;
+        Bitfield<8>     si;
+        Bitfield<13,9>  imn;
+        Bitfield<15,14> reserved;
+    EndBitUnion(_PXCAP)
+    _PXCAP pxcap;
+
+    BitUnion32(PXDCAP)
+        Bitfield<2,0>   mps;
+        Bitfield<4,3>   pfs;
+        Bitfield<5>     etfs;
+        Bitfield<8,6>   l0sl;
+        Bitfield<11,9>  l1l;
+        Bitfield<14,12> reserved0;
+        Bitfield<15>    rer;
+        Bitfield<17,16> reserved1;
+        Bitfield<25,18> csplv;
+        Bitfield<27,26> cspls;
+        Bitfield<28>    flrc;
+        Bitfield<31,29> reserved2;
+    EndBitUnion(PXDCAP)
+    PXDCAP pxdcap;
+
+    BitUnion16(PXDC)
+        Bitfield<0>     cere;
+        Bitfield<1>     nfere;
+        Bitfield<2>     fere;
+        Bitfield<3>     urre;
+        Bitfield<4>     ero;
+        Bitfield<7,5>   mps;
+        Bitfield<8>     ete;
+        Bitfield<9>     pfe;
+        Bitfield<10>    appme;
+        Bitfield<11>    ens;
+        Bitfield<14,12> mrrs;
+        Bitfield<15>    func_reset;
+    EndBitUnion(PXDC)
+    PXDC pxdc;
+
+    BitUnion16(PXDS)
+        Bitfield<0>     ced;
+        Bitfield<1>     nfed;
+        Bitfield<2>     fed;
+        Bitfield<3>     urd;
+        Bitfield<4>     apd;
+        Bitfield<5>     tp;
+        Bitfield<15,6>  reserved;
+    EndBitUnion(PXDS)
+    PXDS pxds;
+
+    BitUnion32(PXLCAP)
+        Bitfield<3,0>   sls;
+        Bitfield<9,4>   mlw;
+        Bitfield<11,10> aspms;
+        Bitfield<14,12> l0sel;
+        Bitfield<17,15> l1el;
+        Bitfield<18>    cpm;
+        Bitfield<19>    sderc;
+        Bitfield<20>    dllla;
+        Bitfield<21>    lbnc;
+        Bitfield<23,22> reserved;
+        Bitfield<31,24> pn;
+    EndBitUnion(PXLCAP)
+    PXLCAP pxlcap;
+
+    BitUnion16(PXLC)
+        Bitfield<1,0>   aspmc;
+        Bitfield<2>     reserved0;
+        Bitfield<3>     rcb;
+        Bitfield<5,4>   reserved1;
+        Bitfield<6>     ccc;
+        Bitfield<7>     es;
+        Bitfield<8>     ecpm;
+        Bitfield<9>     hawd;
+        Bitfield<15,10> reserved2;
+    EndBitUnion(PXLC)
+    PXLC pxlc;
+
+    BitUnion16(PXLS)
+        Bitfield<3,0>   cls;
+        Bitfield<9,4>   nlw;
+        Bitfield<11,10> reserved0;
+        Bitfield<12>    slot_clk_config;
+        Bitfield<15,13> reserved1;
+    EndBitUnion(PXLS)
+    PXLS pxls;
+
+    BitUnion32(PXDCAP2)
+        Bitfield<3,0>   ctrs;
+        Bitfield<4>     ctds;
+        Bitfield<5>     arifs;
+        Bitfield<6>     aors;
+        Bitfield<7>     aocs32;
+        Bitfield<8>     aocs64;
+        Bitfield<9>     ccs128;
+        Bitfield<10>    nprpr;
+        Bitfield<11>    ltrs;
+        Bitfield<13,12> tphcs;
+        Bitfield<17,14> reserved0;
+        Bitfield<19,18> obffs;
+        Bitfield<20>    effs;
+        Bitfield<21>    eetps;
+        Bitfield<23,22> meetp;
+        Bitfield<31,24> reserved1;
+    EndBitUnion(PXDCAP2)
+    PXDCAP2 pxdcap2;
+
+    BitUnion32(PXDC2)
+        Bitfield<3,0>   ctv;
+        Bitfield<4>     ctd;
+        Bitfield<9,5>   reserved0;
+        Bitfield<10>    ltrme;
+        Bitfield<12,11> reserved1;
+        Bitfield<14,13> obffe;
+        Bitfield<31,15> reserved2;
+    EndBitUnion(PXDC2)
+    PXDC2 pxdc2;
+};
 #endif // __PCIREG_H__