diff options
-rw-r--r-- | base/range.hh | 2 | ||||
-rw-r--r-- | dev/ide_ctrl.cc | 313 | ||||
-rw-r--r-- | dev/ide_ctrl.hh | 99 | ||||
-rw-r--r-- | dev/ide_disk.cc | 787 | ||||
-rw-r--r-- | dev/ide_disk.hh | 246 | ||||
-rw-r--r-- | dev/pciconfigall.cc | 15 | ||||
-rw-r--r-- | dev/pciconfigall.hh | 17 | ||||
-rw-r--r-- | dev/platform.hh | 7 | ||||
-rw-r--r-- | dev/tsunami.cc | 9 | ||||
-rw-r--r-- | dev/tsunami.hh | 9 |
10 files changed, 1281 insertions, 223 deletions
diff --git a/base/range.hh b/base/range.hh index d72aa9755..2c4a43f48 100644 --- a/base/range.hh +++ b/base/range.hh @@ -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; } /** 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 *)®s[BMI + BMIS0] = 0x60; - *(uint8_t *)®s[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 *)®s[offset]); + if (type != BMI_BLOCK) { + assert(req->size != sizeof(uint32_t)); - // copy the data from the control registers - memcpy((void *)data, ®s[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 *)®s[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(®s[CMD0], ®s[BMI + BMIDTP0]); - else - disks[disk]->startIO(®s[CMD1], ®s[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 *)®s[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 §ion) 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 §ion) 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 §ion); |