/* * Copyright (c) 2004-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Andrew Schultz * Ali Saidi * Miguel Serrano */ #include #include "base/trace.hh" #include "cpu/intr_control.hh" #include "debug/IdeCtrl.hh" #include "dev/ide_ctrl.hh" #include "dev/ide_disk.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "params/IdeController.hh" #include "sim/byteswap.hh" using namespace std; // Bus master IDE registers enum BMIRegOffset { BMICommand = 0x0, BMIStatus = 0x2, BMIDescTablePtr = 0x4 }; // PCI config space registers enum ConfRegOffset { PrimaryTiming = 0x40, SecondaryTiming = 0x42, DeviceTiming = 0x44, UDMAControl = 0x48, UDMATiming = 0x4A, IDEConfig = 0x54 }; static const uint16_t timeRegWithDecodeEn = 0x8000; IdeController::Channel::Channel( string newName, Addr _cmdSize, Addr _ctrlSize) : _name(newName), cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize), master(NULL), slave(NULL), selected(NULL) { memset(&bmiRegs, 0, sizeof(bmiRegs)); bmiRegs.status.dmaCap0 = 1; bmiRegs.status.dmaCap1 = 1; } IdeController::Channel::~Channel() { } IdeController::IdeController(Params *p) : PciDev(p), primary(name() + ".primary", BARSize[0], BARSize[1]), secondary(name() + ".secondary", BARSize[2], BARSize[3]), bmiAddr(0), bmiSize(BARSize[4]), primaryTiming(htole(timeRegWithDecodeEn)), secondaryTiming(htole(timeRegWithDecodeEn)), deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0), ioEnabled(false), bmEnabled(false), ioShift(p->io_shift), ctrlOffset(p->ctrl_offset) { if (params()->disks.size() > 3) panic("IDE controllers support a maximum of 4 devices attached!\n"); // Assign the disks to channels int numDisks = params()->disks.size(); if (numDisks > 0) primary.master = params()->disks[0]; if (numDisks > 1) primary.slave = params()->disks[1]; if (numDisks > 2) secondary.master = params()->disks[2]; if (numDisks > 3) secondary.slave = params()->disks[3]; for (int i = 0; i < params()->disks.size(); i++) { params()->disks[i]->setController(this); } primary.select(false); secondary.select(false); if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) { primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0]; primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARAddrs[1]; } if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) { secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2]; secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARAddrs[3]; } ioEnabled = (config.command & htole(PCI_CMD_IOSE)); bmEnabled = (config.command & htole(PCI_CMD_BME)); } bool IdeController::isDiskSelected(IdeDisk *diskPtr) { return (primary.selected == diskPtr || secondary.selected == diskPtr); } void IdeController::intrPost() { primary.bmiRegs.status.intStatus = 1; PciDev::intrPost(); } void IdeController::setDmaComplete(IdeDisk *disk) { Channel *channel; if (disk == primary.master || disk == primary.slave) { channel = &primary; } else if (disk == secondary.master || disk == secondary.slave) { channel = &secondary; } else { panic("Unable to find disk based on pointer %#x\n", disk); } channel->bmiRegs.command.startStop = 0; channel->bmiRegs.status.active = 0; channel->bmiRegs.status.intStatus = 1; } Tick IdeController::readConfig(PacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) { return PciDev::readConfig(pkt); } pkt->allocate(); switch (pkt->getSize()) { case sizeof(uint8_t): switch (offset) { case DeviceTiming: pkt->set(deviceTiming); break; case UDMAControl: pkt->set(udmaControl); break; case PrimaryTiming + 1: pkt->set(bits(htole(primaryTiming), 15, 8)); break; case SecondaryTiming + 1: pkt->set(bits(htole(secondaryTiming), 15, 8)); break; case IDEConfig: pkt->set(bits(htole(ideConfig), 7, 0)); break; case IDEConfig + 1: pkt->set(bits(htole(ideConfig), 15, 8)); break; default: panic("Invalid PCI configuration read for size 1 at offset: %#x!\n", offset); } DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset, (uint32_t)pkt->get()); break; case sizeof(uint16_t): switch (offset) { case PrimaryTiming: pkt->set(primaryTiming); break; case SecondaryTiming: pkt->set(secondaryTiming); break; case UDMATiming: pkt->set(udmaTiming); break; case IDEConfig: pkt->set(ideConfig); break; default: panic("Invalid PCI configuration read for size 2 offset: %#x!\n", offset); } DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset, (uint32_t)pkt->get()); break; case sizeof(uint32_t): if (offset == IDEConfig) pkt->set(ideConfig); else panic("No 32bit reads implemented for this device."); DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset, (uint32_t)pkt->get()); break; default: panic("invalid access size(?) for PCI configspace!\n"); } pkt->makeAtomicResponse(); return configDelay; } Tick IdeController::writeConfig(PacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) { PciDev::writeConfig(pkt); } else { switch (pkt->getSize()) { case sizeof(uint8_t): switch (offset) { case DeviceTiming: deviceTiming = pkt->get(); break; case UDMAControl: udmaControl = pkt->get(); break; case IDEConfig: replaceBits(ideConfig, 7, 0, pkt->get()); break; case IDEConfig + 1: replaceBits(ideConfig, 15, 8, pkt->get()); break; default: panic("Invalid PCI configuration write " "for size 1 offset: %#x!\n", offset); } DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", offset, (uint32_t)pkt->get()); break; case sizeof(uint16_t): switch (offset) { case PrimaryTiming: primaryTiming = pkt->get(); break; case SecondaryTiming: secondaryTiming = pkt->get(); break; case UDMATiming: udmaTiming = pkt->get(); break; case IDEConfig: ideConfig = pkt->get(); break; default: panic("Invalid PCI configuration write " "for size 2 offset: %#x!\n", offset); } DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", offset, (uint32_t)pkt->get()); break; case sizeof(uint32_t): if (offset == IDEConfig) ideConfig = pkt->get(); else panic("Write of unimplemented PCI config. register: %x\n", offset); break; default: panic("invalid access size(?) for PCI configspace!\n"); } pkt->makeAtomicResponse(); } /* Trap command register writes and enable IO/BM as appropriate as well as * BARs. */ switch(offset) { case PCI0_BASE_ADDR0: if (BARAddrs[0] != 0) primary.cmdAddr = BARAddrs[0]; break; case PCI0_BASE_ADDR1: if (BARAddrs[1] != 0) primary.ctrlAddr = BARAddrs[1]; break; case PCI0_BASE_ADDR2: if (BARAddrs[2] != 0) secondary.cmdAddr = BARAddrs[2]; break; case PCI0_BASE_ADDR3: if (BARAddrs[3] != 0) secondary.ctrlAddr = BARAddrs[3]; break; case PCI0_BASE_ADDR4: if (BARAddrs[4] != 0) bmiAddr = BARAddrs[4]; break; case PCI_COMMAND: DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command); ioEnabled = (config.command & htole(PCI_CMD_IOSE)); bmEnabled = (config.command & htole(PCI_CMD_BME)); break; } return configDelay; } void IdeController::Channel::accessCommand(Addr offset, int size, uint8_t *data, bool read) { const Addr SelectOffset = 6; const uint8_t SelectDevBit = 0x10; if (!read && offset == SelectOffset) select(*data & SelectDevBit); if (selected == NULL) { assert(size == sizeof(uint8_t)); *data = 0; } else if (read) { selected->readCommand(offset, size, data); } else { selected->writeCommand(offset, size, data); } } void IdeController::Channel::accessControl(Addr offset, int size, uint8_t *data, bool read) { if (selected == NULL) { assert(size == sizeof(uint8_t)); *data = 0; } else if (read) { selected->readControl(offset, size, data); } else { selected->writeControl(offset, size, data); } } void IdeController::Channel::accessBMI(Addr offset, int size, uint8_t *data, bool read) { assert(offset + size <= sizeof(BMIRegs)); if (read) { memcpy(data, (uint8_t *)&bmiRegs + offset, size); } else { switch (offset) { case BMICommand: { if (size != sizeof(uint8_t)) panic("Invalid BMIC write size: %x\n", size); BMICommandReg oldVal = bmiRegs.command; BMICommandReg newVal = *data; // if a DMA transfer is in progress, R/W control cannot change if (oldVal.startStop && oldVal.rw != newVal.rw) oldVal.rw = newVal.rw; if (oldVal.startStop != newVal.startStop) { if (selected == NULL) panic("DMA start for disk which does not exist\n"); if (oldVal.startStop) { DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); bmiRegs.status.active = 0; selected->abortDma(); } else { DPRINTF(IdeCtrl, "Starting DMA transfer\n"); bmiRegs.status.active = 1; selected->startDma(letoh(bmiRegs.bmidtp)); } } bmiRegs.command = newVal; } break; case BMIStatus: { if (size != sizeof(uint8_t)) panic("Invalid BMIS write size: %x\n", size); BMIStatusReg oldVal = bmiRegs.status; BMIStatusReg newVal = *data; // the BMIDEA bit is read only newVal.active = oldVal.active; // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each if (oldVal.intStatus && newVal.intStatus) newVal.intStatus = 0; // clear the interrupt? else newVal.intStatus = oldVal.intStatus; if (oldVal.dmaError && newVal.dmaError) newVal.dmaError = 0; else newVal.dmaError = oldVal.dmaError; bmiRegs.status = newVal; } break; case BMIDescTablePtr: if (size != sizeof(uint32_t)) panic("Invalid BMIDTP write size: %x\n", size); bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3); break; default: if (size != sizeof(uint8_t) && size != sizeof(uint16_t) && size != sizeof(uint32_t)) panic("IDE controller write of invalid write size: %x\n", size); memcpy((uint8_t *)&bmiRegs + offset, data, size); } } } void IdeController::dispatchAccess(PacketPtr pkt, bool read) { pkt->allocate(); if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) panic("Bad IDE read size: %d\n", pkt->getSize()); if (!ioEnabled) { pkt->makeAtomicResponse(); DPRINTF(IdeCtrl, "io not enabled\n"); return; } Addr addr = pkt->getAddr(); int size = pkt->getSize(); uint8_t *dataPtr = pkt->getPtr(); if (addr >= primary.cmdAddr && addr < (primary.cmdAddr + primary.cmdSize)) { addr -= primary.cmdAddr; // linux may have shifted the address by ioShift, // here we shift it back, similarly for ctrlOffset. addr >>= ioShift; primary.accessCommand(addr, size, dataPtr, read); } else if (addr >= primary.ctrlAddr && addr < (primary.ctrlAddr + primary.ctrlSize)) { addr -= primary.ctrlAddr; addr += ctrlOffset; primary.accessControl(addr, size, dataPtr, read); } else if (addr >= secondary.cmdAddr && addr < (secondary.cmdAddr + secondary.cmdSize)) { addr -= secondary.cmdAddr; secondary.accessCommand(addr, size, dataPtr, read); } else if (addr >= secondary.ctrlAddr && addr < (secondary.ctrlAddr + secondary.ctrlSize)) { addr -= secondary.ctrlAddr; secondary.accessControl(addr, size, dataPtr, read); } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { if (!read && !bmEnabled) return; addr -= bmiAddr; if (addr < sizeof(Channel::BMIRegs)) { primary.accessBMI(addr, size, dataPtr, read); } else { addr -= sizeof(Channel::BMIRegs); secondary.accessBMI(addr, size, dataPtr, read); } } else { panic("IDE controller access to invalid address: %#x\n", addr); } uint32_t data; if (pkt->getSize() == 1) data = pkt->get(); else if (pkt->getSize() == 2) data = pkt->get(); else data = pkt->get(); DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n", read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data); pkt->makeAtomicResponse(); } Tick IdeController::read(PacketPtr pkt) { dispatchAccess(pkt, true); return pioDelay; } Tick IdeController::write(PacketPtr pkt) { dispatchAccess(pkt, false); return pioDelay; } void IdeController::serialize(std::ostream &os) { // Serialize the PciDev base class PciDev::serialize(os); // Serialize channels primary.serialize("primary", os); secondary.serialize("secondary", os); // Serialize config registers SERIALIZE_SCALAR(primaryTiming); SERIALIZE_SCALAR(secondaryTiming); SERIALIZE_SCALAR(deviceTiming); SERIALIZE_SCALAR(udmaControl); SERIALIZE_SCALAR(udmaTiming); SERIALIZE_SCALAR(ideConfig); // Serialize internal state SERIALIZE_SCALAR(ioEnabled); SERIALIZE_SCALAR(bmEnabled); SERIALIZE_SCALAR(bmiAddr); SERIALIZE_SCALAR(bmiSize); } void IdeController::Channel::serialize(const std::string &base, std::ostream &os) { paramOut(os, base + ".cmdAddr", cmdAddr); paramOut(os, base + ".cmdSize", cmdSize); paramOut(os, base + ".ctrlAddr", ctrlAddr); paramOut(os, base + ".ctrlSize", ctrlSize); uint8_t command = bmiRegs.command; paramOut(os, base + ".bmiRegs.command", command); paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0); uint8_t status = bmiRegs.status; paramOut(os, base + ".bmiRegs.status", status); paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1); paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); paramOut(os, base + ".selectBit", selectBit); } void IdeController::unserialize(Checkpoint *cp, const std::string §ion) { // Unserialize the PciDev base class PciDev::unserialize(cp, section); // Unserialize channels primary.unserialize("primary", cp, section); secondary.unserialize("secondary", cp, section); // Unserialize config registers UNSERIALIZE_SCALAR(primaryTiming); UNSERIALIZE_SCALAR(secondaryTiming); UNSERIALIZE_SCALAR(deviceTiming); UNSERIALIZE_SCALAR(udmaControl); UNSERIALIZE_SCALAR(udmaTiming); UNSERIALIZE_SCALAR(ideConfig); // Unserialize internal state UNSERIALIZE_SCALAR(ioEnabled); UNSERIALIZE_SCALAR(bmEnabled); UNSERIALIZE_SCALAR(bmiAddr); UNSERIALIZE_SCALAR(bmiSize); } void IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp, const std::string §ion) { paramIn(cp, section, base + ".cmdAddr", cmdAddr); paramIn(cp, section, base + ".cmdSize", cmdSize); paramIn(cp, section, base + ".ctrlAddr", ctrlAddr); paramIn(cp, section, base + ".ctrlSize", ctrlSize); uint8_t command; paramIn(cp, section, base +".bmiRegs.command", command); bmiRegs.command = command; paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0); uint8_t status; paramIn(cp, section, base + ".bmiRegs.status", status); bmiRegs.status = status; paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1); paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); paramIn(cp, section, base + ".selectBit", selectBit); select(selectBit); } IdeController * IdeControllerParams::create() { return new IdeController(this); }