ide_disk.cc revision 896
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    DPRINTF(IdeDisk, "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
334            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
335            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
336            curPrd.getEOT(), curSector);
337
338    // make sure the new curPrdAddr is properly translated from PCI to system
339    curPrdAddr = pciToDma(curPrdAddr + sizeof(PrdEntry_t));
340
341    if (dmaRead)
342        doDmaRead();
343    else
344        doDmaWrite();
345}
346
347void
348IdeDisk::doDmaRead()
349{
350    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
351
352    if (dmaInterface) {
353        if (dmaInterface->busy()) {
354            // reschedule after waiting period
355            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
356            return;
357        }
358
359        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
360
361        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
362                                              (uint32_t)curPrd.getByteCount());
363
364        dmaInterfaceBytes = bytesInPage;
365
366        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
367                            curTick + totalDiskDelay, &dmaReadEvent);
368    } else {
369        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
370        dmaReadEvent.schedule(curTick + totalDiskDelay);
371    }
372}
373
374void
375IdeDisk::dmaReadDone()
376{
377
378    Addr curAddr = 0, dmaAddr = 0;
379    uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
380
381    // continue to use the DMA interface until all pages are read
382    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
383        // see if the interface is busy
384        if (dmaInterface->busy()) {
385            // reschedule after waiting period
386            dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
387            return;
388        }
389
390        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
391        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
392        dmaAddr = pciToDma(curAddr);
393
394        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
395        dmaInterfaceBytes += bytesInPage;
396
397        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
398                            curTick, &dmaReadEvent);
399
400        return;
401    }
402
403    // set initial address
404    curAddr = curPrd.getBaseAddr();
405
406    // clear out the data buffer
407    memset(dataBuffer, 0, MAX_DMA_SIZE);
408
409    // read the data from memory via DMA into a data buffer
410    while (bytesWritten < curPrd.getByteCount()) {
411        if (cmdBytesLeft <= 0)
412            panic("DMA data is larger than # of sectors specified\n");
413
414        dmaAddr = pciToDma(curAddr);
415
416        // calculate how many bytes are in the current page
417        bytesLeft = curPrd.getByteCount() - bytesWritten;
418        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
419
420        // copy the data from memory into the data buffer
421        memcpy((void *)(dataBuffer + bytesWritten),
422               physmem->dma_addr(dmaAddr, bytesInPage),
423               bytesInPage);
424
425        curAddr += bytesInPage;
426        bytesWritten += bytesInPage;
427        cmdBytesLeft -= bytesInPage;
428    }
429
430    // write the data to the disk image
431    for (bytesWritten = 0;
432         bytesWritten < curPrd.getByteCount();
433         bytesWritten += SectorSize) {
434
435        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
436    }
437
438#if 0
439    // actually copy the data from memory to data buffer
440    Addr dmaAddr =
441        ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr());
442    memcpy((void *)dataBuffer,
443           physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
444           curPrd.getByteCount());
445
446    uint32_t bytesWritten = 0;
447
448    while (bytesWritten < curPrd.getByteCount()) {
449        if (cmdBytesLeft <= 0)
450            panic("DMA data is larger than # sectors specified\n");
451
452        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
453
454        bytesWritten += SectorSize;
455        cmdBytesLeft -= SectorSize;
456    }
457#endif
458
459    // check for the EOT
460    if (curPrd.getEOT()){
461        assert(cmdBytesLeft == 0);
462        dmaState = Dma_Idle;
463        updateState(ACT_DMA_DONE);
464    } else {
465        doDmaTransfer();
466    }
467}
468
469void
470IdeDisk::doDmaWrite()
471{
472    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
473
474    if (dmaInterface) {
475        if (dmaInterface->busy()) {
476            // reschedule after waiting period
477            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
478            return;
479        }
480
481        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
482
483        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
484                                              (uint32_t)curPrd.getByteCount());
485
486        dmaInterfaceBytes = bytesInPage;
487
488        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
489                            bytesInPage, curTick + totalDiskDelay,
490                            &dmaWriteEvent);
491    } else {
492        // schedule event with disk delay (dmaWriteDone)
493        dmaWriteEvent.schedule(curTick + totalDiskDelay);
494    }
495}
496
497void
498IdeDisk::dmaWriteDone()
499{
500    Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
501    uint32_t bytesRead = 0, bytesInPage = 0;
502
503    // continue to use the DMA interface until all pages are read
504    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
505        // see if the interface is busy
506        if (dmaInterface->busy()) {
507            // reschedule after waiting period
508            dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
509            return;
510        }
511
512        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
513        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
514        dmaAddr = pciToDma(curAddr);
515
516        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
517        dmaInterfaceBytes += bytesInPage;
518
519        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
520                            bytesInPage, curTick,
521                            &dmaWriteEvent);
522
523        return;
524    }
525
526    // setup the initial page and DMA address
527    curAddr = curPrd.getBaseAddr();
528    pageAddr = alpha_trunc_page(curAddr);
529    dmaAddr = pciToDma(curAddr);
530
531    // clear out the data buffer
532    memset(dataBuffer, 0, MAX_DMA_SIZE);
533
534    while (bytesRead < curPrd.getByteCount()) {
535        // see if we have crossed into a new page
536        if (pageAddr != alpha_trunc_page(curAddr)) {
537            // write the data to memory
538            memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
539                   (void *)(dataBuffer + (bytesRead - bytesInPage)),
540                   bytesInPage);
541
542            // update the DMA address and page address
543            pageAddr = alpha_trunc_page(curAddr);
544            dmaAddr = pciToDma(curAddr);
545
546            bytesInPage = 0;
547        }
548
549        if (cmdBytesLeft <= 0)
550            panic("DMA requested data is larger than # sectors specified\n");
551
552        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
553
554        curAddr += SectorSize;
555        bytesRead += SectorSize;
556        bytesInPage += SectorSize;
557        cmdBytesLeft -= SectorSize;
558    }
559
560    // write the last page worth read to memory
561    if (bytesInPage != 0) {
562        memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
563               (void *)(dataBuffer + (bytesRead - bytesInPage)),
564               bytesInPage);
565    }
566
567#if 0
568    Addr dmaAddr = ctrl->tsunami->pchip->
569        translatePciToDma(curPrd.getBaseAddr());
570
571    memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()),
572           (void *)dataBuffer, curPrd.getByteCount());
573#endif
574
575    // check for the EOT
576    if (curPrd.getEOT()) {
577        assert(cmdBytesLeft == 0);
578        dmaState = Dma_Idle;
579        updateState(ACT_DMA_DONE);
580    } else {
581        doDmaTransfer();
582    }
583}
584
585////
586// Disk utility routines
587///
588
589void
590IdeDisk::readDisk(uint32_t sector, uint8_t *data)
591{
592    uint32_t bytesRead = image->read(data, sector);
593
594    if (bytesRead != SectorSize)
595        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
596              name(), bytesRead, SectorSize, errno);
597}
598
599void
600IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
601{
602    uint32_t bytesWritten = image->write(data, sector);
603
604    if (bytesWritten != SectorSize)
605        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
606              name(), bytesWritten, SectorSize, errno);
607}
608
609////
610// Setup and handle commands
611////
612
613void
614IdeDisk::startDma(const uint32_t &prdTableBase)
615{
616    if (dmaState != Dma_Start)
617        panic("Inconsistent DMA state, should be in Dma_Start!\n");
618
619    if (devState != Transfer_Data_Dma)
620        panic("Inconsistent device state for DMA start!\n");
621
622    // PRD base address is given by bits 31:2
623    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
624
625    dmaState = Dma_Transfer;
626
627    // schedule dma transfer (doDmaTransfer)
628    dmaTransferEvent.schedule(curTick + 1);
629}
630
631void
632IdeDisk::abortDma()
633{
634    if (dmaState == Dma_Idle)
635        panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n");
636
637    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
638        panic("Inconsistent device state, should be in Transfer or Prepare!\n");
639
640    updateState(ACT_CMD_ERROR);
641}
642
643void
644IdeDisk::startCommand()
645{
646    DevAction_t action = ACT_NONE;
647    uint32_t size = 0;
648    dmaRead = false;
649
650    // Decode commands
651    switch (cmdReg.command) {
652        // Supported non-data commands
653      case WIN_READ_NATIVE_MAX:
654        size = image->size() - 1;
655        cmdReg.sec_num = (size & 0xff);
656        cmdReg.cyl_low = ((size & 0xff00) >> 8);
657        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
658        cmdReg.head = ((size & 0xf000000) >> 24);
659
660        devState = Command_Execution;
661        action = ACT_CMD_COMPLETE;
662        break;
663
664      case WIN_RECAL:
665      case WIN_SPECIFY:
666      case WIN_STANDBYNOW1:
667      case WIN_FLUSH_CACHE:
668      case WIN_VERIFY:
669      case WIN_SEEK:
670      case WIN_SETFEATURES:
671      case WIN_SETMULT:
672        devState = Command_Execution;
673        action = ACT_CMD_COMPLETE;
674        break;
675
676        // Supported PIO data-in commands
677      case WIN_IDENTIFY:
678        cmdBytes = cmdBytesLeft = sizeof(struct hd_driveid);
679        devState = Prepare_Data_In;
680        action = ACT_DATA_READY;
681        break;
682
683      case WIN_MULTREAD:
684      case WIN_READ:
685        if (!(cmdReg.drive & DRIVE_LBA_BIT))
686            panic("Attempt to perform CHS access, only supports LBA\n");
687
688        if (cmdReg.sec_count == 0)
689            cmdBytes = cmdBytesLeft = (256 * SectorSize);
690        else
691            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
692
693        curSector = getLBABase();
694
695        /** @todo make this a scheduled event to simulate disk delay */
696        devState = Prepare_Data_In;
697        action = ACT_DATA_READY;
698        break;
699
700        // Supported PIO data-out commands
701      case WIN_MULTWRITE:
702      case WIN_WRITE:
703        if (!(cmdReg.drive & DRIVE_LBA_BIT))
704            panic("Attempt to perform CHS access, only supports LBA\n");
705
706        if (cmdReg.sec_count == 0)
707            cmdBytes = cmdBytesLeft = (256 * SectorSize);
708        else
709            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
710
711        curSector = getLBABase();
712
713        devState = Prepare_Data_Out;
714        action = ACT_DATA_READY;
715        break;
716
717        // Supported DMA commands
718      case WIN_WRITEDMA:
719        dmaRead = true;  // a write to the disk is a DMA read from memory
720      case WIN_READDMA:
721        if (!(cmdReg.drive & DRIVE_LBA_BIT))
722            panic("Attempt to perform CHS access, only supports LBA\n");
723
724        if (cmdReg.sec_count == 0)
725            cmdBytes = cmdBytesLeft = (256 * SectorSize);
726        else
727            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
728
729        curSector = getLBABase();
730
731        devState = Prepare_Data_Dma;
732        action = ACT_DMA_READY;
733        break;
734
735      default:
736        panic("Unsupported ATA command: %#x\n", cmdReg.command);
737    }
738
739    if (action != ACT_NONE) {
740        // set the BSY bit
741        status |= STATUS_BSY_BIT;
742        // clear the DRQ bit
743        status &= ~STATUS_DRQ_BIT;
744        // clear the DF bit
745        status &= ~STATUS_DF_BIT;
746
747        updateState(action);
748    }
749}
750
751////
752// Handle setting and clearing interrupts
753////
754
755void
756IdeDisk::intrPost()
757{
758    if (intrPending)
759        panic("Attempt to post an interrupt with one pending\n");
760
761    intrPending = true;
762
763    // talk to controller to set interrupt
764    if (ctrl)
765        ctrl->intrPost();
766}
767
768void
769IdeDisk::intrClear()
770{
771    if (!intrPending)
772        panic("Attempt to clear a non-pending interrupt\n");
773
774    intrPending = false;
775
776    // talk to controller to clear interrupt
777    if (ctrl)
778        ctrl->intrClear();
779}
780
781////
782// Manage the device internal state machine
783////
784
785void
786IdeDisk::updateState(DevAction_t action)
787{
788    switch (devState) {
789      case Device_Srst:
790        if (action == ACT_SRST_SET) {
791            // set the BSY bit
792            status |= STATUS_BSY_BIT;
793        } else if (action == ACT_SRST_CLEAR) {
794            // clear the BSY bit
795            status &= ~STATUS_BSY_BIT;
796
797            // reset the device state
798            reset(devID);
799        }
800        break;
801
802      case Device_Idle_S:
803        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
804            devState = Device_Idle_NS;
805        } else if (action == ACT_CMD_WRITE) {
806            startCommand();
807        }
808
809        break;
810
811      case Device_Idle_SI:
812        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
813            devState = Device_Idle_NS;
814            intrClear();
815        } else if (action == ACT_STAT_READ || isIENSet()) {
816            devState = Device_Idle_S;
817            intrClear();
818        } else if (action == ACT_CMD_WRITE) {
819            intrClear();
820            startCommand();
821        }
822
823        break;
824
825      case Device_Idle_NS:
826        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
827            if (!isIENSet() && intrPending) {
828                devState = Device_Idle_SI;
829                intrPost();
830            }
831            if (isIENSet() || !intrPending) {
832                devState = Device_Idle_S;
833            }
834        }
835        break;
836
837      case Command_Execution:
838        if (action == ACT_CMD_COMPLETE) {
839            // clear the BSY bit
840            setComplete();
841
842            if (!isIENSet()) {
843                devState = Device_Idle_SI;
844                intrPost();
845            } else {
846                devState = Device_Idle_S;
847            }
848        }
849        break;
850
851      case Prepare_Data_In:
852        if (action == ACT_CMD_ERROR) {
853            // clear the BSY bit
854            setComplete();
855
856            if (!isIENSet()) {
857                devState = Device_Idle_SI;
858                intrPost();
859            } else {
860                devState = Device_Idle_S;
861            }
862        } else if (action == ACT_DATA_READY) {
863            // clear the BSY bit
864            status &= ~STATUS_BSY_BIT;
865            // set the DRQ bit
866            status |= STATUS_DRQ_BIT;
867
868            // copy the data into the data buffer
869            if (cmdReg.command == WIN_IDENTIFY) {
870                // Reset the drqBytes for this block
871                drqBytesLeft = sizeof(struct hd_driveid);
872
873                memcpy((void *)dataBuffer, (void *)&driveID,
874                       sizeof(struct hd_driveid));
875            } else {
876                // Reset the drqBytes for this block
877                drqBytesLeft = SectorSize;
878
879                readDisk(curSector++, dataBuffer);
880            }
881
882            // put the first two bytes into the data register
883            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
884                   sizeof(uint16_t));
885
886            if (!isIENSet()) {
887                devState = Data_Ready_INTRQ_In;
888                intrPost();
889            } else {
890                devState = Transfer_Data_In;
891            }
892        }
893        break;
894
895      case Data_Ready_INTRQ_In:
896        if (action == ACT_STAT_READ) {
897            devState = Transfer_Data_In;
898            intrClear();
899        }
900        break;
901
902      case Transfer_Data_In:
903        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
904            if (action == ACT_DATA_READ_BYTE) {
905                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
906            } else {
907                drqBytesLeft -= 2;
908                cmdBytesLeft -= 2;
909
910                // copy next short into data registers
911                if (drqBytesLeft)
912                    memcpy((void *)&cmdReg.data0,
913                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
914                           sizeof(uint16_t));
915            }
916
917            if (drqBytesLeft == 0) {
918                if (cmdBytesLeft == 0) {
919                    // Clear the BSY bit
920                    setComplete();
921                    devState = Device_Idle_S;
922                } else {
923                    devState = Prepare_Data_In;
924                    // set the BSY_BIT
925                    status |= STATUS_BSY_BIT;
926                    // clear the DRQ_BIT
927                    status &= ~STATUS_DRQ_BIT;
928
929                    /** @todo change this to a scheduled event to simulate
930                        disk delay */
931                    updateState(ACT_DATA_READY);
932                }
933            }
934        }
935        break;
936
937      case Prepare_Data_Out:
938        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
939            // clear the BSY bit
940            setComplete();
941
942            if (!isIENSet()) {
943                devState = Device_Idle_SI;
944                intrPost();
945            } else {
946                devState = Device_Idle_S;
947            }
948        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
949            // clear the BSY bit
950            status &= ~STATUS_BSY_BIT;
951            // set the DRQ bit
952            status |= STATUS_DRQ_BIT;
953
954            // clear the data buffer to get it ready for writes
955            memset(dataBuffer, 0, MAX_DMA_SIZE);
956
957            // reset the drqBytes for this block
958            drqBytesLeft = SectorSize;
959
960            if (cmdBytesLeft == cmdBytes || isIENSet()) {
961                devState = Transfer_Data_Out;
962            } else {
963                devState = Data_Ready_INTRQ_Out;
964                intrPost();
965            }
966        }
967        break;
968
969      case Data_Ready_INTRQ_Out:
970        if (action == ACT_STAT_READ) {
971            devState = Transfer_Data_Out;
972            intrClear();
973        }
974        break;
975
976      case Transfer_Data_Out:
977        if (action == ACT_DATA_WRITE_BYTE ||
978            action == ACT_DATA_WRITE_SHORT) {
979
980            if (action == ACT_DATA_READ_BYTE) {
981                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
982            } else {
983                // copy the latest short into the data buffer
984                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
985                       (void *)&cmdReg.data0,
986                       sizeof(uint16_t));
987
988                drqBytesLeft -= 2;
989                cmdBytesLeft -= 2;
990            }
991
992            if (drqBytesLeft == 0) {
993                // copy the block to the disk
994                writeDisk(curSector++, dataBuffer);
995
996                // set the BSY bit
997                status |= STATUS_BSY_BIT;
998                // set the seek bit
999                status |= STATUS_SEEK_BIT;
1000                // clear the DRQ bit
1001                status &= ~STATUS_DRQ_BIT;
1002
1003                devState = Prepare_Data_Out;
1004
1005                /** @todo change this to a scheduled event to simulate
1006                    disk delay */
1007                updateState(ACT_DATA_READY);
1008            }
1009        }
1010        break;
1011
1012      case Prepare_Data_Dma:
1013        if (action == ACT_CMD_ERROR) {
1014            // clear the BSY bit
1015            setComplete();
1016
1017            if (!isIENSet()) {
1018                devState = Device_Idle_SI;
1019                intrPost();
1020            } else {
1021                devState = Device_Idle_S;
1022            }
1023        } else if (action == ACT_DMA_READY) {
1024            // clear the BSY bit
1025            status &= ~STATUS_BSY_BIT;
1026            // set the DRQ bit
1027            status |= STATUS_DRQ_BIT;
1028
1029            devState = Transfer_Data_Dma;
1030
1031            if (dmaState != Dma_Idle)
1032                panic("Inconsistent DMA state, should be Dma_Idle\n");
1033
1034            dmaState = Dma_Start;
1035            // wait for the write to the DMA start bit
1036        }
1037        break;
1038
1039      case Transfer_Data_Dma:
1040        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
1041            // clear the BSY bit
1042            setComplete();
1043            // set the seek bit
1044            status |= STATUS_SEEK_BIT;
1045            // clear the controller state for DMA transfer
1046            ctrl->setDmaComplete(this);
1047
1048            if (!isIENSet()) {
1049                devState = Device_Idle_SI;
1050                intrPost();
1051            } else {
1052                devState = Device_Idle_S;
1053            }
1054        }
1055        break;
1056
1057      default:
1058        panic("Unknown IDE device state: %#x\n", devState);
1059    }
1060}
1061
1062void
1063IdeDisk::serialize(ostream &os)
1064{
1065    // Check all outstanding events to see if they are scheduled
1066    // these are all mutually exclusive
1067    Tick reschedule = 0;
1068    Events_t event = None;
1069
1070    if (dmaTransferEvent.scheduled()) {
1071        reschedule = dmaTransferEvent.when();
1072        event = Transfer;
1073    } else if (dmaReadWaitEvent.scheduled()) {
1074        reschedule = dmaReadWaitEvent.when();
1075        event = ReadWait;
1076    } else if (dmaWriteWaitEvent.scheduled()) {
1077        reschedule = dmaWriteWaitEvent.when();
1078        event = WriteWait;
1079    } else if (dmaPrdReadEvent.scheduled()) {
1080        reschedule = dmaPrdReadEvent.when();
1081        event = PrdRead;
1082    } else if (dmaReadEvent.scheduled()) {
1083        reschedule = dmaReadEvent.when();
1084        event = DmaRead;
1085    } else if (dmaWriteEvent.scheduled()) {
1086        reschedule = dmaWriteEvent.when();
1087        event = DmaWrite;
1088    }
1089
1090    SERIALIZE_SCALAR(reschedule);
1091    SERIALIZE_ENUM(event);
1092
1093    // Serialize device registers
1094    SERIALIZE_SCALAR(cmdReg.data0);
1095    SERIALIZE_SCALAR(cmdReg.data1);
1096    SERIALIZE_SCALAR(cmdReg.sec_count);
1097    SERIALIZE_SCALAR(cmdReg.sec_num);
1098    SERIALIZE_SCALAR(cmdReg.cyl_low);
1099    SERIALIZE_SCALAR(cmdReg.cyl_high);
1100    SERIALIZE_SCALAR(cmdReg.drive);
1101    SERIALIZE_SCALAR(status);
1102    SERIALIZE_SCALAR(nIENBit);
1103    SERIALIZE_SCALAR(devID);
1104
1105    // Serialize the PRD related information
1106    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1107    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1108    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1109    SERIALIZE_SCALAR(curPrdAddr);
1110
1111    // Serialize current transfer related information
1112    SERIALIZE_SCALAR(cmdBytesLeft);
1113    SERIALIZE_SCALAR(cmdBytes);
1114    SERIALIZE_SCALAR(drqBytesLeft);
1115    SERIALIZE_SCALAR(curSector);
1116    SERIALIZE_SCALAR(dmaRead);
1117    SERIALIZE_SCALAR(dmaInterfaceBytes);
1118    SERIALIZE_SCALAR(intrPending);
1119    SERIALIZE_ENUM(devState);
1120    SERIALIZE_ENUM(dmaState);
1121    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1122}
1123
1124void
1125IdeDisk::unserialize(Checkpoint *cp, const string &section)
1126{
1127    // Reschedule events that were outstanding
1128    // these are all mutually exclusive
1129    Tick reschedule = 0;
1130    Events_t event = None;
1131
1132    UNSERIALIZE_SCALAR(reschedule);
1133    UNSERIALIZE_ENUM(event);
1134
1135    switch (event) {
1136      case None : break;
1137      case Transfer : dmaTransferEvent.schedule(reschedule); break;
1138      case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
1139      case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
1140      case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
1141      case DmaRead : dmaReadEvent.schedule(reschedule); break;
1142      case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
1143    }
1144
1145    // Unserialize device registers
1146    UNSERIALIZE_SCALAR(cmdReg.data0);
1147    UNSERIALIZE_SCALAR(cmdReg.data1);
1148    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1149    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1150    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1151    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1152    UNSERIALIZE_SCALAR(cmdReg.drive);
1153    UNSERIALIZE_SCALAR(status);
1154    UNSERIALIZE_SCALAR(nIENBit);
1155    UNSERIALIZE_SCALAR(devID);
1156
1157    // Unserialize the PRD related information
1158    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1159    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1160    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1161    UNSERIALIZE_SCALAR(curPrdAddr);
1162
1163    // Unserialize current transfer related information
1164    UNSERIALIZE_SCALAR(cmdBytes);
1165    UNSERIALIZE_SCALAR(cmdBytesLeft);
1166    UNSERIALIZE_SCALAR(drqBytesLeft);
1167    UNSERIALIZE_SCALAR(curSector);
1168    UNSERIALIZE_SCALAR(dmaRead);
1169    UNSERIALIZE_SCALAR(dmaInterfaceBytes);
1170    UNSERIALIZE_SCALAR(intrPending);
1171    UNSERIALIZE_ENUM(devState);
1172    UNSERIALIZE_ENUM(dmaState);
1173    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1174}
1175
1176#ifndef DOXYGEN_SHOULD_SKIP_THIS
1177
1178BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1179
1180    SimObjectParam<DiskImage *> image;
1181    SimObjectParam<PhysicalMemory *> physmem;
1182    Param<int> driveID;
1183    Param<int> disk_delay;
1184
1185END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1186
1187BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1188
1189    INIT_PARAM(image, "Disk image"),
1190    INIT_PARAM(physmem, "Physical memory"),
1191    INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"),
1192    INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1)
1193
1194END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1195
1196
1197CREATE_SIM_OBJECT(IdeDisk)
1198{
1199    return new IdeDisk(getInstanceName(), image, physmem, driveID,
1200                       disk_delay);
1201}
1202
1203REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1204
1205#endif //DOXYGEN_SHOULD_SKIP_THIS
1206