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