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:
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user