From: Geoffrey Blake Date: Thu, 31 Oct 2013 18:41:13 +0000 (-0500) Subject: dev: Add support for MSI-X and Capability Lists for ARM and PCI devices X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=c32fbb7c008d86abc59f56c93b1ff5876fff0ab3;p=gem5.git dev: Add support for MSI-X and Capability Lists for ARM and PCI devices This patch adds the registers and fields to the PCI device to support Capability lists and to support MSI-X in the GIC. --- diff --git a/src/dev/Pci.py b/src/dev/Pci.py index fa3fc854e..79ea1f68a 100644 --- a/src/dev/Pci.py +++ b/src/dev/Pci.py @@ -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") diff --git a/src/dev/arm/Gic.py b/src/dev/arm/Gic.py index 0461758ed..766049296 100644 --- a/src/dev/arm/Gic.py +++ b/src/dev/arm/Gic.py @@ -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)") - diff --git a/src/dev/arm/gic_pl390.cc b/src/dev/arm/gic_pl390.cc index fc49aa63e..d2a660e88 100644 --- a/src/dev/arm/gic_pl390.cc +++ b/src/dev/arm/gic_pl390.cc @@ -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(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()); + + switch (daddr) { + case MSIX_SR: + // This value is little endian, just like the ARM guest + msixReg = pkt->get(); + 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 §ion) 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); diff --git a/src/dev/arm/gic_pl390.hh b/src/dev/arm/gic_pl390.hh index 2621e1a27..5b84ea92b 100644 --- a/src/dev/arm/gic_pl390.hh +++ b/src/dev/arm/gic_pl390.hh @@ -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__ diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc index 3ca807d06..a24fa8da8 100644 --- a/src/dev/pcidev.cc +++ b/src/dev/pcidev.cc @@ -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 §ion) 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(); } diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh index d4820c0ee..4f3dfd219 100644 --- a/src/dev/pcidev.hh +++ b/src/dev/pcidev.hh @@ -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 +#include #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 msix_table; + std::vector msix_pba; /** The size of the BARs */ uint32_t BARSize[6]; diff --git a/src/dev/pcireg.h b/src/dev/pcireg.h index 5639d8e29..045b88965 100644 --- a/src/dev/pcireg.h +++ b/src/dev/pcireg.h @@ -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 +#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__