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