Checkin of latest IDE and some separation between platforms (Tsunami and
authorAndrew Schultz <alschult@umich.edu>
Mon, 3 May 2004 15:47:52 +0000 (11:47 -0400)
committerAndrew Schultz <alschult@umich.edu>
Mon, 3 May 2004 15:47:52 +0000 (11:47 -0400)
Turbolaser)

base/range.hh:
    Change semantics of range to be inclusive of the end value, may need to
    check other users of range to make sure they are semantically correct.
    This was needed for access of last byte in range of address on IDE and
    makes sense for case of range from 0 to all f
dev/ide_ctrl.cc:
dev/ide_ctrl.hh:
dev/ide_disk.cc:
dev/ide_disk.hh:
    Whole mess of changes.. at current state simulator will boot and read
    partition table and then have a bunch of errors and panic
dev/pciconfigall.cc:
dev/pciconfigall.hh:
dev/platform.hh:
    Changes to work with platform separation
dev/tsunami.cc:
dev/tsunami.hh:
    Change to work with platform separation

--HG--
extra : convert_revision : e1de22b54df7fdcf391efc2a8555ada93f46beab

base/range.hh
dev/ide_ctrl.cc
dev/ide_ctrl.hh
dev/ide_disk.cc
dev/ide_disk.hh
dev/pciconfigall.cc
dev/pciconfigall.hh
dev/platform.hh
dev/tsunami.cc
dev/tsunami.hh

