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