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