ide_disk.cc revision 2665
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 = 0x400;
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()) {
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()) {
402        panic("shouldn't be reentant??");
403        dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
404        return;
405    } else if (!dmaReadCG->done()) {
406        assert(dmaReadCG->complete() < MAX_DMA_SIZE);
407        ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
408                &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
409        dmaReadBytes += dmaReadCG->size();
410        dmaReadTxs++;
411        if (dmaReadCG->size() == TheISA::PageBytes)
412            dmaReadFullPages++;
413        dmaReadCG->next();
414    } else {
415        assert(dmaReadCG->done());
416        delete dmaReadCG;
417        dmaReadCG = NULL;
418        dmaReadDone();
419    }
420}
421
422void
423IdeDisk::dmaReadDone()
424{
425
426    uint32_t bytesWritten = 0;
427
428
429    // write the data to the disk image
430    for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
431         bytesWritten += SectorSize) {
432
433        cmdBytesLeft -= SectorSize;
434        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
435    }
436
437    // check for the EOT
438    if (curPrd.getEOT()) {
439        assert(cmdBytesLeft == 0);
440        dmaState = Dma_Idle;
441        updateState(ACT_DMA_DONE);
442    } else {
443        doDmaTransfer();
444    }
445}
446
447void
448IdeDisk::doDmaDataWrite()
449{
450    /** @todo we need to figure out what the delay actually will be */
451    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
452    uint32_t bytesRead = 0;
453
454    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
455            diskDelay, totalDiskDelay);
456
457    memset(dataBuffer, 0, MAX_DMA_SIZE);
458    assert(cmdBytesLeft <= MAX_DMA_SIZE);
459    while (bytesRead < curPrd.getByteCount()) {
460        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
461        bytesRead += SectorSize;
462        cmdBytesLeft -= SectorSize;
463    }
464
465    dmaWriteWaitEvent.schedule(curTick + totalDiskDelay);
466}
467
468void
469IdeDisk::doDmaWrite()
470{
471
472    if (!dmaWriteCG) {
473        // clear out the data buffer
474        dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
475                curPrd.getByteCount(), TheISA::PageBytes);
476    }
477    if (ctrl->dmaPending()) {
478        panic("shouldn't be reentant??");
479        dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
480        return;
481    } else if (!dmaWriteCG->done()) {
482        assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
483        ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
484                &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
485        dmaWriteBytes += dmaWriteCG->size();
486        dmaWriteTxs++;
487        if (dmaWriteCG->size() == TheISA::PageBytes)
488            dmaWriteFullPages++;
489        dmaWriteCG->next();
490    } else {
491        assert(dmaWriteCG->done());
492        delete dmaWriteCG;
493        dmaWriteCG = NULL;
494        dmaWriteDone();
495    }
496}
497
498void
499IdeDisk::dmaWriteDone()
500{
501    // check for the EOT
502    if (curPrd.getEOT()) {
503        assert(cmdBytesLeft == 0);
504        dmaState = Dma_Idle;
505        updateState(ACT_DMA_DONE);
506    } else {
507        doDmaTransfer();
508    }
509}
510
511////
512// Disk utility routines
513///
514
515void
516IdeDisk::readDisk(uint32_t sector, uint8_t *data)
517{
518    uint32_t bytesRead = image->read(data, sector);
519
520    if (bytesRead != SectorSize)
521        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
522              name(), bytesRead, SectorSize, errno);
523}
524
525void
526IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
527{
528    uint32_t bytesWritten = image->write(data, sector);
529
530    if (bytesWritten != SectorSize)
531        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
532              name(), bytesWritten, SectorSize, errno);
533}
534
535////
536// Setup and handle commands
537////
538
539void
540IdeDisk::startDma(const uint32_t &prdTableBase)
541{
542    if (dmaState != Dma_Start)
543        panic("Inconsistent DMA state, should be in Dma_Start!\n");
544
545    if (devState != Transfer_Data_Dma)
546        panic("Inconsistent device state for DMA start!\n");
547
548    // PRD base address is given by bits 31:2
549    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
550
551    dmaState = Dma_Transfer;
552
553    // schedule dma transfer (doDmaTransfer)
554    dmaTransferEvent.schedule(curTick + 1);
555}
556
557void
558IdeDisk::abortDma()
559{
560    if (dmaState == Dma_Idle)
561        panic("Inconsistent DMA state, should be Start or Transfer!");
562
563    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
564        panic("Inconsistent device state, should be Transfer or Prepare!\n");
565
566    updateState(ACT_CMD_ERROR);
567}
568
569void
570IdeDisk::startCommand()
571{
572    DevAction_t action = ACT_NONE;
573    uint32_t size = 0;
574    dmaRead = false;
575
576    // Decode commands
577    switch (cmdReg.command) {
578        // Supported non-data commands
579      case WDSF_READ_NATIVE_MAX:
580        size = image->size() - 1;
581        cmdReg.sec_num = (size & 0xff);
582        cmdReg.cyl_low = ((size & 0xff00) >> 8);
583        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
584        cmdReg.head = ((size & 0xf000000) >> 24);
585
586        devState = Command_Execution;
587        action = ACT_CMD_COMPLETE;
588        break;
589
590      case WDCC_RECAL:
591      case WDCC_IDP:
592      case WDCC_STANDBY_IMMED:
593      case WDCC_FLUSHCACHE:
594      case WDSF_VERIFY:
595      case WDSF_SEEK:
596      case SET_FEATURES:
597      case WDCC_SETMULTI:
598        devState = Command_Execution;
599        action = ACT_CMD_COMPLETE;
600        break;
601
602        // Supported PIO data-in commands
603      case WDCC_IDENTIFY:
604        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
605        devState = Prepare_Data_In;
606        action = ACT_DATA_READY;
607        break;
608
609      case WDCC_READMULTI:
610      case WDCC_READ:
611        if (!(cmdReg.drive & DRIVE_LBA_BIT))
612            panic("Attempt to perform CHS access, only supports LBA\n");
613
614        if (cmdReg.sec_count == 0)
615            cmdBytes = cmdBytesLeft = (256 * SectorSize);
616        else
617            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
618
619        curSector = getLBABase();
620
621        /** @todo make this a scheduled event to simulate disk delay */
622        devState = Prepare_Data_In;
623        action = ACT_DATA_READY;
624        break;
625
626        // Supported PIO data-out commands
627      case WDCC_WRITEMULTI:
628      case WDCC_WRITE:
629        if (!(cmdReg.drive & DRIVE_LBA_BIT))
630            panic("Attempt to perform CHS access, only supports LBA\n");
631
632        if (cmdReg.sec_count == 0)
633            cmdBytes = cmdBytesLeft = (256 * SectorSize);
634        else
635            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
636
637        curSector = getLBABase();
638
639        devState = Prepare_Data_Out;
640        action = ACT_DATA_READY;
641        break;
642
643        // Supported DMA commands
644      case WDCC_WRITEDMA:
645        dmaRead = true;  // a write to the disk is a DMA read from memory
646      case WDCC_READDMA:
647        if (!(cmdReg.drive & DRIVE_LBA_BIT))
648            panic("Attempt to perform CHS access, only supports LBA\n");
649
650        if (cmdReg.sec_count == 0)
651            cmdBytes = cmdBytesLeft = (256 * SectorSize);
652        else
653            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
654
655        curSector = getLBABase();
656
657        devState = Prepare_Data_Dma;
658        action = ACT_DMA_READY;
659        break;
660
661      default:
662        panic("Unsupported ATA command: %#x\n", cmdReg.command);
663    }
664
665    if (action != ACT_NONE) {
666        // set the BSY bit
667        status |= STATUS_BSY_BIT;
668        // clear the DRQ bit
669        status &= ~STATUS_DRQ_BIT;
670        // clear the DF bit
671        status &= ~STATUS_DF_BIT;
672
673        updateState(action);
674    }
675}
676
677////
678// Handle setting and clearing interrupts
679////
680
681void
682IdeDisk::intrPost()
683{
684    DPRINTF(IdeDisk, "Posting Interrupt\n");
685    if (intrPending)
686        panic("Attempt to post an interrupt with one pending\n");
687
688    intrPending = true;
689
690    // talk to controller to set interrupt
691    if (ctrl) {
692        ctrl->bmi_regs.bmis0 |= IDEINTS;
693        ctrl->intrPost();
694    }
695}
696
697void
698IdeDisk::intrClear()
699{
700    DPRINTF(IdeDisk, "Clearing Interrupt\n");
701    if (!intrPending)
702        panic("Attempt to clear a non-pending interrupt\n");
703
704    intrPending = false;
705
706    // talk to controller to clear interrupt
707    if (ctrl)
708        ctrl->intrClear();
709}
710
711////
712// Manage the device internal state machine
713////
714
715void
716IdeDisk::updateState(DevAction_t action)
717{
718    switch (devState) {
719      case Device_Srst:
720        if (action == ACT_SRST_SET) {
721            // set the BSY bit
722            status |= STATUS_BSY_BIT;
723        } else if (action == ACT_SRST_CLEAR) {
724            // clear the BSY bit
725            status &= ~STATUS_BSY_BIT;
726
727            // reset the device state
728            reset(devID);
729        }
730        break;
731
732      case Device_Idle_S:
733        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
734            devState = Device_Idle_NS;
735        } else if (action == ACT_CMD_WRITE) {
736            startCommand();
737        }
738
739        break;
740
741      case Device_Idle_SI:
742        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
743            devState = Device_Idle_NS;
744            intrClear();
745        } else if (action == ACT_STAT_READ || isIENSet()) {
746            devState = Device_Idle_S;
747            intrClear();
748        } else if (action == ACT_CMD_WRITE) {
749            intrClear();
750            startCommand();
751        }
752
753        break;
754
755      case Device_Idle_NS:
756        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
757            if (!isIENSet() && intrPending) {
758                devState = Device_Idle_SI;
759                intrPost();
760            }
761            if (isIENSet() || !intrPending) {
762                devState = Device_Idle_S;
763            }
764        }
765        break;
766
767      case Command_Execution:
768        if (action == ACT_CMD_COMPLETE) {
769            // clear the BSY bit
770            setComplete();
771
772            if (!isIENSet()) {
773                devState = Device_Idle_SI;
774                intrPost();
775            } else {
776                devState = Device_Idle_S;
777            }
778        }
779        break;
780
781      case Prepare_Data_In:
782        if (action == ACT_CMD_ERROR) {
783            // clear the BSY bit
784            setComplete();
785
786            if (!isIENSet()) {
787                devState = Device_Idle_SI;
788                intrPost();
789            } else {
790                devState = Device_Idle_S;
791            }
792        } else if (action == ACT_DATA_READY) {
793            // clear the BSY bit
794            status &= ~STATUS_BSY_BIT;
795            // set the DRQ bit
796            status |= STATUS_DRQ_BIT;
797
798            // copy the data into the data buffer
799            if (cmdReg.command == WDCC_IDENTIFY) {
800                // Reset the drqBytes for this block
801                drqBytesLeft = sizeof(struct ataparams);
802
803                memcpy((void *)dataBuffer, (void *)&driveID,
804                       sizeof(struct ataparams));
805            } else {
806                // Reset the drqBytes for this block
807                drqBytesLeft = SectorSize;
808
809                readDisk(curSector++, dataBuffer);
810            }
811
812            // put the first two bytes into the data register
813            memcpy((void *)&cmdReg.data, (void *)dataBuffer,
814                   sizeof(uint16_t));
815
816            if (!isIENSet()) {
817                devState = Data_Ready_INTRQ_In;
818                intrPost();
819            } else {
820                devState = Transfer_Data_In;
821            }
822        }
823        break;
824
825      case Data_Ready_INTRQ_In:
826        if (action == ACT_STAT_READ) {
827            devState = Transfer_Data_In;
828            intrClear();
829        }
830        break;
831
832      case Transfer_Data_In:
833        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
834            if (action == ACT_DATA_READ_BYTE) {
835                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
836            } else {
837                drqBytesLeft -= 2;
838                cmdBytesLeft -= 2;
839
840                // copy next short into data registers
841                if (drqBytesLeft)
842                    memcpy((void *)&cmdReg.data,
843                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
844                           sizeof(uint16_t));
845            }
846
847            if (drqBytesLeft == 0) {
848                if (cmdBytesLeft == 0) {
849                    // Clear the BSY bit
850                    setComplete();
851                    devState = Device_Idle_S;
852                } else {
853                    devState = Prepare_Data_In;
854                    // set the BSY_BIT
855                    status |= STATUS_BSY_BIT;
856                    // clear the DRQ_BIT
857                    status &= ~STATUS_DRQ_BIT;
858
859                    /** @todo change this to a scheduled event to simulate
860                        disk delay */
861                    updateState(ACT_DATA_READY);
862                }
863            }
864        }
865        break;
866
867      case Prepare_Data_Out:
868        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
869            // clear the BSY bit
870            setComplete();
871
872            if (!isIENSet()) {
873                devState = Device_Idle_SI;
874                intrPost();
875            } else {
876                devState = Device_Idle_S;
877            }
878        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
879            // clear the BSY bit
880            status &= ~STATUS_BSY_BIT;
881            // set the DRQ bit
882            status |= STATUS_DRQ_BIT;
883
884            // clear the data buffer to get it ready for writes
885            memset(dataBuffer, 0, MAX_DMA_SIZE);
886
887            // reset the drqBytes for this block
888            drqBytesLeft = SectorSize;
889
890            if (cmdBytesLeft == cmdBytes || isIENSet()) {
891                devState = Transfer_Data_Out;
892            } else {
893                devState = Data_Ready_INTRQ_Out;
894                intrPost();
895            }
896        }
897        break;
898
899      case Data_Ready_INTRQ_Out:
900        if (action == ACT_STAT_READ) {
901            devState = Transfer_Data_Out;
902            intrClear();
903        }
904        break;
905
906      case Transfer_Data_Out:
907        if (action == ACT_DATA_WRITE_BYTE ||
908            action == ACT_DATA_WRITE_SHORT) {
909
910            if (action == ACT_DATA_READ_BYTE) {
911                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
912            } else {
913                // copy the latest short into the data buffer
914                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
915                       (void *)&cmdReg.data,
916                       sizeof(uint16_t));
917
918                drqBytesLeft -= 2;
919                cmdBytesLeft -= 2;
920            }
921
922            if (drqBytesLeft == 0) {
923                // copy the block to the disk
924                writeDisk(curSector++, dataBuffer);
925
926                // set the BSY bit
927                status |= STATUS_BSY_BIT;
928                // set the seek bit
929                status |= STATUS_SEEK_BIT;
930                // clear the DRQ bit
931                status &= ~STATUS_DRQ_BIT;
932
933                devState = Prepare_Data_Out;
934
935                /** @todo change this to a scheduled event to simulate
936                    disk delay */
937                updateState(ACT_DATA_READY);
938            }
939        }
940        break;
941
942      case Prepare_Data_Dma:
943        if (action == ACT_CMD_ERROR) {
944            // clear the BSY bit
945            setComplete();
946
947            if (!isIENSet()) {
948                devState = Device_Idle_SI;
949                intrPost();
950            } else {
951                devState = Device_Idle_S;
952            }
953        } else if (action == ACT_DMA_READY) {
954            // clear the BSY bit
955            status &= ~STATUS_BSY_BIT;
956            // set the DRQ bit
957            status |= STATUS_DRQ_BIT;
958
959            devState = Transfer_Data_Dma;
960
961            if (dmaState != Dma_Idle)
962                panic("Inconsistent DMA state, should be Dma_Idle\n");
963
964            dmaState = Dma_Start;
965            // wait for the write to the DMA start bit
966        }
967        break;
968
969      case Transfer_Data_Dma:
970        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
971            // clear the BSY bit
972            setComplete();
973            // set the seek bit
974            status |= STATUS_SEEK_BIT;
975            // clear the controller state for DMA transfer
976            ctrl->setDmaComplete(this);
977
978            if (!isIENSet()) {
979                devState = Device_Idle_SI;
980                intrPost();
981            } else {
982                devState = Device_Idle_S;
983            }
984        }
985        break;
986
987      default:
988        panic("Unknown IDE device state: %#x\n", devState);
989    }
990}
991
992void
993IdeDisk::serialize(ostream &os)
994{
995    // Check all outstanding events to see if they are scheduled
996    // these are all mutually exclusive
997    Tick reschedule = 0;
998    Events_t event = None;
999
1000    int eventCount = 0;
1001
1002    if (dmaTransferEvent.scheduled()) {
1003        reschedule = dmaTransferEvent.when();
1004        event = Transfer;
1005        eventCount++;
1006    }
1007    if (dmaReadWaitEvent.scheduled()) {
1008        reschedule = dmaReadWaitEvent.when();
1009        event = ReadWait;
1010        eventCount++;
1011    }
1012    if (dmaWriteWaitEvent.scheduled()) {
1013        reschedule = dmaWriteWaitEvent.when();
1014        event = WriteWait;
1015        eventCount++;
1016    }
1017    if (dmaPrdReadEvent.scheduled()) {
1018        reschedule = dmaPrdReadEvent.when();
1019        event = PrdRead;
1020        eventCount++;
1021    }
1022    if (dmaReadEvent.scheduled()) {
1023        reschedule = dmaReadEvent.when();
1024        event = DmaRead;
1025        eventCount++;
1026    }
1027    if (dmaWriteEvent.scheduled()) {
1028        reschedule = dmaWriteEvent.when();
1029        event = DmaWrite;
1030        eventCount++;
1031    }
1032
1033    assert(eventCount <= 1);
1034
1035    SERIALIZE_SCALAR(reschedule);
1036    SERIALIZE_ENUM(event);
1037
1038    // Serialize device registers
1039    SERIALIZE_SCALAR(cmdReg.data);
1040    SERIALIZE_SCALAR(cmdReg.sec_count);
1041    SERIALIZE_SCALAR(cmdReg.sec_num);
1042    SERIALIZE_SCALAR(cmdReg.cyl_low);
1043    SERIALIZE_SCALAR(cmdReg.cyl_high);
1044    SERIALIZE_SCALAR(cmdReg.drive);
1045    SERIALIZE_SCALAR(cmdReg.command);
1046    SERIALIZE_SCALAR(status);
1047    SERIALIZE_SCALAR(nIENBit);
1048    SERIALIZE_SCALAR(devID);
1049
1050    // Serialize the PRD related information
1051    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1052    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1053    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1054    SERIALIZE_SCALAR(curPrdAddr);
1055
1056    /** @todo need to serialized chunk generator stuff!! */
1057    // Serialize current transfer related information
1058    SERIALIZE_SCALAR(cmdBytesLeft);
1059    SERIALIZE_SCALAR(cmdBytes);
1060    SERIALIZE_SCALAR(drqBytesLeft);
1061    SERIALIZE_SCALAR(curSector);
1062    SERIALIZE_SCALAR(dmaRead);
1063    SERIALIZE_SCALAR(intrPending);
1064    SERIALIZE_ENUM(devState);
1065    SERIALIZE_ENUM(dmaState);
1066    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1067}
1068
1069void
1070IdeDisk::unserialize(Checkpoint *cp, const string &section)
1071{
1072    // Reschedule events that were outstanding
1073    // these are all mutually exclusive
1074    Tick reschedule = 0;
1075    Events_t event = None;
1076
1077    UNSERIALIZE_SCALAR(reschedule);
1078    UNSERIALIZE_ENUM(event);
1079
1080    switch (event) {
1081      case None : break;
1082      case Transfer : dmaTransferEvent.schedule(reschedule); break;
1083      case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
1084      case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
1085      case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
1086      case DmaRead : dmaReadEvent.schedule(reschedule); break;
1087      case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
1088    }
1089
1090    // Unserialize device registers
1091    UNSERIALIZE_SCALAR(cmdReg.data);
1092    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1093    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1094    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1095    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1096    UNSERIALIZE_SCALAR(cmdReg.drive);
1097    UNSERIALIZE_SCALAR(cmdReg.command);
1098    UNSERIALIZE_SCALAR(status);
1099    UNSERIALIZE_SCALAR(nIENBit);
1100    UNSERIALIZE_SCALAR(devID);
1101
1102    // Unserialize the PRD related information
1103    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1104    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1105    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1106    UNSERIALIZE_SCALAR(curPrdAddr);
1107
1108    /** @todo need to serialized chunk generator stuff!! */
1109    // Unserialize current transfer related information
1110    UNSERIALIZE_SCALAR(cmdBytes);
1111    UNSERIALIZE_SCALAR(cmdBytesLeft);
1112    UNSERIALIZE_SCALAR(drqBytesLeft);
1113    UNSERIALIZE_SCALAR(curSector);
1114    UNSERIALIZE_SCALAR(dmaRead);
1115    UNSERIALIZE_SCALAR(intrPending);
1116    UNSERIALIZE_ENUM(devState);
1117    UNSERIALIZE_ENUM(dmaState);
1118    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1119}
1120
1121#ifndef DOXYGEN_SHOULD_SKIP_THIS
1122
1123enum DriveID { master, slave };
1124static const char *DriveID_strings[] = { "master", "slave" };
1125BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1126
1127    SimObjectParam<DiskImage *> image;
1128    SimpleEnumParam<DriveID> driveID;
1129    Param<int> delay;
1130
1131END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1132
1133BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1134
1135    INIT_PARAM(image, "Disk image"),
1136    INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings),
1137    INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1)
1138
1139END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1140
1141
1142CREATE_SIM_OBJECT(IdeDisk)
1143{
1144    return new IdeDisk(getInstanceName(), image, driveID, delay);
1145}
1146
1147REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1148
1149#endif //DOXYGEN_SHOULD_SKIP_THIS
1150