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