index d72aa9755d971ff44dd32b1b6016ee2524d7b76c..2c4a43f484f8e9faa92475cb5299744f74d33b0b 100644 (file)
@@ -225,7 +225,7 @@ inline bool
 operator==(const T &pos, const Range<U> &range)
 {
     assert(range.valid());
-    return pos >= range.start && pos < range.end;
+    return pos >= range.start && pos <= range.end;
 }
 
 /**
index 0f53ba3e8eb7c247dead4fd731318aef599d3f40..7507a8d7fd29560fbb614d37653fb358046b9fa4 100644 (file)
@@ -38,6 +38,7 @@
 #include "dev/pciconfigall.hh"
 #include "dev/ide_disk.hh"
 #include "dev/ide_ctrl.hh"
+#include "dev/tsunami_cchip.hh"
 #include "mem/bus/bus.hh"
 #include "mem/bus/pio_interface.hh"
 #include "mem/bus/pio_interface_impl.hh"
@@ -84,13 +85,13 @@ IdeController::IdeController(const string &name, IntrControl *ic,
     bmi_size = BARSize[4];
 
     // zero out all of the registers
-    memset(regs, 0, sizeof(regs));
+    memset(bmi_regs, 0, sizeof(bmi_regs));
     memset(pci_regs, 0, sizeof(pci_regs));
 
     // setup initial values
     *(uint32_t *)&pci_regs[IDETIM] = 0x80008000; // enable both channels
-    *(uint8_t *)&regs[BMI + BMIS0] = 0x60;
-    *(uint8_t *)&regs[BMI + BMIS1] = 0x60;
+    *(uint8_t *)&bmi_regs[BMIS0] = 0x60;
+    *(uint8_t *)&bmi_regs[BMIS1] = 0x60;
 
     // reset all internal variables
     io_enabled = false;
@@ -114,7 +115,7 @@ IdeController::IdeController(const string &name, IntrControl *ic,
 
     for (int i = 0; i < new_disks.size(); i++) {
         disks[i] = new_disks[i];
-        disks[i]->setController(this);
+        disks[i]->setController(this, dmaInterface);
     }
 }
 
@@ -125,6 +126,51 @@ IdeController::~IdeController()
             delete disks[i];
 }
 
+////
+// Command completion
+////
+
+void
+IdeController::setDmaComplete(IdeDisk *disk)
+{
+    int diskNum = getDisk(disk);
+
+    if (diskNum < 0)
+        panic("Unable to find disk based on pointer %#x\n", disk);
+
+    if (diskNum < 2) {
+        // clear the start/stop bit in the command register
+        bmi_regs[BMIC0] &= ~SSBM;
+        // clear the bus master active bit in the status register
+        bmi_regs[BMIS0] &= ~BMIDEA;
+        // set the interrupt bit
+        bmi_regs[BMIS0] |= IDEINTS;
+    } else {
+        // clear the start/stop bit in the command register
+        bmi_regs[BMIC1] &= ~SSBM;
+        // clear the bus master active bit in the status register
+        bmi_regs[BMIS1] &= ~BMIDEA;
+        // set the interrupt bit
+        bmi_regs[BMIS1] |= IDEINTS;
+    }
+}
+
+////
+// Interrupt handling
+////
+
+void
+IdeController::intrPost()
+{
+    tsunami->cchip->postDRIR(configData->config.hdr.pci0.interruptLine);
+}
+
+void
+IdeController::intrClear()
+{
+    tsunami->cchip->clearDRIR(configData->config.hdr.pci0.interruptLine);
+}
+
 ////
 // Bus timing and bus access functions
 ////
@@ -289,7 +335,16 @@ IdeController::WriteConfig(int offset, int size, uint32_t data)
 Fault
 IdeController::read(MemReqPtr &req, uint8_t *data)
 {
-    Addr offset = getOffset(req->paddr);
+    Addr offset;
+    bool primary;
+    bool byte;
+    bool cmdBlk;
+    RegType_t type;
+    int disk;
+
+    parseAddr(req->paddr, offset, primary, type);
+    byte = (req->size == sizeof(uint8_t)) ? true : false;
+    cmdBlk = (type == COMMAND_BLOCK) ? true : false;
 
     if (!io_enabled)
         return No_Fault;
@@ -299,11 +354,18 @@ IdeController::read(MemReqPtr &req, uint8_t *data)
         req->size != sizeof(uint32_t))
         panic("IDE controller read of invalid size: %#x\n", req->size);
 
-    DPRINTF(IdeCtrl, "IDE default read from offset: %#x size: %#x data: %#x\n",
-            offset, req->size, *(uint32_t *)&regs[offset]);
+    if (type != BMI_BLOCK) {
+        assert(req->size != sizeof(uint32_t));
 
-    // copy the data from the control registers
-    memcpy((void *)data, &regs[offset], req->size);
+        disk = getDisk(primary);
+        if (disks[disk])
+            disks[disk]->read(offset, byte, cmdBlk, data);
+    } else {
+        memcpy((void *)data, &bmi_regs[offset], req->size);
+    }
+
+    DPRINTF(IdeCtrl, "IDE read from offset: %#x size: %#x data: %#x\n",
+            offset, req->size, *(uint32_t *)data);
 
     return No_Fault;
 }
@@ -311,153 +373,144 @@ IdeController::read(MemReqPtr &req, uint8_t *data)
 Fault
 IdeController::write(MemReqPtr &req, const uint8_t *data)
 {
-    int disk = 0; // selected disk index
-    uint8_t oldVal, newVal;
+    Addr offset;
+    bool primary;
+    bool byte;
+    bool cmdBlk;
+    RegType_t type;
+    int disk;
 
-    Addr offset = getOffset(req->paddr);
+    parseAddr(req->paddr, offset, primary, type);
+    byte = (req->size == sizeof(uint8_t)) ? true : false;
+    cmdBlk = (type == COMMAND_BLOCK) ? true : false;
+
+    DPRINTF(IdeCtrl, "IDE write from offset: %#x size: %#x data: %#x\n",
+            offset, req->size, *(uint32_t *)data);
+
+    uint8_t oldVal, newVal;
 
     if (!io_enabled)
         return No_Fault;
 
-    if (offset >= BMI && !bm_enabled)
+    if (type == BMI_BLOCK && !bm_enabled)
         return No_Fault;
 
-    switch (offset) {
-        // Bus master IDE command register
-      case (BMI + BMIC1):
-      case (BMI + BMIC0):
-        if (req->size != sizeof(uint8_t))
-            panic("Invalid BMIC write size: %x\n", req->size);
-
-        // select the current disk based on DEV bit
-        disk = getDisk(offset);
-
-        oldVal = regs[offset];
-        newVal = *data;
-
-        // if a DMA transfer is in progress, R/W control cannot change
-        if (oldVal & SSBM) {
-            if ((oldVal & RWCON) ^ (newVal & RWCON)) {
-                (oldVal & RWCON) ? newVal |= RWCON : newVal &= ~RWCON;
-            }
+    if (type != BMI_BLOCK) {
+        // shadow the dev bit
+        if (type == COMMAND_BLOCK && offset == IDE_SELECT_OFFSET) {
+            uint8_t *devBit = (primary ? &dev[0] : &dev[1]);
+            *devBit = ((*data & IDE_SELECT_DEV_BIT) ? 1 : 0);
         }
 
-        // see if the start/stop bit is being changed
-        if ((oldVal & SSBM) ^ (newVal & SSBM)) {
-            if (oldVal & SSBM) {
-                // stopping DMA transfer
-                DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
-
-                // clear the BMIDEA bit
-                regs[offset + 0x2] &= ~BMIDEA;
-
-                if (disks[disk] == NULL)
-                    panic("DMA stop for disk %d which does not exist\n", disk);
+        assert(req->size != sizeof(uint32_t));
 
-                // inform the disk of the DMA transfer abort
-                disks[disk]->dmaStop();
-            } else {
-                // starting DMA transfer
-                DPRINTF(IdeCtrl, "Starting DMA transfer\n");
+        disk = getDisk(primary);
+        if (disks[disk])
+            disks[disk]->write(offset, byte, cmdBlk, data);
+    } else {
+        switch (offset) {
+            // Bus master IDE command register
+          case BMIC1:
+          case BMIC0:
+            if (req->size != sizeof(uint8_t))
+                panic("Invalid BMIC write size: %x\n", req->size);
 
-                // set the BMIDEA bit
-                regs[offset + 0x2] |= BMIDEA;
+            // select the current disk based on DEV bit
+            disk = getDisk(primary);
 
-                if (disks[disk] == NULL)
-                    panic("DMA start for disk %d which does not exist\n",
-                          disk);
+            oldVal = bmi_regs[offset];
+            newVal = *data;
 
-                // inform the disk of the DMA transfer start
-                disks[disk]->dmaStart();
+            // if a DMA transfer is in progress, R/W control cannot change
+            if (oldVal & SSBM) {
+                if ((oldVal & RWCON) ^ (newVal & RWCON)) {
+                    (oldVal & RWCON) ? newVal |= RWCON : newVal &= ~RWCON;
+                }
             }
-        }
-
-        // update the register value
-        regs[offset] = newVal;
-        break;
-
-        // Bus master IDE status register
-      case (BMI + BMIS0):
-      case (BMI + BMIS1):
-        if (req->size != sizeof(uint8_t))
-            panic("Invalid BMIS write size: %x\n", req->size);
-
-        oldVal = regs[offset];
-        newVal = *data;
 
-        // the BMIDEA bit is RO
-        newVal |= (oldVal & BMIDEA);
-
-        // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
-        if ((oldVal & IDEINTS) && (newVal & IDEINTS))
-            newVal &= ~IDEINTS; // clear the interrupt?
-        else
-            (oldVal & IDEINTS) ? newVal |= IDEINTS : newVal &= ~IDEINTS;
-
-        if ((oldVal & IDEDMAE) && (newVal & IDEDMAE))
-            newVal &= ~IDEDMAE;
-        else
-            (oldVal & IDEDMAE) ? newVal |= IDEDMAE : newVal &= ~IDEDMAE;
-
-        regs[offset] = newVal;
-        break;
-
-        // Bus master IDE descriptor table pointer register
-      case (BMI + BMIDTP0):
-      case (BMI + BMIDTP1):
-        if (req->size != sizeof(uint32_t))
-            panic("Invalid BMIDTP write size: %x\n", req->size);
-
-        *(uint32_t *)&regs[offset] = *(uint32_t *)data & ~0x3;
-        break;
-
-        // Write the data word in the command register block
-      case (CMD1 + IDE_DATA_OFFSET):
-      case (CMD0 + IDE_DATA_OFFSET):
-        if (req->size != sizeof(uint16_t))
-            panic("Invalid command block data write size: %x\n", req->size);
+            // see if the start/stop bit is being changed
+            if ((oldVal & SSBM) ^ (newVal & SSBM)) {
+                if (oldVal & SSBM) {
+                    // stopping DMA transfer
+                    DPRINTF(IdeCtrl, "Stopping DMA transfer\n");
+
+                    // clear the BMIDEA bit
+                    bmi_regs[offset + 0x2] &= ~BMIDEA;
+
+                    if (disks[disk] == NULL)
+                        panic("DMA stop for disk %d which does not exist\n",
+                              disk);
+
+                    // inform the disk of the DMA transfer abort
+                    disks[disk]->abortDma();
+                } else {
+                    // starting DMA transfer
+                    DPRINTF(IdeCtrl, "Starting DMA transfer\n");
+
+                    // set the BMIDEA bit
+                    bmi_regs[offset + 0x2] |= BMIDEA;
+
+                    if (disks[disk] == NULL)
+                        panic("DMA start for disk %d which does not exist\n",
+                              disk);
+
+                    // inform the disk of the DMA transfer start
+                    if (primary)
+                        disks[disk]->startDma(*(uint32_t *)&bmi_regs[BMIDTP0]);
+                    else
+                        disks[disk]->startDma(*(uint32_t *)&bmi_regs[BMIDTP1]);
+                }
+            }
 
-        break;
+            // update the register value
+            bmi_regs[offset] = newVal;
+            break;
 
-        // Write the command byte in command register block
-      case (CMD1 + IDE_COMMAND_OFFSET):
-      case (CMD0 + IDE_COMMAND_OFFSET):
-        if (req->size != sizeof(uint8_t))
-            panic("Invalid command block command write size: %x\n", req->size);
+            // Bus master IDE status register
+          case BMIS0:
+          case BMIS1:
+            if (req->size != sizeof(uint8_t))
+                panic("Invalid BMIS write size: %x\n", req->size);
 
-        // select the disk based on the DEV bit
-        disk = getDisk(offset);
+            oldVal = bmi_regs[offset];
+            newVal = *data;
 
-        if (cmd_in_progress[disk])
-            panic("Command on disk %d already in progress!\n", disk);
-        if (disks[disk] == NULL)
-            panic("Specified disk %d does not exist!\n", disk);
+            // the BMIDEA bit is RO
+            newVal |= (oldVal & BMIDEA);
 
-        cmd_in_progress[disk] = true;
+            // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each
+            if ((oldVal & IDEINTS) && (newVal & IDEINTS))
+                newVal &= ~IDEINTS; // clear the interrupt?
+            else
+                (oldVal & IDEINTS) ? newVal |= IDEINTS : newVal &= ~IDEINTS;
 
-        // write to both the command/status and alternate status
-        regs[offset] = *data;
-        regs[offset + 3] = *data;
+            if ((oldVal & IDEDMAE) && (newVal & IDEDMAE))
+                newVal &= ~IDEDMAE;
+            else
+                (oldVal & IDEDMAE) ? newVal |= IDEDMAE : newVal &= ~IDEDMAE;
 
-        // issue the command to the disk
-        if (disk < 2)
-            disks[disk]->startIO(&regs[CMD0], &regs[BMI + BMIDTP0]);
-        else
-            disks[disk]->startIO(&regs[CMD1], &regs[BMI + BMIDTP1]);
+            bmi_regs[offset] = newVal;
+            break;
 
-        break;
+            // Bus master IDE descriptor table pointer register
+          case BMIDTP0:
+          case BMIDTP1:
+            if (req->size != sizeof(uint32_t))
+                panic("Invalid BMIDTP write size: %x\n", req->size);
 
-      default:
-        if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) &&
-            req->size != sizeof(uint32_t))
-            panic("IDE controller write of invalid write size: %x\n",
-                  req->size);
+            *(uint32_t *)&bmi_regs[offset] = *(uint32_t *)data & ~0x3;
+            break;
 
-        DPRINTF(IdeCtrl, "IDE default write offset: %#x size: %#x data: %#x\n",
-                offset, req->size, *(uint32_t *)data);
+          default:
+            if (req->size != sizeof(uint8_t) &&
+                req->size != sizeof(uint16_t) &&
+                req->size != sizeof(uint32_t))
+                panic("IDE controller write of invalid write size: %x\n",
+                      req->size);
 
-        // do a default copy of data into the registers
-        memcpy((void *)&regs[offset], data, req->size);
+            // do a default copy of data into the registers
+            memcpy((void *)&bmi_regs[offset], data, req->size);
+        }
     }
 
     return No_Fault;
index ac25eafe7fc890ca10b33214b14929d41aca5c76..b4de9703602802d061053a7be66d8e102af37e33 100644 (file)
 #include "dev/pcireg.h"
 #include "dev/io_device.hh"
 
-#define CMD0  0x00 // Channel 0 command block offset
-#define CTRL0 0x08 // Channel 0 control block offset
-#define CMD1  0x0c // Channel 1 command block offset
-#define CTRL1 0x14 // Channel 1 control block offset
-#define BMI   0x18 // Bus master IDE offset
-
 #define BMIC0    0x0  // Bus master IDE command register
 #define BMIS0    0x2  // Bus master IDE status register
 #define BMIDTP0  0x4  // Bus master IDE descriptor table pointer register
 #define BMIDEA  0x01 // Bus master IDE active
 
 // IDE Command byte fields
-// Taken from include/linux/ide.h
-#define IDE_DATA_OFFSET         (0)
-#define IDE_ERROR_OFFSET        (1)
-#define IDE_NSECTOR_OFFSET      (2)
-#define IDE_SECTOR_OFFSET       (3)
-#define IDE_LCYL_OFFSET         (4)
-#define IDE_HCYL_OFFSET         (5)
 #define IDE_SELECT_OFFSET       (6)
-#define IDE_STATUS_OFFSET       (7)
-#define IDE_CONTROL_OFFSET      (8)
-#define IDE_IRQ_OFFSET          (9)
+#define IDE_SELECT_DEV_BIT      0x10
 
 #define IDE_FEATURE_OFFSET      IDE_ERROR_OFFSET
 #define IDE_COMMAND_OFFSET      IDE_STATUS_OFFSET
 #define BME     0x04 // Bus master function enable
 #define IOSE    0x01 // I/O space enable
 
-class IntrControl;
+typedef enum RegType {
+    COMMAND_BLOCK = 0,
+    CONTROL_BLOCK,
+    BMI_BLOCK
+} RegType_t;
+
 class IdeDisk;
+class IntrControl;
 class PciConfigAll;
 class Tsunami;
 class PhysicalMemory;
@@ -125,8 +116,10 @@ class IdeController : public PciDev
     Addr bmi_size;
 
   private:
-    /** Registers used for programmed I/O and bus master interface */
-    uint8_t regs[40];
+    /** Registers used for bus master interface */
+    uint8_t bmi_regs[16];
+    /** Shadows of the device select bit */
+    uint8_t dev[2];
     /** Registers used in PCI configuration */
     uint8_t pci_regs[8];
 
