f54790977b
The unresolved destructor call caused a seg fault when called.
590 lines
18 KiB
C++
590 lines
18 KiB
C++
/*
|
|
* 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 <string>
|
|
|
|
#include "base/trace.hh"
|
|
#include "cpu/intr_control.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)
|
|
{
|
|
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);
|
|
|
|
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<uint8_t>(deviceTiming);
|
|
break;
|
|
case UDMAControl:
|
|
pkt->set<uint8_t>(udmaControl);
|
|
break;
|
|
case PrimaryTiming + 1:
|
|
pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8));
|
|
break;
|
|
case SecondaryTiming + 1:
|
|
pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8));
|
|
break;
|
|
case IDEConfig:
|
|
pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0));
|
|
break;
|
|
case IDEConfig + 1:
|
|
pkt->set<uint8_t>(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<uint8_t>());
|
|
break;
|
|
case sizeof(uint16_t):
|
|
switch (offset) {
|
|
case PrimaryTiming:
|
|
pkt->set<uint16_t>(primaryTiming);
|
|
break;
|
|
case SecondaryTiming:
|
|
pkt->set<uint16_t>(secondaryTiming);
|
|
break;
|
|
case UDMATiming:
|
|
pkt->set<uint16_t>(udmaTiming);
|
|
break;
|
|
case IDEConfig:
|
|
pkt->set<uint16_t>(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<uint16_t>());
|
|
break;
|
|
case sizeof(uint32_t):
|
|
panic("No 32bit reads implemented for this device.");
|
|
DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset,
|
|
(uint32_t)pkt->get<uint32_t>());
|
|
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<uint8_t>();
|
|
break;
|
|
case UDMAControl:
|
|
udmaControl = pkt->get<uint8_t>();
|
|
break;
|
|
case IDEConfig:
|
|
replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>());
|
|
break;
|
|
case IDEConfig + 1:
|
|
replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>());
|
|
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<uint8_t>());
|
|
break;
|
|
case sizeof(uint16_t):
|
|
switch (offset) {
|
|
case PrimaryTiming:
|
|
primaryTiming = pkt->get<uint16_t>();
|
|
break;
|
|
case SecondaryTiming:
|
|
secondaryTiming = pkt->get<uint16_t>();
|
|
break;
|
|
case UDMATiming:
|
|
udmaTiming = pkt->get<uint16_t>();
|
|
break;
|
|
case IDEConfig:
|
|
ideConfig = pkt->get<uint16_t>();
|
|
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<uint16_t>());
|
|
break;
|
|
case sizeof(uint32_t):
|
|
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:
|
|
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<uint8_t>();
|
|
|
|
if (addr >= primary.cmdAddr &&
|
|
addr < (primary.cmdAddr + primary.cmdSize)) {
|
|
addr -= primary.cmdAddr;
|
|
primary.accessCommand(addr, size, dataPtr, read);
|
|
} else if (addr >= primary.ctrlAddr &&
|
|
addr < (primary.ctrlAddr + primary.ctrlSize)) {
|
|
addr -= primary.ctrlAddr;
|
|
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<uint8_t>();
|
|
else if (pkt->getSize() == 2)
|
|
data = pkt->get<uint16_t>();
|
|
else
|
|
data = pkt->get<uint32_t>();
|
|
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);
|
|
}
|