summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dev/ide_ctrl.cc313
-rw-r--r--dev/ide_ctrl.hh99
-rw-r--r--dev/ide_disk.cc787
-rw-r--r--dev/ide_disk.hh246
-rw-r--r--dev/pciconfigall.cc15
-rw-r--r--dev/pciconfigall.hh17
-rw-r--r--dev/platform.hh7
-rw-r--r--dev/tsunami.cc9
-rw-r--r--dev/tsunami.hh9
9 files changed, 1280 insertions, 222 deletions
diff --git a/dev/ide_ctrl.cc b/dev/ide_ctrl.cc
index 0f53ba3e8..7507a8d7f 100644
--- a/dev/ide_ctrl.cc
+++ b/dev/ide_ctrl.cc
@@ -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);
}
}
@@ -126,6 +127,51 @@ IdeController::~IdeController()
}
////
+// 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;
diff --git a/dev/ide_ctrl.hh b/dev/ide_ctrl.hh
index ac25eafe7..b4de97036 100644
--- a/dev/ide_ctrl.hh
+++ b/dev/ide_ctrl.hh
@@ -37,12 +37,6 @@
#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
@@ -62,17 +56,8 @@
#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
@@ -92,8 +77,14 @@
#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.
diff --git a/dev/ide_disk.cc b/dev/ide_disk.cc
index edf01552f..77e809ee6 100644
--- a/dev/ide_disk.cc
+++ b/dev/ide_disk.cc
@@ -39,20 +39,795 @@
#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)
diff --git a/dev/ide_disk.hh b/dev/ide_disk.hh
index 29ddd4be7..9bb695bee 100644
--- a/dev/ide_disk.hh
+++ b/dev/ide_disk.hh
@@ -33,34 +33,203 @@
#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.
diff --git a/dev/pciconfigall.cc b/dev/pciconfigall.cc
index 4467ce1e5..226fd2749 100644
--- a/dev/pciconfigall.cc
+++ b/dev/pciconfigall.cc
@@ -39,23 +39,18 @@
#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)
diff --git a/dev/pciconfigall.hh b/dev/pciconfigall.hh
index e0f7f6ada..6df1e2fe7 100644
--- a/dev/pciconfigall.hh
+++ b/dev/pciconfigall.hh
@@ -27,10 +27,6 @@
*/
/*
- * @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);
diff --git a/dev/platform.hh b/dev/platform.hh
index 4425570f2..db6a489ad 100644
--- a/dev/platform.hh
+++ b/dev/platform.hh
@@ -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) {}
};
diff --git a/dev/tsunami.cc b/dev/tsunami.cc
index ba33fa096..252f9f1cc 100644
--- a/dev/tsunami.cc
+++ b/dev/tsunami.cc
@@ -38,14 +38,15 @@
#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);
}
diff --git a/dev/tsunami.hh b/dev/tsunami.hh
index 3c304dccd..2cbacc50b 100644
--- a/dev/tsunami.hh
+++ b/dev/tsunami.hh
@@ -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);