ide_disk.cc revision 11522:348411ec525a
1/*
2 * Copyright (c) 2013 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2004-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Andrew Schultz
41 *          Ali Saidi
42 */
43
44/** @file
45 * Device model implementation for an IDE disk
46 */
47
48#include "dev/storage/ide_disk.hh"
49
50#include <cerrno>
51#include <cstring>
52#include <deque>
53#include <string>
54
55#include "arch/isa_traits.hh"
56#include "base/chunk_generator.hh"
57#include "base/cprintf.hh" // csprintf
58#include "base/trace.hh"
59#include "config/the_isa.hh"
60#include "debug/IdeDisk.hh"
61#include "dev/storage/disk_image.hh"
62#include "dev/storage/ide_ctrl.hh"
63#include "sim/core.hh"
64#include "sim/sim_object.hh"
65
66using namespace std;
67using namespace TheISA;
68
69IdeDisk::IdeDisk(const Params *p)
70    : SimObject(p), ctrl(NULL), image(p->image), diskDelay(p->delay),
71      dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this),
72      dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
73      dmaReadEvent(this), dmaWriteEvent(this)
74{
75    // Reset the device state
76    reset(p->driveID);
77
78    // fill out the drive ID structure
79    memset(&driveID, 0, sizeof(struct ataparams));
80
81    // Calculate LBA and C/H/S values
82    uint16_t cylinders;
83    uint8_t heads;
84    uint8_t sectors;
85
86    uint32_t lba_size = image->size();
87    if (lba_size >= 16383*16*63) {
88        cylinders = 16383;
89        heads = 16;
90        sectors = 63;
91    } else {
92        if (lba_size >= 63)
93            sectors = 63;
94        else if (lba_size == 0)
95            panic("Bad IDE image size: 0\n");
96        else
97            sectors = lba_size;
98
99        if ((lba_size / sectors) >= 16)
100            heads = 16;
101        else
102            heads = (lba_size / sectors);
103
104        cylinders = lba_size / (heads * sectors);
105    }
106
107    // Setup the model name
108    strncpy((char *)driveID.atap_model, "5MI EDD si k",
109            sizeof(driveID.atap_model));
110    // Set the maximum multisector transfer size
111    driveID.atap_multi = MAX_MULTSECT;
112    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
113    driveID.atap_capabilities1 = 0x7;
114    // UDMA support, EIDE support
115    driveID.atap_extensions = 0x6;
116    // Setup default C/H/S settings
117    driveID.atap_cylinders = cylinders;
118    driveID.atap_sectors = sectors;
119    driveID.atap_heads = heads;
120    // Setup the current multisector transfer size
121    driveID.atap_curmulti = MAX_MULTSECT;
122    driveID.atap_curmulti_valid = 0x1;
123    // Number of sectors on disk
124    driveID.atap_capacity = lba_size;
125    // Multiword DMA mode 2 and below supported
126    driveID.atap_dmamode_supp = 0x4;
127    // Set PIO mode 4 and 3 supported
128    driveID.atap_piomode_supp = 0x3;
129    // Set DMA mode 4 and below supported
130    driveID.atap_udmamode_supp = 0x1f;
131    // Statically set hardware config word
132    driveID.atap_hwreset_res = 0x4001;
133
134    //arbitrary for now...
135    driveID.atap_ata_major = WDC_VER_ATA7;
136}
137
138IdeDisk::~IdeDisk()
139{
140    // destroy the data buffer
141    delete [] dataBuffer;
142}
143
144void
145IdeDisk::reset(int id)
146{
147    // initialize the data buffer and shadow registers
148    dataBuffer = new uint8_t[MAX_DMA_SIZE];
149
150    memset(dataBuffer, 0, MAX_DMA_SIZE);
151    memset(&cmdReg, 0, sizeof(CommandReg_t));
152    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
153
154    curPrdAddr = 0;
155    curSector = 0;
156    cmdBytes = 0;
157    cmdBytesLeft = 0;
158    drqBytesLeft = 0;
159    dmaRead = false;
160    intrPending = false;
161    dmaAborted = false;
162
163    // set the device state to idle
164    dmaState = Dma_Idle;
165
166    if (id == DEV0) {
167        devState = Device_Idle_S;
168        devID = DEV0;
169    } else if (id == DEV1) {
170        devState = Device_Idle_NS;
171        devID = DEV1;
172    } else {
173        panic("Invalid device ID: %#x\n", id);
174    }
175
176    // set the device ready bit
177    status = STATUS_DRDY_BIT;
178
179    /* The error register must be set to 0x1 on start-up to
180       indicate that no diagnostic error was detected */
181    cmdReg.error = 0x1;
182}
183
184////
185// Utility functions
186////
187
188bool
189IdeDisk::isDEVSelect()
190{
191    return ctrl->isDiskSelected(this);
192}
193
194Addr
195IdeDisk::pciToDma(Addr pciAddr)
196{
197    if (ctrl)
198        return ctrl->pciToDma(pciAddr);
199    else
200        panic("Access to unset controller!\n");
201}
202
203////
204// Device registers read/write
205////
206
207void
208IdeDisk::readCommand(const Addr offset, int size, uint8_t *data)
209{
210    if (offset == DATA_OFFSET) {
211        if (size == sizeof(uint16_t)) {
212            *(uint16_t *)data = cmdReg.data;
213        } else if (size == sizeof(uint32_t)) {
214            *(uint16_t *)data = cmdReg.data;
215            updateState(ACT_DATA_READ_SHORT);
216            *((uint16_t *)data + 1) = cmdReg.data;
217        } else {
218            panic("Data read of unsupported size %d.\n", size);
219        }
220        updateState(ACT_DATA_READ_SHORT);
221        return;
222    }
223    assert(size == sizeof(uint8_t));
224    switch (offset) {
225      case ERROR_OFFSET:
226        *data = cmdReg.error;
227        break;
228      case NSECTOR_OFFSET:
229        *data = cmdReg.sec_count;
230        break;
231      case SECTOR_OFFSET:
232        *data = cmdReg.sec_num;
233        break;
234      case LCYL_OFFSET:
235        *data = cmdReg.cyl_low;
236        break;
237      case HCYL_OFFSET:
238        *data = cmdReg.cyl_high;
239        break;
240      case DRIVE_OFFSET:
241        *data = cmdReg.drive;
242        break;
243      case STATUS_OFFSET:
244        *data = status;
245        updateState(ACT_STAT_READ);
246        break;
247      default:
248        panic("Invalid IDE command register offset: %#x\n", offset);
249    }
250    DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
251}
252
253void
254IdeDisk::readControl(const Addr offset, int size, uint8_t *data)
255{
256    assert(size == sizeof(uint8_t));
257    *data = status;
258    if (offset != ALTSTAT_OFFSET)
259        panic("Invalid IDE control register offset: %#x\n", offset);
260    DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
261}
262
263void
264IdeDisk::writeCommand(const Addr offset, int size, const uint8_t *data)
265{
266    if (offset == DATA_OFFSET) {
267        if (size == sizeof(uint16_t)) {
268            cmdReg.data = *(const uint16_t *)data;
269        } else if (size == sizeof(uint32_t)) {
270            cmdReg.data = *(const uint16_t *)data;
271            updateState(ACT_DATA_WRITE_SHORT);
272            cmdReg.data = *((const uint16_t *)data + 1);
273        } else {
274            panic("Data write of unsupported size %d.\n", size);
275        }
276        updateState(ACT_DATA_WRITE_SHORT);
277        return;
278    }
279
280    assert(size == sizeof(uint8_t));
281    switch (offset) {
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        updateState(ACT_SELECT_WRITE);
299        break;
300      case COMMAND_OFFSET:
301        cmdReg.command = *data;
302        updateState(ACT_CMD_WRITE);
303        break;
304      default:
305        panic("Invalid IDE command register offset: %#x\n", offset);
306    }
307    DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
308            (uint32_t)*data);
309}
310
311void
312IdeDisk::writeControl(const Addr offset, int size, const uint8_t *data)
313{
314    if (offset != CONTROL_OFFSET)
315        panic("Invalid IDE control register offset: %#x\n", offset);
316
317    if (*data & CONTROL_RST_BIT) {
318        // force the device into the reset state
319        devState = Device_Srst;
320        updateState(ACT_SRST_SET);
321    } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
322        updateState(ACT_SRST_CLEAR);
323    }
324
325    nIENBit = *data & CONTROL_IEN_BIT;
326
327    DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
328            (uint32_t)*data);
329}
330
331////
332// Perform DMA transactions
333////
334
335void
336IdeDisk::doDmaTransfer()
337{
338    if (dmaAborted) {
339        DPRINTF(IdeDisk, "DMA Aborted before reading PRD entry\n");
340        updateState(ACT_CMD_ERROR);
341        return;
342    }
343
344    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
345        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
346              dmaState, devState);
347
348    if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
349        schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
350        return;
351    } else
352        ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
353                (uint8_t*)&curPrd.entry);
354}
355
356void
357IdeDisk::dmaPrdReadDone()
358{
359    if (dmaAborted) {
360        DPRINTF(IdeDisk, "DMA Aborted while reading PRD entry\n");
361        updateState(ACT_CMD_ERROR);
362        return;
363    }
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        doDmaDataRead();
376    else
377        doDmaDataWrite();
378}
379
380void
381IdeDisk::doDmaDataRead()
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
389    schedule(dmaReadWaitEvent, curTick() + totalDiskDelay);
390}
391
392void
393IdeDisk::regStats()
394{
395    SimObject::regStats();
396
397    using namespace Stats;
398    dmaReadFullPages
399        .name(name() + ".dma_read_full_pages")
400        .desc("Number of full page size DMA reads (not PRD).")
401        ;
402    dmaReadBytes
403        .name(name() + ".dma_read_bytes")
404        .desc("Number of bytes transfered via DMA reads (not PRD).")
405        ;
406    dmaReadTxs
407        .name(name() + ".dma_read_txs")
408        .desc("Number of DMA read transactions (not PRD).")
409        ;
410
411    dmaWriteFullPages
412        .name(name() + ".dma_write_full_pages")
413        .desc("Number of full page size DMA writes.")
414        ;
415    dmaWriteBytes
416        .name(name() + ".dma_write_bytes")
417        .desc("Number of bytes transfered via DMA writes.")
418        ;
419    dmaWriteTxs
420        .name(name() + ".dma_write_txs")
421        .desc("Number of DMA write transactions.")
422        ;
423}
424
425void
426IdeDisk::doDmaRead()
427{
428    if (dmaAborted) {
429        DPRINTF(IdeDisk, "DMA Aborted in middle of Dma Read\n");
430        if (dmaReadCG)
431            delete dmaReadCG;
432        dmaReadCG = NULL;
433        updateState(ACT_CMD_ERROR);
434        return;
435    }
436
437    if (!dmaReadCG) {
438        // clear out the data buffer
439        memset(dataBuffer, 0, MAX_DMA_SIZE);
440        dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(),
441                curPrd.getByteCount(), TheISA::PageBytes);
442
443    }
444    if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
445        schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
446        return;
447    } else if (!dmaReadCG->done()) {
448        assert(dmaReadCG->complete() < MAX_DMA_SIZE);
449        ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
450                &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
451        dmaReadBytes += dmaReadCG->size();
452        dmaReadTxs++;
453        if (dmaReadCG->size() == TheISA::PageBytes)
454            dmaReadFullPages++;
455        dmaReadCG->next();
456    } else {
457        assert(dmaReadCG->done());
458        delete dmaReadCG;
459        dmaReadCG = NULL;
460        dmaReadDone();
461    }
462}
463
464void
465IdeDisk::dmaReadDone()
466{
467    uint32_t bytesWritten = 0;
468
469    // write the data to the disk image
470    for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
471         bytesWritten += SectorSize) {
472
473        cmdBytesLeft -= SectorSize;
474        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
475    }
476
477    // check for the EOT
478    if (curPrd.getEOT()) {
479        assert(cmdBytesLeft == 0);
480        dmaState = Dma_Idle;
481        updateState(ACT_DMA_DONE);
482    } else {
483        doDmaTransfer();
484    }
485}
486
487void
488IdeDisk::doDmaDataWrite()
489{
490    /** @todo we need to figure out what the delay actually will be */
491    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
492    uint32_t bytesRead = 0;
493
494    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
495            diskDelay, totalDiskDelay);
496
497    memset(dataBuffer, 0, MAX_DMA_SIZE);
498    assert(cmdBytesLeft <= MAX_DMA_SIZE);
499    while (bytesRead < curPrd.getByteCount()) {
500        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
501        bytesRead += SectorSize;
502        cmdBytesLeft -= SectorSize;
503    }
504    DPRINTF(IdeDisk, "doDmaWrite, bytesRead: %d cmdBytesLeft: %d\n",
505            bytesRead, cmdBytesLeft);
506
507    schedule(dmaWriteWaitEvent, curTick() + totalDiskDelay);
508}
509
510void
511IdeDisk::doDmaWrite()
512{
513    if (dmaAborted) {
514        DPRINTF(IdeDisk, "DMA Aborted while doing DMA Write\n");
515        if (dmaWriteCG)
516            delete dmaWriteCG;
517        dmaWriteCG = NULL;
518        updateState(ACT_CMD_ERROR);
519        return;
520    }
521    if (!dmaWriteCG) {
522        // clear out the data buffer
523        dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
524                curPrd.getByteCount(), TheISA::PageBytes);
525    }
526    if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
527        schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
528        DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
529        return;
530    } else if (!dmaWriteCG->done()) {
531        assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
532        ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
533                &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
534        DPRINTF(IdeDisk, "doDmaWrite: not done curPrd byte count %d, eot %#x\n",
535                curPrd.getByteCount(), curPrd.getEOT());
536        dmaWriteBytes += dmaWriteCG->size();
537        dmaWriteTxs++;
538        if (dmaWriteCG->size() == TheISA::PageBytes)
539            dmaWriteFullPages++;
540        dmaWriteCG->next();
541    } else {
542        DPRINTF(IdeDisk, "doDmaWrite: done curPrd byte count %d, eot %#x\n",
543                curPrd.getByteCount(), curPrd.getEOT());
544        assert(dmaWriteCG->done());
545        delete dmaWriteCG;
546        dmaWriteCG = NULL;
547        dmaWriteDone();
548    }
549}
550
551void
552IdeDisk::dmaWriteDone()
553{
554    DPRINTF(IdeDisk, "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
555                curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
556    // check for the EOT
557    if (curPrd.getEOT()) {
558        assert(cmdBytesLeft == 0);
559        dmaState = Dma_Idle;
560        updateState(ACT_DMA_DONE);
561    } else {
562        doDmaTransfer();
563    }
564}
565
566////
567// Disk utility routines
568///
569
570void
571IdeDisk::readDisk(uint32_t sector, uint8_t *data)
572{
573    uint32_t bytesRead = image->read(data, sector);
574
575    if (bytesRead != SectorSize)
576        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
577              name(), bytesRead, SectorSize, errno);
578}
579
580void
581IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
582{
583    uint32_t bytesWritten = image->write(data, sector);
584
585    if (bytesWritten != SectorSize)
586        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
587              name(), bytesWritten, SectorSize, errno);
588}
589
590////
591// Setup and handle commands
592////
593
594void
595IdeDisk::startDma(const uint32_t &prdTableBase)
596{
597    if (dmaState != Dma_Start)
598        panic("Inconsistent DMA state, should be in Dma_Start!\n");
599
600    if (devState != Transfer_Data_Dma)
601        panic("Inconsistent device state for DMA start!\n");
602
603    // PRD base address is given by bits 31:2
604    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
605
606    dmaState = Dma_Transfer;
607
608    // schedule dma transfer (doDmaTransfer)
609    schedule(dmaTransferEvent, curTick() + 1);
610}
611
612void
613IdeDisk::abortDma()
614{
615    if (dmaState == Dma_Idle)
616        panic("Inconsistent DMA state, should be Start or Transfer!");
617
618    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
619        panic("Inconsistent device state, should be Transfer or Prepare!\n");
620
621    updateState(ACT_CMD_ERROR);
622}
623
624void
625IdeDisk::startCommand()
626{
627    DevAction_t action = ACT_NONE;
628    uint32_t size = 0;
629    dmaRead = false;
630
631    // Decode commands
632    switch (cmdReg.command) {
633        // Supported non-data commands
634      case WDSF_READ_NATIVE_MAX:
635        size = (uint32_t)image->size() - 1;
636        cmdReg.sec_num = (size & 0xff);
637        cmdReg.cyl_low = ((size & 0xff00) >> 8);
638        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
639        cmdReg.head = ((size & 0xf000000) >> 24);
640
641        devState = Command_Execution;
642        action = ACT_CMD_COMPLETE;
643        break;
644
645      case WDCC_RECAL:
646      case WDCC_IDP:
647      case WDCC_STANDBY_IMMED:
648      case WDCC_FLUSHCACHE:
649      case WDSF_VERIFY:
650      case WDSF_SEEK:
651      case SET_FEATURES:
652      case WDCC_SETMULTI:
653      case WDCC_IDLE:
654        devState = Command_Execution;
655        action = ACT_CMD_COMPLETE;
656        break;
657
658        // Supported PIO data-in commands
659      case WDCC_IDENTIFY:
660        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
661        devState = Prepare_Data_In;
662        action = ACT_DATA_READY;
663        break;
664
665      case WDCC_READMULTI:
666      case WDCC_READ:
667        if (!(cmdReg.drive & DRIVE_LBA_BIT))
668            panic("Attempt to perform CHS access, only supports LBA\n");
669
670        if (cmdReg.sec_count == 0)
671            cmdBytes = cmdBytesLeft = (256 * SectorSize);
672        else
673            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
674
675        curSector = getLBABase();
676
677        /** @todo make this a scheduled event to simulate disk delay */
678        devState = Prepare_Data_In;
679        action = ACT_DATA_READY;
680        break;
681
682        // Supported PIO data-out commands
683      case WDCC_WRITEMULTI:
684      case WDCC_WRITE:
685        if (!(cmdReg.drive & DRIVE_LBA_BIT))
686            panic("Attempt to perform CHS access, only supports LBA\n");
687
688        if (cmdReg.sec_count == 0)
689            cmdBytes = cmdBytesLeft = (256 * SectorSize);
690        else
691            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
692        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d\n", cmdBytesLeft);
693        curSector = getLBABase();
694
695        devState = Prepare_Data_Out;
696        action = ACT_DATA_READY;
697        break;
698
699        // Supported DMA commands
700      case WDCC_WRITEDMA:
701        dmaRead = true;  // a write to the disk is a DMA read from memory
702      case WDCC_READDMA:
703        if (!(cmdReg.drive & DRIVE_LBA_BIT))
704            panic("Attempt to perform CHS access, only supports LBA\n");
705
706        if (cmdReg.sec_count == 0)
707            cmdBytes = cmdBytesLeft = (256 * SectorSize);
708        else
709            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
710        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
711
712        curSector = getLBABase();
713
714        devState = Prepare_Data_Dma;
715        action = ACT_DMA_READY;
716        break;
717
718      default:
719        panic("Unsupported ATA command: %#x\n", cmdReg.command);
720    }
721
722    if (action != ACT_NONE) {
723        // set the BSY bit
724        status |= STATUS_BSY_BIT;
725        // clear the DRQ bit
726        status &= ~STATUS_DRQ_BIT;
727        // clear the DF bit
728        status &= ~STATUS_DF_BIT;
729
730        updateState(action);
731    }
732}
733
734////
735// Handle setting and clearing interrupts
736////
737
738void
739IdeDisk::intrPost()
740{
741    DPRINTF(IdeDisk, "Posting Interrupt\n");
742    if (intrPending)
743        panic("Attempt to post an interrupt with one pending\n");
744
745    intrPending = true;
746
747    // talk to controller to set interrupt
748    if (ctrl) {
749        ctrl->intrPost();
750    }
751}
752
753void
754IdeDisk::intrClear()
755{
756    DPRINTF(IdeDisk, "Clearing Interrupt\n");
757    if (!intrPending)
758        panic("Attempt to clear a non-pending interrupt\n");
759
760    intrPending = false;
761
762    // talk to controller to clear interrupt
763    if (ctrl)
764        ctrl->intrClear();
765}
766
767////
768// Manage the device internal state machine
769////
770
771void
772IdeDisk::updateState(DevAction_t action)
773{
774    switch (devState) {
775      case Device_Srst:
776        if (action == ACT_SRST_SET) {
777            // set the BSY bit
778            status |= STATUS_BSY_BIT;
779        } else if (action == ACT_SRST_CLEAR) {
780            // clear the BSY bit
781            status &= ~STATUS_BSY_BIT;
782
783            // reset the device state
784            reset(devID);
785        }
786        break;
787
788      case Device_Idle_S:
789        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
790            devState = Device_Idle_NS;
791        } else if (action == ACT_CMD_WRITE) {
792            startCommand();
793        }
794
795        break;
796
797      case Device_Idle_SI:
798        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
799            devState = Device_Idle_NS;
800            intrClear();
801        } else if (action == ACT_STAT_READ || isIENSet()) {
802            devState = Device_Idle_S;
803            intrClear();
804        } else if (action == ACT_CMD_WRITE) {
805            intrClear();
806            startCommand();
807        }
808
809        break;
810
811      case Device_Idle_NS:
812        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
813            if (!isIENSet() && intrPending) {
814                devState = Device_Idle_SI;
815                intrPost();
816            }
817            if (isIENSet() || !intrPending) {
818                devState = Device_Idle_S;
819            }
820        }
821        break;
822
823      case Command_Execution:
824        if (action == ACT_CMD_COMPLETE) {
825            // clear the BSY bit
826            setComplete();
827
828            if (!isIENSet()) {
829                devState = Device_Idle_SI;
830                intrPost();
831            } else {
832                devState = Device_Idle_S;
833            }
834        }
835        break;
836
837      case Prepare_Data_In:
838        if (action == ACT_CMD_ERROR) {
839            // clear the BSY bit
840            setComplete();
841
842            if (!isIENSet()) {
843                devState = Device_Idle_SI;
844                intrPost();
845            } else {
846                devState = Device_Idle_S;
847            }
848        } else if (action == ACT_DATA_READY) {
849            // clear the BSY bit
850            status &= ~STATUS_BSY_BIT;
851            // set the DRQ bit
852            status |= STATUS_DRQ_BIT;
853
854            // copy the data into the data buffer
855            if (cmdReg.command == WDCC_IDENTIFY) {
856                // Reset the drqBytes for this block
857                drqBytesLeft = sizeof(struct ataparams);
858
859                memcpy((void *)dataBuffer, (void *)&driveID,
860                       sizeof(struct ataparams));
861            } else {
862                // Reset the drqBytes for this block
863                drqBytesLeft = SectorSize;
864
865                readDisk(curSector++, dataBuffer);
866            }
867
868            // put the first two bytes into the data register
869            memcpy((void *)&cmdReg.data, (void *)dataBuffer,
870                   sizeof(uint16_t));
871
872            if (!isIENSet()) {
873                devState = Data_Ready_INTRQ_In;
874                intrPost();
875            } else {
876                devState = Transfer_Data_In;
877            }
878        }
879        break;
880
881      case Data_Ready_INTRQ_In:
882        if (action == ACT_STAT_READ) {
883            devState = Transfer_Data_In;
884            intrClear();
885        }
886        break;
887
888      case Transfer_Data_In:
889        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
890            if (action == ACT_DATA_READ_BYTE) {
891                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
892            } else {
893                drqBytesLeft -= 2;
894                cmdBytesLeft -= 2;
895
896                // copy next short into data registers
897                if (drqBytesLeft)
898                    memcpy((void *)&cmdReg.data,
899                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
900                           sizeof(uint16_t));
901            }
902
903            if (drqBytesLeft == 0) {
904                if (cmdBytesLeft == 0) {
905                    // Clear the BSY bit
906                    setComplete();
907                    devState = Device_Idle_S;
908                } else {
909                    devState = Prepare_Data_In;
910                    // set the BSY_BIT
911                    status |= STATUS_BSY_BIT;
912                    // clear the DRQ_BIT
913                    status &= ~STATUS_DRQ_BIT;
914
915                    /** @todo change this to a scheduled event to simulate
916                        disk delay */
917                    updateState(ACT_DATA_READY);
918                }
919            }
920        }
921        break;
922
923      case Prepare_Data_Out:
924        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
925            // clear the BSY bit
926            setComplete();
927
928            if (!isIENSet()) {
929                devState = Device_Idle_SI;
930                intrPost();
931            } else {
932                devState = Device_Idle_S;
933            }
934        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
935            // clear the BSY bit
936            status &= ~STATUS_BSY_BIT;
937            // set the DRQ bit
938            status |= STATUS_DRQ_BIT;
939
940            // clear the data buffer to get it ready for writes
941            memset(dataBuffer, 0, MAX_DMA_SIZE);
942
943            // reset the drqBytes for this block
944            drqBytesLeft = SectorSize;
945
946            if (cmdBytesLeft == cmdBytes || isIENSet()) {
947                devState = Transfer_Data_Out;
948            } else {
949                devState = Data_Ready_INTRQ_Out;
950                intrPost();
951            }
952        }
953        break;
954
955      case Data_Ready_INTRQ_Out:
956        if (action == ACT_STAT_READ) {
957            devState = Transfer_Data_Out;
958            intrClear();
959        }
960        break;
961
962      case Transfer_Data_Out:
963        if (action == ACT_DATA_WRITE_BYTE ||
964            action == ACT_DATA_WRITE_SHORT) {
965
966            if (action == ACT_DATA_READ_BYTE) {
967                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
968            } else {
969                // copy the latest short into the data buffer
970                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
971                       (void *)&cmdReg.data,
972                       sizeof(uint16_t));
973
974                drqBytesLeft -= 2;
975                cmdBytesLeft -= 2;
976            }
977
978            if (drqBytesLeft == 0) {
979                // copy the block to the disk
980                writeDisk(curSector++, dataBuffer);
981
982                // set the BSY bit
983                status |= STATUS_BSY_BIT;
984                // set the seek bit
985                status |= STATUS_SEEK_BIT;
986                // clear the DRQ bit
987                status &= ~STATUS_DRQ_BIT;
988
989                devState = Prepare_Data_Out;
990
991                /** @todo change this to a scheduled event to simulate
992                    disk delay */
993                updateState(ACT_DATA_READY);
994            }
995        }
996        break;
997
998      case Prepare_Data_Dma:
999        if (action == ACT_CMD_ERROR) {
1000            // clear the BSY bit
1001            setComplete();
1002
1003            if (!isIENSet()) {
1004                devState = Device_Idle_SI;
1005                intrPost();
1006            } else {
1007                devState = Device_Idle_S;
1008            }
1009        } else if (action == ACT_DMA_READY) {
1010            // clear the BSY bit
1011            status &= ~STATUS_BSY_BIT;
1012            // set the DRQ bit
1013            status |= STATUS_DRQ_BIT;
1014
1015            devState = Transfer_Data_Dma;
1016
1017            if (dmaState != Dma_Idle)
1018                panic("Inconsistent DMA state, should be Dma_Idle\n");
1019
1020            dmaState = Dma_Start;
1021            // wait for the write to the DMA start bit
1022        }
1023        break;
1024
1025      case Transfer_Data_Dma:
1026        if (action == ACT_CMD_ERROR) {
1027            dmaAborted = true;
1028            devState = Device_Dma_Abort;
1029        } else if (action == ACT_DMA_DONE) {
1030            // clear the BSY bit
1031            setComplete();
1032            // set the seek bit
1033            status |= STATUS_SEEK_BIT;
1034            // clear the controller state for DMA transfer
1035            ctrl->setDmaComplete(this);
1036
1037            if (!isIENSet()) {
1038                devState = Device_Idle_SI;
1039                intrPost();
1040            } else {
1041                devState = Device_Idle_S;
1042            }
1043        }
1044        break;
1045
1046      case Device_Dma_Abort:
1047        if (action == ACT_CMD_ERROR) {
1048            setComplete();
1049            status |= STATUS_SEEK_BIT;
1050            ctrl->setDmaComplete(this);
1051            dmaAborted = false;
1052            dmaState = Dma_Idle;
1053
1054            if (!isIENSet()) {
1055                devState = Device_Idle_SI;
1056                intrPost();
1057            } else {
1058                devState = Device_Idle_S;
1059            }
1060        } else {
1061            DPRINTF(IdeDisk, "Disk still busy aborting previous DMA command\n");
1062        }
1063        break;
1064
1065      default:
1066        panic("Unknown IDE device state: %#x\n", devState);
1067    }
1068}
1069
1070void
1071IdeDisk::serialize(CheckpointOut &cp) const
1072{
1073    // Check all outstanding events to see if they are scheduled
1074    // these are all mutually exclusive
1075    Tick reschedule = 0;
1076    Events_t event = None;
1077
1078    int eventCount = 0;
1079
1080    if (dmaTransferEvent.scheduled()) {
1081        reschedule = dmaTransferEvent.when();
1082        event = Transfer;
1083        eventCount++;
1084    }
1085    if (dmaReadWaitEvent.scheduled()) {
1086        reschedule = dmaReadWaitEvent.when();
1087        event = ReadWait;
1088        eventCount++;
1089    }
1090    if (dmaWriteWaitEvent.scheduled()) {
1091        reschedule = dmaWriteWaitEvent.when();
1092        event = WriteWait;
1093        eventCount++;
1094    }
1095    if (dmaPrdReadEvent.scheduled()) {
1096        reschedule = dmaPrdReadEvent.when();
1097        event = PrdRead;
1098        eventCount++;
1099    }
1100    if (dmaReadEvent.scheduled()) {
1101        reschedule = dmaReadEvent.when();
1102        event = DmaRead;
1103        eventCount++;
1104    }
1105    if (dmaWriteEvent.scheduled()) {
1106        reschedule = dmaWriteEvent.when();
1107        event = DmaWrite;
1108        eventCount++;
1109    }
1110
1111    assert(eventCount <= 1);
1112
1113    SERIALIZE_SCALAR(reschedule);
1114    SERIALIZE_ENUM(event);
1115
1116    // Serialize device registers
1117    SERIALIZE_SCALAR(cmdReg.data);
1118    SERIALIZE_SCALAR(cmdReg.sec_count);
1119    SERIALIZE_SCALAR(cmdReg.sec_num);
1120    SERIALIZE_SCALAR(cmdReg.cyl_low);
1121    SERIALIZE_SCALAR(cmdReg.cyl_high);
1122    SERIALIZE_SCALAR(cmdReg.drive);
1123    SERIALIZE_SCALAR(cmdReg.command);
1124    SERIALIZE_SCALAR(status);
1125    SERIALIZE_SCALAR(nIENBit);
1126    SERIALIZE_SCALAR(devID);
1127
1128    // Serialize the PRD related information
1129    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1130    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1131    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1132    SERIALIZE_SCALAR(curPrdAddr);
1133
1134    /** @todo need to serialized chunk generator stuff!! */
1135    // Serialize current transfer related information
1136    SERIALIZE_SCALAR(cmdBytesLeft);
1137    SERIALIZE_SCALAR(cmdBytes);
1138    SERIALIZE_SCALAR(drqBytesLeft);
1139    SERIALIZE_SCALAR(curSector);
1140    SERIALIZE_SCALAR(dmaRead);
1141    SERIALIZE_SCALAR(intrPending);
1142    SERIALIZE_SCALAR(dmaAborted);
1143    SERIALIZE_ENUM(devState);
1144    SERIALIZE_ENUM(dmaState);
1145    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1146}
1147
1148void
1149IdeDisk::unserialize(CheckpointIn &cp)
1150{
1151    // Reschedule events that were outstanding
1152    // these are all mutually exclusive
1153    Tick reschedule = 0;
1154    Events_t event = None;
1155
1156    UNSERIALIZE_SCALAR(reschedule);
1157    UNSERIALIZE_ENUM(event);
1158
1159    switch (event) {
1160      case None : break;
1161      case Transfer : schedule(dmaTransferEvent, reschedule); break;
1162      case ReadWait : schedule(dmaReadWaitEvent, reschedule); break;
1163      case WriteWait : schedule(dmaWriteWaitEvent, reschedule); break;
1164      case PrdRead : schedule(dmaPrdReadEvent, reschedule); break;
1165      case DmaRead : schedule(dmaReadEvent, reschedule); break;
1166      case DmaWrite : schedule(dmaWriteEvent, reschedule); break;
1167    }
1168
1169    // Unserialize device registers
1170    UNSERIALIZE_SCALAR(cmdReg.data);
1171    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1172    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1173    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1174    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1175    UNSERIALIZE_SCALAR(cmdReg.drive);
1176    UNSERIALIZE_SCALAR(cmdReg.command);
1177    UNSERIALIZE_SCALAR(status);
1178    UNSERIALIZE_SCALAR(nIENBit);
1179    UNSERIALIZE_SCALAR(devID);
1180
1181    // Unserialize the PRD related information
1182    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1183    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1184    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1185    UNSERIALIZE_SCALAR(curPrdAddr);
1186
1187    /** @todo need to serialized chunk generator stuff!! */
1188    // Unserialize current transfer related information
1189    UNSERIALIZE_SCALAR(cmdBytes);
1190    UNSERIALIZE_SCALAR(cmdBytesLeft);
1191    UNSERIALIZE_SCALAR(drqBytesLeft);
1192    UNSERIALIZE_SCALAR(curSector);
1193    UNSERIALIZE_SCALAR(dmaRead);
1194    UNSERIALIZE_SCALAR(intrPending);
1195    UNSERIALIZE_SCALAR(dmaAborted);
1196    UNSERIALIZE_ENUM(devState);
1197    UNSERIALIZE_ENUM(dmaState);
1198    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1199}
1200
1201IdeDisk *
1202IdeDiskParams::create()
1203{
1204    return new IdeDisk(this);
1205}
1206