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