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