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 <gabeblack@google.com> Maintainer: Gabe Black <gabeblack@google.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -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<uint8_t>();
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user