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