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