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