dev: Rework how IDE controllers, channels and disks relate.

Disks now track the channel they're attached to so that that doesn't
have to be rediscovered by comparing points, channels know if they're
primary or secondary, and interrupts will now set the interrupt bit of
the channel they're associated with instead of always the primary.

Also the interrupt mechanism was adjusted slightly so that it's
implemented by a virtual function which knows whether the interrupt came
from the primary or secondary channel. That will make it possible to
implement separate interrupts, as required by the compatibility mode
which can be used with x86.

Change-Id: Ic5527c238ef7409153e80e4ab843a50be6e452c5
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/55584
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Reviewed-by: Matt Sinclair <mattdsinclair@gmail.com>
Maintainer: Matt Sinclair <mattdsinclair@gmail.com>
Reviewed-by: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabe Black
2022-01-15 21:30:53 -08:00
parent 0e0ca633a2
commit 7399bbc5b6
4 changed files with 159 additions and 129 deletions

View File

@@ -61,7 +61,9 @@ enum BMIRegOffset
BMIDescTablePtr = 0x4
};
IdeController::Channel::Channel(std::string newName) : _name(newName)
IdeController::Channel::Channel(std::string new_name, IdeController *new_ctrl,
bool new_primary) :
Named(new_name), ctrl(new_ctrl), primary(new_primary)
{
bmiRegs.reset();
bmiRegs.status.dmaCap0 = 1;
@@ -70,34 +72,28 @@ IdeController::Channel::Channel(std::string newName) : _name(newName)
IdeController::IdeController(const Params &p)
: PciDevice(p), configSpaceRegs(name() + ".config_space_regs"),
primary(name() + ".primary"),
secondary(name() + ".secondary"),
primary(name() + ".primary", this, true),
secondary(name() + ".secondary", this, false),
ioShift(p.io_shift), ctrlOffset(p.ctrl_offset)
{
panic_if(params().disks.size() > 3,
"IDE controllers support a maximum of 4 devices attached!");
// Assign the disks to channels
for (int i = 0; i < params().disks.size(); i++) {
if (!params().disks[i])
auto *disk = params().disks[i];
auto &channel = (i < 2) ? primary : secondary;
if (!disk)
continue;
switch (i) {
case 0:
primary.device0 = params().disks[0];
break;
case 1:
primary.device1 = params().disks[1];
break;
case 2:
secondary.device0 = params().disks[2];
break;
case 3:
secondary.device1 = params().disks[3];
break;
default:
panic("IDE controllers support a maximum "
"of 4 devices attached!\n");
}
if (i % 2 == 0)
channel.setDevice0(disk);
else
channel.setDevice1(disk);
// Arbitrarily set the chunk size to 4K.
params().disks[i]->setController(this, 4 * 1024);
disk->setChannel(&channel, 4 * 1024);
}
primary.select(false);
@@ -124,34 +120,38 @@ IdeController::ConfigSpaceRegs::unserialize(CheckpointIn &cp)
UNSERIALIZE_SCALAR(udmaTiming);
}
bool
IdeController::isDiskSelected(IdeDisk *diskPtr)
void
IdeController::Channel::postInterrupt()
{
return (primary.selected == diskPtr || secondary.selected == diskPtr);
bmiRegs.status.intStatus = 1;
_pendingInterrupt = true;
ctrl->postInterrupt(isPrimary());
}
void
IdeController::intrPost()
IdeController::Channel::clearInterrupt()
{
primary.bmiRegs.status.intStatus = 1;
PciDevice::intrPost();
bmiRegs.status.intStatus = 0;
_pendingInterrupt = false;
ctrl->clearInterrupt(isPrimary());
}
void
IdeController::setDmaComplete(IdeDisk *disk)
IdeController::postInterrupt(bool is_primary)
{
Channel *channel;
if (disk == primary.device0 || disk == primary.device1) {
channel = &primary;
} else if (disk == secondary.device0 || disk == secondary.device1) {
channel = &secondary;
} else {
panic("Unable to find disk based on pointer %#x\n", disk);
}
auto &other = is_primary ? secondary : primary;
// If an interrupt isn't already posted for the other channel...
if (!other.pendingInterrupt())
PciDevice::intrPost();
}
channel->bmiRegs.command.startStop = 0;
channel->bmiRegs.status.active = 0;
channel->bmiRegs.status.intStatus = 1;
void
IdeController::clearInterrupt(bool is_primary)
{
auto &other = is_primary ? secondary : primary;
// If the interrupt isn't still needed by the other channel...
if (!other.pendingInterrupt())
PciDevice::intrClear();
}
Tick
@@ -202,13 +202,13 @@ IdeController::Channel::accessCommand(Addr offset,
if (!read && offset == SelectOffset)
select(*data & SelectDevBit);
if (selected == NULL) {
if (selected() == NULL) {
assert(size == sizeof(uint8_t));
*data = 0;
} else if (read) {
selected->readCommand(offset, size, data);
selected()->readCommand(offset, size, data);
} else {
selected->writeCommand(offset, size, data);
selected()->writeCommand(offset, size, data);
}
}
@@ -216,13 +216,13 @@ void
IdeController::Channel::accessControl(Addr offset,
int size, uint8_t *data, bool read)
{
if (selected == NULL) {
if (selected() == NULL) {
assert(size == sizeof(uint8_t));
*data = 0;
} else if (read) {
selected->readControl(offset, size, data);
selected()->readControl(offset, size, data);
} else {
selected->writeControl(offset, size, data);
selected()->writeControl(offset, size, data);
}
}
@@ -248,19 +248,19 @@ IdeController::Channel::accessBMI(Addr offset,
oldVal.rw = newVal.rw;
if (oldVal.startStop != newVal.startStop) {
if (selected == NULL)
if (selected() == NULL)
panic("DMA start for disk which does not exist\n");
if (oldVal.startStop) {
DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
bmiRegs.status.active = 0;
selected->abortDma();
selected()->abortDma();
} else {
DPRINTF(IdeCtrl, "Starting DMA transfer\n");
bmiRegs.status.active = 1;
selected->startDma(letoh(bmiRegs.bmidtp));
selected()->startDma(letoh(bmiRegs.bmidtp));
}
}
@@ -375,6 +375,14 @@ IdeController::dispatchAccess(PacketPtr pkt, bool read)
pkt->makeAtomicResponse();
}
void
IdeController::Channel::setDmaComplete()
{
bmiRegs.command.startStop = 0;
bmiRegs.status.active = 0;
bmiRegs.status.intStatus = 1;
}
Tick
IdeController::read(PacketPtr pkt)
{