ide_disk.cc revision 12087
1848SN/A/*
29956SN/A * Copyright (c) 2013 ARM Limited
39956SN/A * All rights reserved
49956SN/A *
59956SN/A * The license below extends only to copyright in the software and shall
69956SN/A * not be construed as granting a license to any other intellectual
79956SN/A * property including but not limited to intellectual property relating
89956SN/A * to a hardware implementation of the functionality of the software
99956SN/A * licensed hereunder.  You may use the software subject to the license
109956SN/A * terms below provided that you ensure that this notice is replicated
119956SN/A * unmodified and in its entirety in all distributions of the software,
129956SN/A * modified or unmodified, in source code or in binary form.
139956SN/A *
141762SN/A * Copyright (c) 2004-2005 The Regents of The University of Michigan
15848SN/A * All rights reserved.
16848SN/A *
17848SN/A * Redistribution and use in source and binary forms, with or without
18848SN/A * modification, are permitted provided that the following conditions are
19848SN/A * met: redistributions of source code must retain the above copyright
20848SN/A * notice, this list of conditions and the following disclaimer;
21848SN/A * redistributions in binary form must reproduce the above copyright
22848SN/A * notice, this list of conditions and the following disclaimer in the
23848SN/A * documentation and/or other materials provided with the distribution;
24848SN/A * neither the name of the copyright holders nor the names of its
25848SN/A * contributors may be used to endorse or promote products derived from
26848SN/A * this software without specific prior written permission.
27848SN/A *
28848SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29848SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30848SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31848SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32848SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33848SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34848SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35848SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36848SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37848SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38848SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
392665SN/A *
402665SN/A * Authors: Andrew Schultz
412665SN/A *          Ali Saidi
42848SN/A */
43848SN/A
44848SN/A/** @file
45848SN/A * Device model implementation for an IDE disk
46848SN/A */
47848SN/A
4811264Sandreas.sandberg@arm.com#include "dev/storage/ide_disk.hh"
4911264Sandreas.sandberg@arm.com
50848SN/A#include <cerrno>
51848SN/A#include <cstring>
52848SN/A#include <deque>
53848SN/A#include <string>
54848SN/A
554762SN/A#include "arch/isa_traits.hh"
562565SN/A#include "base/chunk_generator.hh"
57848SN/A#include "base/cprintf.hh" // csprintf
58848SN/A#include "base/trace.hh"
598229SN/A#include "config/the_isa.hh"
608232SN/A#include "debug/IdeDisk.hh"
6111264Sandreas.sandberg@arm.com#include "dev/storage/disk_image.hh"
6211264Sandreas.sandberg@arm.com#include "dev/storage/ide_ctrl.hh"
634762SN/A#include "sim/core.hh"
64848SN/A#include "sim/sim_object.hh"
65848SN/A
66848SN/Ausing namespace std;
672107SN/Ausing namespace TheISA;
68848SN/A
695034SN/AIdeDisk::IdeDisk(const Params *p)
705034SN/A    : SimObject(p), ctrl(NULL), image(p->image), diskDelay(p->delay),
7112087Sspwilson2@wisc.edu      dmaTransferEvent([this]{ doDmaTransfer(); }, name()),
7212087Sspwilson2@wisc.edu      dmaReadCG(NULL),
7312087Sspwilson2@wisc.edu      dmaReadWaitEvent([this]{ doDmaRead(); }, name()),
7412087Sspwilson2@wisc.edu      dmaWriteCG(NULL),
7512087Sspwilson2@wisc.edu      dmaWriteWaitEvent([this]{ doDmaWrite(); }, name()),
7612087Sspwilson2@wisc.edu      dmaPrdReadEvent([this]{ dmaPrdReadDone(); }, name()),
7712087Sspwilson2@wisc.edu      dmaReadEvent([this]{ dmaReadDone(); }, name()),
7812087Sspwilson2@wisc.edu      dmaWriteEvent([this]{ dmaWriteDone(); }, name())
79848SN/A{
80893SN/A    // Reset the device state
815034SN/A    reset(p->driveID);
82893SN/A
83849SN/A    // fill out the drive ID structure
841722SN/A    memset(&driveID, 0, sizeof(struct ataparams));
85849SN/A
86849SN/A    // Calculate LBA and C/H/S values
87849SN/A    uint16_t cylinders;
88849SN/A    uint8_t heads;
89849SN/A    uint8_t sectors;
90849SN/A
91849SN/A    uint32_t lba_size = image->size();
92849SN/A    if (lba_size >= 16383*16*63) {
93849SN/A        cylinders = 16383;
94849SN/A        heads = 16;
95849SN/A        sectors = 63;
96849SN/A    } else {
97849SN/A        if (lba_size >= 63)
98849SN/A            sectors = 63;
9911101SN/A        else if (lba_size == 0)
10011101SN/A            panic("Bad IDE image size: 0\n");
101849SN/A        else
102849SN/A            sectors = lba_size;
103849SN/A
104849SN/A        if ((lba_size / sectors) >= 16)
105849SN/A            heads = 16;
106849SN/A        else
107849SN/A            heads = (lba_size / sectors);
108849SN/A
109849SN/A        cylinders = lba_size / (heads * sectors);
110849SN/A    }
111849SN/A
112849SN/A    // Setup the model name
1131853SN/A    strncpy((char *)driveID.atap_model, "5MI EDD si k",
1141853SN/A            sizeof(driveID.atap_model));
115849SN/A    // Set the maximum multisector transfer size
1161722SN/A    driveID.atap_multi = MAX_MULTSECT;
117849SN/A    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
1181722SN/A    driveID.atap_capabilities1 = 0x7;
119849SN/A    // UDMA support, EIDE support
1201722SN/A    driveID.atap_extensions = 0x6;
121849SN/A    // Setup default C/H/S settings
1221722SN/A    driveID.atap_cylinders = cylinders;
1231722SN/A    driveID.atap_sectors = sectors;
1241722SN/A    driveID.atap_heads = heads;
125849SN/A    // Setup the current multisector transfer size
1261722SN/A    driveID.atap_curmulti = MAX_MULTSECT;
1271722SN/A    driveID.atap_curmulti_valid = 0x1;
128849SN/A    // Number of sectors on disk
1291722SN/A    driveID.atap_capacity = lba_size;
130849SN/A    // Multiword DMA mode 2 and below supported
1312989SN/A    driveID.atap_dmamode_supp = 0x4;
132849SN/A    // Set PIO mode 4 and 3 supported
1331722SN/A    driveID.atap_piomode_supp = 0x3;
134849SN/A    // Set DMA mode 4 and below supported
1351886SN/A    driveID.atap_udmamode_supp = 0x1f;
136849SN/A    // Statically set hardware config word
1371722SN/A    driveID.atap_hwreset_res = 0x4001;
1381817SN/A
1391817SN/A    //arbitrary for now...
1401817SN/A    driveID.atap_ata_major = WDC_VER_ATA7;
141893SN/A}
142893SN/A
143893SN/AIdeDisk::~IdeDisk()
144893SN/A{
145893SN/A    // destroy the data buffer
146893SN/A    delete [] dataBuffer;
147893SN/A}
148893SN/A
149893SN/Avoid
150893SN/AIdeDisk::reset(int id)
151893SN/A{
152893SN/A    // initialize the data buffer and shadow registers
153893SN/A    dataBuffer = new uint8_t[MAX_DMA_SIZE];
154893SN/A
155893SN/A    memset(dataBuffer, 0, MAX_DMA_SIZE);
156893SN/A    memset(&cmdReg, 0, sizeof(CommandReg_t));
157893SN/A    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
158893SN/A
159893SN/A    curPrdAddr = 0;
160893SN/A    curSector = 0;
161893SN/A    cmdBytes = 0;
162893SN/A    cmdBytesLeft = 0;
163893SN/A    drqBytesLeft = 0;
164893SN/A    dmaRead = false;
165893SN/A    intrPending = false;
1669956SN/A    dmaAborted = false;
167849SN/A
168849SN/A    // set the device state to idle
169849SN/A    dmaState = Dma_Idle;
170849SN/A
171849SN/A    if (id == DEV0) {
172849SN/A        devState = Device_Idle_S;
173849SN/A        devID = DEV0;
174849SN/A    } else if (id == DEV1) {
175849SN/A        devState = Device_Idle_NS;
176849SN/A        devID = DEV1;
177849SN/A    } else {
178849SN/A        panic("Invalid device ID: %#x\n", id);
179849SN/A    }
180849SN/A
181849SN/A    // set the device ready bit
182893SN/A    status = STATUS_DRDY_BIT;
1831817SN/A
1841817SN/A    /* The error register must be set to 0x1 on start-up to
1851817SN/A       indicate that no diagnostic error was detected */
1861817SN/A    cmdReg.error = 0x1;
187849SN/A}
188849SN/A
189864SN/A////
190864SN/A// Utility functions
191864SN/A////
192864SN/A
193929SN/Abool
194929SN/AIdeDisk::isDEVSelect()
195929SN/A{
196929SN/A    return ctrl->isDiskSelected(this);
197929SN/A}
198929SN/A
199861SN/AAddr
200864SN/AIdeDisk::pciToDma(Addr pciAddr)
201861SN/A{
202861SN/A    if (ctrl)
2035772SN/A        return ctrl->pciToDma(pciAddr);
204861SN/A    else
205861SN/A        panic("Access to unset controller!\n");
206861SN/A}
207861SN/A
208849SN/A////
209849SN/A// Device registers read/write
210849SN/A////
211849SN/A
212849SN/Avoid
2135772SN/AIdeDisk::readCommand(const Addr offset, int size, uint8_t *data)
214849SN/A{
2155772SN/A    if (offset == DATA_OFFSET) {
2165772SN/A        if (size == sizeof(uint16_t)) {
2175772SN/A            *(uint16_t *)data = cmdReg.data;
2185772SN/A        } else if (size == sizeof(uint32_t)) {
2195772SN/A            *(uint16_t *)data = cmdReg.data;
2205772SN/A            updateState(ACT_DATA_READ_SHORT);
2215772SN/A            *((uint16_t *)data + 1) = cmdReg.data;
2225772SN/A        } else {
2235772SN/A            panic("Data read of unsupported size %d.\n", size);
2241817SN/A        }
2255772SN/A        updateState(ACT_DATA_READ_SHORT);
2265772SN/A        return;
2275772SN/A    }
2285772SN/A    assert(size == sizeof(uint8_t));
2295772SN/A    switch (offset) {
2305772SN/A      case ERROR_OFFSET:
2315772SN/A        *data = cmdReg.error;
2321817SN/A        break;
2335772SN/A      case NSECTOR_OFFSET:
2345772SN/A        *data = cmdReg.sec_count;
2355772SN/A        break;
2365772SN/A      case SECTOR_OFFSET:
2375772SN/A        *data = cmdReg.sec_num;
2385772SN/A        break;
2395772SN/A      case LCYL_OFFSET:
2405772SN/A        *data = cmdReg.cyl_low;
2415772SN/A        break;
2425772SN/A      case HCYL_OFFSET:
2435772SN/A        *data = cmdReg.cyl_high;
2445772SN/A        break;
2455772SN/A      case DRIVE_OFFSET:
2465772SN/A        *data = cmdReg.drive;
2475772SN/A        break;
2485772SN/A      case STATUS_OFFSET:
2495772SN/A        *data = status;
2505772SN/A        updateState(ACT_STAT_READ);
2511817SN/A        break;
2521817SN/A      default:
2535772SN/A        panic("Invalid IDE command register offset: %#x\n", offset);
254849SN/A    }
2555772SN/A    DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
256849SN/A}
257849SN/A
258849SN/Avoid
2595772SN/AIdeDisk::readControl(const Addr offset, int size, uint8_t *data)
260849SN/A{
2615772SN/A    assert(size == sizeof(uint8_t));
2625772SN/A    *data = status;
2635772SN/A    if (offset != ALTSTAT_OFFSET)
2645772SN/A        panic("Invalid IDE control register offset: %#x\n", offset);
2655772SN/A    DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
2665772SN/A}
267849SN/A
2685772SN/Avoid
2695772SN/AIdeDisk::writeCommand(const Addr offset, int size, const uint8_t *data)
2705772SN/A{
2715772SN/A    if (offset == DATA_OFFSET) {
2725772SN/A        if (size == sizeof(uint16_t)) {
2735772SN/A            cmdReg.data = *(const uint16_t *)data;
2745772SN/A        } else if (size == sizeof(uint32_t)) {
2755772SN/A            cmdReg.data = *(const uint16_t *)data;
2765772SN/A            updateState(ACT_DATA_WRITE_SHORT);
2775772SN/A            cmdReg.data = *((const uint16_t *)data + 1);
2785772SN/A        } else {
2795772SN/A            panic("Data write of unsupported size %d.\n", size);
2801817SN/A        }
2815772SN/A        updateState(ACT_DATA_WRITE_SHORT);
2825772SN/A        return;
2835772SN/A    }
2845772SN/A
2855772SN/A    assert(size == sizeof(uint8_t));
2865772SN/A    switch (offset) {
2875772SN/A      case FEATURES_OFFSET:
2881817SN/A        break;
2895772SN/A      case NSECTOR_OFFSET:
2905772SN/A        cmdReg.sec_count = *data;
2915772SN/A        break;
2925772SN/A      case SECTOR_OFFSET:
2935772SN/A        cmdReg.sec_num = *data;
2945772SN/A        break;
2955772SN/A      case LCYL_OFFSET:
2965772SN/A        cmdReg.cyl_low = *data;
2975772SN/A        break;
2985772SN/A      case HCYL_OFFSET:
2995772SN/A        cmdReg.cyl_high = *data;
3005772SN/A        break;
3015772SN/A      case DRIVE_OFFSET:
3025772SN/A        cmdReg.drive = *data;
3035772SN/A        updateState(ACT_SELECT_WRITE);
3045772SN/A        break;
3055772SN/A      case COMMAND_OFFSET:
3065772SN/A        cmdReg.command = *data;
3075772SN/A        updateState(ACT_CMD_WRITE);
3081817SN/A        break;
3091817SN/A      default:
3105772SN/A        panic("Invalid IDE command register offset: %#x\n", offset);
311849SN/A    }
3125772SN/A    DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
3135772SN/A            (uint32_t)*data);
3145772SN/A}
3155772SN/A
3165772SN/Avoid
3175772SN/AIdeDisk::writeControl(const Addr offset, int size, const uint8_t *data)
3185772SN/A{
3195772SN/A    if (offset != CONTROL_OFFSET)
3205772SN/A        panic("Invalid IDE control register offset: %#x\n", offset);
3215772SN/A
3225772SN/A    if (*data & CONTROL_RST_BIT) {
3235772SN/A        // force the device into the reset state
3245772SN/A        devState = Device_Srst;
3255772SN/A        updateState(ACT_SRST_SET);
3265772SN/A    } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
3275772SN/A        updateState(ACT_SRST_CLEAR);
3285772SN/A    }
3295772SN/A
3305772SN/A    nIENBit = *data & CONTROL_IEN_BIT;
331849SN/A
3322566SN/A    DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
3332566SN/A            (uint32_t)*data);
334849SN/A}
335849SN/A
336849SN/A////
337849SN/A// Perform DMA transactions
338849SN/A////
339849SN/A
340849SN/Avoid
341849SN/AIdeDisk::doDmaTransfer()
342849SN/A{
3439956SN/A    if (dmaAborted) {
3449956SN/A        DPRINTF(IdeDisk, "DMA Aborted before reading PRD entry\n");
3459956SN/A        updateState(ACT_CMD_ERROR);
3469956SN/A        return;
3479956SN/A    }
3489956SN/A
349849SN/A    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
350849SN/A        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
351849SN/A              dmaState, devState);
352849SN/A
35310913SN/A    if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
3547823SN/A        schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
3552565SN/A        return;
3562565SN/A    } else
3572565SN/A        ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
3582565SN/A                (uint8_t*)&curPrd.entry);
359849SN/A}
360849SN/A
361849SN/Avoid
362849SN/AIdeDisk::dmaPrdReadDone()
363849SN/A{
3649956SN/A    if (dmaAborted) {
3659956SN/A        DPRINTF(IdeDisk, "DMA Aborted while reading PRD entry\n");
3669956SN/A        updateState(ACT_CMD_ERROR);
3679956SN/A        return;
3689956SN/A    }
3698522SN/A
3701625SN/A    DPRINTF(IdeDisk,
3711625SN/A            "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
372896SN/A            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
373896SN/A            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
374896SN/A            curPrd.getEOT(), curSector);
375896SN/A
376989SN/A    // the prd pointer has already been translated, so just do an increment
377989SN/A    curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
378849SN/A
379849SN/A    if (dmaRead)
3802565SN/A        doDmaDataRead();
381849SN/A    else
3822565SN/A        doDmaDataWrite();
383849SN/A}
384849SN/A
385849SN/Avoid
3862565SN/AIdeDisk::doDmaDataRead()
387849SN/A{
3881763SN/A    /** @todo we need to figure out what the delay actually will be */
389864SN/A    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
390849SN/A
3911634SN/A    DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
3921634SN/A            diskDelay, totalDiskDelay);
393849SN/A
3947823SN/A    schedule(dmaReadWaitEvent, curTick() + totalDiskDelay);
3952565SN/A}
396864SN/A
3972627SN/Avoid
3982627SN/AIdeDisk::regStats()
3992627SN/A{
40011522Sstephan.diestelhorst@arm.com    SimObject::regStats();
40111522Sstephan.diestelhorst@arm.com
4022627SN/A    using namespace Stats;
4032627SN/A    dmaReadFullPages
4042627SN/A        .name(name() + ".dma_read_full_pages")
4052627SN/A        .desc("Number of full page size DMA reads (not PRD).")
4062627SN/A        ;
4072627SN/A    dmaReadBytes
4082627SN/A        .name(name() + ".dma_read_bytes")
4092627SN/A        .desc("Number of bytes transfered via DMA reads (not PRD).")
4102627SN/A        ;
4112627SN/A    dmaReadTxs
4122627SN/A        .name(name() + ".dma_read_txs")
4132627SN/A        .desc("Number of DMA read transactions (not PRD).")
4142627SN/A        ;
4152627SN/A
4162627SN/A    dmaWriteFullPages
4172627SN/A        .name(name() + ".dma_write_full_pages")
4182627SN/A        .desc("Number of full page size DMA writes.")
4192627SN/A        ;
4202627SN/A    dmaWriteBytes
4212627SN/A        .name(name() + ".dma_write_bytes")
4222627SN/A        .desc("Number of bytes transfered via DMA writes.")
4232627SN/A        ;
4242627SN/A    dmaWriteTxs
4252627SN/A        .name(name() + ".dma_write_txs")
4262627SN/A        .desc("Number of DMA write transactions.")
4272627SN/A        ;
4282627SN/A}
429864SN/A
4302565SN/Avoid
4312565SN/AIdeDisk::doDmaRead()
4322565SN/A{
4339956SN/A    if (dmaAborted) {
4349956SN/A        DPRINTF(IdeDisk, "DMA Aborted in middle of Dma Read\n");
4359956SN/A        if (dmaReadCG)
4369956SN/A            delete dmaReadCG;
4379956SN/A        dmaReadCG = NULL;
4389956SN/A        updateState(ACT_CMD_ERROR);
4399956SN/A        return;
4409956SN/A    }
441864SN/A
4422565SN/A    if (!dmaReadCG) {
4432565SN/A        // clear out the data buffer
4442565SN/A        memset(dataBuffer, 0, MAX_DMA_SIZE);
4452565SN/A        dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(),
4462565SN/A                curPrd.getByteCount(), TheISA::PageBytes);
4472565SN/A
4482565SN/A    }
44910913SN/A    if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
4507823SN/A        schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
4512565SN/A        return;
4522565SN/A    } else if (!dmaReadCG->done()) {
4532565SN/A        assert(dmaReadCG->complete() < MAX_DMA_SIZE);
4542565SN/A        ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
4552565SN/A                &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
4562627SN/A        dmaReadBytes += dmaReadCG->size();
4572627SN/A        dmaReadTxs++;
4582627SN/A        if (dmaReadCG->size() == TheISA::PageBytes)
4592627SN/A            dmaReadFullPages++;
4602565SN/A        dmaReadCG->next();
461849SN/A    } else {
4622565SN/A        assert(dmaReadCG->done());
4632565SN/A        delete dmaReadCG;
4642565SN/A        dmaReadCG = NULL;
4652565SN/A        dmaReadDone();
466849SN/A    }
467849SN/A}
468849SN/A
469849SN/Avoid
470849SN/AIdeDisk::dmaReadDone()
471849SN/A{
4722565SN/A    uint32_t bytesWritten = 0;
473861SN/A
474861SN/A    // write the data to the disk image
4752565SN/A    for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
476864SN/A         bytesWritten += SectorSize) {
477861SN/A
4782565SN/A        cmdBytesLeft -= SectorSize;
479861SN/A        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
480864SN/A    }
481861SN/A
482849SN/A    // check for the EOT
483920SN/A    if (curPrd.getEOT()) {
484849SN/A        assert(cmdBytesLeft == 0);
485849SN/A        dmaState = Dma_Idle;
486849SN/A        updateState(ACT_DMA_DONE);
487849SN/A    } else {
488849SN/A        doDmaTransfer();
489849SN/A    }
490849SN/A}
491849SN/A
492849SN/Avoid
4932565SN/AIdeDisk::doDmaDataWrite()
494849SN/A{
4951763SN/A    /** @todo we need to figure out what the delay actually will be */
496864SN/A    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
4972565SN/A    uint32_t bytesRead = 0;
498849SN/A
4991634SN/A    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
5001634SN/A            diskDelay, totalDiskDelay);
5011634SN/A
5022565SN/A    memset(dataBuffer, 0, MAX_DMA_SIZE);
5032565SN/A    assert(cmdBytesLeft <= MAX_DMA_SIZE);
5042565SN/A    while (bytesRead < curPrd.getByteCount()) {
5052565SN/A        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
5062565SN/A        bytesRead += SectorSize;
5072565SN/A        cmdBytesLeft -= SectorSize;
5082565SN/A    }
5098522SN/A    DPRINTF(IdeDisk, "doDmaWrite, bytesRead: %d cmdBytesLeft: %d\n",
5108522SN/A            bytesRead, cmdBytesLeft);
511849SN/A
5127823SN/A    schedule(dmaWriteWaitEvent, curTick() + totalDiskDelay);
5132565SN/A}
514864SN/A
5152565SN/Avoid
5162565SN/AIdeDisk::doDmaWrite()
5172565SN/A{
5189956SN/A    if (dmaAborted) {
5199956SN/A        DPRINTF(IdeDisk, "DMA Aborted while doing DMA Write\n");
5209956SN/A        if (dmaWriteCG)
5219956SN/A            delete dmaWriteCG;
5229956SN/A        dmaWriteCG = NULL;
5239956SN/A        updateState(ACT_CMD_ERROR);
5249956SN/A        return;
5259956SN/A    }
5262565SN/A    if (!dmaWriteCG) {
5272565SN/A        // clear out the data buffer
5282565SN/A        dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
5292565SN/A                curPrd.getByteCount(), TheISA::PageBytes);
5302565SN/A    }
53110913SN/A    if (ctrl->dmaPending() || ctrl->drainState() != DrainState::Running) {
5327823SN/A        schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
5338522SN/A        DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
5342565SN/A        return;
5352565SN/A    } else if (!dmaWriteCG->done()) {
5362565SN/A        assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
5372565SN/A        ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
5382565SN/A                &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
5398522SN/A        DPRINTF(IdeDisk, "doDmaWrite: not done curPrd byte count %d, eot %#x\n",
5408522SN/A                curPrd.getByteCount(), curPrd.getEOT());
5412627SN/A        dmaWriteBytes += dmaWriteCG->size();
5422627SN/A        dmaWriteTxs++;
5432627SN/A        if (dmaWriteCG->size() == TheISA::PageBytes)
5442627SN/A            dmaWriteFullPages++;
5452565SN/A        dmaWriteCG->next();
546849SN/A    } else {
5478522SN/A        DPRINTF(IdeDisk, "doDmaWrite: done curPrd byte count %d, eot %#x\n",
5488522SN/A                curPrd.getByteCount(), curPrd.getEOT());
5492565SN/A        assert(dmaWriteCG->done());
5502565SN/A        delete dmaWriteCG;
5512565SN/A        dmaWriteCG = NULL;
5522565SN/A        dmaWriteDone();
553849SN/A    }
554849SN/A}
555849SN/A
556849SN/Avoid
557849SN/AIdeDisk::dmaWriteDone()
558849SN/A{
5598522SN/A    DPRINTF(IdeDisk, "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
5608522SN/A                curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
561849SN/A    // check for the EOT
562849SN/A    if (curPrd.getEOT()) {
563849SN/A        assert(cmdBytesLeft == 0);
564849SN/A        dmaState = Dma_Idle;
565849SN/A        updateState(ACT_DMA_DONE);
566849SN/A    } else {
567849SN/A        doDmaTransfer();
568849SN/A    }
569849SN/A}
570849SN/A
571849SN/A////
572849SN/A// Disk utility routines
573849SN/A///
574849SN/A
575849SN/Avoid
576849SN/AIdeDisk::readDisk(uint32_t sector, uint8_t *data)
577849SN/A{
578849SN/A    uint32_t bytesRead = image->read(data, sector);
579849SN/A
580849SN/A    if (bytesRead != SectorSize)
581849SN/A        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
582849SN/A              name(), bytesRead, SectorSize, errno);
583849SN/A}
584849SN/A
585849SN/Avoid
586849SN/AIdeDisk::writeDisk(uint32_t sector, uint8_t *data)
587849SN/A{
588849SN/A    uint32_t bytesWritten = image->write(data, sector);
589849SN/A
590849SN/A    if (bytesWritten != SectorSize)
591849SN/A        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
592849SN/A              name(), bytesWritten, SectorSize, errno);
593849SN/A}
594849SN/A
595849SN/A////
596849SN/A// Setup and handle commands
597849SN/A////
598849SN/A
599849SN/Avoid
600849SN/AIdeDisk::startDma(const uint32_t &prdTableBase)
601849SN/A{
602849SN/A    if (dmaState != Dma_Start)
603849SN/A        panic("Inconsistent DMA state, should be in Dma_Start!\n");
604849SN/A
605849SN/A    if (devState != Transfer_Data_Dma)
606849SN/A        panic("Inconsistent device state for DMA start!\n");
607849SN/A
608896SN/A    // PRD base address is given by bits 31:2
609896SN/A    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
610849SN/A
611849SN/A    dmaState = Dma_Transfer;
612849SN/A
613849SN/A    // schedule dma transfer (doDmaTransfer)
6147823SN/A    schedule(dmaTransferEvent, curTick() + 1);
615849SN/A}
616849SN/A
617849SN/Avoid
618849SN/AIdeDisk::abortDma()
619849SN/A{
620849SN/A    if (dmaState == Dma_Idle)
6211625SN/A        panic("Inconsistent DMA state, should be Start or Transfer!");
622849SN/A
623849SN/A    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
6241625SN/A        panic("Inconsistent device state, should be Transfer or Prepare!\n");
625849SN/A
626849SN/A    updateState(ACT_CMD_ERROR);
627849SN/A}
628849SN/A
629849SN/Avoid
630849SN/AIdeDisk::startCommand()
631849SN/A{
632849SN/A    DevAction_t action = ACT_NONE;
633849SN/A    uint32_t size = 0;
634849SN/A    dmaRead = false;
635849SN/A
636849SN/A    // Decode commands
637849SN/A    switch (cmdReg.command) {
638849SN/A        // Supported non-data commands
6391722SN/A      case WDSF_READ_NATIVE_MAX:
6409533SN/A        size = (uint32_t)image->size() - 1;
641849SN/A        cmdReg.sec_num = (size & 0xff);
642849SN/A        cmdReg.cyl_low = ((size & 0xff00) >> 8);
643849SN/A        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
644849SN/A        cmdReg.head = ((size & 0xf000000) >> 24);
645849SN/A
646849SN/A        devState = Command_Execution;
647849SN/A        action = ACT_CMD_COMPLETE;
648849SN/A        break;
649849SN/A
6501722SN/A      case WDCC_RECAL:
6511722SN/A      case WDCC_IDP:
6521722SN/A      case WDCC_STANDBY_IMMED:
6531722SN/A      case WDCC_FLUSHCACHE:
6541722SN/A      case WDSF_VERIFY:
6551722SN/A      case WDSF_SEEK:
6561722SN/A      case SET_FEATURES:
6571722SN/A      case WDCC_SETMULTI:
65810587SN/A      case WDCC_IDLE:
659849SN/A        devState = Command_Execution;
660849SN/A        action = ACT_CMD_COMPLETE;
661849SN/A        break;
662849SN/A
663849SN/A        // Supported PIO data-in commands
6641722SN/A      case WDCC_IDENTIFY:
66511980Sjason@lowepower.com      case ATAPI_IDENTIFY_DEVICE:
6661722SN/A        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
667849SN/A        devState = Prepare_Data_In;
668849SN/A        action = ACT_DATA_READY;
669849SN/A        break;
670849SN/A
6711722SN/A      case WDCC_READMULTI:
6721722SN/A      case WDCC_READ:
673849SN/A        if (!(cmdReg.drive & DRIVE_LBA_BIT))
674849SN/A            panic("Attempt to perform CHS access, only supports LBA\n");
675849SN/A
676849SN/A        if (cmdReg.sec_count == 0)
677893SN/A            cmdBytes = cmdBytesLeft = (256 * SectorSize);
678849SN/A        else
679893SN/A            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
680849SN/A
681849SN/A        curSector = getLBABase();
682849SN/A
683877SN/A        /** @todo make this a scheduled event to simulate disk delay */
684849SN/A        devState = Prepare_Data_In;
685849SN/A        action = ACT_DATA_READY;
686849SN/A        break;
687849SN/A
688849SN/A        // Supported PIO data-out commands
6891722SN/A      case WDCC_WRITEMULTI:
6901722SN/A      case WDCC_WRITE:
691849SN/A        if (!(cmdReg.drive & DRIVE_LBA_BIT))
692849SN/A            panic("Attempt to perform CHS access, only supports LBA\n");
693849SN/A
694849SN/A        if (cmdReg.sec_count == 0)
695893SN/A            cmdBytes = cmdBytesLeft = (256 * SectorSize);
696849SN/A        else
697893SN/A            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
6988522SN/A        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d\n", cmdBytesLeft);
699849SN/A        curSector = getLBABase();
700849SN/A
701849SN/A        devState = Prepare_Data_Out;
702849SN/A        action = ACT_DATA_READY;
703849SN/A        break;
704849SN/A
705849SN/A        // Supported DMA commands
7061722SN/A      case WDCC_WRITEDMA:
707849SN/A        dmaRead = true;  // a write to the disk is a DMA read from memory
7081722SN/A      case WDCC_READDMA:
709849SN/A        if (!(cmdReg.drive & DRIVE_LBA_BIT))
710849SN/A            panic("Attempt to perform CHS access, only supports LBA\n");
711849SN/A
712849SN/A        if (cmdReg.sec_count == 0)
713893SN/A            cmdBytes = cmdBytesLeft = (256 * SectorSize);
714849SN/A        else
715893SN/A            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
7168522SN/A        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
717849SN/A
718849SN/A        curSector = getLBABase();
719849SN/A
720849SN/A        devState = Prepare_Data_Dma;
721849SN/A        action = ACT_DMA_READY;
722849SN/A        break;
723849SN/A
724849SN/A      default:
725849SN/A        panic("Unsupported ATA command: %#x\n", cmdReg.command);
726849SN/A    }
727849SN/A
728849SN/A    if (action != ACT_NONE) {
729849SN/A        // set the BSY bit
730893SN/A        status |= STATUS_BSY_BIT;
731849SN/A        // clear the DRQ bit
732893SN/A        status &= ~STATUS_DRQ_BIT;
733893SN/A        // clear the DF bit
734893SN/A        status &= ~STATUS_DF_BIT;
735849SN/A
736849SN/A        updateState(action);
737849SN/A    }
738849SN/A}
739849SN/A
740849SN/A////
741849SN/A// Handle setting and clearing interrupts
742849SN/A////
743849SN/A
744849SN/Avoid
745849SN/AIdeDisk::intrPost()
746849SN/A{
7471625SN/A    DPRINTF(IdeDisk, "Posting Interrupt\n");
748849SN/A    if (intrPending)
749849SN/A        panic("Attempt to post an interrupt with one pending\n");
750849SN/A
751849SN/A    intrPending = true;
752849SN/A
753849SN/A    // talk to controller to set interrupt
7541817SN/A    if (ctrl) {
755849SN/A        ctrl->intrPost();
7561817SN/A    }
757849SN/A}
758849SN/A
759849SN/Avoid
760849SN/AIdeDisk::intrClear()
761849SN/A{
7621625SN/A    DPRINTF(IdeDisk, "Clearing Interrupt\n");
763849SN/A    if (!intrPending)
764849SN/A        panic("Attempt to clear a non-pending interrupt\n");
765849SN/A
766849SN/A    intrPending = false;
767849SN/A
768849SN/A    // talk to controller to clear interrupt
769849SN/A    if (ctrl)
770849SN/A        ctrl->intrClear();
771849SN/A}
772849SN/A
773849SN/A////
774849SN/A// Manage the device internal state machine
775849SN/A////
776849SN/A
777849SN/Avoid
778849SN/AIdeDisk::updateState(DevAction_t action)
779849SN/A{
780849SN/A    switch (devState) {
781893SN/A      case Device_Srst:
782893SN/A        if (action == ACT_SRST_SET) {
783893SN/A            // set the BSY bit
784893SN/A            status |= STATUS_BSY_BIT;
785893SN/A        } else if (action == ACT_SRST_CLEAR) {
786893SN/A            // clear the BSY bit
787893SN/A            status &= ~STATUS_BSY_BIT;
788893SN/A
789893SN/A            // reset the device state
790893SN/A            reset(devID);
791893SN/A        }
792893SN/A        break;
793893SN/A
794849SN/A      case Device_Idle_S:
795893SN/A        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
796849SN/A            devState = Device_Idle_NS;
797893SN/A        } else if (action == ACT_CMD_WRITE) {
798849SN/A            startCommand();
799893SN/A        }
800849SN/A
801849SN/A        break;
802849SN/A
803849SN/A      case Device_Idle_SI:
804893SN/A        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
805849SN/A            devState = Device_Idle_NS;
806849SN/A            intrClear();
807849SN/A        } else if (action == ACT_STAT_READ || isIENSet()) {
808849SN/A            devState = Device_Idle_S;
809849SN/A            intrClear();
810849SN/A        } else if (action == ACT_CMD_WRITE) {
811849SN/A            intrClear();
812849SN/A            startCommand();
813849SN/A        }
814849SN/A
815849SN/A        break;
816849SN/A
817849SN/A      case Device_Idle_NS:
818893SN/A        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
819849SN/A            if (!isIENSet() && intrPending) {
820849SN/A                devState = Device_Idle_SI;
821849SN/A                intrPost();
822849SN/A            }
823849SN/A            if (isIENSet() || !intrPending) {
824849SN/A                devState = Device_Idle_S;
825849SN/A            }
826849SN/A        }
827849SN/A        break;
828849SN/A
829849SN/A      case Command_Execution:
830849SN/A        if (action == ACT_CMD_COMPLETE) {
831849SN/A            // clear the BSY bit
832849SN/A            setComplete();
833849SN/A
834849SN/A            if (!isIENSet()) {
835849SN/A                devState = Device_Idle_SI;
836849SN/A                intrPost();
837849SN/A            } else {
838849SN/A                devState = Device_Idle_S;
839849SN/A            }
840849SN/A        }
841849SN/A        break;
842849SN/A
843849SN/A      case Prepare_Data_In:
844849SN/A        if (action == ACT_CMD_ERROR) {
845849SN/A            // clear the BSY bit
846849SN/A            setComplete();
847849SN/A
848849SN/A            if (!isIENSet()) {
849849SN/A                devState = Device_Idle_SI;
850849SN/A                intrPost();
851849SN/A            } else {
852849SN/A                devState = Device_Idle_S;
853849SN/A            }
854849SN/A        } else if (action == ACT_DATA_READY) {
855849SN/A            // clear the BSY bit
856893SN/A            status &= ~STATUS_BSY_BIT;
857849SN/A            // set the DRQ bit
858893SN/A            status |= STATUS_DRQ_BIT;
859849SN/A
860877SN/A            // copy the data into the data buffer
86111980Sjason@lowepower.com            if (cmdReg.command == WDCC_IDENTIFY ||
86211980Sjason@lowepower.com                cmdReg.command == ATAPI_IDENTIFY_DEVICE) {
863877SN/A                // Reset the drqBytes for this block
8641722SN/A                drqBytesLeft = sizeof(struct ataparams);
865877SN/A
866877SN/A                memcpy((void *)dataBuffer, (void *)&driveID,
8671722SN/A                       sizeof(struct ataparams));
868877SN/A            } else {
869877SN/A                // Reset the drqBytes for this block
870877SN/A                drqBytesLeft = SectorSize;
871877SN/A
872877SN/A                readDisk(curSector++, dataBuffer);
873877SN/A            }
874877SN/A
875849SN/A            // put the first two bytes into the data register
8761817SN/A            memcpy((void *)&cmdReg.data, (void *)dataBuffer,
877857SN/A                   sizeof(uint16_t));
878857SN/A
879849SN/A            if (!isIENSet()) {
880849SN/A                devState = Data_Ready_INTRQ_In;
881849SN/A                intrPost();
882849SN/A            } else {
883849SN/A                devState = Transfer_Data_In;
884849SN/A            }
885849SN/A        }
886849SN/A        break;
887849SN/A
888849SN/A      case Data_Ready_INTRQ_In:
889849SN/A        if (action == ACT_STAT_READ) {
890849SN/A            devState = Transfer_Data_In;
891849SN/A            intrClear();
892849SN/A        }
893849SN/A        break;
894849SN/A
895849SN/A      case Transfer_Data_In:
896849SN/A        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
897849SN/A            if (action == ACT_DATA_READ_BYTE) {
898849SN/A                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
899849SN/A            } else {
900849SN/A                drqBytesLeft -= 2;
901849SN/A                cmdBytesLeft -= 2;
902849SN/A
903849SN/A                // copy next short into data registers
904877SN/A                if (drqBytesLeft)
9051817SN/A                    memcpy((void *)&cmdReg.data,
906877SN/A                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
907877SN/A                           sizeof(uint16_t));
908849SN/A            }
909849SN/A
910849SN/A            if (drqBytesLeft == 0) {
911849SN/A                if (cmdBytesLeft == 0) {
912849SN/A                    // Clear the BSY bit
913849SN/A                    setComplete();
914849SN/A                    devState = Device_Idle_S;
915849SN/A                } else {
916849SN/A                    devState = Prepare_Data_In;
917877SN/A                    // set the BSY_BIT
918893SN/A                    status |= STATUS_BSY_BIT;
919877SN/A                    // clear the DRQ_BIT
920893SN/A                    status &= ~STATUS_DRQ_BIT;
921877SN/A
922877SN/A                    /** @todo change this to a scheduled event to simulate
923877SN/A                        disk delay */
924877SN/A                    updateState(ACT_DATA_READY);
925849SN/A                }
926849SN/A            }
927849SN/A        }
928849SN/A        break;
929849SN/A
930849SN/A      case Prepare_Data_Out:
931849SN/A        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
932849SN/A            // clear the BSY bit
933849SN/A            setComplete();
934849SN/A
935849SN/A            if (!isIENSet()) {
936849SN/A                devState = Device_Idle_SI;
937849SN/A                intrPost();
938849SN/A            } else {
939849SN/A                devState = Device_Idle_S;
940849SN/A            }
941893SN/A        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
942849SN/A            // clear the BSY bit
943893SN/A            status &= ~STATUS_BSY_BIT;
944849SN/A            // set the DRQ bit
945893SN/A            status |= STATUS_DRQ_BIT;
946849SN/A
947849SN/A            // clear the data buffer to get it ready for writes
948849SN/A            memset(dataBuffer, 0, MAX_DMA_SIZE);
949849SN/A
950893SN/A            // reset the drqBytes for this block
951893SN/A            drqBytesLeft = SectorSize;
952893SN/A
953893SN/A            if (cmdBytesLeft == cmdBytes || isIENSet()) {
954893SN/A                devState = Transfer_Data_Out;
955893SN/A            } else {
956849SN/A                devState = Data_Ready_INTRQ_Out;
957849SN/A                intrPost();
958849SN/A            }
959849SN/A        }
960849SN/A        break;
961849SN/A
962849SN/A      case Data_Ready_INTRQ_Out:
963849SN/A        if (action == ACT_STAT_READ) {
964849SN/A            devState = Transfer_Data_Out;
965849SN/A            intrClear();
966849SN/A        }
967849SN/A        break;
968849SN/A
969849SN/A      case Transfer_Data_Out:
970857SN/A        if (action == ACT_DATA_WRITE_BYTE ||
971857SN/A            action == ACT_DATA_WRITE_SHORT) {
972857SN/A
973849SN/A            if (action == ACT_DATA_READ_BYTE) {
974849SN/A                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
975849SN/A            } else {
976849SN/A                // copy the latest short into the data buffer
977849SN/A                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
9781817SN/A                       (void *)&cmdReg.data,
979849SN/A                       sizeof(uint16_t));
980849SN/A
981849SN/A                drqBytesLeft -= 2;
982849SN/A                cmdBytesLeft -= 2;
983849SN/A            }
984849SN/A
985849SN/A            if (drqBytesLeft == 0) {
986849SN/A                // copy the block to the disk
987849SN/A                writeDisk(curSector++, dataBuffer);
988849SN/A
989849SN/A                // set the BSY bit
990893SN/A                status |= STATUS_BSY_BIT;
991893SN/A                // set the seek bit
992893SN/A                status |= STATUS_SEEK_BIT;
993849SN/A                // clear the DRQ bit
994893SN/A                status &= ~STATUS_DRQ_BIT;
995849SN/A
996849SN/A                devState = Prepare_Data_Out;
997878SN/A
998878SN/A                /** @todo change this to a scheduled event to simulate
999878SN/A                    disk delay */
1000878SN/A                updateState(ACT_DATA_READY);
1001849SN/A            }
1002849SN/A        }
1003849SN/A        break;
1004849SN/A
1005849SN/A      case Prepare_Data_Dma:
1006849SN/A        if (action == ACT_CMD_ERROR) {
1007849SN/A            // clear the BSY bit
1008849SN/A            setComplete();
1009849SN/A
1010849SN/A            if (!isIENSet()) {
1011849SN/A                devState = Device_Idle_SI;
1012849SN/A                intrPost();
1013849SN/A            } else {
1014849SN/A                devState = Device_Idle_S;
1015849SN/A            }
1016849SN/A        } else if (action == ACT_DMA_READY) {
1017849SN/A            // clear the BSY bit
1018893SN/A            status &= ~STATUS_BSY_BIT;
1019849SN/A            // set the DRQ bit
1020893SN/A            status |= STATUS_DRQ_BIT;
1021849SN/A
1022849SN/A            devState = Transfer_Data_Dma;
1023849SN/A
1024849SN/A            if (dmaState != Dma_Idle)
1025849SN/A                panic("Inconsistent DMA state, should be Dma_Idle\n");
1026849SN/A
1027849SN/A            dmaState = Dma_Start;
1028849SN/A            // wait for the write to the DMA start bit
1029849SN/A        }
1030849SN/A        break;
1031849SN/A
1032849SN/A      case Transfer_Data_Dma:
10339956SN/A        if (action == ACT_CMD_ERROR) {
10349956SN/A            dmaAborted = true;
10359956SN/A            devState = Device_Dma_Abort;
10369956SN/A        } else if (action == ACT_DMA_DONE) {
1037849SN/A            // clear the BSY bit
1038849SN/A            setComplete();
1039849SN/A            // set the seek bit
1040893SN/A            status |= STATUS_SEEK_BIT;
1041849SN/A            // clear the controller state for DMA transfer
1042849SN/A            ctrl->setDmaComplete(this);
1043849SN/A
1044849SN/A            if (!isIENSet()) {
1045849SN/A                devState = Device_Idle_SI;
1046849SN/A                intrPost();
1047849SN/A            } else {
1048849SN/A                devState = Device_Idle_S;
1049849SN/A            }
1050849SN/A        }
1051849SN/A        break;
1052849SN/A
10539956SN/A      case Device_Dma_Abort:
10549956SN/A        if (action == ACT_CMD_ERROR) {
10559956SN/A            setComplete();
10569956SN/A            status |= STATUS_SEEK_BIT;
10579956SN/A            ctrl->setDmaComplete(this);
10589956SN/A            dmaAborted = false;
10599956SN/A            dmaState = Dma_Idle;
10609956SN/A
10619956SN/A            if (!isIENSet()) {
10629956SN/A                devState = Device_Idle_SI;
10639956SN/A                intrPost();
10649956SN/A            } else {
10659956SN/A                devState = Device_Idle_S;
10669956SN/A            }
10679956SN/A        } else {
10689956SN/A            DPRINTF(IdeDisk, "Disk still busy aborting previous DMA command\n");
10699956SN/A        }
10709956SN/A        break;
10719956SN/A
1072849SN/A      default:
1073849SN/A        panic("Unknown IDE device state: %#x\n", devState);
1074849SN/A    }
1075848SN/A}
1076848SN/A
1077848SN/Avoid
107810905SN/AIdeDisk::serialize(CheckpointOut &cp) const
1079848SN/A{
1080864SN/A    // Check all outstanding events to see if they are scheduled
1081864SN/A    // these are all mutually exclusive
1082864SN/A    Tick reschedule = 0;
1083864SN/A    Events_t event = None;
1084864SN/A
1085920SN/A    int eventCount = 0;
1086920SN/A
1087864SN/A    if (dmaTransferEvent.scheduled()) {
1088864SN/A        reschedule = dmaTransferEvent.when();
1089864SN/A        event = Transfer;
1090920SN/A        eventCount++;
1091920SN/A    }
1092920SN/A    if (dmaReadWaitEvent.scheduled()) {
1093864SN/A        reschedule = dmaReadWaitEvent.when();
1094864SN/A        event = ReadWait;
1095920SN/A        eventCount++;
1096920SN/A    }
1097920SN/A    if (dmaWriteWaitEvent.scheduled()) {
1098864SN/A        reschedule = dmaWriteWaitEvent.when();
1099864SN/A        event = WriteWait;
1100920SN/A        eventCount++;
1101920SN/A    }
1102920SN/A    if (dmaPrdReadEvent.scheduled()) {
1103864SN/A        reschedule = dmaPrdReadEvent.when();
1104864SN/A        event = PrdRead;
1105920SN/A        eventCount++;
1106920SN/A    }
1107920SN/A    if (dmaReadEvent.scheduled()) {
1108864SN/A        reschedule = dmaReadEvent.when();
1109864SN/A        event = DmaRead;
1110920SN/A        eventCount++;
1111920SN/A    }
1112920SN/A    if (dmaWriteEvent.scheduled()) {
1113864SN/A        reschedule = dmaWriteEvent.when();
1114864SN/A        event = DmaWrite;
1115920SN/A        eventCount++;
1116864SN/A    }
1117864SN/A
1118920SN/A    assert(eventCount <= 1);
1119920SN/A
1120864SN/A    SERIALIZE_SCALAR(reschedule);
1121864SN/A    SERIALIZE_ENUM(event);
1122864SN/A
1123864SN/A    // Serialize device registers
11241817SN/A    SERIALIZE_SCALAR(cmdReg.data);
1125864SN/A    SERIALIZE_SCALAR(cmdReg.sec_count);
1126864SN/A    SERIALIZE_SCALAR(cmdReg.sec_num);
1127864SN/A    SERIALIZE_SCALAR(cmdReg.cyl_low);
1128864SN/A    SERIALIZE_SCALAR(cmdReg.cyl_high);
1129864SN/A    SERIALIZE_SCALAR(cmdReg.drive);
1130920SN/A    SERIALIZE_SCALAR(cmdReg.command);
1131893SN/A    SERIALIZE_SCALAR(status);
1132864SN/A    SERIALIZE_SCALAR(nIENBit);
1133864SN/A    SERIALIZE_SCALAR(devID);
1134864SN/A
1135864SN/A    // Serialize the PRD related information
1136864SN/A    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1137864SN/A    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1138864SN/A    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1139864SN/A    SERIALIZE_SCALAR(curPrdAddr);
1140864SN/A
11412565SN/A    /** @todo need to serialized chunk generator stuff!! */
1142864SN/A    // Serialize current transfer related information
1143864SN/A    SERIALIZE_SCALAR(cmdBytesLeft);
1144893SN/A    SERIALIZE_SCALAR(cmdBytes);
1145864SN/A    SERIALIZE_SCALAR(drqBytesLeft);
1146864SN/A    SERIALIZE_SCALAR(curSector);
1147864SN/A    SERIALIZE_SCALAR(dmaRead);
1148864SN/A    SERIALIZE_SCALAR(intrPending);
11499956SN/A    SERIALIZE_SCALAR(dmaAborted);
1150864SN/A    SERIALIZE_ENUM(devState);
1151864SN/A    SERIALIZE_ENUM(dmaState);
1152864SN/A    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1153848SN/A}
1154848SN/A
1155848SN/Avoid
115610905SN/AIdeDisk::unserialize(CheckpointIn &cp)
1157848SN/A{
1158864SN/A    // Reschedule events that were outstanding
1159864SN/A    // these are all mutually exclusive
1160864SN/A    Tick reschedule = 0;
1161864SN/A    Events_t event = None;
1162864SN/A
1163864SN/A    UNSERIALIZE_SCALAR(reschedule);
1164864SN/A    UNSERIALIZE_ENUM(event);
1165864SN/A
1166864SN/A    switch (event) {
1167864SN/A      case None : break;
11685606SN/A      case Transfer : schedule(dmaTransferEvent, reschedule); break;
11695606SN/A      case ReadWait : schedule(dmaReadWaitEvent, reschedule); break;
11705606SN/A      case WriteWait : schedule(dmaWriteWaitEvent, reschedule); break;
11715606SN/A      case PrdRead : schedule(dmaPrdReadEvent, reschedule); break;
11725606SN/A      case DmaRead : schedule(dmaReadEvent, reschedule); break;
11735606SN/A      case DmaWrite : schedule(dmaWriteEvent, reschedule); break;
1174864SN/A    }
1175864SN/A
1176864SN/A    // Unserialize device registers
11771817SN/A    UNSERIALIZE_SCALAR(cmdReg.data);
1178864SN/A    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1179864SN/A    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1180864SN/A    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1181864SN/A    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1182864SN/A    UNSERIALIZE_SCALAR(cmdReg.drive);
1183920SN/A    UNSERIALIZE_SCALAR(cmdReg.command);
1184893SN/A    UNSERIALIZE_SCALAR(status);
1185864SN/A    UNSERIALIZE_SCALAR(nIENBit);
1186864SN/A    UNSERIALIZE_SCALAR(devID);
1187864SN/A
1188864SN/A    // Unserialize the PRD related information
1189864SN/A    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1190864SN/A    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1191864SN/A    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1192864SN/A    UNSERIALIZE_SCALAR(curPrdAddr);
1193864SN/A
11942565SN/A    /** @todo need to serialized chunk generator stuff!! */
1195864SN/A    // Unserialize current transfer related information
1196893SN/A    UNSERIALIZE_SCALAR(cmdBytes);
1197864SN/A    UNSERIALIZE_SCALAR(cmdBytesLeft);
1198864SN/A    UNSERIALIZE_SCALAR(drqBytesLeft);
1199864SN/A    UNSERIALIZE_SCALAR(curSector);
1200864SN/A    UNSERIALIZE_SCALAR(dmaRead);
1201864SN/A    UNSERIALIZE_SCALAR(intrPending);
12029956SN/A    UNSERIALIZE_SCALAR(dmaAborted);
1203864SN/A    UNSERIALIZE_ENUM(devState);
1204864SN/A    UNSERIALIZE_ENUM(dmaState);
1205864SN/A    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1206848SN/A}
1207848SN/A
12084762SN/AIdeDisk *
12094762SN/AIdeDiskParams::create()
1210848SN/A{
12115034SN/A    return new IdeDisk(this);
1212848SN/A}
1213