sim, arm: add checkpoint upgrader for d02b45a5
[gem5.git] / src / dev / ide_disk.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 */
43
44 /** @file
45 * Device model implementation for an IDE disk
46 */
47
48 #include <cerrno>
49 #include <cstring>
50 #include <deque>
51 #include <string>
52
53 #include "arch/isa_traits.hh"
54 #include "base/chunk_generator.hh"
55 #include "base/cprintf.hh" // csprintf
56 #include "base/trace.hh"
57 #include "config/the_isa.hh"
58 #include "debug/IdeDisk.hh"
59 #include "dev/disk_image.hh"
60 #include "dev/ide_ctrl.hh"
61 #include "dev/ide_disk.hh"
62 #include "sim/core.hh"
63 #include "sim/sim_object.hh"
64
65 using namespace std;
66 using namespace TheISA;
67
68 IdeDisk::IdeDisk(const Params *p)
69 : SimObject(p), ctrl(NULL), image(p->image), diskDelay(p->delay),
70 dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this),
71 dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
72 dmaReadEvent(this), dmaWriteEvent(this)
73 {
74 // Reset the device state
75 reset(p->driveID);
76
77 // fill out the drive ID structure
78 memset(&driveID, 0, sizeof(struct ataparams));
79
80 // Calculate LBA and C/H/S values
81 uint16_t cylinders;
82 uint8_t heads;
83 uint8_t sectors;
84
85 uint32_t lba_size = image->size();
86 if (lba_size >= 16383*16*63) {
87 cylinders = 16383;
88 heads = 16;
89 sectors = 63;
90 } else {
91 if (lba_size >= 63)
92 sectors = 63;
93 else
94 sectors = lba_size;
95
96 if ((lba_size / sectors) >= 16)
97 heads = 16;
98 else
99 heads = (lba_size / sectors);
100
101 cylinders = lba_size / (heads * sectors);
102 }
103
104 // Setup the model name
105 strncpy((char *)driveID.atap_model, "5MI EDD si k",
106 sizeof(driveID.atap_model));
107 // Set the maximum multisector transfer size
108 driveID.atap_multi = MAX_MULTSECT;
109 // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
110 driveID.atap_capabilities1 = 0x7;
111 // UDMA support, EIDE support
112 driveID.atap_extensions = 0x6;
113 // Setup default C/H/S settings
114 driveID.atap_cylinders = cylinders;
115 driveID.atap_sectors = sectors;
116 driveID.atap_heads = heads;
117 // Setup the current multisector transfer size
118 driveID.atap_curmulti = MAX_MULTSECT;
119 driveID.atap_curmulti_valid = 0x1;
120 // Number of sectors on disk
121 driveID.atap_capacity = lba_size;
122 // Multiword DMA mode 2 and below supported
123 driveID.atap_dmamode_supp = 0x4;
124 // Set PIO mode 4 and 3 supported
125 driveID.atap_piomode_supp = 0x3;
126 // Set DMA mode 4 and below supported
127 driveID.atap_udmamode_supp = 0x1f;
128 // Statically set hardware config word
129 driveID.atap_hwreset_res = 0x4001;
130
131 //arbitrary for now...
132 driveID.atap_ata_major = WDC_VER_ATA7;
133 }
134
135 IdeDisk::~IdeDisk()
136 {
137 // destroy the data buffer
138 delete [] dataBuffer;
139 }
140
141 void
142 IdeDisk::reset(int id)
143 {
144 // initialize the data buffer and shadow registers
145 dataBuffer = new uint8_t[MAX_DMA_SIZE];
146
147 memset(dataBuffer, 0, MAX_DMA_SIZE);
148 memset(&cmdReg, 0, sizeof(CommandReg_t));
149 memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
150
151 curPrdAddr = 0;
152 curSector = 0;
153 cmdBytes = 0;
154 cmdBytesLeft = 0;
155 drqBytesLeft = 0;
156 dmaRead = false;
157 intrPending = false;
158 dmaAborted = false;
159
160 // set the device state to idle
161 dmaState = Dma_Idle;
162
163 if (id == DEV0) {
164 devState = Device_Idle_S;
165 devID = DEV0;
166 } else if (id == DEV1) {
167 devState = Device_Idle_NS;
168 devID = DEV1;
169 } else {
170 panic("Invalid device ID: %#x\n", id);
171 }
172
173 // set the device ready bit
174 status = STATUS_DRDY_BIT;
175
176 /* The error register must be set to 0x1 on start-up to
177 indicate that no diagnostic error was detected */
178 cmdReg.error = 0x1;
179 }
180
181 ////
182 // Utility functions
183 ////
184
185 bool
186 IdeDisk::isDEVSelect()
187 {
188 return ctrl->isDiskSelected(this);
189 }
190
191 Addr
192 IdeDisk::pciToDma(Addr pciAddr)
193 {
194 if (ctrl)
195 return ctrl->pciToDma(pciAddr);
196 else
197 panic("Access to unset controller!\n");
198 }
199
200 ////
201 // Device registers read/write
202 ////
203
204 void
205 IdeDisk::readCommand(const Addr offset, int size, uint8_t *data)
206 {
207 if (offset == DATA_OFFSET) {
208 if (size == sizeof(uint16_t)) {
209 *(uint16_t *)data = cmdReg.data;
210 } else if (size == sizeof(uint32_t)) {
211 *(uint16_t *)data = cmdReg.data;
212 updateState(ACT_DATA_READ_SHORT);
213 *((uint16_t *)data + 1) = cmdReg.data;
214 } else {
215 panic("Data read of unsupported size %d.\n", size);
216 }
217 updateState(ACT_DATA_READ_SHORT);
218 return;
219 }
220 assert(size == sizeof(uint8_t));
221 switch (offset) {
222 case ERROR_OFFSET:
223 *data = cmdReg.error;
224 break;
225 case NSECTOR_OFFSET:
226 *data = cmdReg.sec_count;
227 break;
228 case SECTOR_OFFSET:
229 *data = cmdReg.sec_num;
230 break;
231 case LCYL_OFFSET:
232 *data = cmdReg.cyl_low;
233 break;
234 case HCYL_OFFSET:
235 *data = cmdReg.cyl_high;
236 break;
237 case DRIVE_OFFSET:
238 *data = cmdReg.drive;
239 break;
240 case STATUS_OFFSET:
241 *data = status;
242 updateState(ACT_STAT_READ);
243 break;
244 default:
245 panic("Invalid IDE command register offset: %#x\n", offset);
246 }
247 DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
248 }
249
250 void
251 IdeDisk::readControl(const Addr offset, int size, uint8_t *data)
252 {
253 assert(size == sizeof(uint8_t));
254 *data = status;
255 if (offset != ALTSTAT_OFFSET)
256 panic("Invalid IDE control register offset: %#x\n", offset);
257 DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
258 }
259
260 void
261 IdeDisk::writeCommand(const Addr offset, int size, const uint8_t *data)
262 {
263 if (offset == DATA_OFFSET) {
264 if (size == sizeof(uint16_t)) {
265 cmdReg.data = *(const uint16_t *)data;
266 } else if (size == sizeof(uint32_t)) {
267 cmdReg.data = *(const uint16_t *)data;
268 updateState(ACT_DATA_WRITE_SHORT);
269 cmdReg.data = *((const uint16_t *)data + 1);
270 } else {
271 panic("Data write of unsupported size %d.\n", size);
272 }
273 updateState(ACT_DATA_WRITE_SHORT);
274 return;
275 }
276
277 assert(size == sizeof(uint8_t));
278 switch (offset) {
279 case FEATURES_OFFSET:
280 break;
281 case NSECTOR_OFFSET:
282 cmdReg.sec_count = *data;
283 break;
284 case SECTOR_OFFSET:
285 cmdReg.sec_num = *data;
286 break;
287 case LCYL_OFFSET:
288 cmdReg.cyl_low = *data;
289 break;
290 case HCYL_OFFSET:
291 cmdReg.cyl_high = *data;
292 break;
293 case DRIVE_OFFSET:
294 cmdReg.drive = *data;
295 updateState(ACT_SELECT_WRITE);
296 break;
297 case COMMAND_OFFSET:
298 cmdReg.command = *data;
299 updateState(ACT_CMD_WRITE);
300 break;
301 default:
302 panic("Invalid IDE command register offset: %#x\n", offset);
303 }
304 DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
305 (uint32_t)*data);
306 }
307
308 void
309 IdeDisk::writeControl(const Addr offset, int size, const uint8_t *data)
310 {
311 if (offset != CONTROL_OFFSET)
312 panic("Invalid IDE control register offset: %#x\n", offset);
313
314 if (*data & CONTROL_RST_BIT) {
315 // force the device into the reset state
316 devState = Device_Srst;
317 updateState(ACT_SRST_SET);
318 } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
319 updateState(ACT_SRST_CLEAR);
320 }
321
322 nIENBit = *data & CONTROL_IEN_BIT;
323
324 DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
325 (uint32_t)*data);
326 }
327
328 ////
329 // Perform DMA transactions
330 ////
331
332 void
333 IdeDisk::doDmaTransfer()
334 {
335 if (dmaAborted) {
336 DPRINTF(IdeDisk, "DMA Aborted before reading PRD entry\n");
337 updateState(ACT_CMD_ERROR);
338 return;
339 }
340
341 if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
342 panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
343 dmaState, devState);
344
345 if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
346 schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
347 return;
348 } else
349 ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
350 (uint8_t*)&curPrd.entry);
351 }
352
353 void
354 IdeDisk::dmaPrdReadDone()
355 {
356 if (dmaAborted) {
357 DPRINTF(IdeDisk, "DMA Aborted while reading PRD entry\n");
358 updateState(ACT_CMD_ERROR);
359 return;
360 }
361
362 DPRINTF(IdeDisk,
363 "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
364 curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
365 curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
366 curPrd.getEOT(), curSector);
367
368 // the prd pointer has already been translated, so just do an increment
369 curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
370
371 if (dmaRead)
372 doDmaDataRead();
373 else
374 doDmaDataWrite();
375 }
376
377 void
378 IdeDisk::doDmaDataRead()
379 {
380 /** @todo we need to figure out what the delay actually will be */
381 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
382
383 DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
384 diskDelay, totalDiskDelay);
385
386 schedule(dmaReadWaitEvent, curTick() + totalDiskDelay);
387 }
388
389 void
390 IdeDisk::regStats()
391 {
392 using namespace Stats;
393 dmaReadFullPages
394 .name(name() + ".dma_read_full_pages")
395 .desc("Number of full page size DMA reads (not PRD).")
396 ;
397 dmaReadBytes
398 .name(name() + ".dma_read_bytes")
399 .desc("Number of bytes transfered via DMA reads (not PRD).")
400 ;
401 dmaReadTxs
402 .name(name() + ".dma_read_txs")
403 .desc("Number of DMA read transactions (not PRD).")
404 ;
405
406 dmaWriteFullPages
407 .name(name() + ".dma_write_full_pages")
408 .desc("Number of full page size DMA writes.")
409 ;
410 dmaWriteBytes
411 .name(name() + ".dma_write_bytes")
412 .desc("Number of bytes transfered via DMA writes.")
413 ;
414 dmaWriteTxs
415 .name(name() + ".dma_write_txs")
416 .desc("Number of DMA write transactions.")
417 ;
418 }
419
420 void
421 IdeDisk::doDmaRead()
422 {
423 if (dmaAborted) {
424 DPRINTF(IdeDisk, "DMA Aborted in middle of Dma Read\n");
425 if (dmaReadCG)
426 delete dmaReadCG;
427 dmaReadCG = NULL;
428 updateState(ACT_CMD_ERROR);
429 return;
430 }
431
432 if (!dmaReadCG) {
433 // clear out the data buffer
434 memset(dataBuffer, 0, MAX_DMA_SIZE);
435 dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(),
436 curPrd.getByteCount(), TheISA::PageBytes);
437
438 }
439 if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
440 schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
441 return;
442 } else if (!dmaReadCG->done()) {
443 assert(dmaReadCG->complete() < MAX_DMA_SIZE);
444 ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
445 &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
446 dmaReadBytes += dmaReadCG->size();
447 dmaReadTxs++;
448 if (dmaReadCG->size() == TheISA::PageBytes)
449 dmaReadFullPages++;
450 dmaReadCG->next();
451 } else {
452 assert(dmaReadCG->done());
453 delete dmaReadCG;
454 dmaReadCG = NULL;
455 dmaReadDone();
456 }
457 }
458
459 void
460 IdeDisk::dmaReadDone()
461 {
462 uint32_t bytesWritten = 0;
463
464 // write the data to the disk image
465 for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
466 bytesWritten += SectorSize) {
467
468 cmdBytesLeft -= SectorSize;
469 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
470 }
471
472 // check for the EOT
473 if (curPrd.getEOT()) {
474 assert(cmdBytesLeft == 0);
475 dmaState = Dma_Idle;
476 updateState(ACT_DMA_DONE);
477 } else {
478 doDmaTransfer();
479 }
480 }
481
482 void
483 IdeDisk::doDmaDataWrite()
484 {
485 /** @todo we need to figure out what the delay actually will be */
486 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
487 uint32_t bytesRead = 0;
488
489 DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
490 diskDelay, totalDiskDelay);
491
492 memset(dataBuffer, 0, MAX_DMA_SIZE);
493 assert(cmdBytesLeft <= MAX_DMA_SIZE);
494 while (bytesRead < curPrd.getByteCount()) {
495 readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
496 bytesRead += SectorSize;
497 cmdBytesLeft -= SectorSize;
498 }
499 DPRINTF(IdeDisk, "doDmaWrite, bytesRead: %d cmdBytesLeft: %d\n",
500 bytesRead, cmdBytesLeft);
501
502 schedule(dmaWriteWaitEvent, curTick() + totalDiskDelay);
503 }
504
505 void
506 IdeDisk::doDmaWrite()
507 {
508 if (dmaAborted) {
509 DPRINTF(IdeDisk, "DMA Aborted while doing DMA Write\n");
510 if (dmaWriteCG)
511 delete dmaWriteCG;
512 dmaWriteCG = NULL;
513 updateState(ACT_CMD_ERROR);
514 return;
515 }
516 if (!dmaWriteCG) {
517 // clear out the data buffer
518 dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
519 curPrd.getByteCount(), TheISA::PageBytes);
520 }
521 if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
522 schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
523 DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
524 return;
525 } else if (!dmaWriteCG->done()) {
526 assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
527 ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
528 &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
529 DPRINTF(IdeDisk, "doDmaWrite: not done curPrd byte count %d, eot %#x\n",
530 curPrd.getByteCount(), curPrd.getEOT());
531 dmaWriteBytes += dmaWriteCG->size();
532 dmaWriteTxs++;
533 if (dmaWriteCG->size() == TheISA::PageBytes)
534 dmaWriteFullPages++;
535 dmaWriteCG->next();
536 } else {
537 DPRINTF(IdeDisk, "doDmaWrite: done curPrd byte count %d, eot %#x\n",
538 curPrd.getByteCount(), curPrd.getEOT());
539 assert(dmaWriteCG->done());
540 delete dmaWriteCG;
541 dmaWriteCG = NULL;
542 dmaWriteDone();
543 }
544 }
545
546 void
547 IdeDisk::dmaWriteDone()
548 {
549 DPRINTF(IdeDisk, "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
550 curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
551 // check for the EOT
552 if (curPrd.getEOT()) {
553 assert(cmdBytesLeft == 0);
554 dmaState = Dma_Idle;
555 updateState(ACT_DMA_DONE);
556 } else {
557 doDmaTransfer();
558 }
559 }
560
561 ////
562 // Disk utility routines
563 ///
564
565 void
566 IdeDisk::readDisk(uint32_t sector, uint8_t *data)
567 {
568 uint32_t bytesRead = image->read(data, sector);
569
570 if (bytesRead != SectorSize)
571 panic("Can't read from %s. Only %d of %d read. errno=%d\n",
572 name(), bytesRead, SectorSize, errno);
573 }
574
575 void
576 IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
577 {
578 uint32_t bytesWritten = image->write(data, sector);
579
580 if (bytesWritten != SectorSize)
581 panic("Can't write to %s. Only %d of %d written. errno=%d\n",
582 name(), bytesWritten, SectorSize, errno);
583 }
584
585 ////
586 // Setup and handle commands
587 ////
588
589 void
590 IdeDisk::startDma(const uint32_t &prdTableBase)
591 {
592 if (dmaState != Dma_Start)
593 panic("Inconsistent DMA state, should be in Dma_Start!\n");
594
595 if (devState != Transfer_Data_Dma)
596 panic("Inconsistent device state for DMA start!\n");
597
598 // PRD base address is given by bits 31:2
599 curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
600
601 dmaState = Dma_Transfer;
602
603 // schedule dma transfer (doDmaTransfer)
604 schedule(dmaTransferEvent, curTick() + 1);
605 }
606
607 void
608 IdeDisk::abortDma()
609 {
610 if (dmaState == Dma_Idle)
611 panic("Inconsistent DMA state, should be Start or Transfer!");
612
613 if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
614 panic("Inconsistent device state, should be Transfer or Prepare!\n");
615
616 updateState(ACT_CMD_ERROR);
617 }
618
619 void
620 IdeDisk::startCommand()
621 {
622 DevAction_t action = ACT_NONE;
623 uint32_t size = 0;
624 dmaRead = false;
625
626 // Decode commands
627 switch (cmdReg.command) {
628 // Supported non-data commands
629 case WDSF_READ_NATIVE_MAX:
630 size = (uint32_t)image->size() - 1;
631 cmdReg.sec_num = (size & 0xff);
632 cmdReg.cyl_low = ((size & 0xff00) >> 8);
633 cmdReg.cyl_high = ((size & 0xff0000) >> 16);
634 cmdReg.head = ((size & 0xf000000) >> 24);
635
636 devState = Command_Execution;
637 action = ACT_CMD_COMPLETE;
638 break;
639
640 case WDCC_RECAL:
641 case WDCC_IDP:
642 case WDCC_STANDBY_IMMED:
643 case WDCC_FLUSHCACHE:
644 case WDSF_VERIFY:
645 case WDSF_SEEK:
646 case SET_FEATURES:
647 case WDCC_SETMULTI:
648 case WDCC_IDLE:
649 devState = Command_Execution;
650 action = ACT_CMD_COMPLETE;
651 break;
652
653 // Supported PIO data-in commands
654 case WDCC_IDENTIFY:
655 cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
656 devState = Prepare_Data_In;
657 action = ACT_DATA_READY;
658 break;
659
660 case WDCC_READMULTI:
661 case WDCC_READ:
662 if (!(cmdReg.drive & DRIVE_LBA_BIT))
663 panic("Attempt to perform CHS access, only supports LBA\n");
664
665 if (cmdReg.sec_count == 0)
666 cmdBytes = cmdBytesLeft = (256 * SectorSize);
667 else
668 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
669
670 curSector = getLBABase();
671
672 /** @todo make this a scheduled event to simulate disk delay */
673 devState = Prepare_Data_In;
674 action = ACT_DATA_READY;
675 break;
676
677 // Supported PIO data-out commands
678 case WDCC_WRITEMULTI:
679 case WDCC_WRITE:
680 if (!(cmdReg.drive & DRIVE_LBA_BIT))
681 panic("Attempt to perform CHS access, only supports LBA\n");
682
683 if (cmdReg.sec_count == 0)
684 cmdBytes = cmdBytesLeft = (256 * SectorSize);
685 else
686 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
687 DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d\n", cmdBytesLeft);
688 curSector = getLBABase();
689
690 devState = Prepare_Data_Out;
691 action = ACT_DATA_READY;
692 break;
693
694 // Supported DMA commands
695 case WDCC_WRITEDMA:
696 dmaRead = true; // a write to the disk is a DMA read from memory
697 case WDCC_READDMA:
698 if (!(cmdReg.drive & DRIVE_LBA_BIT))
699 panic("Attempt to perform CHS access, only supports LBA\n");
700
701 if (cmdReg.sec_count == 0)
702 cmdBytes = cmdBytesLeft = (256 * SectorSize);
703 else
704 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
705 DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
706
707 curSector = getLBABase();
708
709 devState = Prepare_Data_Dma;
710 action = ACT_DMA_READY;
711 break;
712
713 default:
714 panic("Unsupported ATA command: %#x\n", cmdReg.command);
715 }
716
717 if (action != ACT_NONE) {
718 // set the BSY bit
719 status |= STATUS_BSY_BIT;
720 // clear the DRQ bit
721 status &= ~STATUS_DRQ_BIT;
722 // clear the DF bit
723 status &= ~STATUS_DF_BIT;
724
725 updateState(action);
726 }
727 }
728
729 ////
730 // Handle setting and clearing interrupts
731 ////
732
733 void
734 IdeDisk::intrPost()
735 {
736 DPRINTF(IdeDisk, "Posting Interrupt\n");
737 if (intrPending)
738 panic("Attempt to post an interrupt with one pending\n");
739
740 intrPending = true;
741
742 // talk to controller to set interrupt
743 if (ctrl) {
744 ctrl->intrPost();
745 }
746 }
747
748 void
749 IdeDisk::intrClear()
750 {
751 DPRINTF(IdeDisk, "Clearing Interrupt\n");
752 if (!intrPending)
753 panic("Attempt to clear a non-pending interrupt\n");
754
755 intrPending = false;
756
757 // talk to controller to clear interrupt
758 if (ctrl)
759 ctrl->intrClear();
760 }
761
762 ////
763 // Manage the device internal state machine
764 ////
765
766 void
767 IdeDisk::updateState(DevAction_t action)
768 {
769 switch (devState) {
770 case Device_Srst:
771 if (action == ACT_SRST_SET) {
772 // set the BSY bit
773 status |= STATUS_BSY_BIT;
774 } else if (action == ACT_SRST_CLEAR) {
775 // clear the BSY bit
776 status &= ~STATUS_BSY_BIT;
777
778 // reset the device state
779 reset(devID);
780 }
781 break;
782
783 case Device_Idle_S:
784 if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
785 devState = Device_Idle_NS;
786 } else if (action == ACT_CMD_WRITE) {
787 startCommand();
788 }
789
790 break;
791
792 case Device_Idle_SI:
793 if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
794 devState = Device_Idle_NS;
795 intrClear();
796 } else if (action == ACT_STAT_READ || isIENSet()) {
797 devState = Device_Idle_S;
798 intrClear();
799 } else if (action == ACT_CMD_WRITE) {
800 intrClear();
801 startCommand();
802 }
803
804 break;
805
806 case Device_Idle_NS:
807 if (action == ACT_SELECT_WRITE && isDEVSelect()) {
808 if (!isIENSet() && intrPending) {
809 devState = Device_Idle_SI;
810 intrPost();
811 }
812 if (isIENSet() || !intrPending) {
813 devState = Device_Idle_S;
814 }
815 }
816 break;
817
818 case Command_Execution:
819 if (action == ACT_CMD_COMPLETE) {
820 // clear the BSY bit
821 setComplete();
822
823 if (!isIENSet()) {
824 devState = Device_Idle_SI;
825 intrPost();
826 } else {
827 devState = Device_Idle_S;
828 }
829 }
830 break;
831
832 case Prepare_Data_In:
833 if (action == ACT_CMD_ERROR) {
834 // clear the BSY bit
835 setComplete();
836
837 if (!isIENSet()) {
838 devState = Device_Idle_SI;
839 intrPost();
840 } else {
841 devState = Device_Idle_S;
842 }
843 } else if (action == ACT_DATA_READY) {
844 // clear the BSY bit
845 status &= ~STATUS_BSY_BIT;
846 // set the DRQ bit
847 status |= STATUS_DRQ_BIT;
848
849 // copy the data into the data buffer
850 if (cmdReg.command == WDCC_IDENTIFY) {
851 // Reset the drqBytes for this block
852 drqBytesLeft = sizeof(struct ataparams);
853
854 memcpy((void *)dataBuffer, (void *)&driveID,
855 sizeof(struct ataparams));
856 } else {
857 // Reset the drqBytes for this block
858 drqBytesLeft = SectorSize;
859
860 readDisk(curSector++, dataBuffer);
861 }
862
863 // put the first two bytes into the data register
864 memcpy((void *)&cmdReg.data, (void *)dataBuffer,
865 sizeof(uint16_t));
866
867 if (!isIENSet()) {
868 devState = Data_Ready_INTRQ_In;
869 intrPost();
870 } else {
871 devState = Transfer_Data_In;
872 }
873 }
874 break;
875
876 case Data_Ready_INTRQ_In:
877 if (action == ACT_STAT_READ) {
878 devState = Transfer_Data_In;
879 intrClear();
880 }
881 break;
882
883 case Transfer_Data_In:
884 if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
885 if (action == ACT_DATA_READ_BYTE) {
886 panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
887 } else {
888 drqBytesLeft -= 2;
889 cmdBytesLeft -= 2;
890
891 // copy next short into data registers
892 if (drqBytesLeft)
893 memcpy((void *)&cmdReg.data,
894 (void *)&dataBuffer[SectorSize - drqBytesLeft],
895 sizeof(uint16_t));
896 }
897
898 if (drqBytesLeft == 0) {
899 if (cmdBytesLeft == 0) {
900 // Clear the BSY bit
901 setComplete();
902 devState = Device_Idle_S;
903 } else {
904 devState = Prepare_Data_In;
905 // set the BSY_BIT
906 status |= STATUS_BSY_BIT;
907 // clear the DRQ_BIT
908 status &= ~STATUS_DRQ_BIT;
909
910 /** @todo change this to a scheduled event to simulate
911 disk delay */
912 updateState(ACT_DATA_READY);
913 }
914 }
915 }
916 break;
917
918 case Prepare_Data_Out:
919 if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
920 // clear the BSY bit
921 setComplete();
922
923 if (!isIENSet()) {
924 devState = Device_Idle_SI;
925 intrPost();
926 } else {
927 devState = Device_Idle_S;
928 }
929 } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
930 // clear the BSY bit
931 status &= ~STATUS_BSY_BIT;
932 // set the DRQ bit
933 status |= STATUS_DRQ_BIT;
934
935 // clear the data buffer to get it ready for writes
936 memset(dataBuffer, 0, MAX_DMA_SIZE);
937
938 // reset the drqBytes for this block
939 drqBytesLeft = SectorSize;
940
941 if (cmdBytesLeft == cmdBytes || isIENSet()) {
942 devState = Transfer_Data_Out;
943 } else {
944 devState = Data_Ready_INTRQ_Out;
945 intrPost();
946 }
947 }
948 break;
949
950 case Data_Ready_INTRQ_Out:
951 if (action == ACT_STAT_READ) {
952 devState = Transfer_Data_Out;
953 intrClear();
954 }
955 break;
956
957 case Transfer_Data_Out:
958 if (action == ACT_DATA_WRITE_BYTE ||
959 action == ACT_DATA_WRITE_SHORT) {
960
961 if (action == ACT_DATA_READ_BYTE) {
962 panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
963 } else {
964 // copy the latest short into the data buffer
965 memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
966 (void *)&cmdReg.data,
967 sizeof(uint16_t));
968
969 drqBytesLeft -= 2;
970 cmdBytesLeft -= 2;
971 }
972
973 if (drqBytesLeft == 0) {
974 // copy the block to the disk
975 writeDisk(curSector++, dataBuffer);
976
977 // set the BSY bit
978 status |= STATUS_BSY_BIT;
979 // set the seek bit
980 status |= STATUS_SEEK_BIT;
981 // clear the DRQ bit
982 status &= ~STATUS_DRQ_BIT;
983
984 devState = Prepare_Data_Out;
985
986 /** @todo change this to a scheduled event to simulate
987 disk delay */
988 updateState(ACT_DATA_READY);
989 }
990 }
991 break;
992
993 case Prepare_Data_Dma:
994 if (action == ACT_CMD_ERROR) {
995 // clear the BSY bit
996 setComplete();
997
998 if (!isIENSet()) {
999 devState = Device_Idle_SI;
1000 intrPost();
1001 } else {
1002 devState = Device_Idle_S;
1003 }
1004 } else if (action == ACT_DMA_READY) {
1005 // clear the BSY bit
1006 status &= ~STATUS_BSY_BIT;
1007 // set the DRQ bit
1008 status |= STATUS_DRQ_BIT;
1009
1010 devState = Transfer_Data_Dma;
1011
1012 if (dmaState != Dma_Idle)
1013 panic("Inconsistent DMA state, should be Dma_Idle\n");
1014
1015 dmaState = Dma_Start;
1016 // wait for the write to the DMA start bit
1017 }
1018 break;
1019
1020 case Transfer_Data_Dma:
1021 if (action == ACT_CMD_ERROR) {
1022 dmaAborted = true;
1023 devState = Device_Dma_Abort;
1024 } else if (action == ACT_DMA_DONE) {
1025 // clear the BSY bit
1026 setComplete();
1027 // set the seek bit
1028 status |= STATUS_SEEK_BIT;
1029 // clear the controller state for DMA transfer
1030 ctrl->setDmaComplete(this);
1031
1032 if (!isIENSet()) {
1033 devState = Device_Idle_SI;
1034 intrPost();
1035 } else {
1036 devState = Device_Idle_S;
1037 }
1038 }
1039 break;
1040
1041 case Device_Dma_Abort:
1042 if (action == ACT_CMD_ERROR) {
1043 setComplete();
1044 status |= STATUS_SEEK_BIT;
1045 ctrl->setDmaComplete(this);
1046 dmaAborted = false;
1047 dmaState = Dma_Idle;
1048
1049 if (!isIENSet()) {
1050 devState = Device_Idle_SI;
1051 intrPost();
1052 } else {
1053 devState = Device_Idle_S;
1054 }
1055 } else {
1056 DPRINTF(IdeDisk, "Disk still busy aborting previous DMA command\n");
1057 }
1058 break;
1059
1060 default:
1061 panic("Unknown IDE device state: %#x\n", devState);
1062 }
1063 }
1064
1065 void
1066 IdeDisk::serialize(ostream &os)
1067 {
1068 // Check all outstanding events to see if they are scheduled
1069 // these are all mutually exclusive
1070 Tick reschedule = 0;
1071 Events_t event = None;
1072
1073 int eventCount = 0;
1074
1075 if (dmaTransferEvent.scheduled()) {
1076 reschedule = dmaTransferEvent.when();
1077 event = Transfer;
1078 eventCount++;
1079 }
1080 if (dmaReadWaitEvent.scheduled()) {
1081 reschedule = dmaReadWaitEvent.when();
1082 event = ReadWait;
1083 eventCount++;
1084 }
1085 if (dmaWriteWaitEvent.scheduled()) {
1086 reschedule = dmaWriteWaitEvent.when();
1087 event = WriteWait;
1088 eventCount++;
1089 }
1090 if (dmaPrdReadEvent.scheduled()) {
1091 reschedule = dmaPrdReadEvent.when();
1092 event = PrdRead;
1093 eventCount++;
1094 }
1095 if (dmaReadEvent.scheduled()) {
1096 reschedule = dmaReadEvent.when();
1097 event = DmaRead;
1098 eventCount++;
1099 }
1100 if (dmaWriteEvent.scheduled()) {
1101 reschedule = dmaWriteEvent.when();
1102 event = DmaWrite;
1103 eventCount++;
1104 }
1105
1106 assert(eventCount <= 1);
1107
1108 SERIALIZE_SCALAR(reschedule);
1109 SERIALIZE_ENUM(event);
1110
1111 // Serialize device registers
1112 SERIALIZE_SCALAR(cmdReg.data);
1113 SERIALIZE_SCALAR(cmdReg.sec_count);
1114 SERIALIZE_SCALAR(cmdReg.sec_num);
1115 SERIALIZE_SCALAR(cmdReg.cyl_low);
1116 SERIALIZE_SCALAR(cmdReg.cyl_high);
1117 SERIALIZE_SCALAR(cmdReg.drive);
1118 SERIALIZE_SCALAR(cmdReg.command);
1119 SERIALIZE_SCALAR(status);
1120 SERIALIZE_SCALAR(nIENBit);
1121 SERIALIZE_SCALAR(devID);
1122
1123 // Serialize the PRD related information
1124 SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1125 SERIALIZE_SCALAR(curPrd.entry.byteCount);
1126 SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1127 SERIALIZE_SCALAR(curPrdAddr);
1128
1129 /** @todo need to serialized chunk generator stuff!! */
1130 // Serialize current transfer related information
1131 SERIALIZE_SCALAR(cmdBytesLeft);
1132 SERIALIZE_SCALAR(cmdBytes);
1133 SERIALIZE_SCALAR(drqBytesLeft);
1134 SERIALIZE_SCALAR(curSector);
1135 SERIALIZE_SCALAR(dmaRead);
1136 SERIALIZE_SCALAR(intrPending);
1137 SERIALIZE_SCALAR(dmaAborted);
1138 SERIALIZE_ENUM(devState);
1139 SERIALIZE_ENUM(dmaState);
1140 SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1141 }
1142
1143 void
1144 IdeDisk::unserialize(Checkpoint *cp, const string &section)
1145 {
1146 // Reschedule events that were outstanding
1147 // these are all mutually exclusive
1148 Tick reschedule = 0;
1149 Events_t event = None;
1150
1151 UNSERIALIZE_SCALAR(reschedule);
1152 UNSERIALIZE_ENUM(event);
1153
1154 switch (event) {
1155 case None : break;
1156 case Transfer : schedule(dmaTransferEvent, reschedule); break;
1157 case ReadWait : schedule(dmaReadWaitEvent, reschedule); break;
1158 case WriteWait : schedule(dmaWriteWaitEvent, reschedule); break;
1159 case PrdRead : schedule(dmaPrdReadEvent, reschedule); break;
1160 case DmaRead : schedule(dmaReadEvent, reschedule); break;
1161 case DmaWrite : schedule(dmaWriteEvent, reschedule); break;
1162 }
1163
1164 // Unserialize device registers
1165 UNSERIALIZE_SCALAR(cmdReg.data);
1166 UNSERIALIZE_SCALAR(cmdReg.sec_count);
1167 UNSERIALIZE_SCALAR(cmdReg.sec_num);
1168 UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1169 UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1170 UNSERIALIZE_SCALAR(cmdReg.drive);
1171 UNSERIALIZE_SCALAR(cmdReg.command);
1172 UNSERIALIZE_SCALAR(status);
1173 UNSERIALIZE_SCALAR(nIENBit);
1174 UNSERIALIZE_SCALAR(devID);
1175
1176 // Unserialize the PRD related information
1177 UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1178 UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1179 UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1180 UNSERIALIZE_SCALAR(curPrdAddr);
1181
1182 /** @todo need to serialized chunk generator stuff!! */
1183 // Unserialize current transfer related information
1184 UNSERIALIZE_SCALAR(cmdBytes);
1185 UNSERIALIZE_SCALAR(cmdBytesLeft);
1186 UNSERIALIZE_SCALAR(drqBytesLeft);
1187 UNSERIALIZE_SCALAR(curSector);
1188 UNSERIALIZE_SCALAR(dmaRead);
1189 UNSERIALIZE_SCALAR(intrPending);
1190 UNSERIALIZE_SCALAR(dmaAborted);
1191 UNSERIALIZE_ENUM(devState);
1192 UNSERIALIZE_ENUM(dmaState);
1193 UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1194 }
1195
1196 IdeDisk *
1197 IdeDiskParams::create()
1198 {
1199 return new IdeDisk(this);
1200 }