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