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