From 9be18aa66ddb8db4da043279819d45bc72b7b086 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Fri, 2 Oct 2020 03:00:04 -0700 Subject: [PATCH] dev: Rework how PCI BARs are set up in python and C++. In python, the BARs had been configured using three arrays and a scalar parameter. The arrays tracked the BAR value in the config, whether the BAR was for a "legacy" IO range, and the size of the BAR, and the scalar parameter was an offset for the "legacy" IO addresses to map into the host physical address space. The nature of a BAR was implied by its raw config space value, with each of the control bits (IO vs. memory, 64 bit, reserved bits) encoded directly in the value. Now, the BARs are represented by objects which have different types depending on what type of BAR they are. There's one for IO, one for memory, one for the upper 32 bits of a 64 bit BAR (so indices work out), and one for legacy IO ranges. Each type has parameters which are appropriate for it, and they're parameters are all grouped together as a unit instead of being spread across all the previous values. The legacy IO offset has been removed, since these addresses can be offset like any other IO address. They can be represented naturally in the config using their typical IO port numbers, and still be turned into an address that gem5 will handle correctly in the back end. Unfortunately, this exposes a problem in the config system where a VectorParam can't be overwritten successfully one element at a time, at least when dealing with SimObject classes. It might work with actual SimObjects in a config, but I haven't tried it. If you were to do that to, for instance, update the BARs for x86 so that they used legacy IO ports for the IDE controller, it would complain that you were trying to instantiate orphaned nodes. Replacing the whole VectorParam with a new list of BAR objects seems to work, so that's what's implemented in this change. On the C++ side, BARs in the config space are treated as flat values on reads, and are stored in the config structure associated with each PCI device. On writes, the value is first passed to the BAR object, and it has a chance to mask any bits which are fixed in hardware and update its idea of what range it corresponds to in memory. When sending AddrRanges up to the parent bus to set up routing, the BARs generate each AddrRange if and only if their type has been enabled in the config space command register. The BAR object which represents the upper 32 bits of a 64 bit BAR does not claim to be IO or memory, and so doesn't contribute a range. It communicates with the BAR which represents the lower 32 bits, so that that BAR has the whole base address. Since the IO or memory BAR enable bits in the command register are now handled by the PCI device base class, the IDE controller no longer has to handle that manually. It does still need to keep track of whether the bus master functionality has been enabled though, which it can check when those registers are accessed. There was already a mechanism for decoding addresses based on BARs in the PCI device base class, but it was overly complicated and not used consistently across devices. It's been consolidated, and used in most places where it makes sense. Finally, a few unnecessary values have been dropped from the base PCI device's and IDE controller's checkpoint output. These were just local copies of information already in the BARs, which in turn are already stored along with the data in the device's config space. Change-Id: I16d5f8cdf86d7a2d02a6b04d1f9e1b3eb1dd189d Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/35516 Reviewed-by: Gabe Black Maintainer: Gabe Black Tested-by: kokoro --- src/dev/arm/RealView.py | 8 +- src/dev/net/Ethernet.py | 29 +--- src/dev/net/sinic.cc | 12 +- src/dev/pci/CopyEngine.py | 15 +- src/dev/pci/PciDevice.py | 66 ++++++--- src/dev/pci/device.cc | 165 ++++++++++------------ src/dev/pci/device.hh | 275 ++++++++++++++++++++++++++++-------- src/dev/pci/pcireg.h | 14 ++ src/dev/storage/Ide.py | 22 ++- src/dev/storage/ide_ctrl.cc | 151 +++++--------------- src/dev/storage/ide_ctrl.hh | 31 ++-- src/dev/virtio/VirtIO.py | 6 +- src/dev/virtio/pci.cc | 2 +- src/dev/x86/SouthBridge.py | 17 +-- 14 files changed, 446 insertions(+), 367 deletions(-) diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index 9ab04725d..4664a19de 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -62,6 +62,7 @@ from m5.objects.PS2 import * from m5.objects.VirtIOMMIO import MmioVirtIO from m5.objects.Display import Display, Display1080p from m5.objects.SMMUv3 import SMMUv3 +from m5.objects.PciDevice import PciLegacyIoBar, PciIoBar # Platforms with KVM support should generally use in-kernel GIC # emulation. Use a GIC model that automatically switches between @@ -717,10 +718,9 @@ class VExpress_EMM(RealView): kmi1 = Pl050(pio_addr=0x1c070000, interrupt=ArmSPI(num=45), ps2=PS2TouchKit()) cf_ctrl = IdeController(disks=[], pci_func=0, pci_dev=0, pci_bus=2, - io_shift = 2, ctrl_offset = 2, Command = 0x1, - BAR0 = 0x1C1A0000, BAR0Size = '256B', - BAR1 = 0x1C1A0100, BAR1Size = '4096B', - BAR0LegacyIO = True, BAR1LegacyIO = True) + io_shift = 2, ctrl_offset = 2, Command = 0x1) + cf_ctrl.BAR0 = PciLegacyIoBar(addr='0x1C1A0000', size='256B') + cf_ctrl.BAR1 = PciLegacyIoBar(addr='0x1C1A0100', size='4096B') bootmem = SimpleMemory(range = AddrRange('64MB'), conf_table_reported = False) diff --git a/src/dev/net/Ethernet.py b/src/dev/net/Ethernet.py index fd16b0970..dd878e295 100644 --- a/src/dev/net/Ethernet.py +++ b/src/dev/net/Ethernet.py @@ -40,7 +40,7 @@ from m5.defines import buildEnv from m5.SimObject import SimObject from m5.params import * from m5.proxy import * -from m5.objects.PciDevice import PciDevice +from m5.objects.PciDevice import PciDevice, PciIoBar, PciMemBar ETHERNET_ROLE = 'ETHERNET' Port.compat(ETHERNET_ROLE, ETHERNET_ROLE) @@ -152,17 +152,11 @@ class IGbE(EtherDevice): SubClassCode = 0x00 ClassCode = 0x02 ProgIF = 0x00 - BAR0 = 0x00000000 - BAR1 = 0x00000000 - BAR2 = 0x00000000 - BAR3 = 0x00000000 - BAR4 = 0x00000000 - BAR5 = 0x00000000 + BAR0 = PciMemBar(size='128kB') MaximumLatency = 0x00 MinimumGrant = 0xff InterruptLine = 0x1e InterruptPin = 0x01 - BAR0Size = '128kB' wb_delay = Param.Latency('10ns', "delay before desc writeback occurs") fetch_delay = Param.Latency('10ns', "delay before desc fetch occurs") fetch_comp_delay = Param.Latency('10ns', "delay after desc fetch occurs") @@ -224,18 +218,11 @@ class NSGigE(EtherDevBase): SubClassCode = 0x00 ClassCode = 0x02 ProgIF = 0x00 - BAR0 = 0x00000001 - BAR1 = 0x00000000 - BAR2 = 0x00000000 - BAR3 = 0x00000000 - BAR4 = 0x00000000 - BAR5 = 0x00000000 + BARs = (PciIoBar(size='256B'), PciMemBar(size='4kB')) MaximumLatency = 0x34 MinimumGrant = 0xb0 InterruptLine = 0x1e InterruptPin = 0x01 - BAR0Size = '256B' - BAR1Size = '4kB' @@ -265,16 +252,8 @@ class Sinic(EtherDevBase): SubClassCode = 0x00 ClassCode = 0x02 ProgIF = 0x00 - BAR0 = 0x00000000 - BAR1 = 0x00000000 - BAR2 = 0x00000000 - BAR3 = 0x00000000 - BAR4 = 0x00000000 - BAR5 = 0x00000000 + BARs = PciMemBar(size='64kB') MaximumLatency = 0x34 MinimumGrant = 0xb0 InterruptLine = 0x1e InterruptPin = 0x01 - BAR0Size = '64kB' - - diff --git a/src/dev/net/sinic.cc b/src/dev/net/sinic.cc index 85eba6f07..c227ab120 100644 --- a/src/dev/net/sinic.cc +++ b/src/dev/net/sinic.cc @@ -209,10 +209,12 @@ Tick Device::read(PacketPtr pkt) { assert(config.command & PCI_CMD_MSE); - assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); + + Addr daddr = pkt->getAddr(); + assert(BARs[0]->range().contains(daddr)); + daddr -= BARs[0]->addr(); ContextID cpu = pkt->req->contextId(); - Addr daddr = pkt->getAddr() - BARAddrs[0]; Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; @@ -294,10 +296,12 @@ Tick Device::write(PacketPtr pkt) { assert(config.command & PCI_CMD_MSE); - assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); + + Addr daddr = pkt->getAddr(); + assert(BARs[0]->range().contains(daddr)); + daddr -= BARs[0]->addr(); ContextID cpu = pkt->req->contextId(); - Addr daddr = pkt->getAddr() - BARAddrs[0]; Addr index = daddr >> Regs::VirtualShift; Addr raddr = daddr & Regs::VirtualMask; diff --git a/src/dev/pci/CopyEngine.py b/src/dev/pci/CopyEngine.py index 78b7e39a7..f5a0f9ee7 100644 --- a/src/dev/pci/CopyEngine.py +++ b/src/dev/pci/CopyEngine.py @@ -28,7 +28,7 @@ from m5.SimObject import SimObject from m5.params import * from m5.proxy import * -from m5.objects.PciDevice import PciDevice +from m5.objects.PciDevice import PciDevice, PciMemBar class CopyEngine(PciDevice): type = 'CopyEngine' @@ -47,12 +47,17 @@ class CopyEngine(PciDevice): MinimumGrant = 0xff InterruptLine = 0x20 InterruptPin = 0x01 - BAR0Size = '1kB' + + BAR0 = PciMemBar(size='1kB') ChanCnt = Param.UInt8(4, "Number of DMA channels that exist on device") - XferCap = Param.MemorySize('4kB', "Number of bits of transfer size that are supported") + XferCap = Param.MemorySize('4kB', + "Number of bits of transfer size that are supported") - latBeforeBegin = Param.Latency('20ns', "Latency after a DMA command is seen before it's proccessed") - latAfterCompletion = Param.Latency('20ns', "Latency after a DMA command is complete before it's reported as such") + latBeforeBegin = Param.Latency('20ns', + "Latency after a DMA command is seen before it's proccessed") + latAfterCompletion = Param.Latency('20ns', + "Latency after a DMA command is complete before " + "it's reported as such") diff --git a/src/dev/pci/PciDevice.py b/src/dev/pci/PciDevice.py index 862bff79a..72473ce24 100644 --- a/src/dev/pci/PciDevice.py +++ b/src/dev/pci/PciDevice.py @@ -42,6 +42,47 @@ from m5.proxy import * from m5.objects.Device import DmaDevice from m5.objects.PciHost import PciHost +class PciBar(SimObject): + type = 'PciBar' + cxx_class = 'PciBar' + cxx_header = "dev/pci/device.hh" + abstract = True + +class PciBarNone(PciBar): + type = 'PciBarNone' + cxx_class = 'PciBarNone' + cxx_header = "dev/pci/device.hh" + +class PciIoBar(PciBar): + type = 'PciIoBar' + cxx_class = 'PciIoBar' + cxx_header = "dev/pci/device.hh" + + size = Param.MemorySize32("IO region size") + +class PciLegacyIoBar(PciIoBar): + type = 'PciLegacyIoBar' + cxx_class = 'PciLegacyIoBar' + cxx_header = "dev/pci/device.hh" + + addr = Param.UInt32("Legacy IO address") + +# To set up a 64 bit memory BAR, put a PciMemUpperBar immediately after +# a PciMemBar. The pair will take up the right number of BARs, and will be +# recognized by the device and turned into a 64 bit BAR when the config is +# consumed. +class PciMemBar(PciBar): + type = 'PciMemBar' + cxx_class = 'PciMemBar' + cxx_header = "dev/pci/device.hh" + + size = Param.MemorySize("Memory region size") + +class PciMemUpperBar(PciBar): + type = 'PciMemUpperBar' + cxx_class = 'PciMemUpperBar' + cxx_header = "dev/pci/device.hh" + class PciDevice(DmaDevice): type = 'PciDevice' cxx_class = 'PciDevice' @@ -69,25 +110,12 @@ class PciDevice(DmaDevice): HeaderType = Param.UInt8(0, "PCI Header Type") BIST = Param.UInt8(0, "Built In Self Test") - BAR0 = Param.UInt32(0x00, "Base Address Register 0") - BAR1 = Param.UInt32(0x00, "Base Address Register 1") - BAR2 = Param.UInt32(0x00, "Base Address Register 2") - BAR3 = Param.UInt32(0x00, "Base Address Register 3") - BAR4 = Param.UInt32(0x00, "Base Address Register 4") - BAR5 = Param.UInt32(0x00, "Base Address Register 5") - BAR0Size = Param.MemorySize32('0B', "Base Address Register 0 Size") - BAR1Size = Param.MemorySize32('0B', "Base Address Register 1 Size") - BAR2Size = Param.MemorySize32('0B', "Base Address Register 2 Size") - BAR3Size = Param.MemorySize32('0B', "Base Address Register 3 Size") - BAR4Size = Param.MemorySize32('0B', "Base Address Register 4 Size") - BAR5Size = Param.MemorySize32('0B', "Base Address Register 5 Size") - BAR0LegacyIO = Param.Bool(False, "Whether BAR0 is hardwired legacy IO") - BAR1LegacyIO = Param.Bool(False, "Whether BAR1 is hardwired legacy IO") - BAR2LegacyIO = Param.Bool(False, "Whether BAR2 is hardwired legacy IO") - BAR3LegacyIO = Param.Bool(False, "Whether BAR3 is hardwired legacy IO") - BAR4LegacyIO = Param.Bool(False, "Whether BAR4 is hardwired legacy IO") - BAR5LegacyIO = Param.Bool(False, "Whether BAR5 is hardwired legacy IO") - LegacyIOBase = Param.Addr(0x0, "Base Address for Legacy IO") + BAR0 = Param.PciBar(PciBarNone(), "Base address register 0"); + BAR1 = Param.PciBar(PciBarNone(), "Base address register 1"); + BAR2 = Param.PciBar(PciBarNone(), "Base address register 2"); + BAR3 = Param.PciBar(PciBarNone(), "Base address register 3"); + BAR4 = Param.PciBar(PciBarNone(), "Base address register 4"); + BAR5 = Param.PciBar(PciBarNone(), "Base address register 5"); CardbusCIS = Param.UInt32(0x00, "Cardbus Card Information Structure") SubsystemID = Param.UInt16(0x00, "Subsystem ID") diff --git a/src/dev/pci/device.cc b/src/dev/pci/device.cc index be132212a..1942bc183 100644 --- a/src/dev/pci/device.cc +++ b/src/dev/pci/device.cc @@ -82,6 +82,29 @@ PciDevice::PciDevice(const PciDeviceParams &p) 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(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(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); @@ -95,12 +118,10 @@ PciDevice::PciDevice(const PciDeviceParams &p) config.headerType = htole(p.HeaderType); config.bist = htole(p.BIST); - config.baseAddr[0] = htole(p.BAR0); - config.baseAddr[1] = htole(p.BAR1); - config.baseAddr[2] = htole(p.BAR2); - config.baseAddr[3] = htole(p.BAR3); - config.baseAddr[4] = htole(p.BAR4); - config.baseAddr[5] = htole(p.BAR5); + 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); @@ -183,33 +204,6 @@ PciDevice::PciDevice(const PciDeviceParams &p) pxcap.pxls = p.PXCAPLinkStatus; pxcap.pxdcap2 = p.PXCAPDevCap2; pxcap.pxdc2 = p.PXCAPDevCtrl2; - - BARSize[0] = p.BAR0Size; - BARSize[1] = p.BAR1Size; - BARSize[2] = p.BAR2Size; - BARSize[3] = p.BAR3Size; - BARSize[4] = p.BAR4Size; - BARSize[5] = p.BAR5Size; - - legacyIO[0] = p.BAR0LegacyIO; - legacyIO[1] = p.BAR1LegacyIO; - legacyIO[2] = p.BAR2LegacyIO; - legacyIO[3] = p.BAR3LegacyIO; - legacyIO[4] = p.BAR4LegacyIO; - legacyIO[5] = p.BAR5LegacyIO; - - for (int i = 0; i < 6; ++i) { - if (legacyIO[i]) { - BARAddrs[i] = p.LegacyIOBase + letoh(config.baseAddr[i]); - config.baseAddr[i] = 0; - } else { - BARAddrs[i] = 0; - uint32_t barsize = BARSize[i]; - if (barsize != 0 && !isPowerOf2(barsize)) { - fatal("BAR %d size %d is not a power of 2\n", i, BARSize[i]); - } - } - } } Tick @@ -273,10 +267,13 @@ AddrRangeList PciDevice::getAddrRanges() const { AddrRangeList ranges; - int x = 0; - for (x = 0; x < 6; x++) - if (BARAddrs[x] != 0) - ranges.push_back(RangeSize(BARAddrs[x],BARSize[x])); + 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; } @@ -333,6 +330,8 @@ PciDevice::writeConfig(PacketPtr pkt) switch (offset) { case PCI_COMMAND: config.command = pkt->getLE(); + // IO or memory space may have been enabled/disabled. + pioPort.sendRangeChange(); break; case PCI_STATUS: config.status = pkt->getLE(); @@ -357,55 +356,11 @@ PciDevice::writeConfig(PacketPtr pkt) case PCI0_BASE_ADDR4: case PCI0_BASE_ADDR5: { - int barnum = BAR_NUMBER(offset); - - if (!legacyIO[barnum]) { - // convert BAR values to host endianness - uint32_t he_old_bar = letoh(config.baseAddr[barnum]); - uint32_t he_new_bar = letoh(pkt->getLE()); - - uint32_t bar_mask = - BAR_IO_SPACE(he_old_bar) ? BAR_IO_MASK : BAR_MEM_MASK; - - // Writing 0xffffffff to a BAR tells the card to set the - // value of the bar to a bitmask indicating the size of - // memory it needs - if (he_new_bar == 0xffffffff) { - he_new_bar = ~(BARSize[barnum] - 1); - } else { - // does it mean something special to write 0 to a BAR? - he_new_bar &= ~bar_mask; - if (he_new_bar) { - if (isLargeBAR(barnum)) { - if (BAR_IO_SPACE(he_old_bar)) - warn("IO BARs can't be set as large BAR"); - uint64_t he_large_bar = - letoh(config.baseAddr[barnum + 1]); - he_large_bar = he_large_bar << 32; - he_large_bar += he_new_bar; - BARAddrs[barnum] = - hostInterface.memAddr(he_large_bar); - } else if (isLargeBAR(barnum - 1)) { - BARAddrs[barnum] = 0; - uint64_t he_large_bar = he_new_bar; - he_large_bar = he_large_bar << 32; - // We need to apply mask to lower bits - he_large_bar += - letoh(config.baseAddr[barnum - 1] - & ~bar_mask); - BARAddrs[barnum - 1] = - hostInterface.memAddr(he_large_bar); - } else { - BARAddrs[barnum] = BAR_IO_SPACE(he_old_bar) ? - hostInterface.pioAddr(he_new_bar) : - hostInterface.memAddr(he_new_bar); - } - pioPort.sendRangeChange(); - } - } - config.baseAddr[barnum] = htole((he_new_bar & ~bar_mask) | - (he_old_bar & bar_mask)); - } + int num = BAR_NUMBER(offset); + auto *bar = BARs[num]; + config.baseAddr[num] = + htole(bar->write(hostInterface, pkt->getLE())); + pioPort.sendRangeChange(); } break; @@ -421,6 +376,8 @@ PciDevice::writeConfig(PacketPtr pkt) // register. However they should never get set, so lets ignore // it for now config.command = pkt->getLE(); + // IO or memory space may have been enabled/disabled. + pioPort.sendRangeChange(); break; default: @@ -441,8 +398,6 @@ PciDevice::writeConfig(PacketPtr pkt) void PciDevice::serialize(CheckpointOut &cp) const { - 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 @@ -506,11 +461,12 @@ PciDevice::serialize(CheckpointOut &cp) const void PciDevice::unserialize(CheckpointIn &cp) { - UNSERIALIZE_ARRAY(BARSize, sizeof(BARSize) / sizeof(BARSize[0])); - UNSERIALIZE_ARRAY(BARAddrs, sizeof(BARAddrs) / sizeof(BARAddrs[0])); 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; @@ -595,3 +551,32 @@ PciDevice::unserialize(CheckpointIn &cp) pioPort.sendRangeChange(); } +PciBarNone * +PciBarNoneParams::create() const +{ + return new PciBarNone(*this); +} + +PciIoBar * +PciIoBarParams::create() const +{ + return new PciIoBar(*this); +} + +PciLegacyIoBar * +PciLegacyIoBarParams::create() const +{ + return new PciLegacyIoBar(*this); +} + +PciMemBar * +PciMemBarParams::create() const +{ + return new PciMemBar(*this); +} + +PciMemUpperBar * +PciMemUpperBarParams::create() const +{ + return new PciMemUpperBar(*this); +} diff --git a/src/dev/pci/device.hh b/src/dev/pci/device.hh index 73e38566a..7e82c4450 100644 --- a/src/dev/pci/device.hh +++ b/src/dev/pci/device.hh @@ -45,21 +45,221 @@ #ifndef __DEV_PCI_DEVICE_HH__ #define __DEV_PCI_DEVICE_HH__ +#include #include #include #include "dev/dma_device.hh" #include "dev/pci/host.hh" #include "dev/pci/pcireg.h" +#include "params/PciBar.hh" +#include "params/PciBarNone.hh" #include "params/PciDevice.hh" +#include "params/PciIoBar.hh" +#include "params/PciLegacyIoBar.hh" +#include "params/PciMemBar.hh" +#include "params/PciMemUpperBar.hh" #include "sim/byteswap.hh" -#define BAR_IO_MASK 0x3 -#define BAR_MEM_MASK 0xF -#define BAR_IO_SPACE_BIT 0x1 -#define BAR_IO_SPACE(x) ((x) & BAR_IO_SPACE_BIT) #define BAR_NUMBER(x) (((x) - PCI0_BASE_ADDR0) >> 0x2); +class PciBar : public SimObject +{ + protected: + // The address and size of the region this decoder recognizes. + Addr _addr = 0; + Addr _size = 0; + + public: + PciBar(const PciBarParams &p) : SimObject(p) {} + + virtual bool isMem() const { return false; } + virtual bool isIo() const { return false; } + + // Accepts a value written to config space, consumes it, and returns what + // value config space should actually be set to. Both should be in host + // endian format. + virtual uint32_t write(const PciHost::DeviceInterface &host, + uint32_t val) = 0; + + AddrRange range() const { return AddrRange(_addr, _addr + _size); } + Addr addr() const { return _addr; } + Addr size() const { return _size; } + + // Hack for devices that don't know their BAR sizes ahead of time :-o. + // Don't use unless you have to, since this may not propogate properly + // outside of a small window. + void size(Addr value) { _size = value; } +}; + +class PciBarNone : public PciBar +{ + public: + PciBarNone(const PciBarNoneParams &p) : PciBar(p) {} + + uint32_t + write(const PciHost::DeviceInterface &host, uint32_t val) override + { + return 0; + } +}; + +class PciIoBar : public PciBar +{ + protected: + BitUnion32(Bar) + Bitfield<31, 2> addr; + Bitfield<1> reserved; + Bitfield<0> io; + EndBitUnion(Bar) + + public: + PciIoBar(const PciIoBarParams &p, bool legacy=false) : PciBar(p) + { + _size = p.size; + if (!legacy) { + Bar bar = _size; + fatal_if(!_size || !isPowerOf2(_size) || bar.io || bar.reserved, + "Illegal size %d for bar %s.", _size, name()); + } + } + + bool isIo() const override { return true; } + + uint32_t + write(const PciHost::DeviceInterface &host, uint32_t val) override + { + // Mask away the bits fixed by hardware. + Bar bar = val & ~(_size - 1); + // Set the fixed bits to their correct values. + bar.reserved = 0; + bar.io = 1; + + // Update our address. + _addr = host.pioAddr(bar.addr << 2); + + // Return what should go into config space. + return bar; + } +}; + +class PciLegacyIoBar : public PciIoBar +{ + protected: + Addr fixedAddr; + + public: + PciLegacyIoBar(const PciLegacyIoBarParams &p) : PciIoBar(p, true) + { + // Save the address until we get a host to translate it. + fixedAddr = p.addr; + } + + uint32_t + write(const PciHost::DeviceInterface &host, uint32_t val) override + { + // Update the address now that we have a host to translate it. + _addr = host.pioAddr(fixedAddr); + // Ignore writes. + return 0; + } +}; + +class PciMemBar : public PciBar +{ + private: + BitUnion32(Bar) + Bitfield<31, 3> addr; + SubBitUnion(type, 2, 1) + Bitfield<2> wide; + Bitfield<1> reserved; + EndSubBitUnion(type) + Bitfield<0> io; + EndBitUnion(Bar) + + bool _wide = false; + uint64_t _lower = 0; + uint64_t _upper = 0; + + public: + PciMemBar(const PciMemBarParams &p) : PciBar(p) + { + _size = p.size; + Bar bar = _size; + fatal_if(!_size || !isPowerOf2(_size) || bar.io || bar.type, + "Illegal size %d for bar %s.", _size, name()); + } + + bool isMem() const override { return true; } + + uint32_t + write(const PciHost::DeviceInterface &host, uint32_t val) override + { + // Mask away the bits fixed by hardware. + Bar bar = val & ~(_size - 1); + // Set the fixed bits to their correct values. + bar.type.wide = wide() ? 1 : 0; + bar.type.reserved = 0; + bar.io = 0; + + // Keep track of our lower 32 bits. + _lower = bar.addr << 3; + + // Update our address. + _addr = host.memAddr(upper() + lower()); + + // Return what should go into config space. + return bar; + } + + bool wide() const { return _wide; } + void wide(bool val) { _wide = val; } + + uint64_t upper() const { return _upper; } + void + upper(const PciHost::DeviceInterface &host, uint32_t val) + { + _upper = (uint64_t)val << 32; + + // Update our address. + _addr = host.memAddr(upper() + lower()); + } + + uint64_t lower() const { return _lower; } +}; + +class PciMemUpperBar : public PciBar +{ + private: + PciMemBar *_lower = nullptr; + + public: + PciMemUpperBar(const PciMemUpperBarParams &p) : PciBar(p) + {} + + void + lower(PciMemBar *val) + { + _lower = val; + // Let our lower half know we're up here. + _lower->wide(true); + } + + uint32_t + write(const PciHost::DeviceInterface &host, uint32_t val) override + { + assert(_lower); + + // Mask away bits fixed by hardware, if any. + Addr upper = val & ~((_lower->size() - 1) >> 32); + + // Let our lower half know about the update. + _lower->upper(host, upper); + + return upper; + } +}; + /** * PCI device, base implementation is only config space. */ @@ -102,68 +302,29 @@ class PciDevice : public DmaDevice std::vector msix_table; std::vector msix_pba; - /** The size of the BARs */ - uint32_t BARSize[6]; - - /** The current address mapping of the BARs */ - Addr BARAddrs[6]; - - /** Whether the BARs are really hardwired legacy IO locations. */ - bool legacyIO[6]; - - /** - * Does the given BAR represent 32 lower bits of a 64-bit address? - */ - bool - isLargeBAR(int bar) const - { - return bits(config.baseAddr[bar], 2, 1) == 0x2; - } - - /** - * Does the given address lie within the space mapped by the given - * base address register? - */ - bool - isBAR(Addr addr, int bar) const - { - assert(bar >= 0 && bar < 6); - return BARAddrs[bar] <= addr && addr < BARAddrs[bar] + BARSize[bar]; - } - - /** - * Which base address register (if any) maps the given address? - * @return The BAR number (0-5 inclusive), or -1 if none. - */ - int - getBAR(Addr addr) - { - for (int i = 0; i <= 5; ++i) - if (isBAR(addr, i)) - return i; - - return -1; - } + std::array BARs{}; /** * Which base address register (if any) maps the given address? * @param addr The address to check. - * @retval bar The BAR number (0-5 inclusive), + * @retval num The BAR number (0-5 inclusive), * only valid if return value is true. * @retval offs The offset from the base address, * only valid if return value is true. * @return True iff address maps to a base address register's region. */ bool - getBAR(Addr addr, int &bar, Addr &offs) + getBAR(Addr addr, int &num, Addr &offs) { - int b = getBAR(addr); - if (b < 0) - return false; - - offs = addr - BARAddrs[b]; - bar = b; - return true; + for (int i = 0; i < BARs.size(); i++) { + auto *bar = BARs[i]; + if (!bar || !bar->range().contains(addr)) + continue; + num = i; + offs = addr - bar->addr(); + return true; + } + return false; } public: // Host configuration interface @@ -191,7 +352,9 @@ class PciDevice : public DmaDevice Tick configDelay; public: - Addr pciToDma(Addr pci_addr) const { + Addr + pciToDma(Addr pci_addr) const + { return hostInterface.dmaAddr(pci_addr); } diff --git a/src/dev/pci/pcireg.h b/src/dev/pci/pcireg.h index 5f001c78b..dc1807bf8 100644 --- a/src/dev/pci/pcireg.h +++ b/src/dev/pci/pcireg.h @@ -50,6 +50,20 @@ #include "base/bitfield.hh" #include "base/bitunion.hh" +BitUnion16(PciCommandRegister) + Bitfield<15, 10> reserved; + Bitfield<9> fastBackToBackEn; + Bitfield<8> serrEn; + Bitfield<7> steppingControl; + Bitfield<6> parityErrResp; + Bitfield<5> vgaPaletteSnoopEn; + Bitfield<4> memWriteInvEn; + Bitfield<3> specialCycles; + Bitfield<2> busMaster; + Bitfield<1> memorySpace; + Bitfield<0> ioSpace; +EndBitUnion(PciCommandRegister) + union PCIConfig { uint8_t data[64]; diff --git a/src/dev/storage/Ide.py b/src/dev/storage/Ide.py index 5edea494f..01bd7594c 100644 --- a/src/dev/storage/Ide.py +++ b/src/dev/storage/Ide.py @@ -26,7 +26,7 @@ from m5.SimObject import SimObject from m5.params import * -from m5.objects.PciDevice import PciDevice +from m5.objects.PciDevice import PciDevice, PciIoBar class IdeID(Enum): vals = ['device0', 'device1'] @@ -50,19 +50,17 @@ class IdeController(PciDevice): ClassCode = 0x01 SubClassCode = 0x01 ProgIF = 0x85 - BAR0 = 0x00000001 - BAR1 = 0x00000001 - BAR2 = 0x00000001 - BAR3 = 0x00000001 - BAR4 = 0x00000001 - BAR5 = 0x00000001 InterruptLine = 0x1f InterruptPin = 0x01 - BAR0Size = '8B' - BAR1Size = '4B' - BAR2Size = '8B' - BAR3Size = '4B' - BAR4Size = '16B' + + # Primary + BAR0 = PciIoBar(size='8B') + BAR1 = PciIoBar(size='4B') + # Secondary + BAR2 = PciIoBar(size='8B') + BAR3 = PciIoBar(size='4B') + # DMA + BAR4 = PciIoBar(size='16B') io_shift = Param.UInt32(0x0, "IO port shift"); ctrl_offset = Param.UInt32(0x0, "IDE disk control offset") diff --git a/src/dev/storage/ide_ctrl.cc b/src/dev/storage/ide_ctrl.cc index bf1d72f7f..d9e628cf6 100644 --- a/src/dev/storage/ide_ctrl.cc +++ b/src/dev/storage/ide_ctrl.cc @@ -73,29 +73,18 @@ enum ConfRegOffset { static const uint16_t timeRegWithDecodeEn = 0x8000; -IdeController::Channel::Channel( - string newName, Addr _cmdSize, Addr _ctrlSize) : - _name(newName), - cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize), - device0(NULL), device1(NULL), selected(NULL) +IdeController::Channel::Channel(string newName) : _name(newName) { bmiRegs.reset(); bmiRegs.status.dmaCap0 = 1; bmiRegs.status.dmaCap1 = 1; } -IdeController::Channel::~Channel() -{ -} - IdeController::IdeController(const Params &p) - : PciDevice(p), primary(name() + ".primary", BARSize[0], BARSize[1]), - secondary(name() + ".secondary", BARSize[2], BARSize[3]), - bmiAddr(0), bmiSize(BARSize[4]), + : PciDevice(p), primary(name() + ".primary"), + secondary(name() + ".secondary"), primaryTiming(htole(timeRegWithDecodeEn)), secondaryTiming(htole(timeRegWithDecodeEn)), - deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0), - ioEnabled(false), bmEnabled(false), ioShift(p.io_shift), ctrlOffset(p.ctrl_offset) { @@ -126,18 +115,6 @@ IdeController::IdeController(const Params &p) primary.select(false); secondary.select(false); - - if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) { - primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0]; - primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARSize[1]; - } - if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) { - secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2]; - secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARSize[3]; - } - - ioEnabled = (config.command & htole(PCI_CMD_IOSE)); - bmEnabled = (config.command & htole(PCI_CMD_BME)); } bool @@ -323,41 +300,6 @@ IdeController::writeConfig(PacketPtr pkt) } pkt->makeAtomicResponse(); } - - /* Trap command register writes and enable IO/BM as appropriate as well as - * BARs. */ - switch(offset) { - case PCI0_BASE_ADDR0: - if (BARAddrs[0] != 0) - primary.cmdAddr = BARAddrs[0]; - break; - - case PCI0_BASE_ADDR1: - if (BARAddrs[1] != 0) - primary.ctrlAddr = BARAddrs[1]; - break; - - case PCI0_BASE_ADDR2: - if (BARAddrs[2] != 0) - secondary.cmdAddr = BARAddrs[2]; - break; - - case PCI0_BASE_ADDR3: - if (BARAddrs[3] != 0) - secondary.ctrlAddr = BARAddrs[3]; - break; - - case PCI0_BASE_ADDR4: - if (BARAddrs[4] != 0) - bmiAddr = BARAddrs[4]; - break; - - case PCI_COMMAND: - DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command); - ioEnabled = (config.command & htole(PCI_CMD_IOSE)); - bmEnabled = (config.command & htole(PCI_CMD_BME)); - break; - } return configDelay; } @@ -487,48 +429,45 @@ IdeController::dispatchAccess(PacketPtr pkt, bool read) if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) panic("Bad IDE read size: %d\n", pkt->getSize()); - if (!ioEnabled) { - pkt->makeAtomicResponse(); - DPRINTF(IdeCtrl, "io not enabled\n"); - return; - } - Addr addr = pkt->getAddr(); int size = pkt->getSize(); uint8_t *dataPtr = pkt->getPtr(); - if (addr >= primary.cmdAddr && - addr < (primary.cmdAddr + primary.cmdSize)) { - addr -= primary.cmdAddr; + int bar_num; + Addr offset; + panic_if(!getBAR(addr, bar_num, offset), + "IDE controller access to invalid address: %#x.", addr); + + switch (bar_num) { + case 0: // linux may have shifted the address by ioShift, // here we shift it back, similarly for ctrlOffset. - addr >>= ioShift; - primary.accessCommand(addr, size, dataPtr, read); - } else if (addr >= primary.ctrlAddr && - addr < (primary.ctrlAddr + primary.ctrlSize)) { - addr -= primary.ctrlAddr; - addr += ctrlOffset; - primary.accessControl(addr, size, dataPtr, read); - } else if (addr >= secondary.cmdAddr && - addr < (secondary.cmdAddr + secondary.cmdSize)) { - addr -= secondary.cmdAddr; - secondary.accessCommand(addr, size, dataPtr, read); - } else if (addr >= secondary.ctrlAddr && - addr < (secondary.ctrlAddr + secondary.ctrlSize)) { - addr -= secondary.ctrlAddr; - secondary.accessControl(addr, size, dataPtr, read); - } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { - if (!read && !bmEnabled) - return; - addr -= bmiAddr; - if (addr < sizeof(Channel::BMIRegs)) { - primary.accessBMI(addr, size, dataPtr, read); - } else { - addr -= sizeof(Channel::BMIRegs); - secondary.accessBMI(addr, size, dataPtr, read); + offset >>= ioShift; + primary.accessCommand(offset, size, dataPtr, read); + break; + case 1: + offset += ctrlOffset; + primary.accessControl(offset, size, dataPtr, read); + break; + case 2: + secondary.accessCommand(offset, size, dataPtr, read); + break; + case 3: + secondary.accessControl(offset, size, dataPtr, read); + break; + case 4: + { + PciCommandRegister command = letoh(config.command); + if (!read && !command.busMaster) + return; + + if (offset < sizeof(Channel::BMIRegs)) { + primary.accessBMI(offset, size, dataPtr, read); + } else { + offset -= sizeof(Channel::BMIRegs); + secondary.accessBMI(offset, size, dataPtr, read); + } } - } else { - panic("IDE controller access to invalid address: %#x\n", addr); } #ifndef NDEBUG @@ -577,22 +516,12 @@ IdeController::serialize(CheckpointOut &cp) const SERIALIZE_SCALAR(udmaControl); SERIALIZE_SCALAR(udmaTiming); SERIALIZE_SCALAR(ideConfig); - - // Serialize internal state - SERIALIZE_SCALAR(ioEnabled); - SERIALIZE_SCALAR(bmEnabled); - SERIALIZE_SCALAR(bmiAddr); - SERIALIZE_SCALAR(bmiSize); } void IdeController::Channel::serialize(const std::string &base, CheckpointOut &cp) const { - paramOut(cp, base + ".cmdAddr", cmdAddr); - paramOut(cp, base + ".cmdSize", cmdSize); - paramOut(cp, base + ".ctrlAddr", ctrlAddr); - paramOut(cp, base + ".ctrlSize", ctrlSize); uint8_t command = bmiRegs.command; paramOut(cp, base + ".bmiRegs.command", command); paramOut(cp, base + ".bmiRegs.reserved0", bmiRegs.reserved0); @@ -620,21 +549,11 @@ IdeController::unserialize(CheckpointIn &cp) UNSERIALIZE_SCALAR(udmaControl); UNSERIALIZE_SCALAR(udmaTiming); UNSERIALIZE_SCALAR(ideConfig); - - // Unserialize internal state - UNSERIALIZE_SCALAR(ioEnabled); - UNSERIALIZE_SCALAR(bmEnabled); - UNSERIALIZE_SCALAR(bmiAddr); - UNSERIALIZE_SCALAR(bmiSize); } void IdeController::Channel::unserialize(const std::string &base, CheckpointIn &cp) { - paramIn(cp, base + ".cmdAddr", cmdAddr); - paramIn(cp, base + ".cmdSize", cmdSize); - paramIn(cp, base + ".ctrlAddr", ctrlAddr); - paramIn(cp, base + ".ctrlSize", ctrlSize); uint8_t command; paramIn(cp, base +".bmiRegs.command", command); bmiRegs.command = command; diff --git a/src/dev/storage/ide_ctrl.hh b/src/dev/storage/ide_ctrl.hh index 470ffd928..dfce83481 100644 --- a/src/dev/storage/ide_ctrl.hh +++ b/src/dev/storage/ide_ctrl.hh @@ -72,13 +72,12 @@ class IdeController : public PciDevice return _name; } - /** Command and control block registers */ - Addr cmdAddr, cmdSize, ctrlAddr, ctrlSize; - /** Registers used for bus master interface */ struct BMIRegs { - void reset() { + void + reset() + { memset(static_cast(this), 0, sizeof(*this)); } @@ -95,12 +94,12 @@ class IdeController : public PciDevice * #Multiple_devices_on_a_cable * */ - IdeDisk *device0, *device1; + IdeDisk *device0 = nullptr, *device1 = nullptr; /** Currently selected disk */ - IdeDisk *selected; + IdeDisk *selected = nullptr; - bool selectBit; + bool selectBit = false; void select(bool select_device_1) @@ -113,8 +112,7 @@ class IdeController : public PciDevice void accessControl(Addr offset, int size, uint8_t *data, bool read); void accessBMI(Addr offset, int size, uint8_t *data, bool read); - Channel(std::string newName, Addr _cmdSize, Addr _ctrlSize); - ~Channel(); + Channel(std::string newName); void serialize(const std::string &base, std::ostream &os) const; void unserialize(const std::string &base, CheckpointIn &cp); @@ -123,19 +121,12 @@ class IdeController : public PciDevice Channel primary; Channel secondary; - /** Bus master interface (BMI) registers */ - Addr bmiAddr, bmiSize; - /** Registers used in device specific PCI configuration */ uint16_t primaryTiming, secondaryTiming; - uint8_t deviceTiming; - uint8_t udmaControl; - uint16_t udmaTiming; - uint16_t ideConfig; - - // Internal management variables - bool ioEnabled; - bool bmEnabled; + uint8_t deviceTiming = 0; + uint8_t udmaControl = 0; + uint16_t udmaTiming = 0; + uint16_t ideConfig = 0; uint32_t ioShift, ctrlOffset; diff --git a/src/dev/virtio/VirtIO.py b/src/dev/virtio/VirtIO.py index ed8cffa53..56c924802 100644 --- a/src/dev/virtio/VirtIO.py +++ b/src/dev/virtio/VirtIO.py @@ -39,7 +39,7 @@ from m5.SimObject import SimObject from m5.params import * from m5.proxy import * from m5.objects.Device import PioDevice -from m5.objects.PciDevice import PciDevice +from m5.objects.PciDevice import PciDevice, PciIoBar class VirtIODeviceBase(SimObject): @@ -68,7 +68,7 @@ class PciVirtIO(PciDevice): ClassCode = 0xff # Misc device - BAR0 = 0x00000001 # Anywhere in 32-bit space; IOREG - BAR0Size = '0B' # Overridden by the device model + # The size is overridden by the device model. + BAR0 = PciIoBar(size='4B') InterruptPin = 0x01 # Use #INTA diff --git a/src/dev/virtio/pci.cc b/src/dev/virtio/pci.cc index 34f27bb22..9fcc09a6b 100644 --- a/src/dev/virtio/pci.cc +++ b/src/dev/virtio/pci.cc @@ -53,7 +53,7 @@ PciVirtIO::PciVirtIO(const Params ¶ms) // two. Nothing else is supported. Therefore, we need to force // that alignment here. We do not touch vio.configSize as this is // used to check accesses later on. - BARSize[0] = alignToPowerOfTwo(BAR0_SIZE_BASE + vio.configSize); + BARs[0]->size(alignToPowerOfTwo(BAR0_SIZE_BASE + vio.configSize)); vio.registerKickCallback([this]() { kick(); }); } diff --git a/src/dev/x86/SouthBridge.py b/src/dev/x86/SouthBridge.py index 095f88bc9..3768215bb 100644 --- a/src/dev/x86/SouthBridge.py +++ b/src/dev/x86/SouthBridge.py @@ -33,6 +33,7 @@ from m5.objects.I8237 import I8237 from m5.objects.I8254 import I8254 from m5.objects.I8259 import I8259 from m5.objects.Ide import IdeController +from m5.objects.PciDevice import PciLegacyIoBar, PciIoBar from m5.objects.PcSpeaker import PcSpeaker from m5.SimObject import SimObject @@ -66,22 +67,14 @@ class SouthBridge(SimObject): # IDE controller ide = IdeController(disks=[], pci_func=0, pci_dev=4, pci_bus=0) - ide.BAR0 = 0x1f0 - ide.BAR0LegacyIO = True - ide.BAR1 = 0x3f4 - ide.BAR1Size = '3B' - ide.BAR1LegacyIO = True - ide.BAR2 = 0x170 - ide.BAR2LegacyIO = True - ide.BAR3 = 0x374 - ide.BAR3Size = '3B' - ide.BAR3LegacyIO = True - ide.BAR4 = 1 + ide.BAR0 = PciLegacyIoBar(addr=0x1f0, size='8B') + ide.BAR1 = PciLegacyIoBar(addr=0x3f4, size='3B') + ide.BAR2 = PciLegacyIoBar(addr=0x170, size='8B') + ide.BAR3 = PciLegacyIoBar(addr=0x374, size='3B') ide.Command = 0 ide.ProgIF = 0x80 ide.InterruptLine = 14 ide.InterruptPin = 1 - ide.LegacyIOBase = x86IOAddress(0) def attachIO(self, bus, dma_ports): # Route interrupt signals -- 2.30.2