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