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