diff options
-rw-r--r-- | dev/ide_ctrl.cc | 108 | ||||
-rw-r--r-- | dev/ide_ctrl.hh | 61 | ||||
-rw-r--r-- | dev/ide_disk.cc | 223 | ||||
-rw-r--r-- | dev/ide_disk.hh | 20 |
4 files changed, 332 insertions, 80 deletions
diff --git a/dev/ide_ctrl.cc b/dev/ide_ctrl.cc index f038aceca..a21cf12d7 100644 --- a/dev/ide_ctrl.cc +++ b/dev/ide_ctrl.cc @@ -127,6 +127,72 @@ IdeController::~IdeController() } //// +// Utility functions +/// + +void +IdeController::parseAddr(const Addr &addr, Addr &offset, bool &primary, + RegType_t &type) +{ + offset = addr; + + if (addr >= pri_cmd_addr && addr < (pri_cmd_addr + pri_cmd_size)) { + offset -= pri_cmd_addr; + type = COMMAND_BLOCK; + primary = true; + } else if (addr >= pri_ctrl_addr && + addr < (pri_ctrl_addr + pri_ctrl_size)) { + offset -= pri_ctrl_addr; + type = CONTROL_BLOCK; + primary = true; + } else if (addr >= sec_cmd_addr && + addr < (sec_cmd_addr + sec_cmd_size)) { + offset -= sec_cmd_addr; + type = COMMAND_BLOCK; + primary = false; + } else if (addr >= sec_ctrl_addr && + addr < (sec_ctrl_addr + sec_ctrl_size)) { + offset -= sec_ctrl_addr; + type = CONTROL_BLOCK; + primary = false; + } else if (addr >= bmi_addr && addr < (bmi_addr + bmi_size)) { + offset -= bmi_addr; + type = BMI_BLOCK; + primary = (offset < BMIC1) ? true : false; + } else { + panic("IDE controller access to invalid address: %#x\n", addr); + } +} + +int +IdeController::getDisk(bool primary) +{ + int disk = 0; + uint8_t *devBit = &dev[0]; + + if (!primary) { + disk += 2; + devBit = &dev[1]; + } + + disk += *devBit; + + assert(*devBit == 0 || *devBit == 1); + + return disk; +} + +int +IdeController::getDisk(IdeDisk *diskPtr) +{ + for (int i = 0; i < 4; i++) { + if ((long)diskPtr == (long)disks[i]) + return i; + } + return -1; +} + +//// // Command completion //// @@ -523,11 +589,53 @@ IdeController::write(MemReqPtr &req, const uint8_t *data) void IdeController::serialize(std::ostream &os) { + // Serialize register addresses and sizes + SERIALIZE_SCALAR(pri_cmd_addr); + SERIALIZE_SCALAR(pri_cmd_size); + SERIALIZE_SCALAR(pri_ctrl_addr); + SERIALIZE_SCALAR(pri_ctrl_size); + SERIALIZE_SCALAR(sec_cmd_addr); + SERIALIZE_SCALAR(sec_cmd_size); + SERIALIZE_SCALAR(sec_ctrl_addr); + SERIALIZE_SCALAR(sec_ctrl_size); + SERIALIZE_SCALAR(bmi_addr); + SERIALIZE_SCALAR(bmi_size); + + // Serialize registers + SERIALIZE_ARRAY(bmi_regs, 16); + SERIALIZE_ARRAY(dev, 2); + SERIALIZE_ARRAY(pci_regs, 8); + + // Serialize internal state + SERIALIZE_SCALAR(io_enabled); + SERIALIZE_SCALAR(bm_enabled); + SERIALIZE_ARRAY(cmd_in_progress, 4); } void IdeController::unserialize(Checkpoint *cp, const std::string §ion) { + // Unserialize register addresses and sizes + UNSERIALIZE_SCALAR(pri_cmd_addr); + UNSERIALIZE_SCALAR(pri_cmd_size); + UNSERIALIZE_SCALAR(pri_ctrl_addr); + UNSERIALIZE_SCALAR(pri_ctrl_size); + UNSERIALIZE_SCALAR(sec_cmd_addr); + UNSERIALIZE_SCALAR(sec_cmd_size); + UNSERIALIZE_SCALAR(sec_ctrl_addr); + UNSERIALIZE_SCALAR(sec_ctrl_size); + UNSERIALIZE_SCALAR(bmi_addr); + UNSERIALIZE_SCALAR(bmi_size); + + // Unserialize registers + UNSERIALIZE_ARRAY(bmi_regs, 16); + UNSERIALIZE_ARRAY(dev, 2); + UNSERIALIZE_ARRAY(pci_regs, 8); + + // Unserialize internal state + UNSERIALIZE_SCALAR(io_enabled); + UNSERIALIZE_SCALAR(bm_enabled); + UNSERIALIZE_ARRAY(cmd_in_progress, 4); } #ifndef DOXYGEN_SHOULD_SKIP_THIS diff --git a/dev/ide_ctrl.hh b/dev/ide_ctrl.hh index b4de97036..9698724c1 100644 --- a/dev/ide_ctrl.hh +++ b/dev/ide_ctrl.hh @@ -27,7 +27,8 @@ */ /** @file - * Simple PCI IDE controller with bus mastering capability + * Simple PCI IDE controller with bus mastering capability and UDMA + * modeled after controller in the Intel PIIX4 chip */ #ifndef __IDE_CTRL_HH__ @@ -139,65 +140,13 @@ class IdeController : public PciDev private: /** Parse the access address to pass on to device */ void parseAddr(const Addr &addr, Addr &offset, bool &primary, - RegType_t &type) - { - offset = addr; - - if (addr >= pri_cmd_addr && addr < (pri_cmd_addr + pri_cmd_size)) { - offset -= pri_cmd_addr; - type = COMMAND_BLOCK; - primary = true; - } else if (addr >= pri_ctrl_addr && - addr < (pri_ctrl_addr + pri_ctrl_size)) { - offset -= pri_ctrl_addr; - type = CONTROL_BLOCK; - primary = true; - } else if (addr >= sec_cmd_addr && - addr < (sec_cmd_addr + sec_cmd_size)) { - offset -= sec_cmd_addr; - type = COMMAND_BLOCK; - primary = false; - } else if (addr >= sec_ctrl_addr && - addr < (sec_ctrl_addr + sec_ctrl_size)) { - offset -= sec_ctrl_addr; - type = CONTROL_BLOCK; - primary = false; - } else if (addr >= bmi_addr && addr < (bmi_addr + bmi_size)) { - offset -= bmi_addr; - type = BMI_BLOCK; - primary = (offset < BMIC1) ? true : false; - } else { - panic("IDE controller access to invalid address: %#x\n", addr); - } - }; + RegType_t &type); /** Select the disk based on the channel and device bit */ - int getDisk(bool primary) - { - int disk = 0; - uint8_t *devBit = &dev[0]; - - if (!primary) { - disk += 2; - devBit = &dev[1]; - } - - disk += *devBit; - - assert(*devBit == 0 || *devBit == 1); - - return disk; - }; + int getDisk(bool primary); /** Select the disk based on a pointer */ - int getDisk(IdeDisk *diskPtr) - { - for (int i = 0; i < 4; i++) { - if ((long)diskPtr == (long)disks[i]) - return i; - } - return -1; - } + int getDisk(IdeDisk *diskPtr); public: /** diff --git a/dev/ide_disk.cc b/dev/ide_disk.cc index 38d6a919b..0d12e797d 100644 --- a/dev/ide_disk.cc +++ b/dev/ide_disk.cc @@ -61,6 +61,7 @@ IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, dmaWriteWaitEvent(this), dmaPrdReadEvent(this), dmaReadEvent(this), dmaWriteEvent(this) { + // calculate disk delay in microseconds diskDelay = (delay * ticksPerSecond / 100000); // initialize the data buffer and shadow registers @@ -70,6 +71,7 @@ IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, memset(&cmdReg, 0, sizeof(CommandReg_t)); memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); + dmaInterfaceBytes = 0; curPrdAddr = 0; curSector = 0; curCommand = 0; @@ -154,8 +156,12 @@ IdeDisk::~IdeDisk() delete [] dataBuffer; } +//// +// Utility functions +//// + Addr -IdeDisk::pciToDma(Addr &pciAddr) +IdeDisk::pciToDma(Addr pciAddr) { if (ctrl) return ctrl->tsunami->pchip->translatePciToDma(pciAddr); @@ -163,6 +169,29 @@ IdeDisk::pciToDma(Addr &pciAddr) panic("Access to unset controller!\n"); } +uint32_t +IdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft) +{ + uint32_t bytesInPage = 0; + + // First calculate how many bytes could be in the page + if (bytesLeft > ALPHA_PGBYTES) + bytesInPage = ALPHA_PGBYTES; + else + bytesInPage = bytesLeft; + + // Next, see if we have crossed a page boundary, and adjust + Addr upperBound = curAddr + bytesInPage; + Addr pageBound = alpha_trunc_page(curAddr) + ALPHA_PGBYTES; + + assert(upperBound >= curAddr && "DMA read wraps around address space!\n"); + + if (upperBound >= pageBound) + bytesInPage = pageBound - curAddr; + + return bytesInPage; +} + //// // Device registers read/write //// @@ -297,7 +326,7 @@ IdeDisk::dmaPrdReadDone() void IdeDisk::doDmaRead() { - Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize); + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); if (dmaInterface) { if (dmaInterface->busy()) { @@ -306,9 +335,14 @@ IdeDisk::doDmaRead() return; } - Addr dmaAddr = - ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr()); - dmaInterface->doDMA(Read, dmaAddr, curPrd.getByteCount(), + Addr dmaAddr = pciToDma(curPrd.getBaseAddr()); + + uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(), + (uint32_t)curPrd.getByteCount()); + + dmaInterfaceBytes = bytesInPage; + + dmaInterface->doDMA(Read, dmaAddr, bytesInPage, curTick + totalDiskDelay, &dmaReadEvent); } else { // schedule dmaReadEvent with sectorDelay (dmaReadDone) @@ -323,6 +357,28 @@ IdeDisk::dmaReadDone() Addr curAddr = 0, dmaAddr = 0; uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0; + // continue to use the DMA interface until all pages are read + if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) { + // see if the interface is busy + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes; + curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes; + dmaAddr = pciToDma(curAddr); + + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); + dmaInterfaceBytes += bytesInPage; + + dmaInterface->doDMA(Read, dmaAddr, bytesInPage, + curTick, &dmaReadEvent); + + return; + } + // set initial address curAddr = curPrd.getBaseAddr(); @@ -338,15 +394,9 @@ IdeDisk::dmaReadDone() // calculate how many bytes are in the current page bytesLeft = curPrd.getByteCount() - bytesWritten; - bytesInPage = (bytesLeft > ALPHA_PGBYTES) ? ALPHA_PGBYTES : bytesLeft; - // check to make sure we don't cross a page boundary - if ((curAddr + bytesInPage) > - (alpha_trunc_page(curAddr) + ALPHA_PGBYTES)) - - bytesInPage = alpha_round_page(curAddr) - curAddr; + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); // copy the data from memory into the data buffer - /** @todo Use real DMA with interfaces here */ memcpy((void *)(dataBuffer + bytesWritten), physmem->dma_addr(dmaAddr, bytesInPage), bytesInPage); @@ -359,9 +409,10 @@ IdeDisk::dmaReadDone() // write the data to the disk image for (bytesWritten = 0; bytesWritten < curPrd.getByteCount(); - bytesWritten += SectorSize) + bytesWritten += SectorSize) { writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); + } #if 0 // actually copy the data from memory to data buffer @@ -397,7 +448,7 @@ IdeDisk::dmaReadDone() void IdeDisk::doDmaWrite() { - Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize); + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); if (dmaInterface) { if (dmaInterface->busy()) { @@ -406,10 +457,15 @@ IdeDisk::doDmaWrite() return; } - Addr dmaAddr = - ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr()); + Addr dmaAddr = pciToDma(curPrd.getBaseAddr()); + + uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(), + (uint32_t)curPrd.getByteCount()); + + dmaInterfaceBytes = bytesInPage; + dmaInterface->doDMA(WriteInvalidate, dmaAddr, - curPrd.getByteCount(), curTick + totalDiskDelay, + bytesInPage, curTick + totalDiskDelay, &dmaWriteEvent); } else { // schedule event with disk delay (dmaWriteDone) @@ -423,6 +479,29 @@ IdeDisk::dmaWriteDone() Addr curAddr = 0, pageAddr = 0, dmaAddr = 0; uint32_t bytesRead = 0, bytesInPage = 0; + // continue to use the DMA interface until all pages are read + if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) { + // see if the interface is busy + if (dmaInterface->busy()) { + // reschedule after waiting period + dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } + + uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes; + curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes; + dmaAddr = pciToDma(curAddr); + + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); + dmaInterfaceBytes += bytesInPage; + + dmaInterface->doDMA(WriteInvalidate, dmaAddr, + bytesInPage, curTick, + &dmaWriteEvent); + + return; + } + // setup the initial page and DMA address curAddr = curPrd.getBaseAddr(); pageAddr = alpha_trunc_page(curAddr); @@ -435,7 +514,6 @@ IdeDisk::dmaWriteDone() // see if we have crossed into a new page if (pageAddr != alpha_trunc_page(curAddr)) { // write the data to memory - /** @todo Do real DMA using interfaces here */ memcpy(physmem->dma_addr(dmaAddr, bytesInPage), (void *)(dataBuffer + (bytesRead - bytesInPage)), bytesInPage); @@ -459,7 +537,6 @@ IdeDisk::dmaWriteDone() } // write the last page worth read to memory - /** @todo Do real DMA using interfaces here */ if (bytesInPage != 0) { memcpy(physmem->dma_addr(dmaAddr, bytesInPage), (void *)(dataBuffer + (bytesRead - bytesInPage)), @@ -521,7 +598,7 @@ IdeDisk::startDma(const uint32_t &prdTableBase) if (devState != Transfer_Data_Dma) panic("Inconsistent device state for DMA start!\n"); - curPrdAddr = ctrl->tsunami->pchip->translatePciToDma(prdTableBase); + curPrdAddr = pciToDma((Addr)prdTableBase); dmaState = Dma_Transfer; @@ -927,11 +1004,115 @@ IdeDisk::updateState(DevAction_t action) void IdeDisk::serialize(ostream &os) { + // Check all outstanding events to see if they are scheduled + // these are all mutually exclusive + Tick reschedule = 0; + Events_t event = None; + + if (dmaTransferEvent.scheduled()) { + reschedule = dmaTransferEvent.when(); + event = Transfer; + } else if (dmaReadWaitEvent.scheduled()) { + reschedule = dmaReadWaitEvent.when(); + event = ReadWait; + } else if (dmaWriteWaitEvent.scheduled()) { + reschedule = dmaWriteWaitEvent.when(); + event = WriteWait; + } else if (dmaPrdReadEvent.scheduled()) { + reschedule = dmaPrdReadEvent.when(); + event = PrdRead; + } else if (dmaReadEvent.scheduled()) { + reschedule = dmaReadEvent.when(); + event = DmaRead; + } else if (dmaWriteEvent.scheduled()) { + reschedule = dmaWriteEvent.when(); + event = DmaWrite; + } + + SERIALIZE_SCALAR(reschedule); + SERIALIZE_ENUM(event); + + // Serialize device registers + SERIALIZE_SCALAR(cmdReg.data0); + SERIALIZE_SCALAR(cmdReg.data1); + SERIALIZE_SCALAR(cmdReg.sec_count); + SERIALIZE_SCALAR(cmdReg.sec_num); + SERIALIZE_SCALAR(cmdReg.cyl_low); + SERIALIZE_SCALAR(cmdReg.cyl_high); + SERIALIZE_SCALAR(cmdReg.drive); + SERIALIZE_SCALAR(cmdReg.status); + SERIALIZE_SCALAR(nIENBit); + SERIALIZE_SCALAR(devID); + + // Serialize the PRD related information + SERIALIZE_SCALAR(curPrd.entry.baseAddr); + SERIALIZE_SCALAR(curPrd.entry.byteCount); + SERIALIZE_SCALAR(curPrd.entry.endOfTable); + SERIALIZE_SCALAR(curPrdAddr); + + // Serialize current transfer related information + SERIALIZE_SCALAR(cmdBytesLeft); + SERIALIZE_SCALAR(drqBytesLeft); + SERIALIZE_SCALAR(curSector); + SERIALIZE_SCALAR(curCommand); + SERIALIZE_SCALAR(dmaRead); + SERIALIZE_SCALAR(dmaInterfaceBytes); + SERIALIZE_SCALAR(intrPending); + SERIALIZE_ENUM(devState); + SERIALIZE_ENUM(dmaState); + SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); } void IdeDisk::unserialize(Checkpoint *cp, const string §ion) { + // Reschedule events that were outstanding + // these are all mutually exclusive + Tick reschedule = 0; + Events_t event = None; + + UNSERIALIZE_SCALAR(reschedule); + UNSERIALIZE_ENUM(event); + + switch (event) { + case None : break; + case Transfer : dmaTransferEvent.schedule(reschedule); break; + case ReadWait : dmaReadWaitEvent.schedule(reschedule); break; + case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break; + case PrdRead : dmaPrdReadEvent.schedule(reschedule); break; + case DmaRead : dmaReadEvent.schedule(reschedule); break; + case DmaWrite : dmaWriteEvent.schedule(reschedule); break; + } + + // Unserialize device registers + UNSERIALIZE_SCALAR(cmdReg.data0); + UNSERIALIZE_SCALAR(cmdReg.data1); + UNSERIALIZE_SCALAR(cmdReg.sec_count); + UNSERIALIZE_SCALAR(cmdReg.sec_num); + UNSERIALIZE_SCALAR(cmdReg.cyl_low); + UNSERIALIZE_SCALAR(cmdReg.cyl_high); + UNSERIALIZE_SCALAR(cmdReg.drive); + UNSERIALIZE_SCALAR(cmdReg.status); + UNSERIALIZE_SCALAR(nIENBit); + UNSERIALIZE_SCALAR(devID); + + // Unserialize the PRD related information + UNSERIALIZE_SCALAR(curPrd.entry.baseAddr); + UNSERIALIZE_SCALAR(curPrd.entry.byteCount); + UNSERIALIZE_SCALAR(curPrd.entry.endOfTable); + UNSERIALIZE_SCALAR(curPrdAddr); + + // Unserialize current transfer related information + UNSERIALIZE_SCALAR(cmdBytesLeft); + UNSERIALIZE_SCALAR(drqBytesLeft); + UNSERIALIZE_SCALAR(curSector); + UNSERIALIZE_SCALAR(curCommand); + UNSERIALIZE_SCALAR(dmaRead); + UNSERIALIZE_SCALAR(dmaInterfaceBytes); + UNSERIALIZE_SCALAR(intrPending); + UNSERIALIZE_ENUM(devState); + UNSERIALIZE_ENUM(dmaState); + UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); } #ifndef DOXYGEN_SHOULD_SKIP_THIS @@ -950,7 +1131,7 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk) INIT_PARAM(image, "Disk image"), INIT_PARAM(physmem, "Physical memory"), INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"), - INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in milliseconds", 0) + INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1) END_INIT_SIM_OBJECT_PARAMS(IdeDisk) diff --git a/dev/ide_disk.hh b/dev/ide_disk.hh index 88a492cbc..35e7404d5 100644 --- a/dev/ide_disk.hh +++ b/dev/ide_disk.hh @@ -94,7 +94,7 @@ class PrdTableEntry { #define STATUS_BSY_BIT 0x80 #define STATUS_DRDY_BIT 0x40 #define STATUS_DRQ_BIT 0x08 -#define DRIVE_LBA_BIT 0x40 +#define DRIVE_LBA_BIT 0x40 #define DEV0 (0) #define DEV1 (1) @@ -120,6 +120,16 @@ typedef struct CommandReg { }; } CommandReg_t; +typedef enum Events { + None = 0, + Transfer, + ReadWait, + WriteWait, + PrdRead, + DmaRead, + DmaWrite +} Events_t; + typedef enum DevAction { ACT_NONE = 0, ACT_CMD_WRITE, @@ -184,7 +194,7 @@ class IdeDisk : public SimObject PhysicalMemory *physmem; protected: - /** The disk delay in milliseconds. */ + /** The disk delay in microseconds. */ int diskDelay; private: @@ -214,6 +224,8 @@ class IdeDisk : public SimObject uint32_t curPrdAddr; /** PRD entry */ PrdTableEntry curPrd; + /** Number of bytes transfered by DMA interface for current transfer */ + uint32_t dmaInterfaceBytes; /** Device ID (master=0/slave=1) */ int devID; /** Interrupt pending */ @@ -313,7 +325,9 @@ class IdeDisk : public SimObject (cmdReg.cyl_low << 8) | (cmdReg.sec_num)); } - inline Addr pciToDma(Addr &pciAddr); + inline Addr pciToDma(Addr pciAddr); + + uint32_t bytesInDmaPage(Addr curAddr, uint32_t bytesLeft); /** * Serialize this object to the given output stream. |