From 6807c319b02af31626ab2ed716da6341924eb857 Mon Sep 17 00:00:00 2001 From: Andrew Schultz Date: Mon, 3 May 2004 11:47:52 -0400 Subject: [PATCH] Checkin of latest IDE and some separation between platforms (Tsunami and Turbolaser) base/range.hh: Change semantics of range to be inclusive of the end value, may need to check other users of range to make sure they are semantically correct. This was needed for access of last byte in range of address on IDE and makes sense for case of range from 0 to all f dev/ide_ctrl.cc: dev/ide_ctrl.hh: dev/ide_disk.cc: dev/ide_disk.hh: Whole mess of changes.. at current state simulator will boot and read partition table and then have a bunch of errors and panic dev/pciconfigall.cc: dev/pciconfigall.hh: dev/platform.hh: Changes to work with platform separation dev/tsunami.cc: dev/tsunami.hh: Change to work with platform separation --HG-- extra : convert_revision : e1de22b54df7fdcf391efc2a8555ada93f46beab --- base/range.hh | 2 +- dev/ide_ctrl.cc | 313 ++++++++++-------- dev/ide_ctrl.hh | 99 +++--- dev/ide_disk.cc | 787 +++++++++++++++++++++++++++++++++++++++++++- dev/ide_disk.hh | 246 +++++++++++++- dev/pciconfigall.cc | 15 +- dev/pciconfigall.hh | 17 +- dev/platform.hh | 7 +- dev/tsunami.cc | 9 +- 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 &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); } } @@ -125,6 +126,51 @@ IdeController::~IdeController() delete disks[i]; } +//// +// Command completion +//// + +void +IdeController::setDmaComplete(IdeDisk *disk) +{ + int diskNum = getDisk(disk); + + if (diskNum < 0) + panic("Unable to find disk based on pointer %#x\n", disk); + + if (diskNum < 2) { + // clear the start/stop bit in the command register + bmi_regs[BMIC0] &= ~SSBM; + // clear the bus master active bit in the status register + bmi_regs[BMIS0] &= ~BMIDEA; + // set the interrupt bit + bmi_regs[BMIS0] |= IDEINTS; + } else { + // clear the start/stop bit in the command register + bmi_regs[BMIC1] &= ~SSBM; + // clear the bus master active bit in the status register + bmi_regs[BMIS1] &= ~BMIDEA; + // set the interrupt bit + bmi_regs[BMIS1] |= IDEINTS; + } +} + +//// +// Interrupt handling +//// + +void +IdeController::intrPost() +{ + tsunami->cchip->postDRIR(configData->config.hdr.pci0.interruptLine); +} + +void +IdeController::intrClear() +{ + tsunami->cchip->clearDRIR(configData->config.hdr.pci0.interruptLine); +} + //// // Bus timing and bus access functions //// @@ -289,7 +335,16 @@ IdeController::WriteConfig(int offset, int size, uint32_t data) Fault IdeController::read(MemReqPtr &req, uint8_t *data) { - Addr offset = getOffset(req->paddr); + Addr offset; + bool primary; + bool byte; + bool cmdBlk; + RegType_t type; + int disk; + + parseAddr(req->paddr, offset, primary, type); + byte = (req->size == sizeof(uint8_t)) ? true : false; + cmdBlk = (type == COMMAND_BLOCK) ? true : false; if (!io_enabled) return No_Fault; @@ -299,11 +354,18 @@ IdeController::read(MemReqPtr &req, uint8_t *data) req->size != sizeof(uint32_t)) panic("IDE controller read of invalid size: %#x\n", req->size); - DPRINTF(IdeCtrl, "IDE default read from offset: %#x size: %#x data: %#x\n", - offset, req->size, *(uint32_t *)®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 &new_disks, + const std::vector &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 image; + SimObjectParam physmem; + Param driveID; Param 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 *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 *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; + EventWrapper dmaTransferEvent; + + void doDmaRead(); + friend class EventWrapper; + EventWrapper dmaReadWaitEvent; + + void doDmaWrite(); + friend class EventWrapper; + EventWrapper dmaWriteWaitEvent; - void dmaStart() {}; - void dmaStop() {}; + void dmaPrdReadDone(); + friend class EventWrapper; + EventWrapper dmaPrdReadEvent; + + void dmaReadDone(); + friend class EventWrapper; + EventWrapper dmaReadEvent; + + void dmaWriteDone(); + friend class EventWrapper; + EventWrapper 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 + 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; SimObjectParam mmu; Param addr; Param 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; SimObjectParam cons; SimObjectParam intrctrl; + SimObjectParam pciconfig; Param 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); -- 2.30.2