diff options
author | Geoffrey Blake <Geoffrey.Blake@arm.com> | 2013-10-31 13:41:13 -0500 |
---|---|---|
committer | Geoffrey Blake <Geoffrey.Blake@arm.com> | 2013-10-31 13:41:13 -0500 |
commit | be4aa2b6ba0b70b13df2ad84a372320c5a7ea939 (patch) | |
tree | ff113f004f0efc3d9aaf708ead4c746ff4a7c8ae /src/dev/ide_disk.cc | |
parent | fb0496498d06def8e9b1b2ae2099b3013716b7dc (diff) | |
download | gem5-be4aa2b6ba0b70b13df2ad84a372320c5a7ea939.tar.xz |
dev: Fix race conditions in IDE device on newer kernels
Newer linux kernels and distros exercise more functionality in the IDE device
than previously, exposing 2 races. The first race is the handling of aborted
DMA commands would immediately report the device is ready back to the kernel
and cause already in flight commands to assert the simulator when they returned
and discovered an inconsitent device state. The second race was due to the
Status register not being handled correctly, the interrupt status bit would get
stuck at 1 and the driver eventually views this as a bad state and logs the
condition to the terminal. This patch fixes these two conditions by making the
device handle aborted commands gracefully and properly handles clearing the
interrupt status bit in the Status register.
Diffstat (limited to 'src/dev/ide_disk.cc')
-rw-r--r-- | src/dev/ide_disk.cc | 69 |
1 files changed, 65 insertions, 4 deletions
diff --git a/src/dev/ide_disk.cc b/src/dev/ide_disk.cc index bc7210d42..b910c8a6a 100644 --- a/src/dev/ide_disk.cc +++ b/src/dev/ide_disk.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2013 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. * @@ -143,6 +155,7 @@ IdeDisk::reset(int id) drqBytesLeft = 0; dmaRead = false; intrPending = false; + dmaAborted = false; // set the device state to idle dmaState = Dma_Idle; @@ -319,6 +332,12 @@ IdeDisk::writeControl(const Addr offset, int size, const uint8_t *data) void IdeDisk::doDmaTransfer() { + if (dmaAborted) { + DPRINTF(IdeDisk, "DMA Aborted before reading PRD entry\n"); + updateState(ACT_CMD_ERROR); + return; + } + if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n", dmaState, devState); @@ -334,6 +353,11 @@ IdeDisk::doDmaTransfer() void IdeDisk::dmaPrdReadDone() { + if (dmaAborted) { + DPRINTF(IdeDisk, "DMA Aborted while reading PRD entry\n"); + updateState(ACT_CMD_ERROR); + return; + } DPRINTF(IdeDisk, "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n", @@ -396,6 +420,14 @@ IdeDisk::regStats() void IdeDisk::doDmaRead() { + if (dmaAborted) { + DPRINTF(IdeDisk, "DMA Aborted in middle of Dma Read\n"); + if (dmaReadCG) + delete dmaReadCG; + dmaReadCG = NULL; + updateState(ACT_CMD_ERROR); + return; + } if (!dmaReadCG) { // clear out the data buffer @@ -427,10 +459,8 @@ IdeDisk::doDmaRead() void IdeDisk::dmaReadDone() { - uint32_t bytesWritten = 0; - // write the data to the disk image for (bytesWritten = 0; bytesWritten < curPrd.getByteCount(); bytesWritten += SectorSize) { @@ -475,7 +505,14 @@ IdeDisk::doDmaDataWrite() void IdeDisk::doDmaWrite() { - DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n"); + if (dmaAborted) { + DPRINTF(IdeDisk, "DMA Aborted while doing DMA Write\n"); + if (dmaWriteCG) + delete dmaWriteCG; + dmaWriteCG = NULL; + updateState(ACT_CMD_ERROR); + return; + } if (!dmaWriteCG) { // clear out the data buffer dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(), @@ -980,7 +1017,10 @@ IdeDisk::updateState(DevAction_t action) break; case Transfer_Data_Dma: - if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) { + if (action == ACT_CMD_ERROR) { + dmaAborted = true; + devState = Device_Dma_Abort; + } else if (action == ACT_DMA_DONE) { // clear the BSY bit setComplete(); // set the seek bit @@ -997,6 +1037,25 @@ IdeDisk::updateState(DevAction_t action) } break; + case Device_Dma_Abort: + if (action == ACT_CMD_ERROR) { + setComplete(); + status |= STATUS_SEEK_BIT; + ctrl->setDmaComplete(this); + dmaAborted = false; + dmaState = Dma_Idle; + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else { + DPRINTF(IdeDisk, "Disk still busy aborting previous DMA command\n"); + } + break; + default: panic("Unknown IDE device state: %#x\n", devState); } @@ -1074,6 +1133,7 @@ IdeDisk::serialize(ostream &os) SERIALIZE_SCALAR(curSector); SERIALIZE_SCALAR(dmaRead); SERIALIZE_SCALAR(intrPending); + SERIALIZE_SCALAR(dmaAborted); SERIALIZE_ENUM(devState); SERIALIZE_ENUM(dmaState); SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); @@ -1126,6 +1186,7 @@ IdeDisk::unserialize(Checkpoint *cp, const string §ion) UNSERIALIZE_SCALAR(curSector); UNSERIALIZE_SCALAR(dmaRead); UNSERIALIZE_SCALAR(intrPending); + UNSERIALIZE_SCALAR(dmaAborted); UNSERIALIZE_ENUM(devState); UNSERIALIZE_ENUM(dmaState); UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); |