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