ide_disk.cc revision 1762
12817Sksewell@umich.edu/*
28733Sgeoffrey.blake@arm.com * Copyright (c) 2004-2005 The Regents of The University of Michigan
37763SAli.Saidi@ARM.com * All rights reserved.
47763SAli.Saidi@ARM.com *
57763SAli.Saidi@ARM.com * Redistribution and use in source and binary forms, with or without
67763SAli.Saidi@ARM.com * modification, are permitted provided that the following conditions are
77763SAli.Saidi@ARM.com * met: redistributions of source code must retain the above copyright
87763SAli.Saidi@ARM.com * notice, this list of conditions and the following disclaimer;
97763SAli.Saidi@ARM.com * redistributions in binary form must reproduce the above copyright
107763SAli.Saidi@ARM.com * notice, this list of conditions and the following disclaimer in the
117763SAli.Saidi@ARM.com * documentation and/or other materials provided with the distribution;
127763SAli.Saidi@ARM.com * neither the name of the copyright holders nor the names of its
137763SAli.Saidi@ARM.com * contributors may be used to endorse or promote products derived from
142817Sksewell@umich.edu * this software without specific prior written permission.
152817Sksewell@umich.edu *
162817Sksewell@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
172817Sksewell@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
182817Sksewell@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
192817Sksewell@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
202817Sksewell@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212817Sksewell@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222817Sksewell@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232817Sksewell@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242817Sksewell@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252817Sksewell@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262817Sksewell@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272817Sksewell@umich.edu */
282817Sksewell@umich.edu
292817Sksewell@umich.edu/** @file
302817Sksewell@umich.edu * Device model implementation for an IDE disk
312817Sksewell@umich.edu */
322817Sksewell@umich.edu
332817Sksewell@umich.edu#include <cerrno>
342817Sksewell@umich.edu#include <cstring>
352817Sksewell@umich.edu#include <deque>
362817Sksewell@umich.edu#include <string>
372817Sksewell@umich.edu
382817Sksewell@umich.edu#include "base/cprintf.hh" // csprintf
392817Sksewell@umich.edu#include "base/trace.hh"
402817Sksewell@umich.edu#include "dev/disk_image.hh"
412817Sksewell@umich.edu#include "dev/ide_disk.hh"
422817Sksewell@umich.edu#include "dev/ide_ctrl.hh"
432817Sksewell@umich.edu#include "dev/tsunami.hh"
448793Sgblack@eecs.umich.edu#include "dev/tsunami_pchip.hh"
456329Sgblack@eecs.umich.edu#include "mem/functional/physical.hh"
466658Snate@binkert.org#include "mem/bus/bus.hh"
478733Sgeoffrey.blake@arm.com#include "mem/bus/dma_interface.hh"
482817Sksewell@umich.edu#include "mem/bus/pio_interface.hh"
492834Sksewell@umich.edu#include "mem/bus/pio_interface_impl.hh"
508232Snate@binkert.org#include "sim/builder.hh"
512817Sksewell@umich.edu#include "sim/sim_object.hh"
522817Sksewell@umich.edu#include "sim/root.hh"
538852Sandreas.hansson@arm.com#include "targetarch/isa_traits.hh"
548706Sandreas.hansson@arm.com
552817Sksewell@umich.eduusing namespace std;
568706Sandreas.hansson@arm.com
572817Sksewell@umich.eduIdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys,
582817Sksewell@umich.edu                 int id, Tick delay)
592817Sksewell@umich.edu    : SimObject(name), ctrl(NULL), image(img), physmem(phys), diskDelay(delay),
602817Sksewell@umich.edu      dmaTransferEvent(this), dmaReadWaitEvent(this),
612817Sksewell@umich.edu      dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
622817Sksewell@umich.edu      dmaReadEvent(this), dmaWriteEvent(this)
633126Sktlim@umich.edu{
642817Sksewell@umich.edu    // Reset the device state
652817Sksewell@umich.edu    reset(id);
662817Sksewell@umich.edu
672817Sksewell@umich.edu    // fill out the drive ID structure
682817Sksewell@umich.edu    memset(&driveID, 0, sizeof(struct ataparams));
692817Sksewell@umich.edu
702817Sksewell@umich.edu    // Calculate LBA and C/H/S values
712817Sksewell@umich.edu    uint16_t cylinders;
722817Sksewell@umich.edu    uint8_t heads;
732817Sksewell@umich.edu    uint8_t sectors;
742817Sksewell@umich.edu
752817Sksewell@umich.edu    uint32_t lba_size = image->size();
762817Sksewell@umich.edu    if (lba_size >= 16383*16*63) {
775714Shsul@eecs.umich.edu        cylinders = 16383;
785715Shsul@eecs.umich.edu        heads = 16;
792817Sksewell@umich.edu        sectors = 63;
808793Sgblack@eecs.umich.edu    } else {
818793Sgblack@eecs.umich.edu        if (lba_size >= 63)
828793Sgblack@eecs.umich.edu            sectors = 63;
838793Sgblack@eecs.umich.edu        else
848793Sgblack@eecs.umich.edu            sectors = lba_size;
858793Sgblack@eecs.umich.edu
868793Sgblack@eecs.umich.edu        if ((lba_size / sectors) >= 16)
878793Sgblack@eecs.umich.edu            heads = 16;
888793Sgblack@eecs.umich.edu        else
898793Sgblack@eecs.umich.edu            heads = (lba_size / sectors);
908793Sgblack@eecs.umich.edu
918793Sgblack@eecs.umich.edu        cylinders = lba_size / (heads * sectors);
928793Sgblack@eecs.umich.edu    }
938793Sgblack@eecs.umich.edu
948793Sgblack@eecs.umich.edu    // Setup the model name
958793Sgblack@eecs.umich.edu    sprintf((char *)driveID.atap_model, "5MI EDD si k");
962817Sksewell@umich.edu    // Set the maximum multisector transfer size
972817Sksewell@umich.edu    driveID.atap_multi = MAX_MULTSECT;
986029Ssteve.reinhardt@amd.com    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
992817Sksewell@umich.edu    driveID.atap_capabilities1 = 0x7;
1002817Sksewell@umich.edu    // UDMA support, EIDE support
1012817Sksewell@umich.edu    driveID.atap_extensions = 0x6;
1022817Sksewell@umich.edu    // Setup default C/H/S settings
1032817Sksewell@umich.edu    driveID.atap_cylinders = cylinders;
1042817Sksewell@umich.edu    driveID.atap_sectors = sectors;
1052817Sksewell@umich.edu    driveID.atap_heads = heads;
1062817Sksewell@umich.edu    // Setup the current multisector transfer size
1072817Sksewell@umich.edu    driveID.atap_curmulti = MAX_MULTSECT;
1082875Sksewell@umich.edu    driveID.atap_curmulti_valid = 0x1;
1095715Shsul@eecs.umich.edu    // Number of sectors on disk
1102817Sksewell@umich.edu    driveID.atap_capacity = lba_size;
1112817Sksewell@umich.edu    // Multiword DMA mode 2 and below supported
1122817Sksewell@umich.edu    driveID.atap_dmamode_supp = 0x400;
1132817Sksewell@umich.edu    // Set PIO mode 4 and 3 supported
1147823Ssteve.reinhardt@amd.com    driveID.atap_piomode_supp = 0x3;
1152817Sksewell@umich.edu    // Set DMA mode 4 and below supported
1162817Sksewell@umich.edu    driveID.atap_udmamode_supp = 0x10;
1172817Sksewell@umich.edu    // Statically set hardware config word
1185715Shsul@eecs.umich.edu    driveID.atap_hwreset_res = 0x4001;
1192817Sksewell@umich.edu}
1202817Sksewell@umich.edu
1212817Sksewell@umich.eduIdeDisk::~IdeDisk()
1222817Sksewell@umich.edu{
1235250Sksewell@umich.edu    // destroy the data buffer
1242817Sksewell@umich.edu    delete [] dataBuffer;
1252875Sksewell@umich.edu}
1265715Shsul@eecs.umich.edu
1272817Sksewell@umich.eduvoid
1282817Sksewell@umich.eduIdeDisk::reset(int id)
1292817Sksewell@umich.edu{
1302817Sksewell@umich.edu    // initialize the data buffer and shadow registers
1317823Ssteve.reinhardt@amd.com    dataBuffer = new uint8_t[MAX_DMA_SIZE];
1327823Ssteve.reinhardt@amd.com
1338793Sgblack@eecs.umich.edu    memset(dataBuffer, 0, MAX_DMA_SIZE);
1342817Sksewell@umich.edu    memset(&cmdReg, 0, sizeof(CommandReg_t));
1355715Shsul@eecs.umich.edu    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
1362817Sksewell@umich.edu
1372817Sksewell@umich.edu    dmaInterfaceBytes = 0;
1382817Sksewell@umich.edu    curPrdAddr = 0;
1392817Sksewell@umich.edu    curSector = 0;
1405250Sksewell@umich.edu    cmdBytes = 0;
1412817Sksewell@umich.edu    cmdBytesLeft = 0;
1422875Sksewell@umich.edu    drqBytesLeft = 0;
1435715Shsul@eecs.umich.edu    dmaRead = false;
1442817Sksewell@umich.edu    intrPending = false;
1452817Sksewell@umich.edu
1462817Sksewell@umich.edu    // set the device state to idle
1472817Sksewell@umich.edu    dmaState = Dma_Idle;
1482817Sksewell@umich.edu
1495715Shsul@eecs.umich.edu    if (id == DEV0) {
1502817Sksewell@umich.edu        devState = Device_Idle_S;
1512817Sksewell@umich.edu        devID = DEV0;
1522817Sksewell@umich.edu    } else if (id == DEV1) {
1532817Sksewell@umich.edu        devState = Device_Idle_NS;
1542817Sksewell@umich.edu        devID = DEV1;
1552817Sksewell@umich.edu    } else {
1568793Sgblack@eecs.umich.edu        panic("Invalid device ID: %#x\n", id);
1578793Sgblack@eecs.umich.edu    }
1588793Sgblack@eecs.umich.edu
1598793Sgblack@eecs.umich.edu    // set the device ready bit
1602817Sksewell@umich.edu    status = STATUS_DRDY_BIT;
1612817Sksewell@umich.edu}
1622817Sksewell@umich.edu
1632817Sksewell@umich.edu////
1642817Sksewell@umich.edu// Utility functions
1652817Sksewell@umich.edu////
1668793Sgblack@eecs.umich.edu
1672817Sksewell@umich.edubool
1682817Sksewell@umich.eduIdeDisk::isDEVSelect()
1692817Sksewell@umich.edu{
1702817Sksewell@umich.edu    return ctrl->isDiskSelected(this);
1712817Sksewell@umich.edu}
1722817Sksewell@umich.edu
1732817Sksewell@umich.eduAddr
1748793Sgblack@eecs.umich.eduIdeDisk::pciToDma(Addr pciAddr)
1752817Sksewell@umich.edu{
1762817Sksewell@umich.edu    if (ctrl)
1772817Sksewell@umich.edu        return ctrl->plat->pciToDma(pciAddr);
1782817Sksewell@umich.edu    else
1792817Sksewell@umich.edu        panic("Access to unset controller!\n");
1802817Sksewell@umich.edu}
1812817Sksewell@umich.edu
1822817Sksewell@umich.eduuint32_t
1832817Sksewell@umich.eduIdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft)
1842817Sksewell@umich.edu{
1852817Sksewell@umich.edu    uint32_t bytesInPage = 0;
1862817Sksewell@umich.edu
1872817Sksewell@umich.edu    // First calculate how many bytes could be in the page
1882817Sksewell@umich.edu    if (bytesLeft > TheISA::PageBytes)
1892817Sksewell@umich.edu        bytesInPage = TheISA::PageBytes;
1902817Sksewell@umich.edu    else
1912817Sksewell@umich.edu        bytesInPage = bytesLeft;
1922817Sksewell@umich.edu
1932817Sksewell@umich.edu    // Next, see if we have crossed a page boundary, and adjust
1942817Sksewell@umich.edu    Addr upperBound = curAddr + bytesInPage;
1953126Sktlim@umich.edu    Addr pageBound = TheISA::TruncPage(curAddr) + TheISA::PageBytes;
1963126Sktlim@umich.edu
1973126Sktlim@umich.edu    assert(upperBound >= curAddr && "DMA read wraps around address space!\n");
1982817Sksewell@umich.edu
1992817Sksewell@umich.edu    if (upperBound >= pageBound)
2002817Sksewell@umich.edu        bytesInPage = pageBound - curAddr;
2012817Sksewell@umich.edu
2023126Sktlim@umich.edu    return bytesInPage;
2033126Sktlim@umich.edu}
2043126Sktlim@umich.edu
2052817Sksewell@umich.edu////
2062817Sksewell@umich.edu// Device registers read/write
2072817Sksewell@umich.edu////
2082817Sksewell@umich.edu
2092817Sksewell@umich.eduvoid
2108208SAli.Saidi@ARM.comIdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data)
2118208SAli.Saidi@ARM.com{
2128208SAli.Saidi@ARM.com    DevAction_t action = ACT_NONE;
2138208SAli.Saidi@ARM.com
2142817Sksewell@umich.edu    if (cmdBlk) {
2158793Sgblack@eecs.umich.edu        if (offset < 0 || offset > sizeof(CommandReg_t))
2168793Sgblack@eecs.umich.edu            panic("Invalid disk command register offset: %#x\n", offset);
2172817Sksewell@umich.edu
2182817Sksewell@umich.edu        if (!byte && offset != DATA_OFFSET)
2192817Sksewell@umich.edu            panic("Invalid 16-bit read, only allowed on data reg\n");
2202817Sksewell@umich.edu
2212817Sksewell@umich.edu        if (!byte)
2227763SAli.Saidi@ARM.com            *(uint16_t *)data = *(uint16_t *)&cmdReg.data0;
2237763SAli.Saidi@ARM.com        else
2247763SAli.Saidi@ARM.com            *data = ((uint8_t *)&cmdReg)[offset];
2252817Sksewell@umich.edu
2262817Sksewell@umich.edu        // determine if an action needs to be taken on the state machine
2272817Sksewell@umich.edu        if (offset == STATUS_OFFSET) {
2282817Sksewell@umich.edu            action = ACT_STAT_READ;
2292817Sksewell@umich.edu            *data = status; // status is in a shadow, explicity copy
2306313Sgblack@eecs.umich.edu        } else if (offset == DATA_OFFSET) {
2315715Shsul@eecs.umich.edu            if (byte)
2322817Sksewell@umich.edu                action = ACT_DATA_READ_BYTE;
2332817Sksewell@umich.edu            else
2342817Sksewell@umich.edu                action = ACT_DATA_READ_SHORT;
2352986Sgblack@eecs.umich.edu        }
2362817Sksewell@umich.edu
2372817Sksewell@umich.edu    } else {
2386313Sgblack@eecs.umich.edu        if (offset != ALTSTAT_OFFSET)
2396314Sgblack@eecs.umich.edu            panic("Invalid disk control register offset: %#x\n", offset);
2402817Sksewell@umich.edu
2412817Sksewell@umich.edu        if (!byte)
2422817Sksewell@umich.edu            panic("Invalid 16-bit read from control block\n");
2432986Sgblack@eecs.umich.edu
2442817Sksewell@umich.edu        *data = status;
2452817Sksewell@umich.edu    }
2466313Sgblack@eecs.umich.edu
2475715Shsul@eecs.umich.edu    if (action != ACT_NONE)
2482817Sksewell@umich.edu        updateState(action);
2492817Sksewell@umich.edu}
2502817Sksewell@umich.edu
2512817Sksewell@umich.eduvoid
2522817Sksewell@umich.eduIdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data)
2532817Sksewell@umich.edu{
2546313Sgblack@eecs.umich.edu    DevAction_t action = ACT_NONE;
2555715Shsul@eecs.umich.edu
2562817Sksewell@umich.edu    if (cmdBlk) {
2572817Sksewell@umich.edu        if (offset < 0 || offset > sizeof(CommandReg_t))
2582817Sksewell@umich.edu            panic("Invalid disk command register offset: %#x\n", offset);
2595715Shsul@eecs.umich.edu
2602817Sksewell@umich.edu        if (!byte && offset != DATA_OFFSET)
2612817Sksewell@umich.edu            panic("Invalid 16-bit write, only allowed on data reg\n");
2622817Sksewell@umich.edu
2632817Sksewell@umich.edu        if (!byte)
2642817Sksewell@umich.edu            *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data;
2652817Sksewell@umich.edu        else
2662817Sksewell@umich.edu            ((uint8_t *)&cmdReg)[offset] = *data;
2676313Sgblack@eecs.umich.edu
2686314Sgblack@eecs.umich.edu        // determine if an action needs to be taken on the state machine
2692817Sksewell@umich.edu        if (offset == COMMAND_OFFSET) {
2702817Sksewell@umich.edu            action = ACT_CMD_WRITE;
2715715Shsul@eecs.umich.edu        } else if (offset == DATA_OFFSET) {
2722817Sksewell@umich.edu            if (byte)
2732817Sksewell@umich.edu                action = ACT_DATA_WRITE_BYTE;
2742817Sksewell@umich.edu            else
2752817Sksewell@umich.edu                action = ACT_DATA_WRITE_SHORT;
2762817Sksewell@umich.edu        } else if (offset == SELECT_OFFSET) {
2772817Sksewell@umich.edu            action = ACT_SELECT_WRITE;
2782817Sksewell@umich.edu        }
2796313Sgblack@eecs.umich.edu
2805715Shsul@eecs.umich.edu    } else {
2812817Sksewell@umich.edu        if (offset != CONTROL_OFFSET)
2822817Sksewell@umich.edu            panic("Invalid disk control register offset: %#x\n", offset);
2832817Sksewell@umich.edu
2845715Shsul@eecs.umich.edu        if (!byte)
2852817Sksewell@umich.edu            panic("Invalid 16-bit write to control block\n");
2862817Sksewell@umich.edu
2872817Sksewell@umich.edu        if (*data & CONTROL_RST_BIT) {
2882817Sksewell@umich.edu            // force the device into the reset state
2892817Sksewell@umich.edu            devState = Device_Srst;
2907720Sgblack@eecs.umich.edu            action = ACT_SRST_SET;
2912817Sksewell@umich.edu        } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
2927720Sgblack@eecs.umich.edu            action = ACT_SRST_CLEAR;
2935258Sksewell@umich.edu        }
2945258Sksewell@umich.edu
2955258Sksewell@umich.edu        nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
2965715Shsul@eecs.umich.edu    }
2975258Sksewell@umich.edu
2985258Sksewell@umich.edu    if (action != ACT_NONE)
2995258Sksewell@umich.edu        updateState(action);
3008733Sgeoffrey.blake@arm.com}
3018733Sgeoffrey.blake@arm.com
3028733Sgeoffrey.blake@arm.com////
3038733Sgeoffrey.blake@arm.com// Perform DMA transactions
3048733Sgeoffrey.blake@arm.com////
3058733Sgeoffrey.blake@arm.com
3068733Sgeoffrey.blake@arm.comvoid
3078733Sgeoffrey.blake@arm.comIdeDisk::doDmaTransfer()
3088733Sgeoffrey.blake@arm.com{
3098733Sgeoffrey.blake@arm.com    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
3108733Sgeoffrey.blake@arm.com        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
3118733Sgeoffrey.blake@arm.com              dmaState, devState);
3128733Sgeoffrey.blake@arm.com
3138733Sgeoffrey.blake@arm.com    // first read the current PRD
3145258Sksewell@umich.edu    if (dmaInterface) {
3156313Sgblack@eecs.umich.edu        if (dmaInterface->busy()) {
3166313Sgblack@eecs.umich.edu            // reschedule after waiting period
3176313Sgblack@eecs.umich.edu            dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
3186313Sgblack@eecs.umich.edu            return;
3196313Sgblack@eecs.umich.edu        }
3206313Sgblack@eecs.umich.edu
3216313Sgblack@eecs.umich.edu        dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick,
3226313Sgblack@eecs.umich.edu                            &dmaPrdReadEvent);
3236313Sgblack@eecs.umich.edu    } else {
3246313Sgblack@eecs.umich.edu        dmaPrdReadDone();
3256313Sgblack@eecs.umich.edu    }
3266313Sgblack@eecs.umich.edu}
3276313Sgblack@eecs.umich.edu
3286313Sgblack@eecs.umich.eduvoid
3295258Sksewell@umich.eduIdeDisk::dmaPrdReadDone()
3304172Ssaidi@eecs.umich.edu{
3312817Sksewell@umich.edu    // actually copy the PRD from physical memory
3325715Shsul@eecs.umich.edu    memcpy((void *)&curPrd.entry,
3332817Sksewell@umich.edu           physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)),
3342817Sksewell@umich.edu           sizeof(PrdEntry_t));
3352817Sksewell@umich.edu
3365715Shsul@eecs.umich.edu    DPRINTF(IdeDisk,
3372817Sksewell@umich.edu            "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
3382817Sksewell@umich.edu            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
3392817Sksewell@umich.edu            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
3402817Sksewell@umich.edu            curPrd.getEOT(), curSector);
3413468Sgblack@eecs.umich.edu
3428518Sgeoffrey.blake@arm.com    // the prd pointer has already been translated, so just do an increment
3432817Sksewell@umich.edu    curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
3445715Shsul@eecs.umich.edu
3452817Sksewell@umich.edu    if (dmaRead)
3462817Sksewell@umich.edu        doDmaRead();
3472817Sksewell@umich.edu    else
3485715Shsul@eecs.umich.edu        doDmaWrite();
3492817Sksewell@umich.edu}
3502817Sksewell@umich.edu
3512817Sksewell@umich.eduvoid
352IdeDisk::doDmaRead()
353{
354    /** @TODO we need to figure out what the delay actually will be */
355    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
356
357    DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
358            diskDelay, totalDiskDelay);
359    if (dmaInterface) {
360        if (dmaInterface->busy()) {
361            // reschedule after waiting period
362            dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
363            return;
364        }
365
366        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
367
368        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
369                                              (uint32_t)curPrd.getByteCount());
370
371        dmaInterfaceBytes = bytesInPage;
372
373        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
374                            curTick + totalDiskDelay, &dmaReadEvent);
375    } else {
376        // schedule dmaReadEvent with sectorDelay (dmaReadDone)
377        dmaReadEvent.schedule(curTick + totalDiskDelay);
378    }
379}
380
381void
382IdeDisk::dmaReadDone()
383{
384
385    Addr curAddr = 0, dmaAddr = 0;
386    uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0;
387
388    // continue to use the DMA interface until all pages are read
389    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
390        // see if the interface is busy
391        if (dmaInterface->busy()) {
392            // reschedule after waiting period
393            dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
394            return;
395        }
396
397        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
398        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
399        dmaAddr = pciToDma(curAddr);
400
401        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
402        dmaInterfaceBytes += bytesInPage;
403
404        dmaInterface->doDMA(Read, dmaAddr, bytesInPage,
405                            curTick, &dmaReadEvent);
406
407        return;
408    }
409
410    // set initial address
411    curAddr = curPrd.getBaseAddr();
412
413    // clear out the data buffer
414    memset(dataBuffer, 0, MAX_DMA_SIZE);
415
416    // read the data from memory via DMA into a data buffer
417    while (bytesWritten < curPrd.getByteCount()) {
418        if (cmdBytesLeft <= 0)
419            panic("DMA data is larger than # of sectors specified\n");
420
421        dmaAddr = pciToDma(curAddr);
422
423        // calculate how many bytes are in the current page
424        bytesLeft = curPrd.getByteCount() - bytesWritten;
425        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
426
427        // copy the data from memory into the data buffer
428        memcpy((void *)(dataBuffer + bytesWritten),
429               physmem->dma_addr(dmaAddr, bytesInPage),
430               bytesInPage);
431
432        curAddr += bytesInPage;
433        bytesWritten += bytesInPage;
434        cmdBytesLeft -= bytesInPage;
435    }
436
437    // write the data to the disk image
438    for (bytesWritten = 0;
439         bytesWritten < curPrd.getByteCount();
440         bytesWritten += SectorSize) {
441
442        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
443    }
444
445    // check for the EOT
446    if (curPrd.getEOT()) {
447        assert(cmdBytesLeft == 0);
448        dmaState = Dma_Idle;
449        updateState(ACT_DMA_DONE);
450    } else {
451        doDmaTransfer();
452    }
453}
454
455void
456IdeDisk::doDmaWrite()
457{
458    /** @TODO we need to figure out what the delay actually will be */
459    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
460
461    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
462            diskDelay, totalDiskDelay);
463
464    if (dmaInterface) {
465        if (dmaInterface->busy()) {
466            // reschedule after waiting period
467            dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
468            return;
469        }
470
471        Addr dmaAddr = pciToDma(curPrd.getBaseAddr());
472
473        uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(),
474                                              (uint32_t)curPrd.getByteCount());
475
476        dmaInterfaceBytes = bytesInPage;
477
478        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
479                            bytesInPage, curTick + totalDiskDelay,
480                            &dmaWriteEvent);
481    } else {
482        // schedule event with disk delay (dmaWriteDone)
483        dmaWriteEvent.schedule(curTick + totalDiskDelay);
484    }
485}
486
487void
488IdeDisk::dmaWriteDone()
489{
490    Addr curAddr = 0, pageAddr = 0, dmaAddr = 0;
491    uint32_t bytesRead = 0, bytesInPage = 0;
492
493    // continue to use the DMA interface until all pages are read
494    if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) {
495        // see if the interface is busy
496        if (dmaInterface->busy()) {
497            // reschedule after waiting period
498            dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
499            return;
500        }
501
502        uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes;
503        curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes;
504        dmaAddr = pciToDma(curAddr);
505
506        bytesInPage = bytesInDmaPage(curAddr, bytesLeft);
507        dmaInterfaceBytes += bytesInPage;
508
509        dmaInterface->doDMA(WriteInvalidate, dmaAddr,
510                            bytesInPage, curTick,
511                            &dmaWriteEvent);
512
513        return;
514    }
515
516    // setup the initial page and DMA address
517    curAddr = curPrd.getBaseAddr();
518    pageAddr = TheISA::TruncPage(curAddr);
519    dmaAddr = pciToDma(curAddr);
520
521    // clear out the data buffer
522    memset(dataBuffer, 0, MAX_DMA_SIZE);
523
524    while (bytesRead < curPrd.getByteCount()) {
525        // see if we have crossed into a new page
526        if (pageAddr != TheISA::TruncPage(curAddr)) {
527            // write the data to memory
528            memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
529                   (void *)(dataBuffer + (bytesRead - bytesInPage)),
530                   bytesInPage);
531
532            // update the DMA address and page address
533            pageAddr = TheISA::TruncPage(curAddr);
534            dmaAddr = pciToDma(curAddr);
535
536            bytesInPage = 0;
537        }
538
539        if (cmdBytesLeft <= 0)
540            panic("DMA requested data is larger than # sectors specified\n");
541
542        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
543
544        curAddr += SectorSize;
545        bytesRead += SectorSize;
546        bytesInPage += SectorSize;
547        cmdBytesLeft -= SectorSize;
548    }
549
550    // write the last page worth read to memory
551    if (bytesInPage != 0) {
552        memcpy(physmem->dma_addr(dmaAddr, bytesInPage),
553               (void *)(dataBuffer + (bytesRead - bytesInPage)),
554               bytesInPage);
555    }
556
557    // check for the EOT
558    if (curPrd.getEOT()) {
559        assert(cmdBytesLeft == 0);
560        dmaState = Dma_Idle;
561        updateState(ACT_DMA_DONE);
562    } else {
563        doDmaTransfer();
564    }
565}
566
567////
568// Disk utility routines
569///
570
571void
572IdeDisk::readDisk(uint32_t sector, uint8_t *data)
573{
574    uint32_t bytesRead = image->read(data, sector);
575
576    if (bytesRead != SectorSize)
577        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
578              name(), bytesRead, SectorSize, errno);
579}
580
581void
582IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
583{
584    uint32_t bytesWritten = image->write(data, sector);
585
586    if (bytesWritten != SectorSize)
587        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
588              name(), bytesWritten, SectorSize, errno);
589}
590
591////
592// Setup and handle commands
593////
594
595void
596IdeDisk::startDma(const uint32_t &prdTableBase)
597{
598    if (dmaState != Dma_Start)
599        panic("Inconsistent DMA state, should be in Dma_Start!\n");
600
601    if (devState != Transfer_Data_Dma)
602        panic("Inconsistent device state for DMA start!\n");
603
604    // PRD base address is given by bits 31:2
605    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
606
607    dmaState = Dma_Transfer;
608
609    // schedule dma transfer (doDmaTransfer)
610    dmaTransferEvent.schedule(curTick + 1);
611}
612
613void
614IdeDisk::abortDma()
615{
616    if (dmaState == Dma_Idle)
617        panic("Inconsistent DMA state, should be Start or Transfer!");
618
619    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
620        panic("Inconsistent device state, should be Transfer or Prepare!\n");
621
622    updateState(ACT_CMD_ERROR);
623}
624
625void
626IdeDisk::startCommand()
627{
628    DevAction_t action = ACT_NONE;
629    uint32_t size = 0;
630    dmaRead = false;
631
632    // Decode commands
633    switch (cmdReg.command) {
634        // Supported non-data commands
635      case WDSF_READ_NATIVE_MAX:
636        size = image->size() - 1;
637        cmdReg.sec_num = (size & 0xff);
638        cmdReg.cyl_low = ((size & 0xff00) >> 8);
639        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
640        cmdReg.head = ((size & 0xf000000) >> 24);
641
642        devState = Command_Execution;
643        action = ACT_CMD_COMPLETE;
644        break;
645
646      case WDCC_RECAL:
647      case WDCC_IDP:
648      case WDCC_STANDBY_IMMED:
649      case WDCC_FLUSHCACHE:
650      case WDSF_VERIFY:
651      case WDSF_SEEK:
652      case SET_FEATURES:
653      case WDCC_SETMULTI:
654        devState = Command_Execution;
655        action = ACT_CMD_COMPLETE;
656        break;
657
658        // Supported PIO data-in commands
659      case WDCC_IDENTIFY:
660        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
661        devState = Prepare_Data_In;
662        action = ACT_DATA_READY;
663        break;
664
665      case WDCC_READMULTI:
666      case WDCC_READ:
667        if (!(cmdReg.drive & DRIVE_LBA_BIT))
668            panic("Attempt to perform CHS access, only supports LBA\n");
669
670        if (cmdReg.sec_count == 0)
671            cmdBytes = cmdBytesLeft = (256 * SectorSize);
672        else
673            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
674
675        curSector = getLBABase();
676
677        /** @todo make this a scheduled event to simulate disk delay */
678        devState = Prepare_Data_In;
679        action = ACT_DATA_READY;
680        break;
681
682        // Supported PIO data-out commands
683      case WDCC_WRITEMULTI:
684      case WDCC_WRITE:
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        devState = Prepare_Data_Out;
696        action = ACT_DATA_READY;
697        break;
698
699        // Supported DMA commands
700      case WDCC_WRITEDMA:
701        dmaRead = true;  // a write to the disk is a DMA read from memory
702      case WDCC_READDMA:
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_Dma;
714        action = ACT_DMA_READY;
715        break;
716
717      default:
718        panic("Unsupported ATA command: %#x\n", cmdReg.command);
719    }
720
721    if (action != ACT_NONE) {
722        // set the BSY bit
723        status |= STATUS_BSY_BIT;
724        // clear the DRQ bit
725        status &= ~STATUS_DRQ_BIT;
726        // clear the DF bit
727        status &= ~STATUS_DF_BIT;
728
729        updateState(action);
730    }
731}
732
733////
734// Handle setting and clearing interrupts
735////
736
737void
738IdeDisk::intrPost()
739{
740    DPRINTF(IdeDisk, "Posting Interrupt\n");
741    if (intrPending)
742        panic("Attempt to post an interrupt with one pending\n");
743
744    intrPending = true;
745
746    // talk to controller to set interrupt
747    if (ctrl)
748        ctrl->intrPost();
749}
750
751void
752IdeDisk::intrClear()
753{
754    DPRINTF(IdeDisk, "Clearing Interrupt\n");
755    if (!intrPending)
756        panic("Attempt to clear a non-pending interrupt\n");
757
758    intrPending = false;
759
760    // talk to controller to clear interrupt
761    if (ctrl)
762        ctrl->intrClear();
763}
764
765////
766// Manage the device internal state machine
767////
768
769void
770IdeDisk::updateState(DevAction_t action)
771{
772    switch (devState) {
773      case Device_Srst:
774        if (action == ACT_SRST_SET) {
775            // set the BSY bit
776            status |= STATUS_BSY_BIT;
777        } else if (action == ACT_SRST_CLEAR) {
778            // clear the BSY bit
779            status &= ~STATUS_BSY_BIT;
780
781            // reset the device state
782            reset(devID);
783        }
784        break;
785
786      case Device_Idle_S:
787        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
788            devState = Device_Idle_NS;
789        } else if (action == ACT_CMD_WRITE) {
790            startCommand();
791        }
792
793        break;
794
795      case Device_Idle_SI:
796        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
797            devState = Device_Idle_NS;
798            intrClear();
799        } else if (action == ACT_STAT_READ || isIENSet()) {
800            devState = Device_Idle_S;
801            intrClear();
802        } else if (action == ACT_CMD_WRITE) {
803            intrClear();
804            startCommand();
805        }
806
807        break;
808
809      case Device_Idle_NS:
810        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
811            if (!isIENSet() && intrPending) {
812                devState = Device_Idle_SI;
813                intrPost();
814            }
815            if (isIENSet() || !intrPending) {
816                devState = Device_Idle_S;
817            }
818        }
819        break;
820
821      case Command_Execution:
822        if (action == ACT_CMD_COMPLETE) {
823            // clear the BSY bit
824            setComplete();
825
826            if (!isIENSet()) {
827                devState = Device_Idle_SI;
828                intrPost();
829            } else {
830                devState = Device_Idle_S;
831            }
832        }
833        break;
834
835      case Prepare_Data_In:
836        if (action == ACT_CMD_ERROR) {
837            // clear the BSY bit
838            setComplete();
839
840            if (!isIENSet()) {
841                devState = Device_Idle_SI;
842                intrPost();
843            } else {
844                devState = Device_Idle_S;
845            }
846        } else if (action == ACT_DATA_READY) {
847            // clear the BSY bit
848            status &= ~STATUS_BSY_BIT;
849            // set the DRQ bit
850            status |= STATUS_DRQ_BIT;
851
852            // copy the data into the data buffer
853            if (cmdReg.command == WDCC_IDENTIFY) {
854                // Reset the drqBytes for this block
855                drqBytesLeft = sizeof(struct ataparams);
856
857                memcpy((void *)dataBuffer, (void *)&driveID,
858                       sizeof(struct ataparams));
859            } else {
860                // Reset the drqBytes for this block
861                drqBytesLeft = SectorSize;
862
863                readDisk(curSector++, dataBuffer);
864            }
865
866            // put the first two bytes into the data register
867            memcpy((void *)&cmdReg.data0, (void *)dataBuffer,
868                   sizeof(uint16_t));
869
870            if (!isIENSet()) {
871                devState = Data_Ready_INTRQ_In;
872                intrPost();
873            } else {
874                devState = Transfer_Data_In;
875            }
876        }
877        break;
878
879      case Data_Ready_INTRQ_In:
880        if (action == ACT_STAT_READ) {
881            devState = Transfer_Data_In;
882            intrClear();
883        }
884        break;
885
886      case Transfer_Data_In:
887        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
888            if (action == ACT_DATA_READ_BYTE) {
889                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
890            } else {
891                drqBytesLeft -= 2;
892                cmdBytesLeft -= 2;
893
894                // copy next short into data registers
895                if (drqBytesLeft)
896                    memcpy((void *)&cmdReg.data0,
897                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
898                           sizeof(uint16_t));
899            }
900
901            if (drqBytesLeft == 0) {
902                if (cmdBytesLeft == 0) {
903                    // Clear the BSY bit
904                    setComplete();
905                    devState = Device_Idle_S;
906                } else {
907                    devState = Prepare_Data_In;
908                    // set the BSY_BIT
909                    status |= STATUS_BSY_BIT;
910                    // clear the DRQ_BIT
911                    status &= ~STATUS_DRQ_BIT;
912
913                    /** @todo change this to a scheduled event to simulate
914                        disk delay */
915                    updateState(ACT_DATA_READY);
916                }
917            }
918        }
919        break;
920
921      case Prepare_Data_Out:
922        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
923            // clear the BSY bit
924            setComplete();
925
926            if (!isIENSet()) {
927                devState = Device_Idle_SI;
928                intrPost();
929            } else {
930                devState = Device_Idle_S;
931            }
932        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
933            // clear the BSY bit
934            status &= ~STATUS_BSY_BIT;
935            // set the DRQ bit
936            status |= STATUS_DRQ_BIT;
937
938            // clear the data buffer to get it ready for writes
939            memset(dataBuffer, 0, MAX_DMA_SIZE);
940
941            // reset the drqBytes for this block
942            drqBytesLeft = SectorSize;
943
944            if (cmdBytesLeft == cmdBytes || isIENSet()) {
945                devState = Transfer_Data_Out;
946            } else {
947                devState = Data_Ready_INTRQ_Out;
948                intrPost();
949            }
950        }
951        break;
952
953      case Data_Ready_INTRQ_Out:
954        if (action == ACT_STAT_READ) {
955            devState = Transfer_Data_Out;
956            intrClear();
957        }
958        break;
959
960      case Transfer_Data_Out:
961        if (action == ACT_DATA_WRITE_BYTE ||
962            action == ACT_DATA_WRITE_SHORT) {
963
964            if (action == ACT_DATA_READ_BYTE) {
965                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
966            } else {
967                // copy the latest short into the data buffer
968                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
969                       (void *)&cmdReg.data0,
970                       sizeof(uint16_t));
971
972                drqBytesLeft -= 2;
973                cmdBytesLeft -= 2;
974            }
975
976            if (drqBytesLeft == 0) {
977                // copy the block to the disk
978                writeDisk(curSector++, dataBuffer);
979
980                // set the BSY bit
981                status |= STATUS_BSY_BIT;
982                // set the seek bit
983                status |= STATUS_SEEK_BIT;
984                // clear the DRQ bit
985                status &= ~STATUS_DRQ_BIT;
986
987                devState = Prepare_Data_Out;
988
989                /** @todo change this to a scheduled event to simulate
990                    disk delay */
991                updateState(ACT_DATA_READY);
992            }
993        }
994        break;
995
996      case Prepare_Data_Dma:
997        if (action == ACT_CMD_ERROR) {
998            // clear the BSY bit
999            setComplete();
1000
1001            if (!isIENSet()) {
1002                devState = Device_Idle_SI;
1003                intrPost();
1004            } else {
1005                devState = Device_Idle_S;
1006            }
1007        } else if (action == ACT_DMA_READY) {
1008            // clear the BSY bit
1009            status &= ~STATUS_BSY_BIT;
1010            // set the DRQ bit
1011            status |= STATUS_DRQ_BIT;
1012
1013            devState = Transfer_Data_Dma;
1014
1015            if (dmaState != Dma_Idle)
1016                panic("Inconsistent DMA state, should be Dma_Idle\n");
1017
1018            dmaState = Dma_Start;
1019            // wait for the write to the DMA start bit
1020        }
1021        break;
1022
1023      case Transfer_Data_Dma:
1024        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
1025            // clear the BSY bit
1026            setComplete();
1027            // set the seek bit
1028            status |= STATUS_SEEK_BIT;
1029            // clear the controller state for DMA transfer
1030            ctrl->setDmaComplete(this);
1031
1032            if (!isIENSet()) {
1033                devState = Device_Idle_SI;
1034                intrPost();
1035            } else {
1036                devState = Device_Idle_S;
1037            }
1038        }
1039        break;
1040
1041      default:
1042        panic("Unknown IDE device state: %#x\n", devState);
1043    }
1044}
1045
1046void
1047IdeDisk::serialize(ostream &os)
1048{
1049    // Check all outstanding events to see if they are scheduled
1050    // these are all mutually exclusive
1051    Tick reschedule = 0;
1052    Events_t event = None;
1053
1054    int eventCount = 0;
1055
1056    if (dmaTransferEvent.scheduled()) {
1057        reschedule = dmaTransferEvent.when();
1058        event = Transfer;
1059        eventCount++;
1060    }
1061    if (dmaReadWaitEvent.scheduled()) {
1062        reschedule = dmaReadWaitEvent.when();
1063        event = ReadWait;
1064        eventCount++;
1065    }
1066    if (dmaWriteWaitEvent.scheduled()) {
1067        reschedule = dmaWriteWaitEvent.when();
1068        event = WriteWait;
1069        eventCount++;
1070    }
1071    if (dmaPrdReadEvent.scheduled()) {
1072        reschedule = dmaPrdReadEvent.when();
1073        event = PrdRead;
1074        eventCount++;
1075    }
1076    if (dmaReadEvent.scheduled()) {
1077        reschedule = dmaReadEvent.when();
1078        event = DmaRead;
1079        eventCount++;
1080    }
1081    if (dmaWriteEvent.scheduled()) {
1082        reschedule = dmaWriteEvent.when();
1083        event = DmaWrite;
1084        eventCount++;
1085    }
1086
1087    assert(eventCount <= 1);
1088
1089    SERIALIZE_SCALAR(reschedule);
1090    SERIALIZE_ENUM(event);
1091
1092    // Serialize device registers
1093    SERIALIZE_SCALAR(cmdReg.data0);
1094    SERIALIZE_SCALAR(cmdReg.data1);
1095    SERIALIZE_SCALAR(cmdReg.sec_count);
1096    SERIALIZE_SCALAR(cmdReg.sec_num);
1097    SERIALIZE_SCALAR(cmdReg.cyl_low);
1098    SERIALIZE_SCALAR(cmdReg.cyl_high);
1099    SERIALIZE_SCALAR(cmdReg.drive);
1100    SERIALIZE_SCALAR(cmdReg.command);
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(cmdReg.command);
1154    UNSERIALIZE_SCALAR(status);
1155    UNSERIALIZE_SCALAR(nIENBit);
1156    UNSERIALIZE_SCALAR(devID);
1157
1158    // Unserialize the PRD related information
1159    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1160    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1161    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1162    UNSERIALIZE_SCALAR(curPrdAddr);
1163
1164    // Unserialize current transfer related information
1165    UNSERIALIZE_SCALAR(cmdBytes);
1166    UNSERIALIZE_SCALAR(cmdBytesLeft);
1167    UNSERIALIZE_SCALAR(drqBytesLeft);
1168    UNSERIALIZE_SCALAR(curSector);
1169    UNSERIALIZE_SCALAR(dmaRead);
1170    UNSERIALIZE_SCALAR(dmaInterfaceBytes);
1171    UNSERIALIZE_SCALAR(intrPending);
1172    UNSERIALIZE_ENUM(devState);
1173    UNSERIALIZE_ENUM(dmaState);
1174    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1175}
1176
1177#ifndef DOXYGEN_SHOULD_SKIP_THIS
1178
1179enum DriveID { master, slave };
1180static const char *DriveID_strings[] = { "master", "slave" };
1181BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1182
1183    SimObjectParam<DiskImage *> image;
1184    SimObjectParam<PhysicalMemory *> physmem;
1185    SimpleEnumParam<DriveID> driveID;
1186    Param<int> delay;
1187
1188END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
1189
1190BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1191
1192    INIT_PARAM(image, "Disk image"),
1193    INIT_PARAM(physmem, "Physical memory"),
1194    INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings),
1195    INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1)
1196
1197END_INIT_SIM_OBJECT_PARAMS(IdeDisk)
1198
1199
1200CREATE_SIM_OBJECT(IdeDisk)
1201{
1202    return new IdeDisk(getInstanceName(), image, physmem, driveID, delay);
1203}
1204
1205REGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
1206
1207#endif //DOXYGEN_SHOULD_SKIP_THIS
1208