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