@@ -135,60 +128,77 @@ class IdeController : public PciDev
     bool bm_enabled;
     bool cmd_in_progress[4];
 
-  private:
+  public:
     /** Pointer to the chipset */
     Tsunami *tsunami;
+
+  private:
     /** IDE disks connected to controller */
     IdeDisk *disks[4];
 
   private:
-    /** Get offset into register file from access address */
-    Addr getOffset(const Addr &addr) {
-        Addr offset = addr;
+    /** 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;
-            offset += CMD0;
+            type = COMMAND_BLOCK;
+            primary = true;
         } else if (addr >= pri_ctrl_addr &&
                    addr < (pri_ctrl_addr + pri_ctrl_size)) {
             offset -= pri_ctrl_addr;
-            offset += CTRL0;
+            type = CONTROL_BLOCK;
+            primary = true;
         } else if (addr >= sec_cmd_addr &&
                    addr < (sec_cmd_addr + sec_cmd_size)) {
             offset -= sec_cmd_addr;
-            offset += CMD1;
+            type = COMMAND_BLOCK;
+            primary = false;
         } else if (addr >= sec_ctrl_addr &&
                    addr < (sec_ctrl_addr + sec_ctrl_size)) {
             offset -= sec_ctrl_addr;
-            offset += CTRL1;
+            type = CONTROL_BLOCK;
+            primary = false;
         } else if (addr >= bmi_addr && addr < (bmi_addr + bmi_size)) {
             offset -= bmi_addr;
-            offset += BMI;
+            type = BMI_BLOCK;
+            primary = (offset < BMIC1) ? true : false;
         } else {
             panic("IDE controller access to invalid address: %#x\n", addr);
         }
-
-        return offset;
     };
-    /** Select the disk based on the register offset */
-    int getDisk(const Addr &offset) {
+
+    /** Select the disk based on the channel and device bit */
+    int getDisk(bool primary)
+    {
         int disk = 0;
+        uint8_t *devBit = &dev[0];
 
-        // If the offset is in the channel 1 range, disks are 2 or 3
-        if (offset >= CMD1 && offset < BMI && offset >= (BMI + BMIC1))
+        if (!primary) {
             disk += 2;
-
-        if (disk < 2) {
-            if (regs[CMD0 + IDE_STATUS_OFFSET] & 0x10)
-                disk += 1;
-        } else {
-            if (regs[CMD1 + IDE_STATUS_OFFSET] & 0x10)
-                disk += 1;
+            devBit = &dev[1];
         }
 
+        disk += *devBit;
+
+        assert(*devBit == 0 || *devBit == 1);
+
         return disk;
     };
 
+    /** 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;
+    }
+
   public:
     /**
      * Constructs and initializes this controller.
@@ -204,7 +214,7 @@ class IdeController : public PciDev
      * @param hier The hierarchy parameters
      */
     IdeController(const std::string &name, IntrControl *ic,
-                  const vector<IdeDisk *> &new_disks,
+                  const std::vector<IdeDisk *> &new_disks,
                   MemoryController *mmu, PciConfigAll *cf,
                   PciConfigData *cd, Tsunami *t,
                   uint32_t bus_num, uint32_t dev_num, uint32_t func_num,
@@ -218,6 +228,11 @@ class IdeController : public PciDev
     virtual void WriteConfig(int offset, int size, uint32_t data);
     virtual void ReadConfig(int offset, int size, uint8_t *data);
 
+    void intrPost();
+    void intrClear();
+
+    void setDmaComplete(IdeDisk *disk);
+
     /**
      * Read a done field for a given target.
      * @param req Contains the address of the field to read.
index edf01552f304a5bb9d01f8091340d64c8de48077..77e809ee67c0bb07f81bdf197b8c6db7cb7cae48 100644 (file)
 #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_mem/physical_memory.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/universe.hh"
 
 using namespace std;
 
-IdeDisk::IdeDisk(const string &name, DiskImage *img, int delay)
-    : SimObject(name), ctrl(NULL), image(img)
+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),
+      dmaReadEvent(this), dmaWriteEvent(this)
 {
-    diskDelay = delay * ticksPerSecond / 1000;
+    diskDelay = (delay * ticksPerSecond / 1000) / image->size();
+
+    // 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;
+
+    // fill out the drive ID structure
+    memset(&driveID, 0, sizeof(struct hd_driveid));
+
+    // 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
+    sprintf((char *)driveID.model, "5MI EDD si k");
+    // Set the maximum multisector transfer size
+    driveID.max_multsect = MAX_MULTSECT;
+    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
+    driveID.capability = 0x7;
+    // UDMA support, EIDE support
+    driveID.field_valid = 0x6;
+    // Setup default C/H/S settings
+    driveID.cyls = cylinders;
+    driveID.sectors = sectors;
+    driveID.heads = heads;
+    // Setup the current multisector transfer size
+    driveID.multsect = MAX_MULTSECT;
+    driveID.multsect_valid = 0x1;
+    // Number of sectors on disk
+    driveID.lba_capacity = lba_size;
+    // Multiword DMA mode 2 and below supported
+    driveID.dma_mword = 0x400;
+    // Set PIO mode 4 and 3 supported
+    driveID.eide_pio_modes = 0x3;
+    // Set DMA mode 4 and below supported
+    driveID.dma_ultra = 0x10;
+    // Statically set hardware config word
+    driveID.hw_config = 0x4001;
+
+    // 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
+    cmdReg.status |= STATUS_DRDY_BIT;
 }
 
 IdeDisk::~IdeDisk()
 {
+    // destroy the data buffer
+    delete [] dataBuffer;
+}
+
+////
+// Device registers read/write
+////
+
+void
+IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
+{
+    DevAction_t action = ACT_NONE;
+
+    if (cmdBlk) {
+        if (offset < 0 || offset > sizeof(CommandReg_t))
+            panic("Invalid disk command register offset: %#x\n", offset);
+
+        if (!byte && offset != DATA_OFFSET)
+            panic("Invalid 16-bit read, only allowed on data reg\n");
+
+        if (!byte)
+            *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
+        else
+            *data = ((uint8_t *)&cmdReg)[offset];
+
+        // determine if an action needs to be taken on the state machine
+        if (offset == STATUS_OFFSET) {
+            action = ACT_STAT_READ;
+        } else if (offset == DATA_OFFSET) {
+            if (byte)
+                action = ACT_DATA_READ_BYTE;
+            else
+                action = ACT_DATA_READ_SHORT;
+        }
+
+    } else {
+        if (offset != ALTSTAT_OFFSET)
+            panic("Invalid disk control register offset: %#x\n", offset);
+
+        if (!byte)
+            panic("Invalid 16-bit read from control block\n");
+
+        *data = ((uint8_t *)&cmdReg)[STATUS_OFFSET];
+    }
+
+    if (action != ACT_NONE)
+        updateState(action);
+}
+
+void
+IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
+{
+    DevAction_t action = ACT_NONE;
+
+    if (cmdBlk) {
+        if (offset < 0 || offset > sizeof(CommandReg_t))
+            panic("Invalid disk command register offset: %#x\n", offset);
+
+        if (!byte && offset != DATA_OFFSET)
+            panic("Invalid 16-bit write, only allowed on data reg\n");
+
+        if (!byte)
+            *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
+        else
+            ((uint8_t *)&cmdReg)[offset] = *data;
+
+        // determine if an action needs to be taken on the state machine
+        if (offset == COMMAND_OFFSET) {
+            action = ACT_CMD_WRITE;
+        } else if (offset == DATA_OFFSET) {
+            if (byte)
+                action = ACT_DATA_WRITE_BYTE;
+            else
+                action = ACT_DATA_WRITE_SHORT;
+        }
+
+    } else {
+        if (offset != CONTROL_OFFSET)
+            panic("Invalid disk control register offset: %#x\n", offset);
+
+        if (!byte)
+            panic("Invalid 16-bit write to control block\n");
+
+        if (*data & CONTROL_RST_BIT)
+            panic("Software reset not supported!\n");
+
+        nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
+    }
+
+    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));
+
+    curPrdAddr += sizeof(PrdEntry_t);
+
+    if (dmaRead)
+        doDmaRead();
+    else
+        doDmaWrite();
+}
+
+void
+IdeDisk::doDmaRead()
+{
+    Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize);
+
+    if (dmaInterface) {
+        if (dmaInterface->busy()) {
+            // reschedule after waiting period
+            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
+            return;
+        }
+
+        Addr dmaAddr =
+            ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
+        dmaInterface->doDMA(Read, dmaAddr, curPrd.getByteCount(),
+                            curTick + totalDiskDelay, &dmaReadEvent);
+    } else {
+        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
+        dmaReadEvent.schedule(curTick + totalDiskDelay);
+    }
+}
+
+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;
+
+    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;
+    }
+
+    // check for the EOT
+    if (curPrd.getEOT()){
+        assert(cmdBytesLeft == 0);
+        dmaState = Dma_Idle;
+        updateState(ACT_DMA_DONE);
+    } else {
+        doDmaTransfer();
+    }
+}
+
+void
+IdeDisk::doDmaWrite()
+{
+    Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize);
+
+    if (dmaInterface) {
+        if (dmaInterface->busy()) {
+            // reschedule after waiting period
+            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
+            return;
+        }
+
+        Addr dmaAddr =
+            ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
+        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
+                            curPrd.getByteCount(), curTick + totalDiskDelay,
+                            &dmaWriteEvent);
+    } else {
+        // schedule event with disk delay (dmaWriteDone)
+        dmaWriteEvent.schedule(curTick + totalDiskDelay);
+    }
+}
+
+void
+IdeDisk::dmaWriteDone()
+{
+    uint32_t bytesRead = 0;
+
+    // clear out the data buffer
+    memset(dataBuffer, 0, MAX_DMA_SIZE);
+
+    while (bytesRead < curPrd.getByteCount()) {
+        if (cmdBytesLeft <= 0)
+            panic("DMA requested data is larger than # sectors specified\n");
+
+        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
+
+        bytesRead += 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());
+
+    // 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");
+
+    curPrdAddr = ctrl->tsunami->pchip->translatePciToDma(prdTableBase);
+
+    dmaState = Dma_Transfer;
+
+    // schedule dma transfer (doDmaTransfer)
+    dmaTransferEvent.schedule(curTick + 1);
+}
+
+void
+IdeDisk::abortDma()
+{
+    if (dmaState == Dma_Idle)
+        panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n");
+
+    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
+        panic("Inconsistent device state, should be in Transfer or Prepare!\n");
+
+    updateState(ACT_CMD_ERROR);
+}
+
+void
+IdeDisk::startCommand()
+{
+    DevAction_t action = ACT_NONE;
+    uint32_t size = 0;
+    dmaRead = false;
+
+    // copy the command to the shadow
+    curCommand = cmdReg.command;
+
+    // Decode commands
+    switch (cmdReg.command) {
+        // Supported non-data commands
+      case WIN_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 WIN_RECAL:
+      case WIN_SPECIFY:
+      case WIN_FLUSH_CACHE:
+      case WIN_VERIFY:
+      case WIN_SEEK:
+      case WIN_SETFEATURES:
+      case WIN_SETMULT:
+        devState = Command_Execution;
+        action = ACT_CMD_COMPLETE;
+        break;
+
+        // Supported PIO data-in commands
+      case WIN_IDENTIFY:
+        cmdBytesLeft = drqBytesLeft = sizeof(struct hd_driveid);
+        devState = Prepare_Data_In;
+        action = ACT_DATA_READY;
+        break;
+
+      case WIN_MULTREAD:
+      case WIN_READ:
+        if (!(cmdReg.drive & DRIVE_LBA_BIT))
+            panic("Attempt to perform CHS access, only supports LBA\n");
+
+        if (cmdReg.sec_count == 0)
+            cmdBytesLeft = (256 * SectorSize);
+        else
+            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
+
+        drqBytesLeft = SectorSize;
+        curSector = getLBABase();
+
+        devState = Prepare_Data_In;
+        action = ACT_DATA_READY;
+        break;
+
+        // Supported PIO data-out commands
+      case WIN_MULTWRITE:
+      case WIN_WRITE:
+        if (!(cmdReg.drive & DRIVE_LBA_BIT))
+            panic("Attempt to perform CHS access, only supports LBA\n");
+
+        if (cmdReg.sec_count == 0)
+            cmdBytesLeft = (256 * SectorSize);
+        else
+            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
+
+        drqBytesLeft = SectorSize;
+        curSector = getLBABase();
+
+        devState = Prepare_Data_Out;
+        action = ACT_DATA_READY;
+        break;
+
+        // Supported DMA commands
+      case WIN_WRITEDMA:
+        dmaRead = true;  // a write to the disk is a DMA read from memory
+      case WIN_READDMA:
+        if (!(cmdReg.drive & DRIVE_LBA_BIT))
+            panic("Attempt to perform CHS access, only supports LBA\n");
+
+        if (cmdReg.sec_count == 0)
+            cmdBytesLeft = (256 * SectorSize);
+        else
+            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
+
+        drqBytesLeft = 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
+        cmdReg.status |= STATUS_BSY_BIT;
+        // clear the DRQ bit
+        cmdReg.status &= ~STATUS_DRQ_BIT;
+
+        updateState(action);
+    }
+}
+
+////
+// Handle setting and clearing interrupts
+////
+
+void
+IdeDisk::intrPost()
+{
+    if (intrPending)
+        panic("Attempt to post an interrupt with one pending\n");
+
+    intrPending = true;
+
+    // talk to controller to set interrupt
+    if (ctrl)
+        ctrl->intrPost();
+}
+
+void
+IdeDisk::intrClear()
+{
+    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_Idle_S:
+        if (!isDEVSelect())
+            devState = Device_Idle_NS;
+        else if (action == ACT_CMD_WRITE)
+            startCommand();
+
+        break;
+
+      case Device_Idle_SI:
+        if (!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 (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
+            cmdReg.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));
+            // copy the data into the data buffer
+            if (curCommand == WIN_IDENTIFY)
+                memcpy((void *)dataBuffer, (void *)&driveID,
+                       sizeof(struct hd_driveid));
+            else
+                readDisk(curSector++, dataBuffer);
+
+            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
+                memcpy((void *)&cmdReg.data0,
+                       (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;
+                    cmdReg.status |= STATUS_BSY_BIT;
+                }
+            }
+        }
+        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 (cmdBytesLeft != 0) {
+            // clear the BSY bit
+            cmdReg.status &= ~STATUS_BSY_BIT;
+            // set the DRQ bit
+            cmdReg.status |= STATUS_DRQ_BIT;
+
+            // clear the data buffer to get it ready for writes
+            memset(dataBuffer, 0, MAX_DMA_SIZE);
+
+            if (!isIENSet()) {
+                devState = Data_Ready_INTRQ_Out;
+                intrPost();
+            } else {
+                devState = Transfer_Data_Out;
+            }
+        }
+        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.data0,
+                       sizeof(uint16_t));
+
+                drqBytesLeft -= 2;
+                cmdBytesLeft -= 2;
+            }
+
+            if (drqBytesLeft == 0) {
+                // copy the block to the disk
+                writeDisk(curSector++, dataBuffer);
+
+                // set the BSY bit
+                cmdReg.status |= STATUS_BSY_BIT;
+                // clear the DRQ bit
+                cmdReg.status &= ~STATUS_DRQ_BIT;
+
+                devState = Prepare_Data_Out;
+            }
+        }
+        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
+            cmdReg.status &= ~STATUS_BSY_BIT;
+            // set the DRQ bit
+            cmdReg.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
+            cmdReg.status |= 0x10;
+            // 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
@@ -70,6 +845,8 @@ IdeDisk::unserialize(Checkpoint *cp, const string &section)
 BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
 
     SimObjectParam<DiskImage *> image;
+    SimObjectParam<PhysicalMemory *> physmem;
+    Param<int> driveID;
     Param<int> disk_delay;
 
 END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
@@ -77,6 +854,8 @@ END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
 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)
 
 END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
@@ -84,7 +863,7 @@ END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
 
 CREATE_SIM_OBJECT(IdeDisk)
 {
-    return new IdeDisk(getInstanceName(), image, disk_delay);
+    return new IdeDisk(getInstanceName(), image, physmem, driveID, disk_delay);
 }
 
 REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
index 29ddd4be7d7d13aa26def21f68efbac3381b68c4..9bb695bee601735813420e4d886b035a1a32f17f 100644 (file)
 #ifndef __IDE_DISK_HH__
 #define __IDE_DISK_HH__
 
+#include "dev/ide.hh"
+#include "dev/disk_image.hh"
 #include "dev/io_device.hh"
+#include "sim/eventq.hh"
 
-class DiskImage;
+#define DMA_BACKOFF_PERIOD 200
+
+#define MAX_DMA_SIZE (16384)
+#define MAX_MULTSECT (32)
+
+#define PRD_BASE_MASK  0xfffffffe
+#define PRD_COUNT_MASK 0xfffe
+#define PRD_EOT_MASK   0x8000
+
+typedef struct PrdEntry {
+    uint32_t baseAddr;
+    uint16_t byteCount;
+    uint16_t endOfTable;
+} PrdEntry_t;
+
+class PrdTableEntry {
+  public:
+    PrdEntry_t entry;
+
+    uint32_t getBaseAddr()
+    {
+        return (entry.baseAddr & PRD_BASE_MASK);
+    }
+
+    uint16_t getByteCount()
+    {
+        return ((entry.byteCount == 0) ? MAX_DMA_SIZE :
+                (entry.byteCount & PRD_COUNT_MASK));
+    }
+
+    uint16_t getEOT()
+    {
+        return (entry.endOfTable & PRD_EOT_MASK);
+    }
+};
+
+#define DATA_OFFSET     (0)
+#define ERROR_OFFSET    (1)
+#define FEATURES_OFFSET (1)
+#define NSECTOR_OFFSET  (2)
+#define SECTOR_OFFSET   (3)
+#define LCYL_OFFSET     (4)
+#define HCYL_OFFSET     (5)
+#define SELECT_OFFSET   (6)
+#define STATUS_OFFSET   (7)
+#define COMMAND_OFFSET  (7)
+
+#define CONTROL_OFFSET  (2)
+#define ALTSTAT_OFFSET  (2)
+
+#define SELECT_DEV_BIT  0x10
+#define CONTROL_RST_BIT 0x04
+#define CONTROL_IEN_BIT 0x02
+#define STATUS_BSY_BIT  0x80
+#define STATUS_DRDY_BIT 0x40
+#define STATUS_DRQ_BIT  0x08
+#define DRIVE_LBA_BIT  0x40
+
+#define DEV0 (0)
+#define DEV1 (1)
+
+typedef struct CommandReg {
+    uint8_t data0;
+    union {
+        uint8_t data1;
+        uint8_t error;
+        uint8_t features;
+    };
+    uint8_t sec_count;
+    uint8_t sec_num;
+    uint8_t cyl_low;
+    uint8_t cyl_high;
+    union {
+        uint8_t drive;
+        uint8_t head;
+    };
+    union {
+        uint8_t status;
+        uint8_t command;
+    };
+} CommandReg_t;
+
+typedef enum DevAction {
+    ACT_NONE = 0,
+    ACT_CMD_WRITE,
+    ACT_CMD_COMPLETE,
+    ACT_CMD_ERROR,
+    ACT_STAT_READ,
+    ACT_DATA_READY,
+    ACT_DATA_READ_BYTE,
+    ACT_DATA_READ_SHORT,
+    ACT_DATA_WRITE_BYTE,
+    ACT_DATA_WRITE_SHORT,
+    ACT_DMA_READY,
+    ACT_DMA_DONE
+} DevAction_t;
+
+typedef enum DevState {
+    // Device idle
+    Device_Idle_S = 0,
+    Device_Idle_SI,
+    Device_Idle_NS,
+
+    // Non-data commands
+    Command_Execution,
+
+    // PIO data-in (data to host)
+    Prepare_Data_In,
+    Data_Ready_INTRQ_In,
+    Transfer_Data_In,
+
+    // PIO data-out (data from host)
+    Prepare_Data_Out,
+    Data_Ready_INTRQ_Out,
+    Transfer_Data_Out,
+
+    // DMA protocol
+    Prepare_Data_Dma,
+    Transfer_Data_Dma
+} DevState_t;
+
+typedef enum DmaState {
+    Dma_Idle = 0,
+    Dma_Start,
+    Dma_Transfer
+} DmaState_t;
+
+class PhysicalMemory;
 class IdeController;
 
 /**
- * SCSI Disk device model
+ * IDE Disk device model
  */
 class IdeDisk : public SimObject
 {
   protected:
     /** The IDE controller for this disk. */
     IdeController *ctrl;
+    /** The DMA interface to use for transfers */
+    DMAInterface<Bus> *dmaInterface;
     /** The image that contains the data of this disk. */
     DiskImage *image;
+    /** Pointer to physical memory for DMA transfers */
+    PhysicalMemory *physmem;
 
   protected:
     /** The disk delay in milliseconds. */
     int diskDelay;
 
+  private:
+    /** Drive identification structure for this disk */
+    struct hd_driveid driveID;
+    /** Data buffer for transfers */
+    uint8_t *dataBuffer;
+    /** Number of bytes left in command data transfer */
+    uint32_t cmdBytesLeft;
+    /** Number of bytes left in DRQ block */
+    uint32_t drqBytesLeft;
+    /** Current sector in access */
+    uint32_t curSector;
+    /** Command block registers */
+    CommandReg_t cmdReg;
+    /** Shadow of the current command code */
+    uint8_t curCommand;
+    /** Interrupt enable bit */
+    bool nIENBit;
+    /** Device state */
+    DevState_t devState;
+    /** Dma state */
+    DmaState_t dmaState;
+    /** Dma transaction is a read */
+    bool dmaRead;
+    /** PRD table base address */
+    uint32_t curPrdAddr;
+    /** PRD entry */
+    PrdTableEntry curPrd;
+    /** Device ID (master=0/slave=1) */
+    int devID;
+    /** Interrupt pending */
+    bool intrPending;
+
   public:
     /**
      * Create and initialize this Disk.
      * @param name The name of this disk.
      * @param img The disk image of this disk.
+     * @param phys Pointer to physical memory
+     * @param id The disk ID (master=0/slave=1)
      * @param disk_delay The disk delay in milliseconds
      */
-    IdeDisk(const std::string &name, DiskImage *img, int disk_delay);
+    IdeDisk(const std::string &name, DiskImage *img, PhysicalMemory *phys,
+            int id, int disk_delay);
 
     /**
      * Delete the data buffer.
@@ -71,15 +240,78 @@ class IdeDisk : public SimObject
      * Set the controller for this device
      * @param c The IDE controller
      */
-    void setController(IdeController *c) {
+    void setController(IdeController *c, DMAInterface<Bus> *dmaIntr) {
         if (ctrl) panic("Cannot change the controller once set!\n");
         ctrl = c;
+        dmaInterface = dmaIntr;
     }
 
-    void startIO(uint8_t *cmdBlk, uint8_t *prdPtr) {};
+    // Device register read/write
+    void read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data);
+    void write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data);
+
+    // Start/abort functions
+    void startDma(const uint32_t &prdTableBase);
+    void abortDma();
+
+  private:
+    void startCommand();
+
+    // Interrupt management
+    void intrPost();
+    void intrClear();
+
+    // DMA stuff
+    void doDmaTransfer();
+    friend class EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer>;
+    EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer> dmaTransferEvent;
+
+    void doDmaRead();
+    friend class EventWrapper<IdeDisk, &IdeDisk::doDmaRead>;
+    EventWrapper<IdeDisk, &IdeDisk::doDmaRead> dmaReadWaitEvent;
+
+    void doDmaWrite();
+    friend class EventWrapper<IdeDisk, &IdeDisk::doDmaWrite>;
+    EventWrapper<IdeDisk, &IdeDisk::doDmaWrite> dmaWriteWaitEvent;
 
-    void dmaStart() {};
-    void dmaStop() {};
+    void dmaPrdReadDone();
+    friend class EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone>;
+    EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone> dmaPrdReadEvent;
+
+    void dmaReadDone();
+    friend class EventWrapper<IdeDisk, &IdeDisk::dmaReadDone>;
+    EventWrapper<IdeDisk, &IdeDisk::dmaReadDone> dmaReadEvent;
+
+    void dmaWriteDone();
+    friend class EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone>;
+    EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone> dmaWriteEvent;
+
+    // Disk image read/write
+    void readDisk(uint32_t sector, uint8_t *data);
+    void writeDisk(uint32_t sector, uint8_t *data);
+
+    // State machine management
+    void updateState(DevAction_t action);
+
+    // Utility functions
+    bool isBSYSet() { return (cmdReg.status & STATUS_BSY_BIT); }
+    bool isIENSet() { return nIENBit; }
+    bool isDEVSelect() { return ((cmdReg.drive & SELECT_DEV_BIT) == devID); }
+
+    void setComplete()
+    {
+        // clear out the status byte
+        cmdReg.status = 0;
+
+        // set the DRDY bit
+        cmdReg.status |= STATUS_DRDY_BIT;
+    }
+
+    uint32_t getLBABase()
+    {
+        return  (Addr)(((cmdReg.head & 0xf) << 24) | (cmdReg.cyl_high << 16) |
+                       (cmdReg.cyl_low << 8) | (cmdReg.sec_num));
+    }
 
     /**
      * Serialize this object to the given output stream.
index 4467ce1e51325aa7ae5183f1d01246c1cd7b15d2..226fd2749ec57b04087ba0fbe17eef0e2b4bcd11 100644 (file)
 #include "dev/scsi_ctrl.hh"
 #include "dev/pciconfigall.hh"
 #include "dev/pcidev.hh"
-#include "dev/tsunamireg.h"
-#include "dev/tsunami.hh"
 #include "mem/functional_mem/memory_control.hh"
 #include "sim/builder.hh"
 #include "sim/system.hh"
 
 using namespace std;
 
-PciConfigAll::PciConfigAll(const string &name, Tsunami *t, Addr a,
+PciConfigAll::PciConfigAll(const string &name, Addr a,
                            MemoryController *mmu)
-    : FunctionalMemory(name), addr(a), tsunami(t)
+    : FunctionalMemory(name), addr(a)
 {
     mmu->add_child(this, Range<Addr>(addr, addr + size));
 
-    // Put back pointer in tsunami
-    tsunami->pciconfig = this;
-
     // Make all the pointers to devices null
     for(int x=0; x < MAX_PCI_DEV; x++)
         for(int y=0; y < MAX_PCI_FUNC; y++)
@@ -103,7 +98,7 @@ PciConfigAll::read(MemReqPtr &req, uint8_t *data)
         }
     }
 
-    DPRINTFN("Tsunami PCI Configspace  ERROR: read  daddr=%#x size=%d\n",
+    DPRINTFN("PCI Configspace  ERROR: read  daddr=%#x size=%d\n",
              daddr, req->size);
 
     return No_Fault;
@@ -166,7 +161,6 @@ PciConfigAll::unserialize(Checkpoint *cp, const std::string &section)
 
 BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll)
 
-    SimObjectParam<Tsunami *> tsunami;
     SimObjectParam<MemoryController *> mmu;
     Param<Addr> addr;
     Param<Addr> mask;
@@ -175,7 +169,6 @@ END_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll)
 
 BEGIN_INIT_SIM_OBJECT_PARAMS(PciConfigAll)
 
-    INIT_PARAM(tsunami, "Tsunami"),
     INIT_PARAM(mmu, "Memory Controller"),
     INIT_PARAM(addr, "Device Address"),
     INIT_PARAM(mask, "Address Mask")
@@ -184,7 +177,7 @@ END_INIT_SIM_OBJECT_PARAMS(PciConfigAll)
 
 CREATE_SIM_OBJECT(PciConfigAll)
 {
-    return new PciConfigAll(getInstanceName(), tsunami, addr, mmu);
+    return new PciConfigAll(getInstanceName(), addr, mmu);
 }
 
 REGISTER_SIM_OBJECT("PciConfigAll", PciConfigAll)
index e0f7f6adae92c024d10813e5391c458016c891be..6df1e2fe78068e7c3ef2e4e1eb171a10cc3f05c8 100644 (file)
  */
 
 /*
- * @todo
- * Should derive Tsunami from common platform class so PCI will work with
- * multiple platforms
- *
  * @file
  * PCI Config space implementation.
  */
@@ -39,7 +35,6 @@
 #define __PCICONFIGALL_HH__
 
 #include "mem/functional_mem/functional_memory.hh"
-#include "dev/tsunami.hh"
 #include "dev/pcireg.h"
 
 #define MAX_PCI_DEV     32
@@ -60,15 +55,6 @@ class PciConfigAll : public FunctionalMemory
     Addr addr;
     static const Addr size = 0xffffff;
 
-  protected:
-
-    /**
-      * Pointer to the Tsunmi Object so we can communicate
-      * to other Tsunami devices in need be.
-      * @todo Make this more generic for multiple platforms
-      */
-    Tsunami *tsunami;
-
   public:
     /**
       * Pointers to all the devices that are registered with this
@@ -79,8 +65,7 @@ class PciConfigAll : public FunctionalMemory
     /**
       * The default constructor.
       */
-    PciConfigAll(const std::string &name, Tsunami *t, Addr a,
-                 MemoryController *mmu);
+    PciConfigAll(const std::string &name, Addr a, MemoryController *mmu);
 
     virtual Fault read(MemReqPtr &req, uint8_t *data);
     virtual Fault write(MemReqPtr &req, const uint8_t *data);
index 4425570f2f889cb174158cf4d10c57114da767b1..db6a489ad656a4e29d8c2a613a645724f775a96b 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "sim/sim_object.hh"
 
+class PciConfigAll;
 class IntrControl;
 class SimConsole;
 
@@ -46,13 +47,15 @@ class Platform : public SimObject
     IntrControl *intrctrl;
     /** Pointer to the simulation console */
     SimConsole *cons;
+    /** Pointer to the PCI configuration space */
+    PciConfigAll *pciconfig;
 
     int interrupt_frequency;
 
   public:
     Platform(const std::string &name, SimConsole *con, IntrControl *intctrl,
-             int intrFreq)
-        : SimObject(name), intrctrl(intctrl), cons(con),
+             PciConfigAll *pci, int intrFreq)
+        : SimObject(name), intrctrl(intctrl), cons(con), pciconfig(pci),
           interrupt_frequency(intrFreq) {}
 };
 
index ba33fa0967d51b97609c396f8c732d5b5fb7877b..252f9f1cc3b5f9de7f51259b65f2ff14968866b9 100644 (file)
 #include "dev/tsunami_cchip.hh"
 #include "dev/tsunami_pchip.hh"
 #include "dev/tsunami.hh"
+#include "dev/pciconfigall.hh"
 #include "sim/builder.hh"
 #include "sim/system.hh"
 
 using namespace std;
 
 Tsunami::Tsunami(const string &name, System *s, SimConsole *con,
-                 IntrControl *ic, int intr_freq)
-    : Platform(name, con, ic, intr_freq), system(s)
+                 IntrControl *ic, PciConfigAll *pci, int intr_freq)
+    : Platform(name, con, ic, pci, intr_freq), system(s)
 {
     // set the back pointer from the system to myself
     system->platform = this;
@@ -71,6 +72,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tsunami)
     SimObjectParam<System *> system;
     SimObjectParam<SimConsole *> cons;
     SimObjectParam<IntrControl *> intrctrl;
+    SimObjectParam<PciConfigAll *> pciconfig;
     Param<int> interrupt_frequency;
 
 END_DECLARE_SIM_OBJECT_PARAMS(Tsunami)
@@ -80,13 +82,14 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Tsunami)
     INIT_PARAM(system, "system"),
     INIT_PARAM(cons, "system console"),
     INIT_PARAM(intrctrl, "interrupt controller"),
