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