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