+    INIT_PARAM(pciconfig, "PCI configuration"),
     INIT_PARAM_DFLT(interrupt_frequency, "frequency of interrupts", 1024)
 
 END_INIT_SIM_OBJECT_PARAMS(Tsunami)
 
 CREATE_SIM_OBJECT(Tsunami)
 {
-    return new Tsunami(getInstanceName(), system, cons, intrctrl,
+    return new Tsunami(getInstanceName(), system, cons, intrctrl, pciconfig,
                        interrupt_frequency);
 }
 
index 3c304dccdec8e3220a6d76e4777bf805591c04f6..2cbacc50be07103d69b4df4074b39682bf4d7874 100644 (file)
@@ -81,12 +81,6 @@ class Tsunami : public Platform
       */
     TsunamiPChip *pchip;
 
-    /** Pointer to the PCI Config Space
-      * The config space in Tsunami all needs to return
-      * -1 if a device is not there.
-      */
-    PciConfigAll *pciconfig;
-
     int intr_sum_type[Tsunami::Max_CPUs];
     int ipi_pending[Tsunami::Max_CPUs];
 
@@ -99,7 +93,8 @@ class Tsunami : public Platform
       * @param intrFreq frequency that interrupts happen
       */
     Tsunami(const std::string &name, System *s, SimConsole *con,
-            IntrControl *intctrl, int intrFreq);
+            IntrControl *intctrl, PciConfigAll *pci,
+            int intrFreq);
 
     virtual void serialize(std::ostream &os);
     virtual void unserialize(Checkpoint *cp, const std::string &section);