ide_ctrl.cc revision 10775
1/* 2 * Copyright (c) 2013 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Copyright (c) 2004-2005 The Regents of The University of Michigan 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are 19 * met: redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer; 21 * redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution; 24 * neither the name of the copyright holders nor the names of its 25 * contributors may be used to endorse or promote products derived from 26 * this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * Authors: Andrew Schultz 41 * Ali Saidi 42 * Miguel Serrano 43 */ 44 45#include <string> 46 47#include "cpu/intr_control.hh" 48#include "debug/IdeCtrl.hh" 49#include "dev/ide_ctrl.hh" 50#include "dev/ide_disk.hh" 51#include "mem/packet.hh" 52#include "mem/packet_access.hh" 53#include "params/IdeController.hh" 54#include "sim/byteswap.hh" 55 56// clang complains about std::set being overloaded with Packet::set if 57// we open up the entire namespace std 58using std::string; 59 60// Bus master IDE registers 61enum BMIRegOffset { 62 BMICommand = 0x0, 63 BMIStatus = 0x2, 64 BMIDescTablePtr = 0x4 65}; 66 67// PCI config space registers 68enum ConfRegOffset { 69 PrimaryTiming = 0x40, 70 SecondaryTiming = 0x42, 71 DeviceTiming = 0x44, 72 UDMAControl = 0x48, 73 UDMATiming = 0x4A, 74 IDEConfig = 0x54 75}; 76 77static const uint16_t timeRegWithDecodeEn = 0x8000; 78 79IdeController::Channel::Channel( 80 string newName, Addr _cmdSize, Addr _ctrlSize) : 81 _name(newName), 82 cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize), 83 master(NULL), slave(NULL), selected(NULL) 84{ 85 memset(&bmiRegs, 0, sizeof(bmiRegs)); 86 bmiRegs.status.dmaCap0 = 1; 87 bmiRegs.status.dmaCap1 = 1; 88} 89 90IdeController::Channel::~Channel() 91{ 92} 93 94IdeController::IdeController(Params *p) 95 : PciDevice(p), primary(name() + ".primary", BARSize[0], BARSize[1]), 96 secondary(name() + ".secondary", BARSize[2], BARSize[3]), 97 bmiAddr(0), bmiSize(BARSize[4]), 98 primaryTiming(htole(timeRegWithDecodeEn)), 99 secondaryTiming(htole(timeRegWithDecodeEn)), 100 deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0), 101 ioEnabled(false), bmEnabled(false), 102 ioShift(p->io_shift), ctrlOffset(p->ctrl_offset) 103{ 104 if (params()->disks.size() > 3) 105 panic("IDE controllers support a maximum of 4 devices attached!\n"); 106 107 // Assign the disks to channels 108 int numDisks = params()->disks.size(); 109 if (numDisks > 0) 110 primary.master = params()->disks[0]; 111 if (numDisks > 1) 112 primary.slave = params()->disks[1]; 113 if (numDisks > 2) 114 secondary.master = params()->disks[2]; 115 if (numDisks > 3) 116 secondary.slave = params()->disks[3]; 117 118 for (int i = 0; i < params()->disks.size(); i++) { 119 params()->disks[i]->setController(this); 120 } 121 primary.select(false); 122 secondary.select(false); 123 124 if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) { 125 primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0]; 126 primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARSize[1]; 127 } 128 if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) { 129 secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2]; 130 secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARSize[3]; 131 } 132 133 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 134 bmEnabled = (config.command & htole(PCI_CMD_BME)); 135} 136 137bool 138IdeController::isDiskSelected(IdeDisk *diskPtr) 139{ 140 return (primary.selected == diskPtr || secondary.selected == diskPtr); 141} 142 143void 144IdeController::intrPost() 145{ 146 primary.bmiRegs.status.intStatus = 1; 147 PciDevice::intrPost(); 148} 149 150void 151IdeController::setDmaComplete(IdeDisk *disk) 152{ 153 Channel *channel; 154 if (disk == primary.master || disk == primary.slave) { 155 channel = &primary; 156 } else if (disk == secondary.master || disk == secondary.slave) { 157 channel = &secondary; 158 } else { 159 panic("Unable to find disk based on pointer %#x\n", disk); 160 } 161 162 channel->bmiRegs.command.startStop = 0; 163 channel->bmiRegs.status.active = 0; 164 channel->bmiRegs.status.intStatus = 1; 165} 166 167Tick 168IdeController::readConfig(PacketPtr pkt) 169{ 170 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 171 if (offset < PCI_DEVICE_SPECIFIC) { 172 return PciDevice::readConfig(pkt); 173 } 174 175 switch (pkt->getSize()) { 176 case sizeof(uint8_t): 177 switch (offset) { 178 case DeviceTiming: 179 pkt->set<uint8_t>(deviceTiming); 180 break; 181 case UDMAControl: 182 pkt->set<uint8_t>(udmaControl); 183 break; 184 case PrimaryTiming + 1: 185 pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8)); 186 break; 187 case SecondaryTiming + 1: 188 pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8)); 189 break; 190 case IDEConfig: 191 pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0)); 192 break; 193 case IDEConfig + 1: 194 pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8)); 195 break; 196 default: 197 panic("Invalid PCI configuration read for size 1 at offset: %#x!\n", 198 offset); 199 } 200 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset, 201 (uint32_t)pkt->get<uint8_t>()); 202 break; 203 case sizeof(uint16_t): 204 switch (offset) { 205 case UDMAControl: 206 pkt->set<uint16_t>(udmaControl); 207 break; 208 case PrimaryTiming: 209 pkt->set<uint16_t>(primaryTiming); 210 break; 211 case SecondaryTiming: 212 pkt->set<uint16_t>(secondaryTiming); 213 break; 214 case UDMATiming: 215 pkt->set<uint16_t>(udmaTiming); 216 break; 217 case IDEConfig: 218 pkt->set<uint16_t>(ideConfig); 219 break; 220 default: 221 panic("Invalid PCI configuration read for size 2 offset: %#x!\n", 222 offset); 223 } 224 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset, 225 (uint32_t)pkt->get<uint16_t>()); 226 break; 227 case sizeof(uint32_t): 228 switch (offset) { 229 case PrimaryTiming: 230 pkt->set<uint32_t>(primaryTiming); 231 break; 232 case IDEConfig: 233 pkt->set<uint32_t>(ideConfig); 234 break; 235 default: 236 panic("No 32bit reads implemented for this device."); 237 } 238 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset, 239 (uint32_t)pkt->get<uint32_t>()); 240 break; 241 default: 242 panic("invalid access size(?) for PCI configspace!\n"); 243 } 244 pkt->makeAtomicResponse(); 245 return configDelay; 246} 247 248 249Tick 250IdeController::writeConfig(PacketPtr pkt) 251{ 252 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 253 if (offset < PCI_DEVICE_SPECIFIC) { 254 PciDevice::writeConfig(pkt); 255 } else { 256 switch (pkt->getSize()) { 257 case sizeof(uint8_t): 258 switch (offset) { 259 case DeviceTiming: 260 deviceTiming = pkt->get<uint8_t>(); 261 break; 262 case UDMAControl: 263 udmaControl = pkt->get<uint8_t>(); 264 break; 265 case IDEConfig: 266 replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>()); 267 break; 268 case IDEConfig + 1: 269 replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>()); 270 break; 271 default: 272 panic("Invalid PCI configuration write " 273 "for size 1 offset: %#x!\n", offset); 274 } 275 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", 276 offset, (uint32_t)pkt->get<uint8_t>()); 277 break; 278 case sizeof(uint16_t): 279 switch (offset) { 280 case UDMAControl: 281 udmaControl = pkt->get<uint16_t>(); 282 break; 283 case PrimaryTiming: 284 primaryTiming = pkt->get<uint16_t>(); 285 break; 286 case SecondaryTiming: 287 secondaryTiming = pkt->get<uint16_t>(); 288 break; 289 case UDMATiming: 290 udmaTiming = pkt->get<uint16_t>(); 291 break; 292 case IDEConfig: 293 ideConfig = pkt->get<uint16_t>(); 294 break; 295 default: 296 panic("Invalid PCI configuration write " 297 "for size 2 offset: %#x!\n", 298 offset); 299 } 300 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", 301 offset, (uint32_t)pkt->get<uint16_t>()); 302 break; 303 case sizeof(uint32_t): 304 switch (offset) { 305 case PrimaryTiming: 306 primaryTiming = pkt->get<uint32_t>(); 307 break; 308 case IDEConfig: 309 ideConfig = pkt->get<uint32_t>(); 310 break; 311 default: 312 panic("Write of unimplemented PCI config. register: %x\n", offset); 313 } 314 break; 315 default: 316 panic("invalid access size(?) for PCI configspace!\n"); 317 } 318 pkt->makeAtomicResponse(); 319 } 320 321 /* Trap command register writes and enable IO/BM as appropriate as well as 322 * BARs. */ 323 switch(offset) { 324 case PCI0_BASE_ADDR0: 325 if (BARAddrs[0] != 0) 326 primary.cmdAddr = BARAddrs[0]; 327 break; 328 329 case PCI0_BASE_ADDR1: 330 if (BARAddrs[1] != 0) 331 primary.ctrlAddr = BARAddrs[1]; 332 break; 333 334 case PCI0_BASE_ADDR2: 335 if (BARAddrs[2] != 0) 336 secondary.cmdAddr = BARAddrs[2]; 337 break; 338 339 case PCI0_BASE_ADDR3: 340 if (BARAddrs[3] != 0) 341 secondary.ctrlAddr = BARAddrs[3]; 342 break; 343 344 case PCI0_BASE_ADDR4: 345 if (BARAddrs[4] != 0) 346 bmiAddr = BARAddrs[4]; 347 break; 348 349 case PCI_COMMAND: 350 DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command); 351 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 352 bmEnabled = (config.command & htole(PCI_CMD_BME)); 353 break; 354 } 355 return configDelay; 356} 357 358void 359IdeController::Channel::accessCommand(Addr offset, 360 int size, uint8_t *data, bool read) 361{ 362 const Addr SelectOffset = 6; 363 const uint8_t SelectDevBit = 0x10; 364 365 if (!read && offset == SelectOffset) 366 select(*data & SelectDevBit); 367 368 if (selected == NULL) { 369 assert(size == sizeof(uint8_t)); 370 *data = 0; 371 } else if (read) { 372 selected->readCommand(offset, size, data); 373 } else { 374 selected->writeCommand(offset, size, data); 375 } 376} 377 378void 379IdeController::Channel::accessControl(Addr offset, 380 int size, uint8_t *data, bool read) 381{ 382 if (selected == NULL) { 383 assert(size == sizeof(uint8_t)); 384 *data = 0; 385 } else if (read) { 386 selected->readControl(offset, size, data); 387 } else { 388 selected->writeControl(offset, size, data); 389 } 390} 391 392void 393IdeController::Channel::accessBMI(Addr offset, 394 int size, uint8_t *data, bool read) 395{ 396 assert(offset + size <= sizeof(BMIRegs)); 397 if (read) { 398 memcpy(data, (uint8_t *)&bmiRegs + offset, size); 399 } else { 400 switch (offset) { 401 case BMICommand: 402 { 403 if (size != sizeof(uint8_t)) 404 panic("Invalid BMIC write size: %x\n", size); 405 406 BMICommandReg oldVal = bmiRegs.command; 407 BMICommandReg newVal = *data; 408 409 // if a DMA transfer is in progress, R/W control cannot change 410 if (oldVal.startStop && oldVal.rw != newVal.rw) 411 oldVal.rw = newVal.rw; 412 413 if (oldVal.startStop != newVal.startStop) { 414 if (selected == NULL) 415 panic("DMA start for disk which does not exist\n"); 416 417 if (oldVal.startStop) { 418 DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); 419 bmiRegs.status.active = 0; 420 421 selected->abortDma(); 422 } else { 423 DPRINTF(IdeCtrl, "Starting DMA transfer\n"); 424 bmiRegs.status.active = 1; 425 426 selected->startDma(letoh(bmiRegs.bmidtp)); 427 } 428 } 429 430 bmiRegs.command = newVal; 431 } 432 break; 433 case BMIStatus: 434 { 435 if (size != sizeof(uint8_t)) 436 panic("Invalid BMIS write size: %x\n", size); 437 438 BMIStatusReg oldVal = bmiRegs.status; 439 BMIStatusReg newVal = *data; 440 441 // the BMIDEA bit is read only 442 newVal.active = oldVal.active; 443 444 // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each 445 if ((oldVal.intStatus == 1) && (newVal.intStatus == 1)) { 446 newVal.intStatus = 0; // clear the interrupt? 447 } else { 448 // Assigning two bitunion fields to each other does not 449 // work as intended, so we need to use this temporary variable 450 // to get around the bug. 451 uint8_t tmp = oldVal.intStatus; 452 newVal.intStatus = tmp; 453 } 454 if ((oldVal.dmaError == 1) && (newVal.dmaError == 1)) { 455 newVal.dmaError = 0; 456 } else { 457 uint8_t tmp = oldVal.dmaError; 458 newVal.dmaError = tmp; 459 } 460 461 bmiRegs.status = newVal; 462 } 463 break; 464 case BMIDescTablePtr: 465 if (size != sizeof(uint32_t)) 466 panic("Invalid BMIDTP write size: %x\n", size); 467 bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3); 468 break; 469 default: 470 if (size != sizeof(uint8_t) && size != sizeof(uint16_t) && 471 size != sizeof(uint32_t)) 472 panic("IDE controller write of invalid write size: %x\n", size); 473 memcpy((uint8_t *)&bmiRegs + offset, data, size); 474 } 475 } 476} 477 478void 479IdeController::dispatchAccess(PacketPtr pkt, bool read) 480{ 481 if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) 482 panic("Bad IDE read size: %d\n", pkt->getSize()); 483 484 if (!ioEnabled) { 485 pkt->makeAtomicResponse(); 486 DPRINTF(IdeCtrl, "io not enabled\n"); 487 return; 488 } 489 490 Addr addr = pkt->getAddr(); 491 int size = pkt->getSize(); 492 uint8_t *dataPtr = pkt->getPtr<uint8_t>(); 493 494 if (addr >= primary.cmdAddr && 495 addr < (primary.cmdAddr + primary.cmdSize)) { 496 addr -= primary.cmdAddr; 497 // linux may have shifted the address by ioShift, 498 // here we shift it back, similarly for ctrlOffset. 499 addr >>= ioShift; 500 primary.accessCommand(addr, size, dataPtr, read); 501 } else if (addr >= primary.ctrlAddr && 502 addr < (primary.ctrlAddr + primary.ctrlSize)) { 503 addr -= primary.ctrlAddr; 504 addr += ctrlOffset; 505 primary.accessControl(addr, size, dataPtr, read); 506 } else if (addr >= secondary.cmdAddr && 507 addr < (secondary.cmdAddr + secondary.cmdSize)) { 508 addr -= secondary.cmdAddr; 509 secondary.accessCommand(addr, size, dataPtr, read); 510 } else if (addr >= secondary.ctrlAddr && 511 addr < (secondary.ctrlAddr + secondary.ctrlSize)) { 512 addr -= secondary.ctrlAddr; 513 secondary.accessControl(addr, size, dataPtr, read); 514 } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { 515 if (!read && !bmEnabled) 516 return; 517 addr -= bmiAddr; 518 if (addr < sizeof(Channel::BMIRegs)) { 519 primary.accessBMI(addr, size, dataPtr, read); 520 } else { 521 addr -= sizeof(Channel::BMIRegs); 522 secondary.accessBMI(addr, size, dataPtr, read); 523 } 524 } else { 525 panic("IDE controller access to invalid address: %#x\n", addr); 526 } 527 528#ifndef NDEBUG 529 uint32_t data; 530 if (pkt->getSize() == 1) 531 data = pkt->get<uint8_t>(); 532 else if (pkt->getSize() == 2) 533 data = pkt->get<uint16_t>(); 534 else 535 data = pkt->get<uint32_t>(); 536 DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n", 537 read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data); 538#endif 539 540 pkt->makeAtomicResponse(); 541} 542 543Tick 544IdeController::read(PacketPtr pkt) 545{ 546 dispatchAccess(pkt, true); 547 return pioDelay; 548} 549 550Tick 551IdeController::write(PacketPtr pkt) 552{ 553 dispatchAccess(pkt, false); 554 return pioDelay; 555} 556 557void 558IdeController::serialize(std::ostream &os) 559{ 560 // Serialize the PciDevice base class 561 PciDevice::serialize(os); 562 563 // Serialize channels 564 primary.serialize("primary", os); 565 secondary.serialize("secondary", os); 566 567 // Serialize config registers 568 SERIALIZE_SCALAR(primaryTiming); 569 SERIALIZE_SCALAR(secondaryTiming); 570 SERIALIZE_SCALAR(deviceTiming); 571 SERIALIZE_SCALAR(udmaControl); 572 SERIALIZE_SCALAR(udmaTiming); 573 SERIALIZE_SCALAR(ideConfig); 574 575 // Serialize internal state 576 SERIALIZE_SCALAR(ioEnabled); 577 SERIALIZE_SCALAR(bmEnabled); 578 SERIALIZE_SCALAR(bmiAddr); 579 SERIALIZE_SCALAR(bmiSize); 580} 581 582void 583IdeController::Channel::serialize(const std::string &base, std::ostream &os) 584{ 585 paramOut(os, base + ".cmdAddr", cmdAddr); 586 paramOut(os, base + ".cmdSize", cmdSize); 587 paramOut(os, base + ".ctrlAddr", ctrlAddr); 588 paramOut(os, base + ".ctrlSize", ctrlSize); 589 uint8_t command = bmiRegs.command; 590 paramOut(os, base + ".bmiRegs.command", command); 591 paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 592 uint8_t status = bmiRegs.status; 593 paramOut(os, base + ".bmiRegs.status", status); 594 paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 595 paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 596 paramOut(os, base + ".selectBit", selectBit); 597} 598 599void 600IdeController::unserialize(Checkpoint *cp, const std::string §ion) 601{ 602 // Unserialize the PciDevice base class 603 PciDevice::unserialize(cp, section); 604 605 // Unserialize channels 606 primary.unserialize("primary", cp, section); 607 secondary.unserialize("secondary", cp, section); 608 609 // Unserialize config registers 610 UNSERIALIZE_SCALAR(primaryTiming); 611 UNSERIALIZE_SCALAR(secondaryTiming); 612 UNSERIALIZE_SCALAR(deviceTiming); 613 UNSERIALIZE_SCALAR(udmaControl); 614 UNSERIALIZE_SCALAR(udmaTiming); 615 UNSERIALIZE_SCALAR(ideConfig); 616 617 // Unserialize internal state 618 UNSERIALIZE_SCALAR(ioEnabled); 619 UNSERIALIZE_SCALAR(bmEnabled); 620 UNSERIALIZE_SCALAR(bmiAddr); 621 UNSERIALIZE_SCALAR(bmiSize); 622} 623 624void 625IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp, 626 const std::string §ion) 627{ 628 paramIn(cp, section, base + ".cmdAddr", cmdAddr); 629 paramIn(cp, section, base + ".cmdSize", cmdSize); 630 paramIn(cp, section, base + ".ctrlAddr", ctrlAddr); 631 paramIn(cp, section, base + ".ctrlSize", ctrlSize); 632 uint8_t command; 633 paramIn(cp, section, base +".bmiRegs.command", command); 634 bmiRegs.command = command; 635 paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 636 uint8_t status; 637 paramIn(cp, section, base + ".bmiRegs.status", status); 638 bmiRegs.status = status; 639 paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 640 paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 641 paramIn(cp, section, base + ".selectBit", selectBit); 642 select(selectBit); 643} 644 645IdeController * 646IdeControllerParams::create() 647{ 648 return new IdeController(this); 649} 650