| /* |
| * Copyright (c) 2013, 2015 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. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* @file |
| * A single PCI device configuration space entry. |
| */ |
| |
| #include "dev/pci/device.hh" |
| |
| #include <list> |
| #include <string> |
| #include <vector> |
| |
| #include "base/inifile.hh" |
| #include "base/intmath.hh" |
| #include "base/logging.hh" |
| #include "base/str.hh" |
| #include "base/trace.hh" |
| #include "debug/PciDevice.hh" |
| #include "mem/packet.hh" |
| #include "mem/packet_access.hh" |
| #include "sim/byteswap.hh" |
| #include "sim/core.hh" |
| |
| PciDevice::PciDevice(const PciDeviceParams &p) |
| : DmaDevice(p), |
| _busAddr(p.pci_bus, p.pci_dev, p.pci_func), |
| PMCAP_BASE(p.PMCAPBaseOffset), |
| PMCAP_ID_OFFSET(p.PMCAPBaseOffset+PMCAP_ID), |
| PMCAP_PC_OFFSET(p.PMCAPBaseOffset+PMCAP_PC), |
| PMCAP_PMCS_OFFSET(p.PMCAPBaseOffset+PMCAP_PMCS), |
| MSICAP_BASE(p.MSICAPBaseOffset), |
| MSIXCAP_BASE(p.MSIXCAPBaseOffset), |
| MSIXCAP_ID_OFFSET(p.MSIXCAPBaseOffset+MSIXCAP_ID), |
| MSIXCAP_MXC_OFFSET(p.MSIXCAPBaseOffset+MSIXCAP_MXC), |
| MSIXCAP_MTAB_OFFSET(p.MSIXCAPBaseOffset+MSIXCAP_MTAB), |
| MSIXCAP_MPBA_OFFSET(p.MSIXCAPBaseOffset+MSIXCAP_MPBA), |
| PXCAP_BASE(p.PXCAPBaseOffset), |
| |
| hostInterface(p.host->registerDevice(this, _busAddr, |
| (PciIntPin)p.InterruptPin)), |
| pioDelay(p.pio_latency), |
| configDelay(p.config_latency) |
| { |
| fatal_if(p.InterruptPin >= 5, |
| "Invalid PCI interrupt '%i' specified.", p.InterruptPin); |
| |
| BARs[0] = p.BAR0; |
| BARs[1] = p.BAR1; |
| BARs[2] = p.BAR2; |
| BARs[3] = p.BAR3; |
| BARs[4] = p.BAR4; |
| BARs[5] = p.BAR5; |
| |
| int idx = 0; |
| for (auto *bar: BARs) { |
| auto *mu = dynamic_cast<PciMemUpperBar *>(bar); |
| // If this is the upper 32 bits of a memory BAR, try to connect it to |
| // the lower 32 bits. |
| if (mu) { |
| fatal_if(idx == 0, |
| "First BAR in %s is upper 32 bits of a memory BAR.", idx); |
| auto *ml = dynamic_cast<PciMemBar *>(BARs[idx - 1]); |
| fatal_if(!ml, "Upper 32 bits of memory BAR in %s doesn't come " |
| "after the lower 32."); |
| mu->lower(ml); |
| } |
| idx++; |
| } |
| |
| config.vendor = htole(p.VendorID); |
| config.device = htole(p.DeviceID); |
| config.command = htole(p.Command); |
| config.status = htole(p.Status); |
| config.revision = htole(p.Revision); |
| config.progIF = htole(p.ProgIF); |
| config.subClassCode = htole(p.SubClassCode); |
| config.classCode = htole(p.ClassCode); |
| config.cacheLineSize = htole(p.CacheLineSize); |
| config.latencyTimer = htole(p.LatencyTimer); |
| config.headerType = htole(p.HeaderType); |
| config.bist = htole(p.BIST); |
| |
| idx = 0; |
| for (auto *bar: BARs) |
| config.baseAddr[idx++] = bar->write(hostInterface, 0); |
| |
| config.cardbusCIS = htole(p.CardbusCIS); |
| config.subsystemVendorID = htole(p.SubsystemVendorID); |
| config.subsystemID = htole(p.SubsystemID); |
| config.expansionROM = htole(p.ExpansionROM); |
| 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 = (uint16_t)p.PMCAPCapId; // pid.cid |
| pmcap.pid |= (uint16_t)p.PMCAPNextCapability << 8; //pid.next |
| pmcap.pc = p.PMCAPCapabilities; |
| pmcap.pmcs = p.PMCAPCtrlStatus; |
| |
| // MSICAP |
| msicap.mid = (uint16_t)p.MSICAPCapId; //mid.cid |
| msicap.mid |= (uint16_t)p.MSICAPNextCapability << 8; //mid.next |
| 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 = (uint16_t)p.MSIXCAPCapId; //mxid.cid |
| msixcap.mxid |= (uint16_t)p.MSIXCAPNextCapability << 8; //mxid.next |
| 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 |
| uint16_t msixcap_mxc_ts = msixcap.mxc & 0x07ff; |
| 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); |
| } |
| MSIX_TABLE_OFFSET = msixcap.mtab & 0xfffffffc; |
| MSIX_TABLE_END = MSIX_TABLE_OFFSET + |
| (msixcap_mxc_ts + 1) * sizeof(MSIXTable); |
| MSIX_PBA_OFFSET = msixcap.mpba & 0xfffffffc; |
| MSIX_PBA_END = MSIX_PBA_OFFSET + |
| ((msixcap_mxc_ts + 1) / MSIXVECS_PER_PBA) |
| * sizeof(MSIXPbaEntry); |
| if (((msixcap_mxc_ts + 1) % MSIXVECS_PER_PBA) > 0) { |
| MSIX_PBA_END += sizeof(MSIXPbaEntry); |
| } |
| |
| // PXCAP |
| pxcap.pxid = (uint16_t)p.PXCAPCapId; //pxid.cid |
| pxcap.pxid |= (uint16_t)p.PXCAPNextCapability << 8; //pxid.next |
| 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; |
| } |
| |
| Tick |
| PciDevice::readConfig(PacketPtr pkt) |
| { |
| int offset = pkt->getAddr() & PCI_CONFIG_SIZE; |
| |
| /* Return 0 for accesses to unimplemented PCI configspace areas */ |
| if (offset >= PCI_DEVICE_SPECIFIC && |
| offset < PCI_CONFIG_SIZE) { |
| warn_once("Device specific PCI config space " |
| "not implemented for %s!\n", this->name()); |
| switch (pkt->getSize()) { |
| case sizeof(uint8_t): |
| pkt->setLE<uint8_t>(0); |
| break; |
| case sizeof(uint16_t): |
| pkt->setLE<uint16_t>(0); |
| break; |
| case sizeof(uint32_t): |
| pkt->setLE<uint32_t>(0); |
| break; |
| default: |
| panic("invalid access size(?) for PCI configspace!\n"); |
| } |
| } else if (offset > PCI_CONFIG_SIZE) { |
| panic("Out-of-range access to PCI config space!\n"); |
| } |
| |
| switch (pkt->getSize()) { |
| case sizeof(uint8_t): |
| pkt->setLE<uint8_t>(config.data[offset]); |
| DPRINTF(PciDevice, |
| "readConfig: dev %#x func %#x reg %#x 1 bytes: data = %#x\n", |
| _busAddr.dev, _busAddr.func, offset, |
| (uint32_t)pkt->getLE<uint8_t>()); |
| break; |
| case sizeof(uint16_t): |
| pkt->setLE<uint16_t>(*(uint16_t*)&config.data[offset]); |
| DPRINTF(PciDevice, |
| "readConfig: dev %#x func %#x reg %#x 2 bytes: data = %#x\n", |
| _busAddr.dev, _busAddr.func, offset, |
| (uint32_t)pkt->getLE<uint16_t>()); |
| break; |
| case sizeof(uint32_t): |
| pkt->setLE<uint32_t>(*(uint32_t*)&config.data[offset]); |
| DPRINTF(PciDevice, |
| "readConfig: dev %#x func %#x reg %#x 4 bytes: data = %#x\n", |
| _busAddr.dev, _busAddr.func, offset, |
| (uint32_t)pkt->getLE<uint32_t>()); |
| break; |
| default: |
| panic("invalid access size(?) for PCI configspace!\n"); |
| } |
| pkt->makeAtomicResponse(); |
| return configDelay; |
| |
| } |
| |
| AddrRangeList |
| PciDevice::getAddrRanges() const |
| { |
| AddrRangeList ranges; |
| PciCommandRegister command = letoh(config.command); |
| for (auto *bar: BARs) { |
| if (command.ioSpace && bar->isIo()) |
| ranges.push_back(bar->range()); |
| if (command.memorySpace && bar->isMem()) |
| ranges.push_back(bar->range()); |
| } |
| return ranges; |
| } |
| |
| Tick |
| PciDevice::writeConfig(PacketPtr pkt) |
| { |
| int offset = pkt->getAddr() & PCI_CONFIG_SIZE; |
| |
| /* No effect if we write to config space that is not implemented*/ |
| if (offset >= PCI_DEVICE_SPECIFIC && |
| offset < PCI_CONFIG_SIZE) { |
| warn_once("Device specific PCI config space " |
| "not implemented for %s!\n", this->name()); |
| switch (pkt->getSize()) { |
| case sizeof(uint8_t): |
| case sizeof(uint16_t): |
| case sizeof(uint32_t): |
| break; |
| default: |
| panic("invalid access size(?) for PCI configspace!\n"); |
| } |
| } else if (offset > PCI_CONFIG_SIZE) { |
| panic("Out-of-range access to PCI config space!\n"); |
| } |
| |
| switch (pkt->getSize()) { |
| case sizeof(uint8_t): |
| switch (offset) { |
| case PCI0_INTERRUPT_LINE: |
| config.interruptLine = pkt->getLE<uint8_t>(); |
| break; |
| case PCI_CACHE_LINE_SIZE: |
| config.cacheLineSize = pkt->getLE<uint8_t>(); |
| break; |
| case PCI_LATENCY_TIMER: |
| config.latencyTimer = pkt->getLE<uint8_t>(); |
| break; |
| /* Do nothing for these read-only registers */ |
| case PCI0_INTERRUPT_PIN: |
| case PCI0_MINIMUM_GRANT: |
| case PCI0_MAXIMUM_LATENCY: |
| case PCI_CLASS_CODE: |
| case PCI_REVISION_ID: |
| break; |
| default: |
| panic("writing to a read only register"); |
| } |
| DPRINTF(PciDevice, |
| "writeConfig: dev %#x func %#x reg %#x 1 bytes: data = %#x\n", |
| _busAddr.dev, _busAddr.func, offset, |
| (uint32_t)pkt->getLE<uint8_t>()); |
| break; |
| case sizeof(uint16_t): |
| switch (offset) { |
| case PCI_COMMAND: |
| config.command = pkt->getLE<uint8_t>(); |
| // IO or memory space may have been enabled/disabled. |
| pioPort.sendRangeChange(); |
| break; |
| case PCI_STATUS: |
| config.status = pkt->getLE<uint8_t>(); |
| break; |
| case PCI_CACHE_LINE_SIZE: |
| config.cacheLineSize = pkt->getLE<uint8_t>(); |
| break; |
| default: |
| panic("writing to a read only register"); |
| } |
| DPRINTF(PciDevice, |
| "writeConfig: dev %#x func %#x reg %#x 2 bytes: data = %#x\n", |
| _busAddr.dev, _busAddr.func, offset, |
| (uint32_t)pkt->getLE<uint16_t>()); |
| break; |
| case sizeof(uint32_t): |
| switch (offset) { |
| case PCI0_BASE_ADDR0: |
| case PCI0_BASE_ADDR1: |
| case PCI0_BASE_ADDR2: |
| case PCI0_BASE_ADDR3: |
| case PCI0_BASE_ADDR4: |
| case PCI0_BASE_ADDR5: |
| { |
| int num = BAR_NUMBER(offset); |
| auto *bar = BARs[num]; |
| config.baseAddr[num] = |
| htole(bar->write(hostInterface, pkt->getLE<uint32_t>())); |
| pioPort.sendRangeChange(); |
| } |
| break; |
| |
| case PCI0_ROM_BASE_ADDR: |
| if (letoh(pkt->getLE<uint32_t>()) == 0xfffffffe) |
| config.expansionROM = htole((uint32_t)0xffffffff); |
| else |
| config.expansionROM = pkt->getLE<uint32_t>(); |
| break; |
| |
| case PCI_COMMAND: |
| // This could also clear some of the error bits in the Status |
| // register. However they should never get set, so lets ignore |
| // it for now |
| config.command = pkt->getLE<uint32_t>(); |
| // IO or memory space may have been enabled/disabled. |
| pioPort.sendRangeChange(); |
| break; |
| |
| default: |
| DPRINTF(PciDevice, "Writing to a read only register"); |
| } |
| DPRINTF(PciDevice, |
| "writeConfig: dev %#x func %#x reg %#x 4 bytes: data = %#x\n", |
| _busAddr.dev, _busAddr.func, offset, |
| (uint32_t)pkt->getLE<uint32_t>()); |
| break; |
| default: |
| panic("invalid access size(?) for PCI configspace!\n"); |
| } |
| pkt->makeAtomicResponse(); |
| return configDelay; |
| } |
| |
| void |
| PciDevice::serialize(CheckpointOut &cp) const |
| { |
| SERIALIZE_ARRAY(config.data, sizeof(config.data) / sizeof(config.data[0])); |
| |
| // serialize the capability list registers |
| paramOut(cp, csprintf("pmcap.pid"), uint16_t(pmcap.pid)); |
| paramOut(cp, csprintf("pmcap.pc"), uint16_t(pmcap.pc)); |
| paramOut(cp, csprintf("pmcap.pmcs"), uint16_t(pmcap.pmcs)); |
| |
| paramOut(cp, csprintf("msicap.mid"), uint16_t(msicap.mid)); |
| paramOut(cp, csprintf("msicap.mc"), uint16_t(msicap.mc)); |
| paramOut(cp, csprintf("msicap.ma"), uint32_t(msicap.ma)); |
| SERIALIZE_SCALAR(msicap.mua); |
| paramOut(cp, csprintf("msicap.md"), uint16_t(msicap.md)); |
| SERIALIZE_SCALAR(msicap.mmask); |
| SERIALIZE_SCALAR(msicap.mpend); |
| |
| paramOut(cp, csprintf("msixcap.mxid"), uint16_t(msixcap.mxid)); |
| paramOut(cp, csprintf("msixcap.mxc"), uint16_t(msixcap.mxc)); |
| paramOut(cp, csprintf("msixcap.mtab"), uint32_t(msixcap.mtab)); |
| paramOut(cp, csprintf("msixcap.mpba"), uint32_t(msixcap.mpba)); |
| |
| // Only serialize if we have a non-zero base address |
| if (MSIXCAP_BASE != 0x0) { |
| uint16_t msixcap_mxc_ts = msixcap.mxc & 0x07ff; |
| 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(cp, csprintf("msix_table[%d].addr_lo", i), |
| msix_table[i].fields.addr_lo); |
| paramOut(cp, csprintf("msix_table[%d].addr_hi", i), |
| msix_table[i].fields.addr_hi); |
| paramOut(cp, csprintf("msix_table[%d].msg_data", i), |
| msix_table[i].fields.msg_data); |
| paramOut(cp, csprintf("msix_table[%d].vec_ctrl", i), |
| msix_table[i].fields.vec_ctrl); |
| } |
| for (int i = 0; i < pba_array_size; i++) { |
| paramOut(cp, csprintf("msix_pba[%d].bits", i), |
| msix_pba[i].bits); |
| } |
| } |
| |
| paramOut(cp, csprintf("pxcap.pxid"), uint16_t(pxcap.pxid)); |
| paramOut(cp, csprintf("pxcap.pxcap"), uint16_t(pxcap.pxcap)); |
| paramOut(cp, csprintf("pxcap.pxdcap"), uint32_t(pxcap.pxdcap)); |
| paramOut(cp, csprintf("pxcap.pxdc"), uint16_t(pxcap.pxdc)); |
| paramOut(cp, csprintf("pxcap.pxds"), uint16_t(pxcap.pxds)); |
| paramOut(cp, csprintf("pxcap.pxlcap"), uint32_t(pxcap.pxlcap)); |
| paramOut(cp, csprintf("pxcap.pxlc"), uint16_t(pxcap.pxlc)); |
| paramOut(cp, csprintf("pxcap.pxls"), uint16_t(pxcap.pxls)); |
| paramOut(cp, csprintf("pxcap.pxdcap2"), uint32_t(pxcap.pxdcap2)); |
| paramOut(cp, csprintf("pxcap.pxdc2"), uint32_t(pxcap.pxdc2)); |
| } |
| |
| void |
| PciDevice::unserialize(CheckpointIn &cp) |
| { |
| UNSERIALIZE_ARRAY(config.data, |
| sizeof(config.data) / sizeof(config.data[0])); |
| |
| for (int idx = 0; idx < BARs.size(); idx++) |
| BARs[idx]->write(hostInterface, config.baseAddr[idx]); |
| |
| // unserialize the capability list registers |
| uint16_t tmp16; |
| uint32_t tmp32; |
| paramIn(cp, csprintf("pmcap.pid"), tmp16); |
| pmcap.pid = tmp16; |
| paramIn(cp, csprintf("pmcap.pc"), tmp16); |
| pmcap.pc = tmp16; |
| paramIn(cp, csprintf("pmcap.pmcs"), tmp16); |
| pmcap.pmcs = tmp16; |
| |
| paramIn(cp, csprintf("msicap.mid"), tmp16); |
| msicap.mid = tmp16; |
| paramIn(cp, csprintf("msicap.mc"), tmp16); |
| msicap.mc = tmp16; |
| paramIn(cp, csprintf("msicap.ma"), tmp32); |
| msicap.ma = tmp32; |
| UNSERIALIZE_SCALAR(msicap.mua); |
| paramIn(cp, csprintf("msicap.md"), tmp16);; |
| msicap.md = tmp16; |
| UNSERIALIZE_SCALAR(msicap.mmask); |
| UNSERIALIZE_SCALAR(msicap.mpend); |
| |
| paramIn(cp, csprintf("msixcap.mxid"), tmp16); |
| msixcap.mxid = tmp16; |
| paramIn(cp, csprintf("msixcap.mxc"), tmp16); |
| msixcap.mxc = tmp16; |
| paramIn(cp, csprintf("msixcap.mtab"), tmp32); |
| msixcap.mtab = tmp32; |
| paramIn(cp, 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, csprintf("msix_table[%d].addr_lo", i), |
| msix_table[i].fields.addr_lo); |
| paramIn(cp, csprintf("msix_table[%d].addr_hi", i), |
| msix_table[i].fields.addr_hi); |
| paramIn(cp, csprintf("msix_table[%d].msg_data", i), |
| msix_table[i].fields.msg_data); |
| paramIn(cp, csprintf("msix_table[%d].vec_ctrl", i), |
| msix_table[i].fields.vec_ctrl); |
| } |
| for (int i = 0; i < pba_array_size; i++) { |
| paramIn(cp, csprintf("msix_pba[%d].bits", i), |
| msix_pba[i].bits); |
| } |
| } |
| |
| paramIn(cp, csprintf("pxcap.pxid"), tmp16); |
| pxcap.pxid = tmp16; |
| paramIn(cp, csprintf("pxcap.pxcap"), tmp16); |
| pxcap.pxcap = tmp16; |
| paramIn(cp, csprintf("pxcap.pxdcap"), tmp32); |
| pxcap.pxdcap = tmp32; |
| paramIn(cp, csprintf("pxcap.pxdc"), tmp16); |
| pxcap.pxdc = tmp16; |
| paramIn(cp, csprintf("pxcap.pxds"), tmp16); |
| pxcap.pxds = tmp16; |
| paramIn(cp, csprintf("pxcap.pxlcap"), tmp32); |
| pxcap.pxlcap = tmp32; |
| paramIn(cp, csprintf("pxcap.pxlc"), tmp16); |
| pxcap.pxlc = tmp16; |
| paramIn(cp, csprintf("pxcap.pxls"), tmp16); |
| pxcap.pxls = tmp16; |
| paramIn(cp, csprintf("pxcap.pxdcap2"), tmp32); |
| pxcap.pxdcap2 = tmp32; |
| paramIn(cp, csprintf("pxcap.pxdc2"), tmp32); |
| pxcap.pxdc2 = tmp32; |
| pioPort.sendRangeChange(); |
| } |