ide_disk.cc revision 861
1/*
2 * Copyright (c) 2003 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 "arch/alpha/pmap.h"
39#include "base/cprintf.hh" // csprintf
40#include "base/trace.hh"
41#include "dev/disk_image.hh"
42#include "dev/ide_disk.hh"
43#include "dev/ide_ctrl.hh"
44#include "dev/tsunami.hh"
45#include "dev/tsunami_pchip.hh"
46#include "mem/functional_mem/physical_memory.hh"
47#include "mem/bus/bus.hh"
48#include "mem/bus/dma_interface.hh"
49#include "mem/bus/pio_interface.hh"
50#include "mem/bus/pio_interface_impl.hh"
51#include "sim/builder.hh"
52#include "sim/sim_object.hh"
53#include "sim/universe.hh"
54
55using namespace std;
56
57IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
58                 int id, int delay)
59    : SimObject(name), ctrl(NULL), image(img), physmem(phys),
60      dmaTransferEvent(this), dmaReadWaitEvent(this),
61      dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
62      dmaReadEvent(this), dmaWriteEvent(this)
63{
64    diskDelay = (delay * ticksPerSecond / 100000);
65
66    // initialize the data buffer and shadow registers
67    dataBuffer = new uint8_t[MAX_DMA_SIZE];
68
69    memset(dataBuffer, 0, MAX_DMA_SIZE);
70    memset(&cmdReg, 0, sizeof(CommandReg_t));
71    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
72
73    curPrdAddr = 0;
74    curSector = 0;
75    curCommand = 0;
76    cmdBytesLeft = 0;
77    drqBytesLeft = 0;
78    dmaRead = false;
79    intrPending = false;
80
81    // fill out the drive ID structure
82    memset(&driveID, 0, sizeof(struct hd_driveid));
83
84    // Calculate LBA and C/H/S values
85    uint16_t cylinders;
86    uint8_t heads;
87    uint8_t sectors;
88
89    uint32_t lba_size = image->size();
90    if (lba_size >= 16383*16*63) {
91        cylinders = 16383;
92        heads = 16;
93        sectors = 63;
94    } else {
95        if (lba_size >= 63)
96            sectors = 63;
97        else
98            sectors = lba_size;
99
100        if ((lba_size / sectors) >= 16)
101            heads = 16;
102        else
103            heads = (lba_size / sectors);
104
105        cylinders = lba_size / (heads * sectors);
106    }
107
108    // Setup the model name
109    sprintf((char *)driveID.model, "5MI EDD si k");
110    // Set the maximum multisector transfer size
111    driveID.max_multsect = MAX_MULTSECT;
112    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
113    driveID.capability = 0x7;
114    // UDMA support, EIDE support
115    driveID.field_valid = 0x6;
116    // Setup default C/H/S settings
117    driveID.cyls = cylinders;
118    driveID.sectors = sectors;
119    driveID.heads = heads;
120    // Setup the current multisector transfer size
121    driveID.multsect = MAX_MULTSECT;
122    driveID.multsect_valid = 0x1;
123    // Number of sectors on disk
124    driveID.lba_capacity = lba_size;
125    // Multiword DMA mode 2 and below supported
126    driveID.dma_mword = 0x400;
127    // Set PIO mode 4 and 3 supported
128    driveID.eide_pio_modes = 0x3;
129    // Set DMA mode 4 and below supported
130    driveID.dma_ultra = 0x10;
131    // Statically set hardware config word
132    driveID.hw_config = 0x4001;
133
134    // set the device state to idle
135    dmaState = Dma_Idle;
136
137    if (id == DEV0) {
138        devState = Device_Idle_S;
139        devID = DEV0;
140    } else if (id == DEV1) {
141        devState = Device_Idle_NS;
142        devID = DEV1;
143    } else {
144        panic("Invalid device ID: %#x\n", id);
145    }
146
147    // set the device ready bit
148    cmdReg.status |= STATUS_DRDY_BIT;
149}
150
151IdeDisk::~IdeDisk()
152{
153    // destroy the data buffer
154    delete [] dataBuffer;
155}
156
157Addr
158IdeDisk::pciToDma(Addr &pciAddr)
159{
160    if (ctrl)
161        return ctrl->tsunami->pchip->translatePciToDma(pciAddr);
162    else
163        panic("Access to unset controller!\n");
164}
165
166////
167// Device registers read/write
168////
169
170void
171IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
172{
173    DevAction_t action = ACT_NONE;
174
175    if (cmdBlk) {
176        if (offset < 0 || offset > sizeof(CommandReg_t))
177            panic("Invalid disk command register offset: %#x\n", offset);
178
179        if (!byte && offset != DATA_OFFSET)
180            panic("Invalid 16-bit read, only allowed on data reg\n");
181
182        if (!byte)
183            *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
184        else
185            *data = ((uint8_t *)&cmdReg)[offset];
186
187        // determine if an action needs to be taken on the state machine
188        if (offset == STATUS_OFFSET) {
189            action = ACT_STAT_READ;
190        } else if (offset == DATA_OFFSET) {
191            if (byte)
192                action = ACT_DATA_READ_BYTE;
193            else
194                action = ACT_DATA_READ_SHORT;
195        }
196
197    } else {
198        if (offset != ALTSTAT_OFFSET)
199            panic("Invalid disk control register offset: %#x\n", offset);
200
201        if (!byte)
202            panic("Invalid 16-bit read from control block\n");
203
204        *data = ((uint8_t *)&cmdReg)[STATUS_OFFSET];
205    }
206
207    if (action != ACT_NONE)
208        updateState(action);
209}
210
211void
212IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
213{
214    DevAction_t action = ACT_NONE;
215
216    if (cmdBlk) {
217        if (offset < 0 || offset > sizeof(CommandReg_t))
218            panic("Invalid disk command register offset: %#x\n", offset);
219
220        if (!byte && offset != DATA_OFFSET)
221            panic("Invalid 16-bit write, only allowed on data reg\n");
222
223        if (!byte)
224            *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
225        else
226            ((uint8_t *)&cmdReg)[offset] = *data;
227
228        // determine if an action needs to be taken on the state machine
229        if (offset == COMMAND_OFFSET) {
230            action = ACT_CMD_WRITE;
231        } else if (offset == DATA_OFFSET) {
232            if (byte)
233                action = ACT_DATA_WRITE_BYTE;
234            else
235                action = ACT_DATA_WRITE_SHORT;
236        }
237
238    } else {
239        if (offset != CONTROL_OFFSET)
240            panic("Invalid disk control register offset: %#x\n", offset);
241
242        if (!byte)
243            panic("Invalid 16-bit write to control block\n");
244
245        if (*data & CONTROL_RST_BIT)
246            panic("Software reset not supported!\n");
247
248        nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
249    }
250
251    if (action != ACT_NONE)
252        updateState(action);
253}
254
255////
256// Perform DMA transactions
257////
258
259void
260IdeDisk::doDmaTransfer()
261{
262    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
263        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
264              dmaState, devState);
265
266    // first read the current PRD
267    if (dmaInterface) {
268        if (dmaInterface->busy()) {
269            // reschedule after waiting period
270            dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
271            return;
272        }
273
274        dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
275                            &dmaPrdReadEvent);
276    } else {
277        dmaPrdReadDone();
278    }
279}
280
281void
282IdeDisk::dmaPrdReadDone()
283{
284    // actually copy the PRD from physical memory
285    memcpy((void *)&curPrd.entry,
286           physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
287           sizeof(PrdEntry_t));
288
289    curPrdAddr += sizeof(PrdEntry_t);
290
291    if (dmaRead)
292        doDmaRead();
293    else
294        doDmaWrite();
295}
296
297void
298IdeDisk::doDmaRead()
299{
300    Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize);
301
302    if (dmaInterface) {
303        if (dmaInterface->busy()) {
304            // reschedule after waiting period
305            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
306            return;
307        }
308
309        Addr dmaAddr =
310            ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
311        dmaInterface->doDMA(Read, dmaAddr, curPrd.getByteCount(),
312                            curTick + totalDiskDelay, &dmaReadEvent);
313    } else {
314        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
315        dmaReadEvent.schedule(curTick + totalDiskDelay);
316    }
317}
318
319void
320IdeDisk::dmaReadDone()
321{
322
323    Addr curAddr = 0, dmaAddr = 0;
324    uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
325
326    // set initial address
327    curAddr = curPrd.getBaseAddr();
328
329    // clear out the data buffer
330    memset(dataBuffer, 0, MAX_DMA_SIZE);
331
332    // read the data from memory via DMA into a data buffer
333    while (bytesWritten < curPrd.getByteCount()) {
334        if (cmdBytesLeft <= 0)
335            panic("DMA data is larger than # of sectors specified\n");
336
337        dmaAddr = pciToDma(curAddr);
338
339        // calculate how many bytes are in the current page
340        bytesLeft = curPrd.getByteCount() - bytesWritten;
341        bytesInPage = (bytesLeft > ALPHA_PGBYTES) ? ALPHA_PGBYTES : bytesLeft;
342        // check to make sure we don't cross a page boundary
343        if ((curAddr + bytesInPage) >
344            (alpha_trunc_page(curAddr) + ALPHA_PGBYTES))
345
346            bytesInPage = alpha_round_page(curAddr) - curAddr;
347
348        // copy the data from memory into the data buffer
349        /** @todo Use real DMA with interfaces here */
350        memcpy((void *)(dataBuffer + bytesWritten),
351               physmem->dma_addr(dmaAddr, bytesInPage),
352               bytesInPage);
353
354        curAddr += bytesInPage;
355        bytesWritten += bytesInPage;
356        cmdBytesLeft -= bytesInPage;
357    }
358
359    // write the data to the disk image
360    for (bytesWritten = 0;
361         bytesWritten < curPrd.getByteCount();
362         bytesWritten += SectorSize)
363
364        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
365
366#if 0
367    // actually copy the data from memory to data buffer
368    Addr dmaAddr =
369        ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
370    memcpy((void *)dataBuffer,
371           physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
372           curPrd.getByteCount());
373
374    uint32_t bytesWritten = 0;
375
376    while (bytesWritten < curPrd.getByteCount()) {
377        if (cmdBytesLeft <= 0)
378            panic("DMA data is larger than # sectors specified\n");
379
380        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
381
382        bytesWritten += SectorSize;
383        cmdBytesLeft -= SectorSize;
384    }
385#endif
386
387    // check for the EOT
388    if (curPrd.getEOT()){
389        assert(cmdBytesLeft == 0);
390        dmaState = Dma_Idle;
391        updateState(ACT_DMA_DONE);
392    } else {
393        doDmaTransfer();
394    }
395}
396
397void
398IdeDisk::doDmaWrite()
399{
400    Tick totalDiskDelay = diskDelay * (curPrd.getByteCount() / SectorSize);
401
402    if (dmaInterface) {
403        if (dmaInterface->busy()) {
404            // reschedule after waiting period
405            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
406            return;
407        }
408
409        Addr dmaAddr =
410            ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
411        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
412                            curPrd.getByteCount(), curTick + totalDiskDelay,
413                            &dmaWriteEvent);
414    } else {
415        // schedule event with disk delay (dmaWriteDone)
416        dmaWriteEvent.schedule(curTick + totalDiskDelay);
417    }
418}
419
420void
421IdeDisk::dmaWriteDone()
422{
423    Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
424    uint32_t bytesRead = 0, bytesInPage = 0;
425
426    // setup the initial page and DMA address
427    curAddr = curPrd.getBaseAddr();
428    pageAddr = alpha_trunc_page(curAddr);
429    dmaAddr = pciToDma(curAddr);
430
431    // clear out the data buffer
432    memset(dataBuffer, 0, MAX_DMA_SIZE);
433
434    while (bytesRead < curPrd.getByteCount()) {
435        // see if we have crossed into a new page
436        if (pageAddr != alpha_trunc_page(curAddr)) {
437            // write the data to memory
438            /** @todo Do real DMA using interfaces here */
439            memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
440                   (void *)(dataBuffer + (bytesRead - bytesInPage)),
441                   bytesInPage);
442
443            // update the DMA address and page address
444            pageAddr = alpha_trunc_page(curAddr);
445            dmaAddr = pciToDma(curAddr);
446
447            bytesInPage = 0;
448        }
449
450        if (cmdBytesLeft <= 0)
451            panic("DMA requested data is larger than # sectors specified\n");
452
453        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
454
455        curAddr += SectorSize;
456        bytesRead += SectorSize;
457        bytesInPage += SectorSize;
458        cmdBytesLeft -= SectorSize;
459    }
460
461    // write the last page worth read to memory
462    /** @todo Do real DMA using interfaces here */
463    if (bytesInPage != 0) {
464        memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
465               (void *)(dataBuffer + (bytesRead - bytesInPage)),
466               bytesInPage);
467    }
468
469#if 0
470    Addr dmaAddr = ctrl->tsunami->pchip->
471        translatePciToDma(curPrd.getBaseAddr());
472
473    memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
474           (void *)dataBuffer, curPrd.getByteCount());
475#endif
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
487////
488// Disk utility routines
489///
490
491void
492IdeDisk::readDisk(uint32_t sector, uint8_t *data)
493{
494    uint32_t bytesRead = image->read(data, sector);
495
496    if (bytesRead != SectorSize)
497        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
498              name(), bytesRead, SectorSize, errno);
499}
500
501void
502IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
503{
504    uint32_t bytesWritten = image->write(data, sector);
505
506    if (bytesWritten != SectorSize)
507        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
508              name(), bytesWritten, SectorSize, errno);
509}
510
511////
512// Setup and handle commands
513////
514
515void
516IdeDisk::startDma(const uint32_t &prdTableBase)
517{
518    if (dmaState != Dma_Start)
519        panic("Inconsistent DMA state, should be in Dma_Start!\n");
520
521    if (devState != Transfer_Data_Dma)
522        panic("Inconsistent device state for DMA start!\n");
523
524    curPrdAddr = ctrl->tsunami->pchip->translatePciToDma(prdTableBase);
525
526    dmaState = Dma_Transfer;
527
528    // schedule dma transfer (doDmaTransfer)
529    dmaTransferEvent.schedule(curTick + 1);
530}
531
532void
533IdeDisk::abortDma()
534{
535    if (dmaState == Dma_Idle)
536        panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n");
537
538    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
539        panic("Inconsistent device state, should be in Transfer or Prepare!\n");
540
541    updateState(ACT_CMD_ERROR);
542}
543
544void
545IdeDisk::startCommand()
546{
547    DevAction_t action = ACT_NONE;
548    uint32_t size = 0;
549    dmaRead = false;
550
551    // copy the command to the shadow
552    curCommand = cmdReg.command;
553
554    // Decode commands
555    switch (cmdReg.command) {
556        // Supported non-data commands
557      case WIN_READ_NATIVE_MAX:
558        size = image->size() - 1;
559        cmdReg.sec_num = (size & 0xff);
560        cmdReg.cyl_low = ((size & 0xff00) >> 8);
561        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
562        cmdReg.head = ((size & 0xf000000) >> 24);
563
564        devState = Command_Execution;
565        action = ACT_CMD_COMPLETE;
566        break;
567
568      case WIN_RECAL:
569      case WIN_SPECIFY:
570      case WIN_FLUSH_CACHE:
571      case WIN_VERIFY:
572      case WIN_SEEK:
573      case WIN_SETFEATURES:
574      case WIN_SETMULT:
575        devState = Command_Execution;
576        action = ACT_CMD_COMPLETE;
577        break;
578
579        // Supported PIO data-in commands
580      case WIN_IDENTIFY:
581        cmdBytesLeft = drqBytesLeft = sizeof(struct hd_driveid);
582        devState = Prepare_Data_In;
583        action = ACT_DATA_READY;
584        break;
585
586      case WIN_MULTREAD:
587      case WIN_READ:
588        if (!(cmdReg.drive & DRIVE_LBA_BIT))
589            panic("Attempt to perform CHS access, only supports LBA\n");
590
591        if (cmdReg.sec_count == 0)
592            cmdBytesLeft = (256 * SectorSize);
593        else
594            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
595
596        drqBytesLeft = SectorSize;
597        curSector = getLBABase();
598
599        devState = Prepare_Data_In;
600        action = ACT_DATA_READY;
601        break;
602
603        // Supported PIO data-out commands
604      case WIN_MULTWRITE:
605      case WIN_WRITE:
606        if (!(cmdReg.drive & DRIVE_LBA_BIT))
607            panic("Attempt to perform CHS access, only supports LBA\n");
608
609        if (cmdReg.sec_count == 0)
610            cmdBytesLeft = (256 * SectorSize);
611        else
612            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
613
614        drqBytesLeft = SectorSize;
615        curSector = getLBABase();
616
617        devState = Prepare_Data_Out;
618        action = ACT_DATA_READY;
619        break;
620
621        // Supported DMA commands
622      case WIN_WRITEDMA:
623        dmaRead = true;  // a write to the disk is a DMA read from memory
624      case WIN_READDMA:
625        if (!(cmdReg.drive & DRIVE_LBA_BIT))
626            panic("Attempt to perform CHS access, only supports LBA\n");
627
628        if (cmdReg.sec_count == 0)
629            cmdBytesLeft = (256 * SectorSize);
630        else
631            cmdBytesLeft = (cmdReg.sec_count * SectorSize);
632
633        drqBytesLeft = SectorSize;
634        curSector = getLBABase();
635
636        devState = Prepare_Data_Dma;
637        action = ACT_DMA_READY;
638        break;
639
640      default:
641        panic("Unsupported ATA command: %#x\n", cmdReg.command);
642    }
643
644    if (action != ACT_NONE) {
645        // set the BSY bit
646        cmdReg.status |= STATUS_BSY_BIT;
647        // clear the DRQ bit
648        cmdReg.status &= ~STATUS_DRQ_BIT;
649
650        updateState(action);
651    }
652}
653
654////
655// Handle setting and clearing interrupts
656////
657
658void
659IdeDisk::intrPost()
660{
661    if (intrPending)
662        panic("Attempt to post an interrupt with one pending\n");
663
664    intrPending = true;
665
666    // talk to controller to set interrupt
667    if (ctrl)
668        ctrl->intrPost();
669}
670
671void
672IdeDisk::intrClear()
673{
674    if (!intrPending)
675        panic("Attempt to clear a non-pending interrupt\n");
676
677    intrPending = false;
678
679    // talk to controller to clear interrupt
680    if (ctrl)
681        ctrl->intrClear();
682}
683
684////
685// Manage the device internal state machine
686////
687
688void
689IdeDisk::updateState(DevAction_t action)
690{
691    switch (devState) {
692      case Device_Idle_S:
693        if (!isDEVSelect())
694            devState = Device_Idle_NS;
695        else if (action == ACT_CMD_WRITE)
696            startCommand();
697
698        break;
699
700      case Device_Idle_SI:
701        if (!isDEVSelect()) {
702            devState = Device_Idle_NS;
703            intrClear();
704        } else if (action == ACT_STAT_READ || isIENSet()) {
705            devState = Device_Idle_S;
706            intrClear();
707        } else if (action == ACT_CMD_WRITE) {
708            intrClear();
709            startCommand();
710        }
711
712        break;
713
714      case Device_Idle_NS:
715        if (isDEVSelect()) {
716            if (!isIENSet() && intrPending) {
717                devState = Device_Idle_SI;
718                intrPost();
719            }
720            if (isIENSet() || !intrPending) {
721                devState = Device_Idle_S;
722            }
723        }
724        break;
725
726      case Command_Execution:
727        if (action == ACT_CMD_COMPLETE) {
728            // clear the BSY bit
729            setComplete();
730
731            if (!isIENSet()) {
732                devState = Device_Idle_SI;
733                intrPost();
734            } else {
735                devState = Device_Idle_S;
736            }
737        }
738        break;
739
740      case Prepare_Data_In:
741        if (action == ACT_CMD_ERROR) {
742            // clear the BSY bit
743            setComplete();
744
745            if (!isIENSet()) {
746                devState = Device_Idle_SI;
747                intrPost();
748            } else {
749                devState = Device_Idle_S;
750            }
751        } else if (action == ACT_DATA_READY) {
752            // clear the BSY bit
753            cmdReg.status &= ~STATUS_BSY_BIT;
754            // set the DRQ bit
755            cmdReg.status |= STATUS_DRQ_BIT;
756
757            // put the first two bytes into the data register
758            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
759                   sizeof(uint16_t));
760
761            // copy the data into the data buffer
762            if (curCommand == WIN_IDENTIFY)
763                memcpy((void *)dataBuffer, (void *)&driveID,
764                       sizeof(struct hd_driveid));
765            else
766                readDisk(curSector++, dataBuffer);
767
768            if (!isIENSet()) {
769                devState = Data_Ready_INTRQ_In;
770                intrPost();
771            } else {
772                devState = Transfer_Data_In;
773            }
774        }
775        break;
776
777      case Data_Ready_INTRQ_In:
778        if (action == ACT_STAT_READ) {
779            devState = Transfer_Data_In;
780            intrClear();
781        }
782        break;
783
784      case Transfer_Data_In:
785        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
786            if (action == ACT_DATA_READ_BYTE) {
787                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
788            } else {
789                drqBytesLeft -= 2;
790                cmdBytesLeft -= 2;
791
792                // copy next short into data registers
793                memcpy((void *)&cmdReg.data0,
794                       (void *)&dataBuffer[SectorSize - drqBytesLeft],
795                       sizeof(uint16_t));
796            }
797
798            if (drqBytesLeft == 0) {
799                if (cmdBytesLeft == 0) {
800                    // Clear the BSY bit
801                    setComplete();
802                    devState = Device_Idle_S;
803                } else {
804                    devState = Prepare_Data_In;
805                    cmdReg.status |= STATUS_BSY_BIT;
806                }
807            }
808        }
809        break;
810
811      case Prepare_Data_Out:
812        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
813            // clear the BSY bit
814            setComplete();
815
816            if (!isIENSet()) {
817                devState = Device_Idle_SI;
818                intrPost();
819            } else {
820                devState = Device_Idle_S;
821            }
822        } else if (cmdBytesLeft != 0) {
823            // clear the BSY bit
824            cmdReg.status &= ~STATUS_BSY_BIT;
825            // set the DRQ bit
826            cmdReg.status |= STATUS_DRQ_BIT;
827
828            // clear the data buffer to get it ready for writes
829            memset(dataBuffer, 0, MAX_DMA_SIZE);
830
831            if (!isIENSet()) {
832                devState = Data_Ready_INTRQ_Out;
833                intrPost();
834            } else {
835                devState = Transfer_Data_Out;
836            }
837        }
838        break;
839
840      case Data_Ready_INTRQ_Out:
841        if (action == ACT_STAT_READ) {
842            devState = Transfer_Data_Out;
843            intrClear();
844        }
845        break;
846
847      case Transfer_Data_Out:
848        if (action == ACT_DATA_WRITE_BYTE ||
849            action == ACT_DATA_WRITE_SHORT) {
850
851            if (action == ACT_DATA_READ_BYTE) {
852                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
853            } else {
854                // copy the latest short into the data buffer
855                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
856                       (void *)&cmdReg.data0,
857                       sizeof(uint16_t));
858
859                drqBytesLeft -= 2;
860                cmdBytesLeft -= 2;
861            }
862
863            if (drqBytesLeft == 0) {
864                // copy the block to the disk
865                writeDisk(curSector++, dataBuffer);
866
867                // set the BSY bit
868                cmdReg.status |= STATUS_BSY_BIT;
869                // clear the DRQ bit
870                cmdReg.status &= ~STATUS_DRQ_BIT;
871
872                devState = Prepare_Data_Out;
873            }
874        }
875        break;
876
877      case Prepare_Data_Dma:
878        if (action == ACT_CMD_ERROR) {
879            // clear the BSY bit
880            setComplete();
881
882            if (!isIENSet()) {
883                devState = Device_Idle_SI;
884                intrPost();
885            } else {
886                devState = Device_Idle_S;
887            }
888        } else if (action == ACT_DMA_READY) {
889            // clear the BSY bit
890            cmdReg.status &= ~STATUS_BSY_BIT;
891            // set the DRQ bit
892            cmdReg.status |= STATUS_DRQ_BIT;
893
894            devState = Transfer_Data_Dma;
895
896            if (dmaState != Dma_Idle)
897                panic("Inconsistent DMA state, should be Dma_Idle\n");
898
899            dmaState = Dma_Start;
900            // wait for the write to the DMA start bit
901        }
902        break;
903
904      case Transfer_Data_Dma:
905        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
906            // clear the BSY bit
907            setComplete();
908            // set the seek bit
909            cmdReg.status |= 0x10;
910            // clear the controller state for DMA transfer
911            ctrl->setDmaComplete(this);
912
913            if (!isIENSet()) {
914                devState = Device_Idle_SI;
915                intrPost();
916            } else {
917                devState = Device_Idle_S;
918            }
919        }
920        break;
921
922      default:
923        panic("Unknown IDE device state: %#x\n", devState);
924    }
925}
926
927void
928IdeDisk::serialize(ostream &os)
929{
930}
931
932void
933IdeDisk::unserialize(Checkpoint *cp, const string &section)
934{
935}
936
937#ifndef DOXYGEN_SHOULD_SKIP_THIS
938
939BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
940
941    SimObjectParam<DiskImage *> image;
942    SimObjectParam<PhysicalMemory *> physmem;
943    Param<int> driveID;
944    Param<int> disk_delay;
945
946END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
947
948BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
949
950    INIT_PARAM(image, "Disk image"),
951    INIT_PARAM(physmem, "Physical memory"),
952    INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"),
953    INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in milliseconds", 0)
954
955END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
956
957
958CREATE_SIM_OBJECT(IdeDisk)
959{
960    return new IdeDisk(getInstanceName(), image, physmem, driveID,
961                       disk_delay);
962}
963
964REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
965
966#endif //DOXYGEN_SHOULD_SKIP_THIS
967