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