summaryrefslogtreecommitdiff
path: root/dev/ide_disk.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dev/ide_disk.cc')
-rw-r--r--dev/ide_disk.cc1286
1 files changed, 0 insertions, 1286 deletions
diff --git a/dev/ide_disk.cc b/dev/ide_disk.cc
deleted file mode 100644
index c13556ed6..000000000
--- a/dev/ide_disk.cc
+++ /dev/null
@@ -1,1286 +0,0 @@
-/*
- * Copyright (c) 2004-2005 The Regents of The University of Michigan
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met: redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer;
- * redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution;
- * neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @file
- * Device model implementation for an IDE disk
- */
-
-#include <cerrno>
-#include <cstring>
-#include <deque>
-#include <string>
-
-#include "base/cprintf.hh" // csprintf
-#include "base/trace.hh"
-#include "dev/disk_image.hh"
-#include "dev/ide_disk.hh"
-#include "dev/ide_ctrl.hh"
-#include "dev/tsunami.hh"
-#include "dev/tsunami_pchip.hh"
-#include "mem/functional/physical.hh"
-#include "mem/bus/bus.hh"
-#include "mem/bus/dma_interface.hh"
-#include "mem/bus/pio_interface.hh"
-#include "mem/bus/pio_interface_impl.hh"
-#include "sim/builder.hh"
-#include "sim/sim_object.hh"
-#include "sim/root.hh"
-#include "arch/isa_traits.hh"
-
-using namespace std;
-using namespace TheISA;
-
-IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
- int id, Tick delay)
- : SimObject(name), ctrl(NULL), image(img), physmem(phys), diskDelay(delay),
- dmaTransferEvent(this), dmaReadWaitEvent(this),
- dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
- dmaReadEvent(this), dmaWriteEvent(this)
-{
- // Reset the device state
- reset(id);
-
- // fill out the drive ID structure
- memset(&driveID, 0, sizeof(struct ataparams));
-
- // Calculate LBA and C/H/S values
- uint16_t cylinders;
- uint8_t heads;
- uint8_t sectors;
-
- uint32_t lba_size = image->size();
- if (lba_size >= 16383*16*63) {
- cylinders = 16383;
- heads = 16;
- sectors = 63;
- } else {
- if (lba_size >= 63)
- sectors = 63;
- else
- sectors = lba_size;
-
- if ((lba_size / sectors) >= 16)
- heads = 16;
- else
- heads = (lba_size / sectors);
-
- cylinders = lba_size / (heads * sectors);
- }
-
- // Setup the model name
- strncpy((char *)driveID.atap_model, "5MI EDD si k",
- sizeof(driveID.atap_model));
- // Set the maximum multisector transfer size
- driveID.atap_multi = MAX_MULTSECT;
- // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
- driveID.atap_capabilities1 = 0x7;
- // UDMA support, EIDE support
- driveID.atap_extensions = 0x6;
- // Setup default C/H/S settings
- driveID.atap_cylinders = cylinders;
- driveID.atap_sectors = sectors;
- driveID.atap_heads = heads;
- // Setup the current multisector transfer size
- driveID.atap_curmulti = MAX_MULTSECT;
- driveID.atap_curmulti_valid = 0x1;
- // Number of sectors on disk
- driveID.atap_capacity = lba_size;
- // Multiword DMA mode 2 and below supported
- driveID.atap_dmamode_supp = 0x400;
- // Set PIO mode 4 and 3 supported
- driveID.atap_piomode_supp = 0x3;
- // Set DMA mode 4 and below supported
- driveID.atap_udmamode_supp = 0x1f;
- // Statically set hardware config word
- driveID.atap_hwreset_res = 0x4001;
-
- //arbitrary for now...
- driveID.atap_ata_major = WDC_VER_ATA7;
-}
-
-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;
-
- if (id == DEV0) {
- devState = Device_Idle_S;
- devID = DEV0;
- } else if (id == DEV1) {
- devState = Device_Idle_NS;
- devID = DEV1;
- } else {
- panic("Invalid device ID: %#x\n", id);
- }
-
- // set the device ready bit
- status = STATUS_DRDY_BIT;
-
- /* The error register must be set to 0x1 on start-up to
- indicate that no diagnostic error was detected */
- cmdReg.error = 0x1;
-}
-
-////
-// Utility functions
-////
-
-bool
-IdeDisk::isDEVSelect()
-{
- return ctrl->isDiskSelected(this);
-}
-
-Addr
-IdeDisk::pciToDma(Addr pciAddr)
-{
- if (ctrl)
- return ctrl->plat->pciToDma(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;
-}
-
-////
-// Device registers read/write
-////
-
-void
-IdeDisk::read(const Addr &offset, IdeRegType reg_type, uint8_t *data)
-{
- DevAction_t action = ACT_NONE;
-
- switch (reg_type) {
- case COMMAND_BLOCK:
- switch (offset) {
- // Data transfers occur two bytes at a time
- case DATA_OFFSET:
- *(uint16_t*)data = cmdReg.data;
- action = ACT_DATA_READ_SHORT;
- break;
- case ERROR_OFFSET:
- *data = cmdReg.error;
- break;
- case NSECTOR_OFFSET:
- *data = cmdReg.sec_count;
- break;
- case SECTOR_OFFSET:
- *data = cmdReg.sec_num;
- break;
- case LCYL_OFFSET:
- *data = cmdReg.cyl_low;
- break;
- case HCYL_OFFSET:
- *data = cmdReg.cyl_high;
- break;
- case DRIVE_OFFSET:
- *data = cmdReg.drive;
- break;
- case STATUS_OFFSET:
- *data = status;
- action = ACT_STAT_READ;
- break;
- default:
- panic("Invalid IDE command register offset: %#x\n", offset);
- }
- break;
- case CONTROL_BLOCK:
- if (offset == ALTSTAT_OFFSET)
- *data = status;
- else
- panic("Invalid IDE control register offset: %#x\n", offset);
- break;
- default:
- panic("Unknown register block!\n");
- }
-
- if (action != ACT_NONE)
- updateState(action);
-}
-
-void
-IdeDisk::write(const Addr &offset, IdeRegType reg_type, const uint8_t *data)
-{
- DevAction_t action = ACT_NONE;
-
- switch (reg_type) {
- case COMMAND_BLOCK:
- switch (offset) {
- case DATA_OFFSET:
- cmdReg.data = *(uint16_t*)data;
- action = ACT_DATA_WRITE_SHORT;
- break;
- case FEATURES_OFFSET:
- break;
- case NSECTOR_OFFSET:
- cmdReg.sec_count = *data;
- break;
- case SECTOR_OFFSET:
- cmdReg.sec_num = *data;
- break;
- case LCYL_OFFSET:
- cmdReg.cyl_low = *data;
- break;
- case HCYL_OFFSET:
- cmdReg.cyl_high = *data;
- break;
- case DRIVE_OFFSET:
- cmdReg.drive = *data;
- action = ACT_SELECT_WRITE;
- break;
- case COMMAND_OFFSET:
- cmdReg.command = *data;
- action = ACT_CMD_WRITE;
- break;
- default:
- panic("Invalid IDE command register offset: %#x\n", offset);
- }
- break;
- case CONTROL_BLOCK:
- if (offset == CONTROL_OFFSET) {
- 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;
- }
- else
- panic("Invalid IDE control register offset: %#x\n", offset);
- break;
- default:
- panic("Unknown register block!\n");
- }
-
- if (action != ACT_NONE)
- updateState(action);
-}
-
-////
-// Perform DMA transactions
-////
-
-void
-IdeDisk::doDmaTransfer()
-{
- if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
- panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
- dmaState, devState);
-
- // first read the current PRD
- if (dmaInterface) {
- if (dmaInterface->busy()) {
- // reschedule after waiting period
- dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
- return;
- }
-
- dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
- &dmaPrdReadEvent);
- } else {
- dmaPrdReadDone();
- }
-}
-
-void
-IdeDisk::dmaPrdReadDone()
-{
- // actually copy the PRD from physical memory
- memcpy((void *)&curPrd.entry,
- physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
- 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();
- else
- doDmaWrite();
-}
-
-void
-IdeDisk::regStats()
-{
- using namespace Stats;
- dmaReadFullPages
- .name(name() + ".dma_read_full_pages")
- .desc("Number of full page size DMA reads (not PRD).")
- ;
- dmaReadBytes
- .name(name() + ".dma_read_bytes")
- .desc("Number of bytes transfered via DMA reads (not PRD).")
- ;
- dmaReadTxs
- .name(name() + ".dma_read_txs")
- .desc("Number of DMA read transactions (not PRD).")
- ;
-
- dmaWriteFullPages
- .name(name() + ".dma_write_full_pages")
- .desc("Number of full page size DMA writes.")
- ;
- dmaWriteBytes
- .name(name() + ".dma_write_bytes")
- .desc("Number of bytes transfered via DMA writes.")
- ;
- dmaWriteTxs
- .name(name() + ".dma_write_txs")
- .desc("Number of DMA write transactions.")
- ;
-}
-
-void
-IdeDisk::doDmaRead()
-{
- /** @todo we need to figure out what the delay actually will be */
- Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
-
- DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
- diskDelay, totalDiskDelay);
- if (dmaInterface) {
- if (dmaInterface->busy()) {
- // reschedule after waiting period
- dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
- return;
- }
-
- Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
-
- uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
- (uint32_t)curPrd.getByteCount());
-
- dmaInterfaceBytes = bytesInPage;
-
- if (bytesInPage == TheISA::VMPageSize)
- dmaReadFullPages++;
- dmaReadBytes += bytesInPage;
- dmaReadTxs++;
- dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
- curTick + totalDiskDelay, &dmaReadEvent);
- } else {
- // schedule dmaReadEvent with sectorDelay (dmaReadDone)
- dmaReadEvent.schedule(curTick + totalDiskDelay);
- }
-}
-
-void
-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;
-
- if (bytesInPage == TheISA::VMPageSize)
- dmaReadFullPages++;
- dmaReadBytes += bytesInPage;
- dmaReadTxs++;
-
- 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 # of sectors specified\n");
-
- dmaAddr = pciToDma(curAddr);
-
- // 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()) {
- assert(cmdBytesLeft == 0);
- dmaState = Dma_Idle;
- updateState(ACT_DMA_DONE);
- } else {
- doDmaTransfer();
- }
-}
-
-void
-IdeDisk::doDmaWrite()
-{
- /** @todo we need to figure out what the delay actually will be */
- Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
-
- DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
- diskDelay, totalDiskDelay);
-
- if (dmaInterface) {
- if (dmaInterface->busy()) {
- // reschedule after waiting period
- dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
- return;
- }
-
- Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
-
- uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
- (uint32_t)curPrd.getByteCount());
-
- dmaInterfaceBytes = bytesInPage;
-
- if (bytesInPage == TheISA::VMPageSize)
- dmaWriteFullPages++;
- dmaWriteBytes += bytesInPage;
- dmaWriteTxs++;
-
- dmaInterface->doDMA(WriteInvalidate, dmaAddr,
- bytesInPage, curTick + totalDiskDelay,
- &dmaWriteEvent);
- } else {
- // schedule event with disk delay (dmaWriteDone)
- dmaWriteEvent.schedule(curTick + totalDiskDelay);
- }
-}
-
-void
-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;
-
- if (bytesInPage == TheISA::VMPageSize)
- dmaWriteFullPages++;
- dmaWriteBytes += bytesInPage;
- dmaWriteTxs++;
-
- 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;
- }
-
- // 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()) {
- assert(cmdBytesLeft == 0);
- dmaState = Dma_Idle;
- updateState(ACT_DMA_DONE);
- } else {
- doDmaTransfer();
- }
-}
-
-////
-// Disk utility routines
-///
-
-void
-IdeDisk::readDisk(uint32_t sector, uint8_t *data)
-{
- uint32_t bytesRead = image->read(data, sector);
-
- if (bytesRead != SectorSize)
- panic("Can't read from %s. Only %d of %d read. errno=%d\n",
- name(), bytesRead, SectorSize, errno);
-}
-
-void
-IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
-{
- uint32_t bytesWritten = image->write(data, sector);
-
- if (bytesWritten != SectorSize)
- panic("Can't write to %s. Only %d of %d written. errno=%d\n",
- name(), bytesWritten, SectorSize, errno);
-}
-
-////
-// Setup and handle commands
-////
-
-void
-IdeDisk::startDma(const uint32_t &prdTableBase)
-{
- if (dmaState != Dma_Start)
- panic("Inconsistent DMA state, should be in Dma_Start!\n");
-
- if (devState != Transfer_Data_Dma)
- panic("Inconsistent device state for DMA start!\n");
-
- // PRD base address is given by bits 31:2
- curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
-
- dmaState = Dma_Transfer;
-
- // schedule dma transfer (doDmaTransfer)
- dmaTransferEvent.schedule(curTick + 1);
-}
-
-void
-IdeDisk::abortDma()
-{
- if (dmaState == Dma_Idle)
- panic("Inconsistent DMA state, should be Start or Transfer!");
-
- if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
- panic("Inconsistent device state, should be Transfer or Prepare!\n");
-
- updateState(ACT_CMD_ERROR);
-}
-
-void
-IdeDisk::startCommand()
-{
- DevAction_t action = ACT_NONE;
- uint32_t size = 0;
- dmaRead = false;
-
- // Decode commands
- switch (cmdReg.command) {
- // Supported non-data commands
- case WDSF_READ_NATIVE_MAX:
- size = image->size() - 1;
- cmdReg.sec_num = (size & 0xff);
- cmdReg.cyl_low = ((size & 0xff00) >> 8);
- cmdReg.cyl_high = ((size & 0xff0000) >> 16);
- cmdReg.head = ((size & 0xf000000) >> 24);
-
- devState = Command_Execution;
- action = ACT_CMD_COMPLETE;
- break;
-
- case WDCC_RECAL:
- case WDCC_IDP:
- case WDCC_STANDBY_IMMED:
- case WDCC_FLUSHCACHE:
- case WDSF_VERIFY:
- case WDSF_SEEK:
- case SET_FEATURES:
- case WDCC_SETMULTI:
- devState = Command_Execution;
- action = ACT_CMD_COMPLETE;
- break;
-
- // Supported PIO data-in commands
- case WDCC_IDENTIFY:
- cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
- devState = Prepare_Data_In;
- action = ACT_DATA_READY;
- break;
-
- case WDCC_READMULTI:
- case WDCC_READ:
- if (!(cmdReg.drive & DRIVE_LBA_BIT))
- panic("Attempt to perform CHS access, only supports LBA\n");
-
- if (cmdReg.sec_count == 0)
- cmdBytes = cmdBytesLeft = (256 * SectorSize);
- else
- cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
-
- curSector = getLBABase();
-
- /** @todo make this a scheduled event to simulate disk delay */
- devState = Prepare_Data_In;
- action = ACT_DATA_READY;
- break;
-
- // Supported PIO data-out commands
- case WDCC_WRITEMULTI:
- case WDCC_WRITE:
- if (!(cmdReg.drive & DRIVE_LBA_BIT))
- panic("Attempt to perform CHS access, only supports LBA\n");
-
- if (cmdReg.sec_count == 0)
- cmdBytes = cmdBytesLeft = (256 * SectorSize);
- else
- cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
-
- curSector = getLBABase();
-
- devState = Prepare_Data_Out;
- action = ACT_DATA_READY;
- break;
-
- // Supported DMA commands
- case WDCC_WRITEDMA:
- dmaRead = true; // a write to the disk is a DMA read from memory
- case WDCC_READDMA:
- if (!(cmdReg.drive & DRIVE_LBA_BIT))
- panic("Attempt to perform CHS access, only supports LBA\n");
-
- if (cmdReg.sec_count == 0)
- cmdBytes = cmdBytesLeft = (256 * SectorSize);
- else
- cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
-
- curSector = getLBABase();
-
- devState = Prepare_Data_Dma;
- action = ACT_DMA_READY;
- break;
-
- default:
- panic("Unsupported ATA command: %#x\n", cmdReg.command);
- }
-
- if (action != ACT_NONE) {
- // set the BSY bit
- status |= STATUS_BSY_BIT;
- // clear the DRQ bit
- status &= ~STATUS_DRQ_BIT;
- // clear the DF bit
- status &= ~STATUS_DF_BIT;
-
- updateState(action);
- }
-}
-
-////
-// Handle setting and clearing interrupts
-////
-
-void
-IdeDisk::intrPost()
-{
- DPRINTF(IdeDisk, "Posting Interrupt\n");
- if (intrPending)
- panic("Attempt to post an interrupt with one pending\n");
-
- intrPending = true;
-
- // talk to controller to set interrupt
- if (ctrl) {
- ctrl->bmi_regs.bmis0 |= IDEINTS;
- ctrl->intrPost();
- }
-}
-
-void
-IdeDisk::intrClear()
-{
- DPRINTF(IdeDisk, "Clearing Interrupt\n");
- if (!intrPending)
- panic("Attempt to clear a non-pending interrupt\n");
-
- intrPending = false;
-
- // talk to controller to clear interrupt
- if (ctrl)
- ctrl->intrClear();
-}
-
-////
-// Manage the device internal state machine
-////
-
-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 (action == ACT_SELECT_WRITE && !isDEVSelect()) {
- devState = Device_Idle_NS;
- } else if (action == ACT_CMD_WRITE) {
- startCommand();
- }
-
- break;
-
- case Device_Idle_SI:
- if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
- devState = Device_Idle_NS;
- intrClear();
- } else if (action == ACT_STAT_READ || isIENSet()) {
- devState = Device_Idle_S;
- intrClear();
- } else if (action == ACT_CMD_WRITE) {
- intrClear();
- startCommand();
- }
-
- break;
-
- case Device_Idle_NS:
- if (action == ACT_SELECT_WRITE && isDEVSelect()) {
- if (!isIENSet() && intrPending) {
- devState = Device_Idle_SI;
- intrPost();
- }
- if (isIENSet() || !intrPending) {
- devState = Device_Idle_S;
- }
- }
- break;
-
- case Command_Execution:
- if (action == ACT_CMD_COMPLETE) {
- // clear the BSY bit
- setComplete();
-
- if (!isIENSet()) {
- devState = Device_Idle_SI;
- intrPost();
- } else {
- devState = Device_Idle_S;
- }
- }
- break;
-
- case Prepare_Data_In:
- if (action == ACT_CMD_ERROR) {
- // clear the BSY bit
- setComplete();
-
- if (!isIENSet()) {
- devState = Device_Idle_SI;
- intrPost();
- } else {
- devState = Device_Idle_S;
- }
- } else if (action == ACT_DATA_READY) {
- // clear the BSY bit
- status &= ~STATUS_BSY_BIT;
- // set the DRQ bit
- status |= STATUS_DRQ_BIT;
-
- // copy the data into the data buffer
- if (cmdReg.command == WDCC_IDENTIFY) {
- // Reset the drqBytes for this block
- drqBytesLeft = sizeof(struct ataparams);
-
- memcpy((void *)dataBuffer, (void *)&driveID,
- sizeof(struct ataparams));
- } else {
- // Reset the drqBytes for this block
- drqBytesLeft = SectorSize;
-
- readDisk(curSector++, dataBuffer);
- }
-
- // put the first two bytes into the data register
- memcpy((void *)&cmdReg.data, (void *)dataBuffer,
- sizeof(uint16_t));
-
- if (!isIENSet()) {
- devState = Data_Ready_INTRQ_In;
- intrPost();
- } else {
- devState = Transfer_Data_In;
- }
- }
- break;
-
- case Data_Ready_INTRQ_In:
- if (action == ACT_STAT_READ) {
- devState = Transfer_Data_In;
- intrClear();
- }
- break;
-
- case Transfer_Data_In:
- if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
- if (action == ACT_DATA_READ_BYTE) {
- panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
- } else {
- drqBytesLeft -= 2;
- cmdBytesLeft -= 2;
-
- // copy next short into data registers
- if (drqBytesLeft)
- memcpy((void *)&cmdReg.data,
- (void *)&dataBuffer[SectorSize - drqBytesLeft],
- sizeof(uint16_t));
- }
-
- if (drqBytesLeft == 0) {
- if (cmdBytesLeft == 0) {
- // Clear the BSY bit
- setComplete();
- devState = Device_Idle_S;
- } else {
- devState = Prepare_Data_In;
- // 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);
- }
- }
- }
- break;
-
- case Prepare_Data_Out:
- if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
- // clear the BSY bit
- setComplete();
-
- if (!isIENSet()) {
- devState = Device_Idle_SI;
- intrPost();
- } else {
- devState = Device_Idle_S;
- }
- } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
- // clear the BSY bit
- status &= ~STATUS_BSY_BIT;
- // set the DRQ bit
- status |= STATUS_DRQ_BIT;
-
- // clear the data buffer to get it ready for writes
- memset(dataBuffer, 0, MAX_DMA_SIZE);
-
- // reset the drqBytes for this block
- drqBytesLeft = SectorSize;
-
- if (cmdBytesLeft == cmdBytes || isIENSet()) {
- devState = Transfer_Data_Out;
- } else {
- devState = Data_Ready_INTRQ_Out;
- intrPost();
- }
- }
- break;
-
- case Data_Ready_INTRQ_Out:
- if (action == ACT_STAT_READ) {
- devState = Transfer_Data_Out;
- intrClear();
- }
- break;
-
- case Transfer_Data_Out:
- 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 {
- // copy the latest short into the data buffer
- memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
- (void *)&cmdReg.data,
- sizeof(uint16_t));
-
- drqBytesLeft -= 2;
- cmdBytesLeft -= 2;
- }
-
- if (drqBytesLeft == 0) {
- // copy the block to the disk
- writeDisk(curSector++, dataBuffer);
-
- // set the BSY bit
- status |= STATUS_BSY_BIT;
- // set the seek bit
- status |= STATUS_SEEK_BIT;
- // clear the 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;
-
- case Prepare_Data_Dma:
- if (action == ACT_CMD_ERROR) {
- // clear the BSY bit
- setComplete();
-
- if (!isIENSet()) {
- devState = Device_Idle_SI;
- intrPost();
- } else {
- devState = Device_Idle_S;
- }
- } else if (action == ACT_DMA_READY) {
- // clear the BSY bit
- status &= ~STATUS_BSY_BIT;
- // set the DRQ bit
- status |= STATUS_DRQ_BIT;
-
- devState = Transfer_Data_Dma;
-
- if (dmaState != Dma_Idle)
- panic("Inconsistent DMA state, should be Dma_Idle\n");
-
- dmaState = Dma_Start;
- // wait for the write to the DMA start bit
- }
- break;
-
- case Transfer_Data_Dma:
- if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
- // clear the BSY bit
- setComplete();
- // set the seek bit
- status |= STATUS_SEEK_BIT;
- // clear the controller state for DMA transfer
- ctrl->setDmaComplete(this);
-
- if (!isIENSet()) {
- devState = Device_Idle_SI;
- intrPost();
- } else {
- devState = Device_Idle_S;
- }
- }
- break;
-
- default:
- panic("Unknown IDE device state: %#x\n", devState);
- }
-}
-
-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.data);
- 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 &section)
-{
- // 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.data);
- 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
-
-enum DriveID { master, slave };
-static const char *DriveID_strings[] = { "master", "slave" };
-BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
-
- SimObjectParam<DiskImage *> image;
- SimObjectParam<PhysicalMemory *> physmem;
- SimpleEnumParam<DriveID> driveID;
- Param<int> delay;
-
-END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
-
-BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
-
- INIT_PARAM(image, "Disk image"),
- INIT_PARAM(physmem, "Physical memory"),
- INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings),
- INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1)
-
-END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
-
-
-CREATE_SIM_OBJECT(IdeDisk)
-{
- return new IdeDisk(getInstanceName(), image, physmem, driveID, delay);
-}
-
-REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
-
-#endif //DOXYGEN_SHOULD_SKIP_THIS