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