ide_disk.cc revision 989
12SN/A/*
22188SN/A * Copyright (c) 2004 The Regents of The University of Michigan
32SN/A * All rights reserved.
42SN/A *
52SN/A * Redistribution and use in source and binary forms, with or without
62SN/A * modification, are permitted provided that the following conditions are
72SN/A * met: redistributions of source code must retain the above copyright
82SN/A * notice, this list of conditions and the following disclaimer;
92SN/A * redistributions in binary form must reproduce the above copyright
102SN/A * notice, this list of conditions and the following disclaimer in the
112SN/A * documentation and/or other materials provided with the distribution;
122SN/A * neither the name of the copyright holders nor the names of its
132SN/A * contributors may be used to endorse or promote products derived from
142SN/A * this software without specific prior written permission.
152SN/A *
162SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
172SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
182SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
192SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
202SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272665SN/A */
282665SN/A
292665SN/A/** @file
302SN/A * Device model implementation for an IDE disk
312SN/A */
322683Sktlim@umich.edu
332683Sktlim@umich.edu#include <cerrno>
342SN/A#include <cstring>
356313Sgblack@eecs.umich.edu#include <deque>
362190SN/A#include <string>
373776Sgblack@eecs.umich.edu
384997Sgblack@eecs.umich.edu#include "arch/alpha/pmap.h"
396216Snate@binkert.org#include "base/cprintf.hh" // csprintf
401858SN/A#include "base/trace.hh"
412680SN/A#include "dev/disk_image.hh"
422683Sktlim@umich.edu#include "dev/ide_disk.hh"
432395SN/A#include "dev/ide_ctrl.hh"
442190SN/A#include "dev/tsunami.hh"
452188SN/A#include "dev/tsunami_pchip.hh"
46217SN/A#include "mem/functional_mem/physical_memory.hh"
472SN/A#include "mem/bus/bus.hh"
482SN/A#include "mem/bus/dma_interface.hh"
492SN/A#include "mem/bus/pio_interface.hh"
501858SN/A#include "mem/bus/pio_interface_impl.hh"
512SN/A#include "sim/builder.hh"
521070SN/A#include "sim/sim_object.hh"
531070SN/A#include "sim/universe.hh"
541917SN/A
551917SN/Ausing namespace std;
562521SN/A
572521SN/AIdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
582521SN/A                 int id, int delay)
593548Sgblack@eecs.umich.edu    : SimObject(name), ctrl(NULL), image(img), physmem(phys),
603548Sgblack@eecs.umich.edu      dmaTransferEvent(this), dmaReadWaitEvent(this),
613548Sgblack@eecs.umich.edu      dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
623548Sgblack@eecs.umich.edu      dmaReadEvent(this), dmaWriteEvent(this)
632330SN/A{
642330SN/A    // Reset the device state
652SN/A    reset(id);
662SN/A
67360SN/A    // calculate disk delay in microseconds
682462SN/A    diskDelay = (delay * ticksPerSecond / 100000);
692420SN/A
702SN/A    // fill out the drive ID structure
712SN/A    memset(&driveID, 0, sizeof(struct hd_driveid));
722SN/A
732683Sktlim@umich.edu    // Calculate LBA and C/H/S values
742683Sktlim@umich.edu    uint16_t cylinders;
752683Sktlim@umich.edu    uint8_t heads;
762683Sktlim@umich.edu    uint8_t sectors;
772683Sktlim@umich.edu
782683Sktlim@umich.edu    uint32_t lba_size = image->size();
792683Sktlim@umich.edu    if (lba_size >= 16383*16*63) {
802683Sktlim@umich.edu        cylinders = 16383;
812683Sktlim@umich.edu        heads = 16;
822683Sktlim@umich.edu        sectors = 63;
832683Sktlim@umich.edu    } else {
842683Sktlim@umich.edu        if (lba_size >= 63)
852683Sktlim@umich.edu            sectors = 63;
862683Sktlim@umich.edu        else
872683Sktlim@umich.edu            sectors = lba_size;
882SN/A
892683Sktlim@umich.edu        if ((lba_size / sectors) >= 16)
902SN/A            heads = 16;
912107SN/A        else
922107SN/A            heads = (lba_size / sectors);
932107SN/A
942159SN/A        cylinders = lba_size / (heads * sectors);
952455SN/A    }
962455SN/A
972SN/A    // Setup the model name
982680SN/A    sprintf((char *)driveID.model, "5MI EDD si k");
992SN/A    // Set the maximum multisector transfer size
1002190SN/A    driveID.max_multsect = MAX_MULTSECT;
1015543Ssaidi@eecs.umich.edu    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
1026315Sgblack@eecs.umich.edu    driveID.capability = 0x7;
1036315Sgblack@eecs.umich.edu    // UDMA support, EIDE support
1046315Sgblack@eecs.umich.edu    driveID.field_valid = 0x6;
1056315Sgblack@eecs.umich.edu    // Setup default C/H/S settings
1066313Sgblack@eecs.umich.edu    driveID.cyls = cylinders;
1072SN/A    driveID.sectors = sectors;
1082190SN/A    driveID.heads = heads;
1092683Sktlim@umich.edu    // Setup the current multisector transfer size
1102SN/A    driveID.multsect = MAX_MULTSECT;
1112SN/A    driveID.multsect_valid = 0x1;
1122683Sktlim@umich.edu    // Number of sectors on disk
1132188SN/A    driveID.lba_capacity = lba_size;
1142378SN/A    // Multiword DMA mode 2 and below supported
1152400SN/A    driveID.dma_mword = 0x400;
1166022Sgblack@eecs.umich.edu    // Set PIO mode 4 and 3 supported
1176022Sgblack@eecs.umich.edu    driveID.eide_pio_modes = 0x3;
1182SN/A    // Set DMA mode 4 and below supported
1192683Sktlim@umich.edu    driveID.dma_ultra = 0x10;
1201858SN/A    // Statically set hardware config word
1212683Sktlim@umich.edu    driveID.hw_config = 0x4001;
1226022Sgblack@eecs.umich.edu}
1232683Sktlim@umich.edu
1242SN/AIdeDisk::~IdeDisk()
1254997Sgblack@eecs.umich.edu{
1266022Sgblack@eecs.umich.edu    // destroy the data buffer
1272SN/A    delete [] dataBuffer;
1282862Sktlim@umich.edu}
1292864Sktlim@umich.edu
1302862Sktlim@umich.eduvoid
1312683Sktlim@umich.eduIdeDisk::reset(int id)
1322SN/A{
1332680SN/A    // initialize the data buffer and shadow registers
134180SN/A    dataBuffer = new uint8_t[MAX_DMA_SIZE];
1352SN/A
1362SN/A    memset(dataBuffer, 0, MAX_DMA_SIZE);
1372864Sktlim@umich.edu    memset(&cmdReg, 0, sizeof(CommandReg_t));
1382864Sktlim@umich.edu    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
1392862Sktlim@umich.edu
1402862Sktlim@umich.edu    dmaInterfaceBytes = 0;
141217SN/A    curPrdAddr = 0;
142237SN/A    curSector = 0;
143217SN/A    cmdBytes = 0;
1442683Sktlim@umich.edu    cmdBytesLeft = 0;
1452683Sktlim@umich.edu    drqBytesLeft = 0;
1465891Sgblack@eecs.umich.edu    dmaRead = false;
1472683Sktlim@umich.edu    intrPending = false;
1482190SN/A
1492683Sktlim@umich.edu    // set the device state to idle
1502683Sktlim@umich.edu    dmaState = Dma_Idle;
1512683Sktlim@umich.edu
1522683Sktlim@umich.edu    if (id == DEV0) {
1532680SN/A        devState = Device_Idle_S;
1542190SN/A        devID = DEV0;
1555358Sgblack@eecs.umich.edu    } else if (id == DEV1) {
1565358Sgblack@eecs.umich.edu        devState = Device_Idle_NS;
1575358Sgblack@eecs.umich.edu        devID = DEV1;
1585358Sgblack@eecs.umich.edu    } else {
1595358Sgblack@eecs.umich.edu        panic("Invalid device ID: %#x\n", id);
1605358Sgblack@eecs.umich.edu    }
1615358Sgblack@eecs.umich.edu
1625358Sgblack@eecs.umich.edu    // set the device ready bit
1635358Sgblack@eecs.umich.edu    status = STATUS_DRDY_BIT;
1645358Sgblack@eecs.umich.edu}
1655358Sgblack@eecs.umich.edu
1665358Sgblack@eecs.umich.edu////
1675358Sgblack@eecs.umich.edu// Utility functions
1685358Sgblack@eecs.umich.edu////
1695358Sgblack@eecs.umich.edu
1705358Sgblack@eecs.umich.edubool
1714997Sgblack@eecs.umich.eduIdeDisk::isDEVSelect()
1726313Sgblack@eecs.umich.edu{
1736313Sgblack@eecs.umich.edu    return ctrl->isDiskSelected(this);
1744997Sgblack@eecs.umich.edu}
1752683Sktlim@umich.edu
1762521SN/AAddr
1775702Ssaidi@eecs.umich.eduIdeDisk::pciToDma(Addr pciAddr)
1785702Ssaidi@eecs.umich.edu{
1795702Ssaidi@eecs.umich.edu    if (ctrl)
1805702Ssaidi@eecs.umich.edu        return ctrl->tsunami->pchip->translatePciToDma(pciAddr);
1812683Sktlim@umich.edu    else
1822SN/A        panic("Access to unset controller!\n");
1832683Sktlim@umich.edu}
1842683Sktlim@umich.edu
1852683Sktlim@umich.eduuint32_t
1862683Sktlim@umich.eduIdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft)
1872683Sktlim@umich.edu{
1882683Sktlim@umich.edu    uint32_t bytesInPage = 0;
1896022Sgblack@eecs.umich.edu
1902683Sktlim@umich.edu    // First calculate how many bytes could be in the page
1916022Sgblack@eecs.umich.edu    if (bytesLeft > ALPHA_PGBYTES)
1922683Sktlim@umich.edu        bytesInPage = ALPHA_PGBYTES;
1934997Sgblack@eecs.umich.edu    else
1944997Sgblack@eecs.umich.edu        bytesInPage = bytesLeft;
1955803Snate@binkert.org
1962683Sktlim@umich.edu    // Next, see if we have crossed a page boundary, and adjust
1972683Sktlim@umich.edu    Addr upperBound = curAddr + bytesInPage;
1985499Ssaidi@eecs.umich.edu    Addr pageBound = alpha_trunc_page(curAddr) + ALPHA_PGBYTES;
1995499Ssaidi@eecs.umich.edu
2005499Ssaidi@eecs.umich.edu    assert(upperBound >= curAddr && "DMA read wraps around address space!\n");
2015499Ssaidi@eecs.umich.edu
2025499Ssaidi@eecs.umich.edu    if (upperBound >= pageBound)
2032SN/A        bytesInPage = pageBound - curAddr;
2042SN/A
2052683Sktlim@umich.edu    return bytesInPage;
2062683Sktlim@umich.edu}
2072683Sktlim@umich.edu
2082683Sktlim@umich.edu////
2092683Sktlim@umich.edu// Device registers read/write
2102683Sktlim@umich.edu////
2112683Sktlim@umich.edu
2122683Sktlim@umich.eduvoid
2132683Sktlim@umich.eduIdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
2142683Sktlim@umich.edu{
2152683Sktlim@umich.edu    DevAction_t action = ACT_NONE;
2162683Sktlim@umich.edu
2172683Sktlim@umich.edu    if (cmdBlk) {
2182683Sktlim@umich.edu        if (offset < 0 || offset > sizeof(CommandReg_t))
2192SN/A            panic("Invalid disk command register offset: %#x\n", offset);
2202SN/A
2212532SN/A        if (!byte && offset != DATA_OFFSET)
222716SN/A            panic("Invalid 16-bit read, only allowed on data reg\n");
2232378SN/A
2242378SN/A        if (!byte)
2252423SN/A            *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
226716SN/A        else
227716SN/A            *data = ((uint8_t *)&cmdReg)[offset];
2282683Sktlim@umich.edu
2292190SN/A        // determine if an action needs to be taken on the state machine
2306315Sgblack@eecs.umich.edu        if (offset == STATUS_OFFSET) {
2316315Sgblack@eecs.umich.edu            action = ACT_STAT_READ;
2326315Sgblack@eecs.umich.edu            *data = status; // status is in a shadow, explicity copy
2336315Sgblack@eecs.umich.edu        } else if (offset == DATA_OFFSET) {
2346315Sgblack@eecs.umich.edu            if (byte)
2352190SN/A                action = ACT_DATA_READ_BYTE;
2362SN/A            else
2372SN/A                action = ACT_DATA_READ_SHORT;
2382SN/A        }
2392SN/A
2402SN/A    } else {
2416313Sgblack@eecs.umich.edu        if (offset != ALTSTAT_OFFSET)
2425082Sgblack@eecs.umich.edu            panic("Invalid disk control register offset: %#x\n", offset);
2432SN/A
2442SN/A        if (!byte)
2452455SN/A            panic("Invalid 16-bit read from control block\n");
2462SN/A
2476313Sgblack@eecs.umich.edu        *data = status;
2486315Sgblack@eecs.umich.edu    }
2492SN/A
2502SN/A    if (action != ACT_NONE)
2512455SN/A        updateState(action);
2522455SN/A}
2536313Sgblack@eecs.umich.edu
2546315Sgblack@eecs.umich.eduvoid
2552SN/AIdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
2562SN/A{
2572SN/A    DevAction_t action = ACT_NONE;
2582SN/A
2596313Sgblack@eecs.umich.edu    if (cmdBlk) {
2605082Sgblack@eecs.umich.edu        if (offset < 0 || offset > sizeof(CommandReg_t))
2612SN/A            panic("Invalid disk command register offset: %#x\n", offset);
2622SN/A
2632455SN/A        if (!byte && offset != DATA_OFFSET)
2642SN/A            panic("Invalid 16-bit write, only allowed on data reg\n");
2656313Sgblack@eecs.umich.edu
2666315Sgblack@eecs.umich.edu        if (!byte)
2672SN/A            *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
2682SN/A        else
2692455SN/A            ((uint8_t *)&cmdReg)[offset] = *data;
2702455SN/A
2716313Sgblack@eecs.umich.edu        // determine if an action needs to be taken on the state machine
2726315Sgblack@eecs.umich.edu        if (offset == COMMAND_OFFSET) {
2732SN/A            action = ACT_CMD_WRITE;
2742SN/A        } else if (offset == DATA_OFFSET) {
2752SN/A            if (byte)
2762SN/A                action = ACT_DATA_WRITE_BYTE;
2772525SN/A            else
2782SN/A                action = ACT_DATA_WRITE_SHORT;
2792SN/A        } else if (offset == SELECT_OFFSET) {
2802190SN/A            action = ACT_SELECT_WRITE;
2812190SN/A        }
2822525SN/A
2832190SN/A    } else {
2842190SN/A        if (offset != CONTROL_OFFSET)
2853276Sgblack@eecs.umich.edu            panic("Invalid disk control register offset: %#x\n", offset);
2863276Sgblack@eecs.umich.edu
2873276Sgblack@eecs.umich.edu        if (!byte)
2883276Sgblack@eecs.umich.edu            panic("Invalid 16-bit write to control block\n");
2893276Sgblack@eecs.umich.edu
2903276Sgblack@eecs.umich.edu        if (*data & CONTROL_RST_BIT) {
2913276Sgblack@eecs.umich.edu            // force the device into the reset state
2923276Sgblack@eecs.umich.edu            devState = Device_Srst;
2933276Sgblack@eecs.umich.edu            action = ACT_SRST_SET;
2943276Sgblack@eecs.umich.edu        } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
2952190SN/A            action = ACT_SRST_CLEAR;
2962190SN/A        }
2972525SN/A
2982190SN/A        nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
2992190SN/A    }
3002SN/A
3012SN/A    if (action != ACT_NONE)
3022525SN/A        updateState(action);
3032SN/A}
3042SN/A
3053276Sgblack@eecs.umich.edu////
3063276Sgblack@eecs.umich.edu// Perform DMA transactions
3073276Sgblack@eecs.umich.edu////
3083276Sgblack@eecs.umich.edu
3093276Sgblack@eecs.umich.eduvoid
3103276Sgblack@eecs.umich.eduIdeDisk::doDmaTransfer()
3113276Sgblack@eecs.umich.edu{
3123276Sgblack@eecs.umich.edu    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
3133276Sgblack@eecs.umich.edu        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
3143276Sgblack@eecs.umich.edu              dmaState, devState);
3152252SN/A
3162252SN/A    // first read the current PRD
3172525SN/A    if (dmaInterface) {
3182252SN/A        if (dmaInterface->busy()) {
3192252SN/A            // reschedule after waiting period
3202251SN/A            dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
3212251SN/A            return;
3222525SN/A        }
3232251SN/A
3242251SN/A        dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
3256221Snate@binkert.org                            &dmaPrdReadEvent);
3266221Snate@binkert.org    } else {
3274172Ssaidi@eecs.umich.edu        dmaPrdReadDone();
3286313Sgblack@eecs.umich.edu    }
3294172Ssaidi@eecs.umich.edu}
3304172Ssaidi@eecs.umich.edu
3316221Snate@binkert.orgvoid
3326221Snate@binkert.orgIdeDisk::dmaPrdReadDone()
3332SN/A{
3346313Sgblack@eecs.umich.edu    // actually copy the PRD from physical memory
3352SN/A    memcpy((void *)&curPrd.entry,
3362SN/A           physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
3376221Snate@binkert.org           sizeof(PrdEntry_t));
3386221Snate@binkert.org
3392SN/A    DPRINTF(IdeDisk, "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
3406313Sgblack@eecs.umich.edu            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
3412SN/A            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
3422SN/A            curPrd.getEOT(), curSector);
3436221Snate@binkert.org
3446221Snate@binkert.org    // the prd pointer has already been translated, so just do an increment
3452SN/A    curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
3466313Sgblack@eecs.umich.edu
3476313Sgblack@eecs.umich.edu    if (dmaRead)
3486313Sgblack@eecs.umich.edu        doDmaRead();
3496313Sgblack@eecs.umich.edu    else
3506313Sgblack@eecs.umich.edu        doDmaWrite();
3516313Sgblack@eecs.umich.edu}
3526313Sgblack@eecs.umich.edu
3536313Sgblack@eecs.umich.eduvoid
3546313Sgblack@eecs.umich.eduIdeDisk::doDmaRead()
3556313Sgblack@eecs.umich.edu{
3566313Sgblack@eecs.umich.edu    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
3576313Sgblack@eecs.umich.edu
3586313Sgblack@eecs.umich.edu    if (dmaInterface) {
3592SN/A        if (dmaInterface->busy()) {
3602SN/A            // reschedule after waiting period
3612190SN/A            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
3622190SN/A            return;
3632190SN/A        }
3642190SN/A
3652190SN/A        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
3661858SN/A
3672561SN/A        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
3682SN/A                                              (uint32_t)curPrd.getByteCount());
3692680SN/A
3702SN/A        dmaInterfaceBytes = bytesInPage;
3712SN/A
3722SN/A        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
3732SN/A                            curTick + totalDiskDelay, &dmaReadEvent);
3742SN/A    } else {
3752SN/A        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
3762SN/A        dmaReadEvent.schedule(curTick + totalDiskDelay);
3772683Sktlim@umich.edu    }
3782SN/A}
3792SN/A
3802SN/Avoid
3812SN/AIdeDisk::dmaReadDone()
3822190SN/A{
383
384    Addr curAddr = 0, dmaAddr = 0;
385    uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
386
387    // continue to use the DMA interface until all pages are read
388    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
389        // see if the interface is busy
390        if (dmaInterface->busy()) {
391            // reschedule after waiting period
392            dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
393            return;
394        }
395
396        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
397        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
398        dmaAddr = pciToDma(curAddr);
399
400        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
401        dmaInterfaceBytes += bytesInPage;
402
403        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
404                            curTick, &dmaReadEvent);
405
406        return;
407    }
408
409    // set initial address
410    curAddr = curPrd.getBaseAddr();
411
412    // clear out the data buffer
413    memset(dataBuffer, 0, MAX_DMA_SIZE);
414
415    // read the data from memory via DMA into a data buffer
416    while (bytesWritten < curPrd.getByteCount()) {
417        if (cmdBytesLeft <= 0)
418            panic("DMA data is larger than # of sectors specified\n");
419
420        dmaAddr = pciToDma(curAddr);
421
422        // calculate how many bytes are in the current page
423        bytesLeft = curPrd.getByteCount() - bytesWritten;
424        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
425
426        // copy the data from memory into the data buffer
427        memcpy((void *)(dataBuffer + bytesWritten),
428               physmem->dma_addr(dmaAddr, bytesInPage),
429               bytesInPage);
430
431        curAddr += bytesInPage;
432        bytesWritten += bytesInPage;
433        cmdBytesLeft -= bytesInPage;
434    }
435
436    // write the data to the disk image
437    for (bytesWritten = 0;
438         bytesWritten < curPrd.getByteCount();
439         bytesWritten += SectorSize) {
440
441        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
442    }
443
444    // check for the EOT
445    if (curPrd.getEOT()) {
446        assert(cmdBytesLeft == 0);
447        dmaState = Dma_Idle;
448        updateState(ACT_DMA_DONE);
449    } else {
450        doDmaTransfer();
451    }
452}
453
454void
455IdeDisk::doDmaWrite()
456{
457    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
458
459    if (dmaInterface) {
460        if (dmaInterface->busy()) {
461            // reschedule after waiting period
462            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
463            return;
464        }
465
466        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
467
468        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
469                                              (uint32_t)curPrd.getByteCount());
470
471        dmaInterfaceBytes = bytesInPage;
472
473        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
474                            bytesInPage, curTick + totalDiskDelay,
475                            &dmaWriteEvent);
476    } else {
477        // schedule event with disk delay (dmaWriteDone)
478        dmaWriteEvent.schedule(curTick + totalDiskDelay);
479    }
480}
481
482void
483IdeDisk::dmaWriteDone()
484{
485    Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
486    uint32_t bytesRead = 0, bytesInPage = 0;
487
488    // continue to use the DMA interface until all pages are read
489    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
490        // see if the interface is busy
491        if (dmaInterface->busy()) {
492            // reschedule after waiting period
493            dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
494            return;
495        }
496
497        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
498        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
499        dmaAddr = pciToDma(curAddr);
500
501        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
502        dmaInterfaceBytes += bytesInPage;
503
504        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
505                            bytesInPage, curTick,
506                            &dmaWriteEvent);
507
508        return;
509    }
510
511    // setup the initial page and DMA address
512    curAddr = curPrd.getBaseAddr();
513    pageAddr = alpha_trunc_page(curAddr);
514    dmaAddr = pciToDma(curAddr);
515
516    // clear out the data buffer
517    memset(dataBuffer, 0, MAX_DMA_SIZE);
518
519    while (bytesRead < curPrd.getByteCount()) {
520        // see if we have crossed into a new page
521        if (pageAddr != alpha_trunc_page(curAddr)) {
522            // write the data to memory
523            memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
524                   (void *)(dataBuffer + (bytesRead - bytesInPage)),
525                   bytesInPage);
526
527            // update the DMA address and page address
528            pageAddr = alpha_trunc_page(curAddr);
529            dmaAddr = pciToDma(curAddr);
530
531            bytesInPage = 0;
532        }
533
534        if (cmdBytesLeft <= 0)
535            panic("DMA requested data is larger than # sectors specified\n");
536
537        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
538
539        curAddr += SectorSize;
540        bytesRead += SectorSize;
541        bytesInPage += SectorSize;
542        cmdBytesLeft -= SectorSize;
543    }
544
545    // write the last page worth read to memory
546    if (bytesInPage != 0) {
547        memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
548               (void *)(dataBuffer + (bytesRead - bytesInPage)),
549               bytesInPage);
550    }
551
552    // check for the EOT
553    if (curPrd.getEOT()) {
554        assert(cmdBytesLeft == 0);
555        dmaState = Dma_Idle;
556        updateState(ACT_DMA_DONE);
557    } else {
558        doDmaTransfer();
559    }
560}
561
562////
563// Disk utility routines
564///
565
566void
567IdeDisk::readDisk(uint32_t sector, uint8_t *data)
568{
569    uint32_t bytesRead = image->read(data, sector);
570
571    if (bytesRead != SectorSize)
572        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
573              name(), bytesRead, SectorSize, errno);
574}
575
576void
577IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
578{
579    uint32_t bytesWritten = image->write(data, sector);
580
581    if (bytesWritten != SectorSize)
582        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
583              name(), bytesWritten, SectorSize, errno);
584}
585
586////
587// Setup and handle commands
588////
589
590void
591IdeDisk::startDma(const uint32_t &prdTableBase)
592{
593    if (dmaState != Dma_Start)
594        panic("Inconsistent DMA state, should be in Dma_Start!\n");
595
596    if (devState != Transfer_Data_Dma)
597        panic("Inconsistent device state for DMA start!\n");
598
599    // PRD base address is given by bits 31:2
600    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
601
602    dmaState = Dma_Transfer;
603
604    // schedule dma transfer (doDmaTransfer)
605    dmaTransferEvent.schedule(curTick + 1);
606}
607
608void
609IdeDisk::abortDma()
610{
611    if (dmaState == Dma_Idle)
612        panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n");
613
614    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
615        panic("Inconsistent device state, should be in Transfer or Prepare!\n");
616
617    updateState(ACT_CMD_ERROR);
618}
619
620void
621IdeDisk::startCommand()
622{
623    DevAction_t action = ACT_NONE;
624    uint32_t size = 0;
625    dmaRead = false;
626
627    // Decode commands
628    switch (cmdReg.command) {
629        // Supported non-data commands
630      case WIN_READ_NATIVE_MAX:
631        size = image->size() - 1;
632        cmdReg.sec_num = (size & 0xff);
633        cmdReg.cyl_low = ((size & 0xff00) >> 8);
634        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
635        cmdReg.head = ((size & 0xf000000) >> 24);
636
637        devState = Command_Execution;
638        action = ACT_CMD_COMPLETE;
639        break;
640
641      case WIN_RECAL:
642      case WIN_SPECIFY:
643      case WIN_STANDBYNOW1:
644      case WIN_FLUSH_CACHE:
645      case WIN_VERIFY:
646      case WIN_SEEK:
647      case WIN_SETFEATURES:
648      case WIN_SETMULT:
649        devState = Command_Execution;
650        action = ACT_CMD_COMPLETE;
651        break;
652
653        // Supported PIO data-in commands
654      case WIN_IDENTIFY:
655        cmdBytes = cmdBytesLeft = sizeof(struct hd_driveid);
656        devState = Prepare_Data_In;
657        action = ACT_DATA_READY;
658        break;
659
660      case WIN_MULTREAD:
661      case WIN_READ:
662        if (!(cmdReg.drive & DRIVE_LBA_BIT))
663            panic("Attempt to perform CHS access, only supports LBA\n");
664
665        if (cmdReg.sec_count == 0)
666            cmdBytes = cmdBytesLeft = (256 * SectorSize);
667        else
668            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
669
670        curSector = getLBABase();
671
672        /** @todo make this a scheduled event to simulate disk delay */
673        devState = Prepare_Data_In;
674        action = ACT_DATA_READY;
675        break;
676
677        // Supported PIO data-out commands
678      case WIN_MULTWRITE:
679      case WIN_WRITE:
680        if (!(cmdReg.drive & DRIVE_LBA_BIT))
681            panic("Attempt to perform CHS access, only supports LBA\n");
682
683        if (cmdReg.sec_count == 0)
684            cmdBytes = cmdBytesLeft = (256 * SectorSize);
685        else
686            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
687
688        curSector = getLBABase();
689
690        devState = Prepare_Data_Out;
691        action = ACT_DATA_READY;
692        break;
693
694        // Supported DMA commands
695      case WIN_WRITEDMA:
696        dmaRead = true;  // a write to the disk is a DMA read from memory
697      case WIN_READDMA:
698        if (!(cmdReg.drive & DRIVE_LBA_BIT))
699            panic("Attempt to perform CHS access, only supports LBA\n");
700
701        if (cmdReg.sec_count == 0)
702            cmdBytes = cmdBytesLeft = (256 * SectorSize);
703        else
704            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
705
706        curSector = getLBABase();
707
708        devState = Prepare_Data_Dma;
709        action = ACT_DMA_READY;
710        break;
711
712      default:
713        panic("Unsupported ATA command: %#x\n", cmdReg.command);
714    }
715
716    if (action != ACT_NONE) {
717        // set the BSY bit
718        status |= STATUS_BSY_BIT;
719        // clear the DRQ bit
720        status &= ~STATUS_DRQ_BIT;
721        // clear the DF bit
722        status &= ~STATUS_DF_BIT;
723
724        updateState(action);
725    }
726}
727
728////
729// Handle setting and clearing interrupts
730////
731
732void
733IdeDisk::intrPost()
734{
735    if (intrPending)
736        panic("Attempt to post an interrupt with one pending\n");
737
738    intrPending = true;
739
740    // talk to controller to set interrupt
741    if (ctrl)
742        ctrl->intrPost();
743}
744
745void
746IdeDisk::intrClear()
747{
748    if (!intrPending)
749        panic("Attempt to clear a non-pending interrupt\n");
750
751    intrPending = false;
752
753    // talk to controller to clear interrupt
754    if (ctrl)
755        ctrl->intrClear();
756}
757
758////
759// Manage the device internal state machine
760////
761
762void
763IdeDisk::updateState(DevAction_t action)
764{
765    switch (devState) {
766      case Device_Srst:
767        if (action == ACT_SRST_SET) {
768            // set the BSY bit
769            status |= STATUS_BSY_BIT;
770        } else if (action == ACT_SRST_CLEAR) {
771            // clear the BSY bit
772            status &= ~STATUS_BSY_BIT;
773
774            // reset the device state
775            reset(devID);
776        }
777        break;
778
779      case Device_Idle_S:
780        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
781            devState = Device_Idle_NS;
782        } else if (action == ACT_CMD_WRITE) {
783            startCommand();
784        }
785
786        break;
787
788      case Device_Idle_SI:
789        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
790            devState = Device_Idle_NS;
791            intrClear();
792        } else if (action == ACT_STAT_READ || isIENSet()) {
793            devState = Device_Idle_S;
794            intrClear();
795        } else if (action == ACT_CMD_WRITE) {
796            intrClear();
797            startCommand();
798        }
799
800        break;
801
802      case Device_Idle_NS:
803        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
804            if (!isIENSet() && intrPending) {
805                devState = Device_Idle_SI;
806                intrPost();
807            }
808            if (isIENSet() || !intrPending) {
809                devState = Device_Idle_S;
810            }
811        }
812        break;
813
814      case Command_Execution:
815        if (action == ACT_CMD_COMPLETE) {
816            // clear the BSY bit
817            setComplete();
818
819            if (!isIENSet()) {
820                devState = Device_Idle_SI;
821                intrPost();
822            } else {
823                devState = Device_Idle_S;
824            }
825        }
826        break;
827
828      case Prepare_Data_In:
829        if (action == ACT_CMD_ERROR) {
830            // clear the BSY bit
831            setComplete();
832
833            if (!isIENSet()) {
834                devState = Device_Idle_SI;
835                intrPost();
836            } else {
837                devState = Device_Idle_S;
838            }
839        } else if (action == ACT_DATA_READY) {
840            // clear the BSY bit
841            status &= ~STATUS_BSY_BIT;
842            // set the DRQ bit
843            status |= STATUS_DRQ_BIT;
844
845            // copy the data into the data buffer
846            if (cmdReg.command == WIN_IDENTIFY) {
847                // Reset the drqBytes for this block
848                drqBytesLeft = sizeof(struct hd_driveid);
849
850                memcpy((void *)dataBuffer, (void *)&driveID,
851                       sizeof(struct hd_driveid));
852            } else {
853                // Reset the drqBytes for this block
854                drqBytesLeft = SectorSize;
855
856                readDisk(curSector++, dataBuffer);
857            }
858
859            // put the first two bytes into the data register
860            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
861                   sizeof(uint16_t));
862
863            if (!isIENSet()) {
864                devState = Data_Ready_INTRQ_In;
865                intrPost();
866            } else {
867                devState = Transfer_Data_In;
868            }
869        }
870        break;
871
872      case Data_Ready_INTRQ_In:
873        if (action == ACT_STAT_READ) {
874            devState = Transfer_Data_In;
875            intrClear();
876        }
877        break;
878
879      case Transfer_Data_In:
880        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
881            if (action == ACT_DATA_READ_BYTE) {
882                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
883            } else {
884                drqBytesLeft -= 2;
885                cmdBytesLeft -= 2;
886
887                // copy next short into data registers
888                if (drqBytesLeft)
889                    memcpy((void *)&cmdReg.data0,
890                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
891                           sizeof(uint16_t));
892            }
893
894            if (drqBytesLeft == 0) {
895                if (cmdBytesLeft == 0) {
896                    // Clear the BSY bit
897                    setComplete();
898                    devState = Device_Idle_S;
899                } else {
900                    devState = Prepare_Data_In;
901                    // set the BSY_BIT
902                    status |= STATUS_BSY_BIT;
903                    // clear the DRQ_BIT
904                    status &= ~STATUS_DRQ_BIT;
905
906                    /** @todo change this to a scheduled event to simulate
907                        disk delay */
908                    updateState(ACT_DATA_READY);
909                }
910            }
911        }
912        break;
913
914      case Prepare_Data_Out:
915        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
916            // clear the BSY bit
917            setComplete();
918
919            if (!isIENSet()) {
920                devState = Device_Idle_SI;
921                intrPost();
922            } else {
923                devState = Device_Idle_S;
924            }
925        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
926            // clear the BSY bit
927            status &= ~STATUS_BSY_BIT;
928            // set the DRQ bit
929            status |= STATUS_DRQ_BIT;
930
931            // clear the data buffer to get it ready for writes
932            memset(dataBuffer, 0, MAX_DMA_SIZE);
933
934            // reset the drqBytes for this block
935            drqBytesLeft = SectorSize;
936
937            if (cmdBytesLeft == cmdBytes || isIENSet()) {
938                devState = Transfer_Data_Out;
939            } else {
940                devState = Data_Ready_INTRQ_Out;
941                intrPost();
942            }
943        }
944        break;
945
946      case Data_Ready_INTRQ_Out:
947        if (action == ACT_STAT_READ) {
948            devState = Transfer_Data_Out;
949            intrClear();
950        }
951        break;
952
953      case Transfer_Data_Out:
954        if (action == ACT_DATA_WRITE_BYTE ||
955            action == ACT_DATA_WRITE_SHORT) {
956
957            if (action == ACT_DATA_READ_BYTE) {
958                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
959            } else {
960                // copy the latest short into the data buffer
961                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
962                       (void *)&cmdReg.data0,
963                       sizeof(uint16_t));
964
965                drqBytesLeft -= 2;
966                cmdBytesLeft -= 2;
967            }
968
969            if (drqBytesLeft == 0) {
970                // copy the block to the disk
971                writeDisk(curSector++, dataBuffer);
972
973                // set the BSY bit
974                status |= STATUS_BSY_BIT;
975                // set the seek bit
976                status |= STATUS_SEEK_BIT;
977                // clear the DRQ bit
978                status &= ~STATUS_DRQ_BIT;
979
980                devState = Prepare_Data_Out;
981
982                /** @todo change this to a scheduled event to simulate
983                    disk delay */
984                updateState(ACT_DATA_READY);
985            }
986        }
987        break;
988
989      case Prepare_Data_Dma:
990        if (action == ACT_CMD_ERROR) {
991            // clear the BSY bit
992            setComplete();
993
994            if (!isIENSet()) {
995                devState = Device_Idle_SI;
996                intrPost();
997            } else {
998                devState = Device_Idle_S;
999            }
1000        } else if (action == ACT_DMA_READY) {
1001            // clear the BSY bit
1002            status &= ~STATUS_BSY_BIT;
1003            // set the DRQ bit
1004            status |= STATUS_DRQ_BIT;
1005
1006            devState = Transfer_Data_Dma;
1007
1008            if (dmaState != Dma_Idle)
1009                panic("Inconsistent DMA state, should be Dma_Idle\n");
1010
1011            dmaState = Dma_Start;
1012            // wait for the write to the DMA start bit
1013        }
1014        break;
1015
1016      case Transfer_Data_Dma:
1017        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
1018            // clear the BSY bit
1019            setComplete();
1020            // set the seek bit
1021            status |= STATUS_SEEK_BIT;
1022            // clear the controller state for DMA transfer
1023            ctrl->setDmaComplete(this);
1024
1025            if (!isIENSet()) {
1026                devState = Device_Idle_SI;
1027                intrPost();
1028            } else {
1029                devState = Device_Idle_S;
1030            }
1031        }
1032        break;
1033
1034      default:
1035        panic("Unknown IDE device state: %#x\n", devState);
1036    }
1037}
1038
1039void
1040IdeDisk::serialize(ostream &os)
1041{
1042    // Check all outstanding events to see if they are scheduled
1043    // these are all mutually exclusive
1044    Tick reschedule = 0;
1045    Events_t event = None;
1046
1047    int eventCount = 0;
1048
1049    if (dmaTransferEvent.scheduled()) {
1050        reschedule = dmaTransferEvent.when();
1051        event = Transfer;
1052        eventCount++;
1053    }
1054    if (dmaReadWaitEvent.scheduled()) {
1055        reschedule = dmaReadWaitEvent.when();
1056        event = ReadWait;
1057        eventCount++;
1058    }
1059    if (dmaWriteWaitEvent.scheduled()) {
1060        reschedule = dmaWriteWaitEvent.when();
1061        event = WriteWait;
1062        eventCount++;
1063    }
1064    if (dmaPrdReadEvent.scheduled()) {
1065        reschedule = dmaPrdReadEvent.when();
1066        event = PrdRead;
1067        eventCount++;
1068    }
1069    if (dmaReadEvent.scheduled()) {
1070        reschedule = dmaReadEvent.when();
1071        event = DmaRead;
1072        eventCount++;
1073    }
1074    if (dmaWriteEvent.scheduled()) {
1075        reschedule = dmaWriteEvent.when();
1076        event = DmaWrite;
1077        eventCount++;
1078    }
1079
1080    assert(eventCount <= 1);
1081
1082    SERIALIZE_SCALAR(reschedule);
1083    SERIALIZE_ENUM(event);
1084
1085    // Serialize device registers
1086    SERIALIZE_SCALAR(cmdReg.data0);
1087    SERIALIZE_SCALAR(cmdReg.data1);
1088    SERIALIZE_SCALAR(cmdReg.sec_count);
1089    SERIALIZE_SCALAR(cmdReg.sec_num);
1090    SERIALIZE_SCALAR(cmdReg.cyl_low);
1091    SERIALIZE_SCALAR(cmdReg.cyl_high);
1092    SERIALIZE_SCALAR(cmdReg.drive);
1093    SERIALIZE_SCALAR(cmdReg.command);
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(cmdReg.command);
1147    UNSERIALIZE_SCALAR(status);
1148    UNSERIALIZE_SCALAR(nIENBit);
1149    UNSERIALIZE_SCALAR(devID);
1150
1151    // Unserialize the PRD related information
1152    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1153    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1154    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1155    UNSERIALIZE_SCALAR(curPrdAddr);
1156
1157    // Unserialize current transfer related information
1158    UNSERIALIZE_SCALAR(cmdBytes);
1159    UNSERIALIZE_SCALAR(cmdBytesLeft);
1160    UNSERIALIZE_SCALAR(drqBytesLeft);
1161    UNSERIALIZE_SCALAR(curSector);
1162    UNSERIALIZE_SCALAR(dmaRead);
1163    UNSERIALIZE_SCALAR(dmaInterfaceBytes);
1164    UNSERIALIZE_SCALAR(intrPending);
1165    UNSERIALIZE_ENUM(devState);
1166    UNSERIALIZE_ENUM(dmaState);
1167    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1168}
1169
1170#ifndef DOXYGEN_SHOULD_SKIP_THIS
1171
1172BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1173
1174    SimObjectParam<DiskImage *> image;
1175    SimObjectParam<PhysicalMemory *> physmem;
1176    Param<int> driveID;
1177    Param<int> disk_delay;
1178
1179END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1180
1181BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1182
1183    INIT_PARAM(image, "Disk image"),
1184    INIT_PARAM(physmem, "Physical memory"),
1185    INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"),
1186    INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1)
1187
1188END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1189
1190
1191CREATE_SIM_OBJECT(IdeDisk)
1192{
1193    return new IdeDisk(getInstanceName(), image, physmem, driveID,
1194                       disk_delay);
1195}
1196
1197REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1198
1199#endif //DOXYGEN_SHOULD_SKIP_THIS
1200