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