X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=dev%2Fide_disk.cc;h=f3760bd5efa67924125dfc266cf53b9d5dd230f6;hb=e9f3279334f714de7bbd1415377715cd00a763d5;hp=77e809ee67c0bb07f81bdf197b8c6db7cb7cae48;hpb=6807c319b02af31626ab2ed716da6341924eb857;p=gem5.git diff --git a/dev/ide_disk.cc b/dev/ide_disk.cc index 77e809ee6..f3760bd5e 100644 --- a/dev/ide_disk.cc +++ b/dev/ide_disk.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 The Regents of The University of Michigan + * Copyright (c) 2004 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,31 +50,22 @@ #include "sim/builder.hh" #include "sim/sim_object.hh" #include "sim/universe.hh" +#include "targetarch/isa_traits.hh" using namespace std; IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, int id, int delay) - : SimObject(name), ctrl(NULL), image(img), physmem(phys), dmaTransferEvent(this), - dmaReadWaitEvent(this), dmaWriteWaitEvent(this), dmaPrdReadEvent(this), + : SimObject(name), ctrl(NULL), image(img), physmem(phys), + dmaTransferEvent(this), dmaReadWaitEvent(this), + dmaWriteWaitEvent(this), dmaPrdReadEvent(this), dmaReadEvent(this), dmaWriteEvent(this) { - diskDelay = (delay * ticksPerSecond / 1000) / image->size(); + // Reset the device state + reset(id); - // initialize the data buffer and shadow registers - dataBuffer = new uint8_t[MAX_DMA_SIZE]; - - memset(dataBuffer, 0, MAX_DMA_SIZE); - memset(&cmdReg, 0, sizeof(CommandReg_t)); - memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); - - curPrdAddr = 0; - curSector = 0; - curCommand = 0; - cmdBytesLeft = 0; - drqBytesLeft = 0; - dmaRead = false; - intrPending = false; + // calculate disk delay in microseconds + diskDelay = (delay * ticksPerSecond / 100000); // fill out the drive ID structure memset(&driveID, 0, sizeof(struct hd_driveid)); @@ -128,6 +119,32 @@ IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, driveID.dma_ultra = 0x10; // Statically set hardware config word driveID.hw_config = 0x4001; +} + +IdeDisk::~IdeDisk() +{ + // destroy the data buffer + delete [] dataBuffer; +} + +void +IdeDisk::reset(int id) +{ + // initialize the data buffer and shadow registers + dataBuffer = new uint8_t[MAX_DMA_SIZE]; + + memset(dataBuffer, 0, MAX_DMA_SIZE); + memset(&cmdReg, 0, sizeof(CommandReg_t)); + memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); + + dmaInterfaceBytes = 0; + curPrdAddr = 0; + curSector = 0; + cmdBytes = 0; + cmdBytesLeft = 0; + drqBytesLeft = 0; + dmaRead = false; + intrPending = false; // set the device state to idle dmaState = Dma_Idle; @@ -143,13 +160,49 @@ IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, } // set the device ready bit - cmdReg.status |= STATUS_DRDY_BIT; + status = STATUS_DRDY_BIT; } -IdeDisk::~IdeDisk() +//// +// Utility functions +//// + +bool +IdeDisk::isDEVSelect() { - // destroy the data buffer - delete [] dataBuffer; + return ctrl->isDiskSelected(this); +} + +Addr +IdeDisk::pciToDma(Addr pciAddr) +{ + if (ctrl) + return ctrl->tsunami->pchip->translatePciToDma(pciAddr); + else + 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 > TheISA::PageBytes) + bytesInPage = TheISA::PageBytes; + else + bytesInPage = bytesLeft; + + // Next, see if we have crossed a page boundary, and adjust + Addr upperBound = curAddr + bytesInPage; + Addr pageBound = TheISA::TruncPage(curAddr) + TheISA::PageBytes; + + assert(upperBound >= curAddr && "DMA read wraps around address space!\n"); + + if (upperBound >= pageBound) + bytesInPage = pageBound - curAddr; + + return bytesInPage; } //// @@ -176,6 +229,7 @@ IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data) // determine if an action needs to be taken on the state machine if (offset == STATUS_OFFSET) { action = ACT_STAT_READ; + *data = status; // status is in a shadow, explicity copy } else if (offset == DATA_OFFSET) { if (byte) action = ACT_DATA_READ_BYTE; @@ -190,7 +244,7 @@ IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data) if (!byte) panic("Invalid 16-bit read from control block\n"); - *data = ((uint8_t *)&cmdReg)[STATUS_OFFSET]; + *data = status; } if (action != ACT_NONE) @@ -222,6 +276,8 @@ IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data) action = ACT_DATA_WRITE_BYTE; else action = ACT_DATA_WRITE_SHORT; + } else if (offset == SELECT_OFFSET) { + action = ACT_SELECT_WRITE; } } else { @@ -231,8 +287,13 @@ IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data) if (!byte) panic("Invalid 16-bit write to control block\n"); - if (*data & CONTROL_RST_BIT) - panic("Software reset not supported!\n"); + if (*data & CONTROL_RST_BIT) { + // force the device into the reset state + devState = Device_Srst; + action = ACT_SRST_SET; + } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) { + action = ACT_SRST_CLEAR; + } nIENBit = (*data & CONTROL_IEN_BIT) ? true : false; } @@ -275,7 +336,13 @@ IdeDisk::dmaPrdReadDone() physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)), sizeof(PrdEntry_t)); - curPrdAddr += sizeof(PrdEntry_t); + DPRINTF(IdeDisk, "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n", + curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()), + curPrd.getByteCount(), (cmdBytesLeft/SectorSize), + curPrd.getEOT(), curSector); + + // the prd pointer has already been translated, so just do an increment + curPrdAddr = curPrdAddr + sizeof(PrdEntry_t); if (dmaRead) doDmaRead(); @@ -286,7 +353,7 @@ IdeDisk::dmaPrdReadDone() void IdeDisk::doDmaRead() { - Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize); + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); if (dmaInterface) { if (dmaInterface->busy()) { @@ -295,9 +362,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) @@ -308,27 +380,69 @@ IdeDisk::doDmaRead() void IdeDisk::dmaReadDone() { - // actually copy the data from memory to data buffer - Addr dmaAddr = - ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr()); - memcpy((void *)dataBuffer, - physmem->dma_addr(dmaAddr, curPrd.getByteCount()), - curPrd.getByteCount()); - uint32_t bytesWritten = 0; + 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(); + + // clear out the data buffer + memset(dataBuffer, 0, MAX_DMA_SIZE); + + // read the data from memory via DMA into a data buffer while (bytesWritten < curPrd.getByteCount()) { if (cmdBytesLeft <= 0) - panic("DMA data is larger than # sectors specified\n"); + panic("DMA data is larger than # of sectors specified\n"); - writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); + dmaAddr = pciToDma(curAddr); - bytesWritten += SectorSize; - cmdBytesLeft -= SectorSize; + // calculate how many bytes are in the current page + bytesLeft = curPrd.getByteCount() - bytesWritten; + bytesInPage = bytesInDmaPage(curAddr, bytesLeft); + + // copy the data from memory into the data buffer + memcpy((void *)(dataBuffer + bytesWritten), + physmem->dma_addr(dmaAddr, bytesInPage), + bytesInPage); + + curAddr += bytesInPage; + bytesWritten += bytesInPage; + cmdBytesLeft -= bytesInPage; + } + + // write the data to the disk image + for (bytesWritten = 0; + bytesWritten < curPrd.getByteCount(); + bytesWritten += SectorSize) { + + writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); } // check for the EOT - if (curPrd.getEOT()){ + if (curPrd.getEOT()) { assert(cmdBytesLeft == 0); dmaState = Dma_Idle; updateState(ACT_DMA_DONE); @@ -340,7 +454,7 @@ IdeDisk::dmaReadDone() void IdeDisk::doDmaWrite() { - Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize); + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); if (dmaInterface) { if (dmaInterface->busy()) { @@ -349,10 +463,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) @@ -363,26 +482,72 @@ IdeDisk::doDmaWrite() void IdeDisk::dmaWriteDone() { - uint32_t bytesRead = 0; + 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 = TheISA::TruncPage(curAddr); + dmaAddr = pciToDma(curAddr); // clear out the data buffer memset(dataBuffer, 0, MAX_DMA_SIZE); while (bytesRead < curPrd.getByteCount()) { + // see if we have crossed into a new page + if (pageAddr != TheISA::TruncPage(curAddr)) { + // write the data to memory + memcpy(physmem->dma_addr(dmaAddr, bytesInPage), + (void *)(dataBuffer + (bytesRead - bytesInPage)), + bytesInPage); + + // update the DMA address and page address + pageAddr = TheISA::TruncPage(curAddr); + dmaAddr = pciToDma(curAddr); + + bytesInPage = 0; + } + if (cmdBytesLeft <= 0) panic("DMA requested data is larger than # sectors specified\n"); readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead)); + curAddr += SectorSize; bytesRead += SectorSize; + bytesInPage += SectorSize; cmdBytesLeft -= SectorSize; } - // copy the data to memory - Addr dmaAddr = - ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr()); - memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()), - (void *)dataBuffer, curPrd.getByteCount()); + // write the last page worth read to memory + if (bytesInPage != 0) { + memcpy(physmem->dma_addr(dmaAddr, bytesInPage), + (void *)(dataBuffer + (bytesRead - bytesInPage)), + bytesInPage); + } // check for the EOT if (curPrd.getEOT()) { @@ -431,7 +596,8 @@ 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); + // PRD base address is given by bits 31:2 + curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3))); dmaState = Dma_Transfer; @@ -458,9 +624,6 @@ IdeDisk::startCommand() uint32_t size = 0; dmaRead = false; - // copy the command to the shadow - curCommand = cmdReg.command; - // Decode commands switch (cmdReg.command) { // Supported non-data commands @@ -477,6 +640,7 @@ IdeDisk::startCommand() case WIN_RECAL: case WIN_SPECIFY: + case WIN_STANDBYNOW1: case WIN_FLUSH_CACHE: case WIN_VERIFY: case WIN_SEEK: @@ -488,7 +652,7 @@ IdeDisk::startCommand() // Supported PIO data-in commands case WIN_IDENTIFY: - cmdBytesLeft = drqBytesLeft = sizeof(struct hd_driveid); + cmdBytes = cmdBytesLeft = sizeof(struct hd_driveid); devState = Prepare_Data_In; action = ACT_DATA_READY; break; @@ -499,13 +663,13 @@ IdeDisk::startCommand() panic("Attempt to perform CHS access, only supports LBA\n"); if (cmdReg.sec_count == 0) - cmdBytesLeft = (256 * SectorSize); + cmdBytes = cmdBytesLeft = (256 * SectorSize); else - cmdBytesLeft = (cmdReg.sec_count * SectorSize); + cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); - drqBytesLeft = SectorSize; curSector = getLBABase(); + /** @todo make this a scheduled event to simulate disk delay */ devState = Prepare_Data_In; action = ACT_DATA_READY; break; @@ -517,11 +681,10 @@ IdeDisk::startCommand() panic("Attempt to perform CHS access, only supports LBA\n"); if (cmdReg.sec_count == 0) - cmdBytesLeft = (256 * SectorSize); + cmdBytes = cmdBytesLeft = (256 * SectorSize); else - cmdBytesLeft = (cmdReg.sec_count * SectorSize); + cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); - drqBytesLeft = SectorSize; curSector = getLBABase(); devState = Prepare_Data_Out; @@ -536,11 +699,10 @@ IdeDisk::startCommand() panic("Attempt to perform CHS access, only supports LBA\n"); if (cmdReg.sec_count == 0) - cmdBytesLeft = (256 * SectorSize); + cmdBytes = cmdBytesLeft = (256 * SectorSize); else - cmdBytesLeft = (cmdReg.sec_count * SectorSize); + cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); - drqBytesLeft = SectorSize; curSector = getLBABase(); devState = Prepare_Data_Dma; @@ -553,9 +715,11 @@ IdeDisk::startCommand() if (action != ACT_NONE) { // set the BSY bit - cmdReg.status |= STATUS_BSY_BIT; + status |= STATUS_BSY_BIT; // clear the DRQ bit - cmdReg.status &= ~STATUS_DRQ_BIT; + status &= ~STATUS_DRQ_BIT; + // clear the DF bit + status &= ~STATUS_DF_BIT; updateState(action); } @@ -568,6 +732,7 @@ IdeDisk::startCommand() void IdeDisk::intrPost() { + DPRINTF(IdeDisk, "IDE Disk Posting Interrupt\n"); if (intrPending) panic("Attempt to post an interrupt with one pending\n"); @@ -581,6 +746,7 @@ IdeDisk::intrPost() void IdeDisk::intrClear() { + DPRINTF(IdeDisk, "IDE Disk Clearing Interrupt\n"); if (!intrPending) panic("Attempt to clear a non-pending interrupt\n"); @@ -599,16 +765,30 @@ void IdeDisk::updateState(DevAction_t action) { switch (devState) { + case Device_Srst: + if (action == ACT_SRST_SET) { + // set the BSY bit + status |= STATUS_BSY_BIT; + } else if (action == ACT_SRST_CLEAR) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + + // reset the device state + reset(devID); + } + break; + case Device_Idle_S: - if (!isDEVSelect()) + if (action == ACT_SELECT_WRITE && !isDEVSelect()) { devState = Device_Idle_NS; - else if (action == ACT_CMD_WRITE) + } else if (action == ACT_CMD_WRITE) { startCommand(); + } break; case Device_Idle_SI: - if (!isDEVSelect()) { + if (action == ACT_SELECT_WRITE && !isDEVSelect()) { devState = Device_Idle_NS; intrClear(); } else if (action == ACT_STAT_READ || isIENSet()) { @@ -622,7 +802,7 @@ IdeDisk::updateState(DevAction_t action) break; case Device_Idle_NS: - if (isDEVSelect()) { + if (action == ACT_SELECT_WRITE && isDEVSelect()) { if (!isIENSet() && intrPending) { devState = Device_Idle_SI; intrPost(); @@ -660,18 +840,27 @@ IdeDisk::updateState(DevAction_t action) } } else if (action == ACT_DATA_READY) { // clear the BSY bit - cmdReg.status &= ~STATUS_BSY_BIT; + status &= ~STATUS_BSY_BIT; // set the DRQ bit - cmdReg.status |= STATUS_DRQ_BIT; + status |= STATUS_DRQ_BIT; - // put the first two bytes into the data register - memcpy((void *)&cmdReg.data0, (void *)dataBuffer, sizeof(uint16_t)); // copy the data into the data buffer - if (curCommand == WIN_IDENTIFY) + if (cmdReg.command == WIN_IDENTIFY) { + // Reset the drqBytes for this block + drqBytesLeft = sizeof(struct hd_driveid); + memcpy((void *)dataBuffer, (void *)&driveID, sizeof(struct hd_driveid)); - else + } else { + // Reset the drqBytes for this block + drqBytesLeft = SectorSize; + readDisk(curSector++, dataBuffer); + } + + // put the first two bytes into the data register + memcpy((void *)&cmdReg.data0, (void *)dataBuffer, + sizeof(uint16_t)); if (!isIENSet()) { devState = Data_Ready_INTRQ_In; @@ -698,9 +887,10 @@ IdeDisk::updateState(DevAction_t action) cmdBytesLeft -= 2; // copy next short into data registers - memcpy((void *)&cmdReg.data0, - (void *)&dataBuffer[SectorSize - drqBytesLeft], - sizeof(uint16_t)); + if (drqBytesLeft) + memcpy((void *)&cmdReg.data0, + (void *)&dataBuffer[SectorSize - drqBytesLeft], + sizeof(uint16_t)); } if (drqBytesLeft == 0) { @@ -710,7 +900,14 @@ IdeDisk::updateState(DevAction_t action) devState = Device_Idle_S; } else { devState = Prepare_Data_In; - cmdReg.status |= STATUS_BSY_BIT; + // set the BSY_BIT + status |= STATUS_BSY_BIT; + // clear the DRQ_BIT + status &= ~STATUS_DRQ_BIT; + + /** @todo change this to a scheduled event to simulate + disk delay */ + updateState(ACT_DATA_READY); } } } @@ -727,20 +924,23 @@ IdeDisk::updateState(DevAction_t action) } else { devState = Device_Idle_S; } - } else if (cmdBytesLeft != 0) { + } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) { // clear the BSY bit - cmdReg.status &= ~STATUS_BSY_BIT; + status &= ~STATUS_BSY_BIT; // set the DRQ bit - cmdReg.status |= STATUS_DRQ_BIT; + status |= STATUS_DRQ_BIT; // clear the data buffer to get it ready for writes memset(dataBuffer, 0, MAX_DMA_SIZE); - if (!isIENSet()) { + // reset the drqBytes for this block + drqBytesLeft = SectorSize; + + if (cmdBytesLeft == cmdBytes || isIENSet()) { + devState = Transfer_Data_Out; + } else { devState = Data_Ready_INTRQ_Out; intrPost(); - } else { - devState = Transfer_Data_Out; } } break; @@ -753,7 +953,9 @@ IdeDisk::updateState(DevAction_t action) break; case Transfer_Data_Out: - if (action == ACT_DATA_WRITE_BYTE || action == ACT_DATA_WRITE_SHORT) { + if (action == ACT_DATA_WRITE_BYTE || + action == ACT_DATA_WRITE_SHORT) { + if (action == ACT_DATA_READ_BYTE) { panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n"); } else { @@ -771,11 +973,17 @@ IdeDisk::updateState(DevAction_t action) writeDisk(curSector++, dataBuffer); // set the BSY bit - cmdReg.status |= STATUS_BSY_BIT; + status |= STATUS_BSY_BIT; + // set the seek bit + status |= STATUS_SEEK_BIT; // clear the DRQ bit - cmdReg.status &= ~STATUS_DRQ_BIT; + status &= ~STATUS_DRQ_BIT; devState = Prepare_Data_Out; + + /** @todo change this to a scheduled event to simulate + disk delay */ + updateState(ACT_DATA_READY); } } break; @@ -793,9 +1001,9 @@ IdeDisk::updateState(DevAction_t action) } } else if (action == ACT_DMA_READY) { // clear the BSY bit - cmdReg.status &= ~STATUS_BSY_BIT; + status &= ~STATUS_BSY_BIT; // set the DRQ bit - cmdReg.status |= STATUS_DRQ_BIT; + status |= STATUS_DRQ_BIT; devState = Transfer_Data_Dma; @@ -812,7 +1020,7 @@ IdeDisk::updateState(DevAction_t action) // clear the BSY bit setComplete(); // set the seek bit - cmdReg.status |= 0x10; + status |= STATUS_SEEK_BIT; // clear the controller state for DMA transfer ctrl->setDmaComplete(this); @@ -833,11 +1041,132 @@ 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; + + int eventCount = 0; + + if (dmaTransferEvent.scheduled()) { + reschedule = dmaTransferEvent.when(); + event = Transfer; + eventCount++; + } + if (dmaReadWaitEvent.scheduled()) { + reschedule = dmaReadWaitEvent.when(); + event = ReadWait; + eventCount++; + } + if (dmaWriteWaitEvent.scheduled()) { + reschedule = dmaWriteWaitEvent.when(); + event = WriteWait; + eventCount++; + } + if (dmaPrdReadEvent.scheduled()) { + reschedule = dmaPrdReadEvent.when(); + event = PrdRead; + eventCount++; + } + if (dmaReadEvent.scheduled()) { + reschedule = dmaReadEvent.when(); + event = DmaRead; + eventCount++; + } + if (dmaWriteEvent.scheduled()) { + reschedule = dmaWriteEvent.when(); + event = DmaWrite; + eventCount++; + } + + assert(eventCount <= 1); + + 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.command); + SERIALIZE_SCALAR(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(cmdBytes); + SERIALIZE_SCALAR(drqBytesLeft); + SERIALIZE_SCALAR(curSector); + 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.command); + UNSERIALIZE_SCALAR(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(cmdBytes); + UNSERIALIZE_SCALAR(cmdBytesLeft); + UNSERIALIZE_SCALAR(drqBytesLeft); + UNSERIALIZE_SCALAR(curSector); + 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 @@ -856,14 +1185,15 @@ 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) CREATE_SIM_OBJECT(IdeDisk) { - return new IdeDisk(getInstanceName(), image, physmem, driveID, disk_delay); + return new IdeDisk(getInstanceName(), image, physmem, driveID, + disk_delay); } REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)