Fix a small bug in parameter processing that would always result
[gem5.git] / dev / ide_disk.cc
index 38d6a919be0de5893f577d440ce65e7dfe2777f6..99724f07788c1c5f7b5cde237ccf465274da6319 100644 (file)
@@ -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
@@ -61,22 +61,11 @@ IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
       dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
       dmaReadEvent(this), dmaWriteEvent(this)
 {
-    diskDelay = (delay * ticksPerSecond / 100000);
+    // 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));
@@ -130,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;
@@ -145,17 +160,21 @@ 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)
+IdeDisk::pciToDma(Addr pciAddr)
 {
     if (ctrl)
         return ctrl->tsunami->pchip->translatePciToDma(pciAddr);
@@ -163,6 +182,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
 ////
@@ -187,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;
@@ -201,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)
@@ -233,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 {
@@ -242,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;
     }
@@ -286,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();
@@ -297,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()) {
@@ -306,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)
@@ -323,6 +384,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 +421,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,33 +436,13 @@ 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
-    Addr dmaAddr =
-        ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
-    memcpy((void *)dataBuffer,
-           physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
-           curPrd.getByteCount());
-
-    uint32_t bytesWritten = 0;
-
-    while (bytesWritten < curPrd.getByteCount()) {
-        if (cmdBytesLeft <= 0)
-            panic("DMA data is larger than # sectors specified\n");
-
-        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
-
-        bytesWritten += SectorSize;
-        cmdBytesLeft -= SectorSize;
     }
-#endif
 
     // check for the EOT
-    if (curPrd.getEOT()){
+    if (curPrd.getEOT()) {
         assert(cmdBytesLeft == 0);
         dmaState = Dma_Idle;
         updateState(ACT_DMA_DONE);
@@ -397,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()) {
@@ -406,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)
@@ -423,6 +485,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 +520,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,21 +543,12 @@ 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)),
                bytesInPage);
     }
 
-#if 0
-    Addr dmaAddr = ctrl->tsunami->pchip->
-        translatePciToDma(curPrd.getBaseAddr());
-
-    memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
-           (void *)dataBuffer, curPrd.getByteCount());
-#endif
-
     // check for the EOT
     if (curPrd.getEOT()) {
         assert(cmdBytesLeft == 0);
@@ -521,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;
 
@@ -548,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
@@ -567,6 +640,7 @@ IdeDisk::startCommand()
 
       case WIN_RECAL:
       case WIN_SPECIFY:
+      case WIN_STANDBYNOW1:
       case WIN_FLUSH_CACHE:
       case WIN_VERIFY:
       case WIN_SEEK:
@@ -578,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;
@@ -589,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;
@@ -607,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;
@@ -626,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;
@@ -643,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);
     }
@@ -689,16 +763,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()) {
@@ -712,7 +800,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();
@@ -750,20 +838,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;
-
-            // put the first two bytes into the data register
-            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
-                   sizeof(uint16_t));
+            status |= STATUS_DRQ_BIT;
 
             // 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;
@@ -790,9 +885,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) {
@@ -802,7 +898,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);
                 }
             }
         }
@@ -819,20 +922,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;
@@ -865,11 +971,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;
@@ -887,9 +999,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;
 
@@ -906,7 +1018,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);
 
@@ -927,11 +1039,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 &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.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
@@ -950,7 +1183,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)