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
70812392Sjason@lowepower.com        M5_FALLTHROUGH;
7091722SN/A      case WDCC_READDMA:
710849SN/A        if (!(cmdReg.drive & DRIVE_LBA_BIT))
711849SN/A            panic("Attempt to perform CHS access, only supports LBA\n");
712849SN/A
713849SN/A        if (cmdReg.sec_count == 0)
714893SN/A            cmdBytes = cmdBytesLeft = (256 * SectorSize);
715849SN/A        else
716893SN/A            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
7178522SN/A        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
718849SN/A
719849SN/A        curSector = getLBABase();
720849SN/A
721849SN/A        devState = Prepare_Data_Dma;
722849SN/A        action = ACT_DMA_READY;
723849SN/A        break;
724849SN/A
725849SN/A      default:
726849SN/A        panic("Unsupported ATA command: %#x\n", cmdReg.command);
727849SN/A    }
728849SN/A
729849SN/A    if (action != ACT_NONE) {
730849SN/A        // set the BSY bit
731893SN/A        status |= STATUS_BSY_BIT;
732849SN/A        // clear the DRQ bit
733893SN/A        status &= ~STATUS_DRQ_BIT;
734893SN/A        // clear the DF bit
735893SN/A        status &= ~STATUS_DF_BIT;
736849SN/A
737849SN/A        updateState(action);
738849SN/A    }
739849SN/A}
740849SN/A
741849SN/A////
742849SN/A// Handle setting and clearing interrupts
743849SN/A////
744849SN/A
745849SN/Avoid
746849SN/AIdeDisk::intrPost()
747849SN/A{
7481625SN/A    DPRINTF(IdeDisk, "Posting Interrupt\n");
749849SN/A    if (intrPending)
750849SN/A        panic("Attempt to post an interrupt with one pending\n");
751849SN/A
752849SN/A    intrPending = true;
753849SN/A
754849SN/A    // talk to controller to set interrupt
7551817SN/A    if (ctrl) {
756849SN/A        ctrl->intrPost();
7571817SN/A    }
758849SN/A}
759849SN/A
760849SN/Avoid
761849SN/AIdeDisk::intrClear()
762849SN/A{
7631625SN/A    DPRINTF(IdeDisk, "Clearing Interrupt\n");
764849SN/A    if (!intrPending)
765849SN/A        panic("Attempt to clear a non-pending interrupt\n");
766849SN/A
767849SN/A    intrPending = false;
768849SN/A
769849SN/A    // talk to controller to clear interrupt
770849SN/A    if (ctrl)
771849SN/A        ctrl->intrClear();
772849SN/A}
773849SN/A
774849SN/A////
775849SN/A// Manage the device internal state machine
776849SN/A////
777849SN/A
778849SN/Avoid
779849SN/AIdeDisk::updateState(DevAction_t action)
780849SN/A{
781849SN/A    switch (devState) {
782893SN/A      case Device_Srst:
783893SN/A        if (action == ACT_SRST_SET) {
784893SN/A            // set the BSY bit
785893SN/A            status |= STATUS_BSY_BIT;
786893SN/A        } else if (action == ACT_SRST_CLEAR) {
787893SN/A            // clear the BSY bit
788893SN/A            status &= ~STATUS_BSY_BIT;
789893SN/A
790893SN/A            // reset the device state
791893SN/A            reset(devID);
792893SN/A        }
793893SN/A        break;
794893SN/A
795849SN/A      case Device_Idle_S:
796893SN/A        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
797849SN/A            devState = Device_Idle_NS;
798893SN/A        } else if (action == ACT_CMD_WRITE) {
799849SN/A            startCommand();
800893SN/A        }
801849SN/A
802849SN/A        break;
803849SN/A
804849SN/A      case Device_Idle_SI:
805893SN/A        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
806849SN/A            devState = Device_Idle_NS;
807849SN/A            intrClear();
808849SN/A        } else if (action == ACT_STAT_READ || isIENSet()) {
809849SN/A            devState = Device_Idle_S;
810849SN/A            intrClear();
811849SN/A        } else if (action == ACT_CMD_WRITE) {
812849SN/A            intrClear();
813849SN/A            startCommand();
814849SN/A        }
815849SN/A
816849SN/A        break;
817849SN/A
818849SN/A      case Device_Idle_NS:
819893SN/A        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
820849SN/A            if (!isIENSet() && intrPending) {
821849SN/A                devState = Device_Idle_SI;
822849SN/A                intrPost();
823849SN/A            }
824849SN/A            if (isIENSet() || !intrPending) {
825849SN/A                devState = Device_Idle_S;
826849SN/A            }
827849SN/A        }
828849SN/A        break;
829849SN/A
830849SN/A      case Command_Execution:
831849SN/A        if (action == ACT_CMD_COMPLETE) {
832849SN/A            // clear the BSY bit
833849SN/A            setComplete();
834849SN/A
835849SN/A            if (!isIENSet()) {
836849SN/A                devState = Device_Idle_SI;
837849SN/A                intrPost();
838849SN/A            } else {
839849SN/A                devState = Device_Idle_S;
840849SN/A            }
841849SN/A        }
842849SN/A        break;
843849SN/A
844849SN/A      case Prepare_Data_In:
845849SN/A        if (action == ACT_CMD_ERROR) {
846849SN/A            // clear the BSY bit
847849SN/A            setComplete();
848849SN/A
849849SN/A            if (!isIENSet()) {
850849SN/A                devState = Device_Idle_SI;
851849SN/A                intrPost();
852849SN/A            } else {
853849SN/A                devState = Device_Idle_S;
854849SN/A            }
855849SN/A        } else if (action == ACT_DATA_READY) {
856849SN/A            // clear the BSY bit
857893SN/A            status &= ~STATUS_BSY_BIT;
858849SN/A            // set the DRQ bit
859893SN/A            status |= STATUS_DRQ_BIT;
860849SN/A
861877SN/A            // copy the data into the data buffer
86211980Sjason@lowepower.com            if (cmdReg.command == WDCC_IDENTIFY ||
86311980Sjason@lowepower.com                cmdReg.command == ATAPI_IDENTIFY_DEVICE) {
864877SN/A                // Reset the drqBytes for this block
8651722SN/A                drqBytesLeft = sizeof(struct ataparams);
866877SN/A
867877SN/A                memcpy((void *)dataBuffer, (void *)&driveID,
8681722SN/A                       sizeof(struct ataparams));
869877SN/A            } else {
870877SN/A                // Reset the drqBytes for this block
871877SN/A                drqBytesLeft = SectorSize;
872877SN/A
873877SN/A                readDisk(curSector++, dataBuffer);
874877SN/A            }
875877SN/A
876849SN/A            // put the first two bytes into the data register
8771817SN/A            memcpy((void *)&cmdReg.data, (void *)dataBuffer,
878857SN/A                   sizeof(uint16_t));
879857SN/A
880849SN/A            if (!isIENSet()) {
881849SN/A                devState = Data_Ready_INTRQ_In;
882849SN/A                intrPost();
883849SN/A            } else {
884849SN/A                devState = Transfer_Data_In;
885849SN/A            }
886849SN/A        }
887849SN/A        break;
888849SN/A
889849SN/A      case Data_Ready_INTRQ_In:
890849SN/A        if (action == ACT_STAT_READ) {
891849SN/A            devState = Transfer_Data_In;
892849SN/A            intrClear();
893849SN/A        }
894849SN/A        break;
895849SN/A
896849SN/A      case Transfer_Data_In:
897849SN/A        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
898849SN/A            if (action == ACT_DATA_READ_BYTE) {
899849SN/A                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
900849SN/A            } else {
901849SN/A                drqBytesLeft -= 2;
902849SN/A                cmdBytesLeft -= 2;
903849SN/A
904849SN/A                // copy next short into data registers
905877SN/A                if (drqBytesLeft)
9061817SN/A                    memcpy((void *)&cmdReg.data,
907877SN/A                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
908877SN/A                           sizeof(uint16_t));
909849SN/A            }
910849SN/A
911849SN/A            if (drqBytesLeft == 0) {
912849SN/A                if (cmdBytesLeft == 0) {
913849SN/A                    // Clear the BSY bit
914849SN/A                    setComplete();
915849SN/A                    devState = Device_Idle_S;
916849SN/A                } else {
917849SN/A                    devState = Prepare_Data_In;
918877SN/A                    // set the BSY_BIT
919893SN/A                    status |= STATUS_BSY_BIT;
920877SN/A                    // clear the DRQ_BIT
921893SN/A                    status &= ~STATUS_DRQ_BIT;
922877SN/A
923877SN/A                    /** @todo change this to a scheduled event to simulate
924877SN/A                        disk delay */
925877SN/A                    updateState(ACT_DATA_READY);
926849SN/A                }
927849SN/A            }
928849SN/A        }
929849SN/A        break;
930849SN/A
931849SN/A      case Prepare_Data_Out:
932849SN/A        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
933849SN/A            // clear the BSY bit
934849SN/A            setComplete();
935849SN/A
936849SN/A            if (!isIENSet()) {
937849SN/A                devState = Device_Idle_SI;
938849SN/A                intrPost();
939849SN/A            } else {
940849SN/A                devState = Device_Idle_S;
941849SN/A            }
942893SN/A        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
943849SN/A            // clear the BSY bit
944893SN/A            status &= ~STATUS_BSY_BIT;
945849SN/A            // set the DRQ bit
946893SN/A            status |= STATUS_DRQ_BIT;
947849SN/A
948849SN/A            // clear the data buffer to get it ready for writes
949849SN/A            memset(dataBuffer, 0, MAX_DMA_SIZE);
950849SN/A
951893SN/A            // reset the drqBytes for this block
952893SN/A            drqBytesLeft = SectorSize;
953893SN/A
954893SN/A            if (cmdBytesLeft == cmdBytes || isIENSet()) {
955893SN/A                devState = Transfer_Data_Out;
956893SN/A            } else {
957849SN/A                devState = Data_Ready_INTRQ_Out;
958849SN/A                intrPost();
959849SN/A            }
960849SN/A        }
961849SN/A        break;
962849SN/A
963849SN/A      case Data_Ready_INTRQ_Out:
964849SN/A        if (action == ACT_STAT_READ) {
965849SN/A            devState = Transfer_Data_Out;
966849SN/A            intrClear();
967849SN/A        }
968849SN/A        break;
969849SN/A
970849SN/A      case Transfer_Data_Out:
971857SN/A        if (action == ACT_DATA_WRITE_BYTE ||
972857SN/A            action == ACT_DATA_WRITE_SHORT) {
973857SN/A
974849SN/A            if (action == ACT_DATA_READ_BYTE) {
975849SN/A                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
976849SN/A            } else {
977849SN/A                // copy the latest short into the data buffer
978849SN/A                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
9791817SN/A                       (void *)&cmdReg.data,
980849SN/A                       sizeof(uint16_t));
981849SN/A
982849SN/A                drqBytesLeft -= 2;
983849SN/A                cmdBytesLeft -= 2;
984849SN/A            }
985849SN/A
986849SN/A            if (drqBytesLeft == 0) {
987849SN/A                // copy the block to the disk
988849SN/A                writeDisk(curSector++, dataBuffer);
989849SN/A
990849SN/A                // set the BSY bit
991893SN/A                status |= STATUS_BSY_BIT;
992893SN/A                // set the seek bit
993893SN/A                status |= STATUS_SEEK_BIT;
994849SN/A                // clear the DRQ bit
995893SN/A                status &= ~STATUS_DRQ_BIT;
996849SN/A
997849SN/A                devState = Prepare_Data_Out;
998878SN/A
999878SN/A                /** @todo change this to a scheduled event to simulate
1000878SN/A                    disk delay */
1001878SN/A                updateState(ACT_DATA_READY);
1002849SN/A            }
1003849SN/A        }
1004849SN/A        break;
1005849SN/A
1006849SN/A      case Prepare_Data_Dma:
1007849SN/A        if (action == ACT_CMD_ERROR) {
1008849SN/A            // clear the BSY bit
1009849SN/A            setComplete();
1010849SN/A
1011849SN/A            if (!isIENSet()) {
1012849SN/A                devState = Device_Idle_SI;
1013849SN/A                intrPost();
1014849SN/A            } else {
1015849SN/A                devState = Device_Idle_S;
1016849SN/A            }
1017849SN/A        } else if (action == ACT_DMA_READY) {
1018849SN/A            // clear the BSY bit
1019893SN/A            status &= ~STATUS_BSY_BIT;
1020849SN/A            // set the DRQ bit
1021893SN/A            status |= STATUS_DRQ_BIT;
1022849SN/A
1023849SN/A            devState = Transfer_Data_Dma;
1024849SN/A
1025849SN/A            if (dmaState != Dma_Idle)
1026849SN/A                panic("Inconsistent DMA state, should be Dma_Idle\n");
1027849SN/A
1028849SN/A            dmaState = Dma_Start;
1029849SN/A            // wait for the write to the DMA start bit
1030849SN/A        }
1031849SN/A        break;
1032849SN/A
1033849SN/A      case Transfer_Data_Dma:
10349956SN/A        if (action == ACT_CMD_ERROR) {
10359956SN/A            dmaAborted = true;
10369956SN/A            devState = Device_Dma_Abort;
10379956SN/A        } else if (action == ACT_DMA_DONE) {
1038849SN/A            // clear the BSY bit
1039849SN/A            setComplete();
1040849SN/A            // set the seek bit
1041893SN/A            status |= STATUS_SEEK_BIT;
1042849SN/A            // clear the controller state for DMA transfer
1043849SN/A            ctrl->setDmaComplete(this);
1044849SN/A
1045849SN/A            if (!isIENSet()) {
1046849SN/A                devState = Device_Idle_SI;
1047849SN/A                intrPost();
1048849SN/A            } else {
1049849SN/A                devState = Device_Idle_S;
1050849SN/A            }
1051849SN/A        }
1052849SN/A        break;
1053849SN/A
10549956SN/A      case Device_Dma_Abort:
10559956SN/A        if (action == ACT_CMD_ERROR) {
10569956SN/A            setComplete();
10579956SN/A            status |= STATUS_SEEK_BIT;
10589956SN/A            ctrl->setDmaComplete(this);
10599956SN/A            dmaAborted = false;
10609956SN/A            dmaState = Dma_Idle;
10619956SN/A
10629956SN/A            if (!isIENSet()) {
10639956SN/A                devState = Device_Idle_SI;
10649956SN/A                intrPost();
10659956SN/A            } else {
10669956SN/A                devState = Device_Idle_S;
10679956SN/A            }
10689956SN/A        } else {
10699956SN/A            DPRINTF(IdeDisk, "Disk still busy aborting previous DMA command\n");
10709956SN/A        }
10719956SN/A        break;
10729956SN/A
1073849SN/A      default:
1074849SN/A        panic("Unknown IDE device state: %#x\n", devState);
1075849SN/A    }
1076848SN/A}
1077848SN/A
1078848SN/Avoid
107910905SN/AIdeDisk::serialize(CheckpointOut &cp) const
1080848SN/A{
1081864SN/A    // Check all outstanding events to see if they are scheduled
1082864SN/A    // these are all mutually exclusive
1083864SN/A    Tick reschedule = 0;
1084864SN/A    Events_t event = None;
1085864SN/A
1086920SN/A    int eventCount = 0;
1087920SN/A
1088864SN/A    if (dmaTransferEvent.scheduled()) {
1089864SN/A        reschedule = dmaTransferEvent.when();
1090864SN/A        event = Transfer;
1091920SN/A        eventCount++;
1092920SN/A    }
1093920SN/A    if (dmaReadWaitEvent.scheduled()) {
1094864SN/A        reschedule = dmaReadWaitEvent.when();
1095864SN/A        event = ReadWait;
1096920SN/A        eventCount++;
1097920SN/A    }
1098920SN/A    if (dmaWriteWaitEvent.scheduled()) {
1099864SN/A        reschedule = dmaWriteWaitEvent.when();
1100864SN/A        event = WriteWait;
1101920SN/A        eventCount++;
1102920SN/A    }
1103920SN/A    if (dmaPrdReadEvent.scheduled()) {
1104864SN/A        reschedule = dmaPrdReadEvent.when();
1105864SN/A        event = PrdRead;
1106920SN/A        eventCount++;
1107920SN/A    }
1108920SN/A    if (dmaReadEvent.scheduled()) {
1109864SN/A        reschedule = dmaReadEvent.when();
1110864SN/A        event = DmaRead;
1111920SN/A        eventCount++;
1112920SN/A    }
1113920SN/A    if (dmaWriteEvent.scheduled()) {
1114864SN/A        reschedule = dmaWriteEvent.when();
1115864SN/A        event = DmaWrite;
1116920SN/A        eventCount++;
1117864SN/A    }
1118864SN/A
1119920SN/A    assert(eventCount <= 1);
1120920SN/A
1121864SN/A    SERIALIZE_SCALAR(reschedule);
1122864SN/A    SERIALIZE_ENUM(event);
1123864SN/A
1124864SN/A    // Serialize device registers
11251817SN/A    SERIALIZE_SCALAR(cmdReg.data);
1126864SN/A    SERIALIZE_SCALAR(cmdReg.sec_count);
1127864SN/A    SERIALIZE_SCALAR(cmdReg.sec_num);
1128864SN/A    SERIALIZE_SCALAR(cmdReg.cyl_low);
1129864SN/A    SERIALIZE_SCALAR(cmdReg.cyl_high);
1130864SN/A    SERIALIZE_SCALAR(cmdReg.drive);
1131920SN/A    SERIALIZE_SCALAR(cmdReg.command);
1132893SN/A    SERIALIZE_SCALAR(status);
1133864SN/A    SERIALIZE_SCALAR(nIENBit);
1134864SN/A    SERIALIZE_SCALAR(devID);
1135864SN/A
1136864SN/A    // Serialize the PRD related information
1137864SN/A    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1138864SN/A    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1139864SN/A    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1140864SN/A    SERIALIZE_SCALAR(curPrdAddr);
1141864SN/A
11422565SN/A    /** @todo need to serialized chunk generator stuff!! */
1143864SN/A    // Serialize current transfer related information
1144864SN/A    SERIALIZE_SCALAR(cmdBytesLeft);
1145893SN/A    SERIALIZE_SCALAR(cmdBytes);
1146864SN/A    SERIALIZE_SCALAR(drqBytesLeft);
1147864SN/A    SERIALIZE_SCALAR(curSector);
1148864SN/A    SERIALIZE_SCALAR(dmaRead);
1149864SN/A    SERIALIZE_SCALAR(intrPending);
11509956SN/A    SERIALIZE_SCALAR(dmaAborted);
1151864SN/A    SERIALIZE_ENUM(devState);
1152864SN/A    SERIALIZE_ENUM(dmaState);
1153864SN/A    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1154848SN/A}
1155848SN/A
1156848SN/Avoid
115710905SN/AIdeDisk::unserialize(CheckpointIn &cp)
1158848SN/A{
1159864SN/A    // Reschedule events that were outstanding
1160864SN/A    // these are all mutually exclusive
1161864SN/A    Tick reschedule = 0;
1162864SN/A    Events_t event = None;
1163864SN/A
1164864SN/A    UNSERIALIZE_SCALAR(reschedule);
1165864SN/A    UNSERIALIZE_ENUM(event);
1166864SN/A
1167864SN/A    switch (event) {
1168864SN/A      case None : break;
11695606SN/A      case Transfer : schedule(dmaTransferEvent, reschedule); break;
11705606SN/A      case ReadWait : schedule(dmaReadWaitEvent, reschedule); break;
11715606SN/A      case WriteWait : schedule(dmaWriteWaitEvent, reschedule); break;
11725606SN/A      case PrdRead : schedule(dmaPrdReadEvent, reschedule); break;
11735606SN/A      case DmaRead : schedule(dmaReadEvent, reschedule); break;
11745606SN/A      case DmaWrite : schedule(dmaWriteEvent, reschedule); break;
1175864SN/A    }
1176864SN/A
1177864SN/A    // Unserialize device registers
11781817SN/A    UNSERIALIZE_SCALAR(cmdReg.data);
1179864SN/A    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1180864SN/A    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1181864SN/A    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1182864SN/A    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1183864SN/A    UNSERIALIZE_SCALAR(cmdReg.drive);
1184920SN/A    UNSERIALIZE_SCALAR(cmdReg.command);
1185893SN/A    UNSERIALIZE_SCALAR(status);
1186864SN/A    UNSERIALIZE_SCALAR(nIENBit);
1187864SN/A    UNSERIALIZE_SCALAR(devID);
1188864SN/A
1189864SN/A    // Unserialize the PRD related information
1190864SN/A    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1191864SN/A    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1192864SN/A    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1193864SN/A    UNSERIALIZE_SCALAR(curPrdAddr);
1194864SN/A
11952565SN/A    /** @todo need to serialized chunk generator stuff!! */
1196864SN/A    // Unserialize current transfer related information
1197893SN/A    UNSERIALIZE_SCALAR(cmdBytes);
1198864SN/A    UNSERIALIZE_SCALAR(cmdBytesLeft);
1199864SN/A    UNSERIALIZE_SCALAR(drqBytesLeft);
1200864SN/A    UNSERIALIZE_SCALAR(curSector);
1201864SN/A    UNSERIALIZE_SCALAR(dmaRead);
1202864SN/A    UNSERIALIZE_SCALAR(intrPending);
12039956SN/A    UNSERIALIZE_SCALAR(dmaAborted);
1204864SN/A    UNSERIALIZE_ENUM(devState);
1205864SN/A    UNSERIALIZE_ENUM(dmaState);
1206864SN/A    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1207848SN/A}
1208848SN/A
12094762SN/AIdeDisk *
12104762SN/AIdeDiskParams::create()
1211848SN/A{
12125034SN/A    return new IdeDisk(this);
1213848SN/A}
1214