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