ide_disk.cc revision 9533
15625Sgblack@eecs.umich.edu/*
25625Sgblack@eecs.umich.edu * Copyright (c) 2004-2005 The Regents of The University of Michigan
35625Sgblack@eecs.umich.edu * All rights reserved.
45625Sgblack@eecs.umich.edu *
57087Snate@binkert.org * Redistribution and use in source and binary forms, with or without
67087Snate@binkert.org * modification, are permitted provided that the following conditions are
77087Snate@binkert.org * met: redistributions of source code must retain the above copyright
87087Snate@binkert.org * notice, this list of conditions and the following disclaimer;
97087Snate@binkert.org * redistributions in binary form must reproduce the above copyright
107087Snate@binkert.org * notice, this list of conditions and the following disclaimer in the
117087Snate@binkert.org * documentation and/or other materials provided with the distribution;
127087Snate@binkert.org * neither the name of the copyright holders nor the names of its
135625Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
147087Snate@binkert.org * this software without specific prior written permission.
157087Snate@binkert.org *
167087Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
177087Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
187087Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
197087Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
207087Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
217087Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
225625Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
237087Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245625Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255625Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
265625Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275625Sgblack@eecs.umich.edu *
285625Sgblack@eecs.umich.edu * Authors: Andrew Schultz
295625Sgblack@eecs.umich.edu *          Ali Saidi
305625Sgblack@eecs.umich.edu */
315625Sgblack@eecs.umich.edu
325625Sgblack@eecs.umich.edu/** @file
335625Sgblack@eecs.umich.edu * Device model implementation for an IDE disk
345625Sgblack@eecs.umich.edu */
355625Sgblack@eecs.umich.edu
365625Sgblack@eecs.umich.edu#include <cerrno>
375625Sgblack@eecs.umich.edu#include <cstring>
385625Sgblack@eecs.umich.edu#include <deque>
395625Sgblack@eecs.umich.edu#include <string>
405625Sgblack@eecs.umich.edu
4111793Sbrandon.potter@amd.com#include "arch/isa_traits.hh"
425625Sgblack@eecs.umich.edu#include "base/chunk_generator.hh"
4312334Sgabeblack@google.com#include "base/cprintf.hh" // csprintf
446216Snate@binkert.org#include "base/trace.hh"
458706Sandreas.hansson@arm.com#include "config/the_isa.hh"
465625Sgblack@eecs.umich.edu#include "debug/IdeDisk.hh"
475625Sgblack@eecs.umich.edu#include "dev/disk_image.hh"
485625Sgblack@eecs.umich.edu#include "dev/ide_ctrl.hh"
495625Sgblack@eecs.umich.edu#include "dev/ide_disk.hh"
505625Sgblack@eecs.umich.edu#include "sim/core.hh"
515625Sgblack@eecs.umich.edu#include "sim/sim_object.hh"
525625Sgblack@eecs.umich.edu
535625Sgblack@eecs.umich.eduusing namespace std;
545625Sgblack@eecs.umich.eduusing namespace TheISA;
555625Sgblack@eecs.umich.edu
565625Sgblack@eecs.umich.eduIdeDisk::IdeDisk(const Params *p)
575625Sgblack@eecs.umich.edu    : SimObject(p), ctrl(NULL), image(p->image), diskDelay(p->delay),
585625Sgblack@eecs.umich.edu      dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this),
595625Sgblack@eecs.umich.edu      dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
605625Sgblack@eecs.umich.edu      dmaReadEvent(this), dmaWriteEvent(this)
615625Sgblack@eecs.umich.edu{
625625Sgblack@eecs.umich.edu    // Reset the device state
635625Sgblack@eecs.umich.edu    reset(p->driveID);
645625Sgblack@eecs.umich.edu
655625Sgblack@eecs.umich.edu    // fill out the drive ID structure
665625Sgblack@eecs.umich.edu    memset(&driveID, 0, sizeof(struct ataparams));
675625Sgblack@eecs.umich.edu
685625Sgblack@eecs.umich.edu    // Calculate LBA and C/H/S values
695625Sgblack@eecs.umich.edu    uint16_t cylinders;
705625Sgblack@eecs.umich.edu    uint8_t heads;
715625Sgblack@eecs.umich.edu    uint8_t sectors;
725625Sgblack@eecs.umich.edu
735625Sgblack@eecs.umich.edu    uint32_t lba_size = image->size();
748852Sandreas.hansson@arm.com    if (lba_size >= 16383*16*63) {
755625Sgblack@eecs.umich.edu        cylinders = 16383;
768737Skoansin.tan@gmail.com        heads = 16;
7714010Sgabeblack@google.com        sectors = 63;
785625Sgblack@eecs.umich.edu    } else {
795625Sgblack@eecs.umich.edu        if (lba_size >= 63)
8011321Ssteve.reinhardt@amd.com            sectors = 63;
815625Sgblack@eecs.umich.edu        else
825625Sgblack@eecs.umich.edu            sectors = lba_size;
835625Sgblack@eecs.umich.edu
845625Sgblack@eecs.umich.edu        if ((lba_size / sectors) >= 16)
855625Sgblack@eecs.umich.edu            heads = 16;
865625Sgblack@eecs.umich.edu        else
875625Sgblack@eecs.umich.edu            heads = (lba_size / sectors);
888852Sandreas.hansson@arm.com
895625Sgblack@eecs.umich.edu        cylinders = lba_size / (heads * sectors);
905625Sgblack@eecs.umich.edu    }
915625Sgblack@eecs.umich.edu
925625Sgblack@eecs.umich.edu    // Setup the model name
935625Sgblack@eecs.umich.edu    strncpy((char *)driveID.atap_model, "5MI EDD si k",
945625Sgblack@eecs.umich.edu            sizeof(driveID.atap_model));
955625Sgblack@eecs.umich.edu    // Set the maximum multisector transfer size
9610292SAndreas.Sandberg@ARM.com    driveID.atap_multi = MAX_MULTSECT;
975625Sgblack@eecs.umich.edu    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
985625Sgblack@eecs.umich.edu    driveID.atap_capabilities1 = 0x7;
995625Sgblack@eecs.umich.edu    // UDMA support, EIDE support
1005625Sgblack@eecs.umich.edu    driveID.atap_extensions = 0x6;
10114010Sgabeblack@google.com    // Setup default C/H/S settings
1025625Sgblack@eecs.umich.edu    driveID.atap_cylinders = cylinders;
1035625Sgblack@eecs.umich.edu    driveID.atap_sectors = sectors;
1045625Sgblack@eecs.umich.edu    driveID.atap_heads = heads;
1055625Sgblack@eecs.umich.edu    // Setup the current multisector transfer size
1065625Sgblack@eecs.umich.edu    driveID.atap_curmulti = MAX_MULTSECT;
1075625Sgblack@eecs.umich.edu    driveID.atap_curmulti_valid = 0x1;
1085625Sgblack@eecs.umich.edu    // Number of sectors on disk
1095625Sgblack@eecs.umich.edu    driveID.atap_capacity = lba_size;
1105625Sgblack@eecs.umich.edu    // Multiword DMA mode 2 and below supported
1118852Sandreas.hansson@arm.com    driveID.atap_dmamode_supp = 0x4;
1125625Sgblack@eecs.umich.edu    // Set PIO mode 4 and 3 supported
1135625Sgblack@eecs.umich.edu    driveID.atap_piomode_supp = 0x3;
1145625Sgblack@eecs.umich.edu    // Set DMA mode 4 and below supported
1155625Sgblack@eecs.umich.edu    driveID.atap_udmamode_supp = 0x1f;
1165625Sgblack@eecs.umich.edu    // Statically set hardware config word
1175625Sgblack@eecs.umich.edu    driveID.atap_hwreset_res = 0x4001;
1185625Sgblack@eecs.umich.edu
1195625Sgblack@eecs.umich.edu    //arbitrary for now...
1205625Sgblack@eecs.umich.edu    driveID.atap_ata_major = WDC_VER_ATA7;
1215625Sgblack@eecs.umich.edu}
1225625Sgblack@eecs.umich.edu
1235625Sgblack@eecs.umich.eduIdeDisk::~IdeDisk()
12414010Sgabeblack@google.com{
1255625Sgblack@eecs.umich.edu    // destroy the data buffer
1265625Sgblack@eecs.umich.edu    delete [] dataBuffer;
1275625Sgblack@eecs.umich.edu}
1288706Sandreas.hansson@arm.com
1295625Sgblack@eecs.umich.eduvoid
1305625Sgblack@eecs.umich.eduIdeDisk::reset(int id)
1315625Sgblack@eecs.umich.edu{
1328852Sandreas.hansson@arm.com    // initialize the data buffer and shadow registers
1335625Sgblack@eecs.umich.edu    dataBuffer = new uint8_t[MAX_DMA_SIZE];
1345625Sgblack@eecs.umich.edu
1358852Sandreas.hansson@arm.com    memset(dataBuffer, 0, MAX_DMA_SIZE);
1365625Sgblack@eecs.umich.edu    memset(&cmdReg, 0, sizeof(CommandReg_t));
1375625Sgblack@eecs.umich.edu    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
1388852Sandreas.hansson@arm.com
1395625Sgblack@eecs.umich.edu    curPrdAddr = 0;
1405625Sgblack@eecs.umich.edu    curSector = 0;
1415625Sgblack@eecs.umich.edu    cmdBytes = 0;
1428706Sandreas.hansson@arm.com    cmdBytesLeft = 0;
1435625Sgblack@eecs.umich.edu    drqBytesLeft = 0;
1445625Sgblack@eecs.umich.edu    dmaRead = false;
1458852Sandreas.hansson@arm.com    intrPending = false;
1465625Sgblack@eecs.umich.edu
1475625Sgblack@eecs.umich.edu    // set the device state to idle
1485625Sgblack@eecs.umich.edu    dmaState = Dma_Idle;
1495625Sgblack@eecs.umich.edu
1505625Sgblack@eecs.umich.edu    if (id == DEV0) {
1515625Sgblack@eecs.umich.edu        devState = Device_Idle_S;
1525625Sgblack@eecs.umich.edu        devID = DEV0;
1535625Sgblack@eecs.umich.edu    } else if (id == DEV1) {
1545625Sgblack@eecs.umich.edu        devState = Device_Idle_NS;
1555625Sgblack@eecs.umich.edu        devID = DEV1;
1565625Sgblack@eecs.umich.edu    } else {
1575625Sgblack@eecs.umich.edu        panic("Invalid device ID: %#x\n", id);
1585625Sgblack@eecs.umich.edu    }
1595625Sgblack@eecs.umich.edu
1605625Sgblack@eecs.umich.edu    // set the device ready bit
1615625Sgblack@eecs.umich.edu    status = STATUS_DRDY_BIT;
1628852Sandreas.hansson@arm.com
1635625Sgblack@eecs.umich.edu    /* The error register must be set to 0x1 on start-up to
1645625Sgblack@eecs.umich.edu       indicate that no diagnostic error was detected */
1658852Sandreas.hansson@arm.com    cmdReg.error = 0x1;
1665625Sgblack@eecs.umich.edu}
1675625Sgblack@eecs.umich.edu
1685625Sgblack@eecs.umich.edu////
1695625Sgblack@eecs.umich.edu// Utility functions
1705625Sgblack@eecs.umich.edu////
1715625Sgblack@eecs.umich.edu
1725625Sgblack@eecs.umich.edubool
1735625Sgblack@eecs.umich.eduIdeDisk::isDEVSelect()
1745625Sgblack@eecs.umich.edu{
1758852Sandreas.hansson@arm.com    return ctrl->isDiskSelected(this);
1765625Sgblack@eecs.umich.edu}
1775625Sgblack@eecs.umich.edu
1788852Sandreas.hansson@arm.comAddr
1795625Sgblack@eecs.umich.eduIdeDisk::pciToDma(Addr pciAddr)
1808852Sandreas.hansson@arm.com{
1815625Sgblack@eecs.umich.edu    if (ctrl)
1825625Sgblack@eecs.umich.edu        return ctrl->pciToDma(pciAddr);
1835625Sgblack@eecs.umich.edu    else
1845625Sgblack@eecs.umich.edu        panic("Access to unset controller!\n");
1855625Sgblack@eecs.umich.edu}
1865625Sgblack@eecs.umich.edu
1875625Sgblack@eecs.umich.edu////
1885625Sgblack@eecs.umich.edu// Device registers read/write
1895625Sgblack@eecs.umich.edu////
1905625Sgblack@eecs.umich.edu
1915625Sgblack@eecs.umich.eduvoid
1925625Sgblack@eecs.umich.eduIdeDisk::readCommand(const Addr offset, int size, uint8_t *data)
1938852Sandreas.hansson@arm.com{
1945625Sgblack@eecs.umich.edu    if (offset == DATA_OFFSET) {
1955625Sgblack@eecs.umich.edu        if (size == sizeof(uint16_t)) {
1965625Sgblack@eecs.umich.edu            *(uint16_t *)data = cmdReg.data;
19714010Sgabeblack@google.com        } else if (size == sizeof(uint32_t)) {
1985625Sgblack@eecs.umich.edu            *(uint16_t *)data = cmdReg.data;
1995625Sgblack@eecs.umich.edu            updateState(ACT_DATA_READ_SHORT);
2005625Sgblack@eecs.umich.edu            *((uint16_t *)data + 1) = cmdReg.data;
2015625Sgblack@eecs.umich.edu        } else {
2025625Sgblack@eecs.umich.edu            panic("Data read of unsupported size %d.\n", size);
20314010Sgabeblack@google.com        }
2045625Sgblack@eecs.umich.edu        updateState(ACT_DATA_READ_SHORT);
2055625Sgblack@eecs.umich.edu        return;
2065625Sgblack@eecs.umich.edu    }
2075625Sgblack@eecs.umich.edu    assert(size == sizeof(uint8_t));
2088706Sandreas.hansson@arm.com    switch (offset) {
2098706Sandreas.hansson@arm.com      case ERROR_OFFSET:
2105625Sgblack@eecs.umich.edu        *data = cmdReg.error;
2118706Sandreas.hansson@arm.com        break;
2128706Sandreas.hansson@arm.com      case NSECTOR_OFFSET:
2138706Sandreas.hansson@arm.com        *data = cmdReg.sec_count;
2148706Sandreas.hansson@arm.com        break;
2155625Sgblack@eecs.umich.edu      case SECTOR_OFFSET:
2165625Sgblack@eecs.umich.edu        *data = cmdReg.sec_num;
2178852Sandreas.hansson@arm.com        break;
2185625Sgblack@eecs.umich.edu      case LCYL_OFFSET:
2195625Sgblack@eecs.umich.edu        *data = cmdReg.cyl_low;
2205625Sgblack@eecs.umich.edu        break;
2215625Sgblack@eecs.umich.edu      case HCYL_OFFSET:
2225625Sgblack@eecs.umich.edu        *data = cmdReg.cyl_high;
2235625Sgblack@eecs.umich.edu        break;
2248706Sandreas.hansson@arm.com      case DRIVE_OFFSET:
2255625Sgblack@eecs.umich.edu        *data = cmdReg.drive;
2265625Sgblack@eecs.umich.edu        break;
2275625Sgblack@eecs.umich.edu      case STATUS_OFFSET:
2288706Sandreas.hansson@arm.com        *data = status;
2295625Sgblack@eecs.umich.edu        updateState(ACT_STAT_READ);
2305625Sgblack@eecs.umich.edu        break;
2315625Sgblack@eecs.umich.edu      default:
2325625Sgblack@eecs.umich.edu        panic("Invalid IDE command register offset: %#x\n", offset);
2335625Sgblack@eecs.umich.edu    }
2345625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
2358706Sandreas.hansson@arm.com}
2365625Sgblack@eecs.umich.edu
2375625Sgblack@eecs.umich.eduvoid
2385625Sgblack@eecs.umich.eduIdeDisk::readControl(const Addr offset, int size, uint8_t *data)
2398706Sandreas.hansson@arm.com{
2405625Sgblack@eecs.umich.edu    assert(size == sizeof(uint8_t));
2418706Sandreas.hansson@arm.com    *data = status;
2425625Sgblack@eecs.umich.edu    if (offset != ALTSTAT_OFFSET)
2435625Sgblack@eecs.umich.edu        panic("Invalid IDE control register offset: %#x\n", offset);
2445625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data);
2458706Sandreas.hansson@arm.com}
2465625Sgblack@eecs.umich.edu
2475625Sgblack@eecs.umich.eduvoid
2485625Sgblack@eecs.umich.eduIdeDisk::writeCommand(const Addr offset, int size, const uint8_t *data)
2495625Sgblack@eecs.umich.edu{
2505625Sgblack@eecs.umich.edu    if (offset == DATA_OFFSET) {
2515625Sgblack@eecs.umich.edu        if (size == sizeof(uint16_t)) {
2525625Sgblack@eecs.umich.edu            cmdReg.data = *(const uint16_t *)data;
2535625Sgblack@eecs.umich.edu        } else if (size == sizeof(uint32_t)) {
2545625Sgblack@eecs.umich.edu            cmdReg.data = *(const uint16_t *)data;
2555625Sgblack@eecs.umich.edu            updateState(ACT_DATA_WRITE_SHORT);
2565625Sgblack@eecs.umich.edu            cmdReg.data = *((const uint16_t *)data + 1);
2575625Sgblack@eecs.umich.edu        } else {
2585625Sgblack@eecs.umich.edu            panic("Data write of unsupported size %d.\n", size);
2595625Sgblack@eecs.umich.edu        }
2605625Sgblack@eecs.umich.edu        updateState(ACT_DATA_WRITE_SHORT);
2615625Sgblack@eecs.umich.edu        return;
2625625Sgblack@eecs.umich.edu    }
2635625Sgblack@eecs.umich.edu
2645625Sgblack@eecs.umich.edu    assert(size == sizeof(uint8_t));
2658852Sandreas.hansson@arm.com    switch (offset) {
2665625Sgblack@eecs.umich.edu      case FEATURES_OFFSET:
2678706Sandreas.hansson@arm.com        break;
2688706Sandreas.hansson@arm.com      case NSECTOR_OFFSET:
2698706Sandreas.hansson@arm.com        cmdReg.sec_count = *data;
2708706Sandreas.hansson@arm.com        break;
2718706Sandreas.hansson@arm.com      case SECTOR_OFFSET:
2728706Sandreas.hansson@arm.com        cmdReg.sec_num = *data;
2735625Sgblack@eecs.umich.edu        break;
2745625Sgblack@eecs.umich.edu      case LCYL_OFFSET:
27514010Sgabeblack@google.com        cmdReg.cyl_low = *data;
27614010Sgabeblack@google.com        break;
2775625Sgblack@eecs.umich.edu      case HCYL_OFFSET:
2785625Sgblack@eecs.umich.edu        cmdReg.cyl_high = *data;
2795625Sgblack@eecs.umich.edu        break;
2805625Sgblack@eecs.umich.edu      case DRIVE_OFFSET:
2815625Sgblack@eecs.umich.edu        cmdReg.drive = *data;
2825625Sgblack@eecs.umich.edu        updateState(ACT_SELECT_WRITE);
2835625Sgblack@eecs.umich.edu        break;
2845625Sgblack@eecs.umich.edu      case COMMAND_OFFSET:
2855625Sgblack@eecs.umich.edu        cmdReg.command = *data;
2865625Sgblack@eecs.umich.edu        updateState(ACT_CMD_WRITE);
2875625Sgblack@eecs.umich.edu        break;
2885625Sgblack@eecs.umich.edu      default:
2895625Sgblack@eecs.umich.edu        panic("Invalid IDE command register offset: %#x\n", offset);
2905625Sgblack@eecs.umich.edu    }
2915625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
2925625Sgblack@eecs.umich.edu            (uint32_t)*data);
2935625Sgblack@eecs.umich.edu}
2945625Sgblack@eecs.umich.edu
2955625Sgblack@eecs.umich.eduvoid
2965625Sgblack@eecs.umich.eduIdeDisk::writeControl(const Addr offset, int size, const uint8_t *data)
2975625Sgblack@eecs.umich.edu{
2985625Sgblack@eecs.umich.edu    if (offset != CONTROL_OFFSET)
2995625Sgblack@eecs.umich.edu        panic("Invalid IDE control register offset: %#x\n", offset);
3005625Sgblack@eecs.umich.edu
3015625Sgblack@eecs.umich.edu    if (*data & CONTROL_RST_BIT) {
3028852Sandreas.hansson@arm.com        // force the device into the reset state
3035625Sgblack@eecs.umich.edu        devState = Device_Srst;
3048706Sandreas.hansson@arm.com        updateState(ACT_SRST_SET);
3058706Sandreas.hansson@arm.com    } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) {
3068706Sandreas.hansson@arm.com        updateState(ACT_SRST_CLEAR);
3075625Sgblack@eecs.umich.edu    }
3085625Sgblack@eecs.umich.edu
3095625Sgblack@eecs.umich.edu    nIENBit = *data & CONTROL_IEN_BIT;
3105625Sgblack@eecs.umich.edu
3115625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset,
3125625Sgblack@eecs.umich.edu            (uint32_t)*data);
3135625Sgblack@eecs.umich.edu}
3145625Sgblack@eecs.umich.edu
3155625Sgblack@eecs.umich.edu////
3165625Sgblack@eecs.umich.edu// Perform DMA transactions
3175625Sgblack@eecs.umich.edu////
3185625Sgblack@eecs.umich.edu
3195625Sgblack@eecs.umich.eduvoid
3205625Sgblack@eecs.umich.eduIdeDisk::doDmaTransfer()
3215625Sgblack@eecs.umich.edu{
3228852Sandreas.hansson@arm.com    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
3235625Sgblack@eecs.umich.edu        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
3248706Sandreas.hansson@arm.com              dmaState, devState);
3258706Sandreas.hansson@arm.com
3268706Sandreas.hansson@arm.com    if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
3278706Sandreas.hansson@arm.com        schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD);
3288706Sandreas.hansson@arm.com        return;
3295625Sgblack@eecs.umich.edu    } else
3305625Sgblack@eecs.umich.edu        ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
3315625Sgblack@eecs.umich.edu                (uint8_t*)&curPrd.entry);
3325625Sgblack@eecs.umich.edu}
3335625Sgblack@eecs.umich.edu
3345625Sgblack@eecs.umich.eduvoid
3355625Sgblack@eecs.umich.eduIdeDisk::dmaPrdReadDone()
3365625Sgblack@eecs.umich.edu{
3375625Sgblack@eecs.umich.edu
3385625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk,
3395625Sgblack@eecs.umich.edu            "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
3405625Sgblack@eecs.umich.edu            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
3415625Sgblack@eecs.umich.edu            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
3425625Sgblack@eecs.umich.edu            curPrd.getEOT(), curSector);
3435625Sgblack@eecs.umich.edu
3445625Sgblack@eecs.umich.edu    // the prd pointer has already been translated, so just do an increment
3455625Sgblack@eecs.umich.edu    curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
3465625Sgblack@eecs.umich.edu
3478852Sandreas.hansson@arm.com    if (dmaRead)
3485625Sgblack@eecs.umich.edu        doDmaDataRead();
3498706Sandreas.hansson@arm.com    else
3508706Sandreas.hansson@arm.com        doDmaDataWrite();
3518706Sandreas.hansson@arm.com}
3528706Sandreas.hansson@arm.com
3538706Sandreas.hansson@arm.comvoid
3548706Sandreas.hansson@arm.comIdeDisk::doDmaDataRead()
3558706Sandreas.hansson@arm.com{
3565625Sgblack@eecs.umich.edu    /** @todo we need to figure out what the delay actually will be */
3575625Sgblack@eecs.umich.edu    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
3585625Sgblack@eecs.umich.edu
3595625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
3605625Sgblack@eecs.umich.edu            diskDelay, totalDiskDelay);
3615625Sgblack@eecs.umich.edu
3625625Sgblack@eecs.umich.edu    schedule(dmaReadWaitEvent, curTick() + totalDiskDelay);
3635625Sgblack@eecs.umich.edu}
3645625Sgblack@eecs.umich.edu
3655625Sgblack@eecs.umich.eduvoid
3665625Sgblack@eecs.umich.eduIdeDisk::regStats()
3675625Sgblack@eecs.umich.edu{
3685625Sgblack@eecs.umich.edu    using namespace Stats;
3695625Sgblack@eecs.umich.edu    dmaReadFullPages
3705625Sgblack@eecs.umich.edu        .name(name() + ".dma_read_full_pages")
3715625Sgblack@eecs.umich.edu        .desc("Number of full page size DMA reads (not PRD).")
3725625Sgblack@eecs.umich.edu        ;
3735625Sgblack@eecs.umich.edu    dmaReadBytes
3745625Sgblack@eecs.umich.edu        .name(name() + ".dma_read_bytes")
3755625Sgblack@eecs.umich.edu        .desc("Number of bytes transfered via DMA reads (not PRD).")
3765625Sgblack@eecs.umich.edu        ;
3775625Sgblack@eecs.umich.edu    dmaReadTxs
3785625Sgblack@eecs.umich.edu        .name(name() + ".dma_read_txs")
3795625Sgblack@eecs.umich.edu        .desc("Number of DMA read transactions (not PRD).")
3805625Sgblack@eecs.umich.edu        ;
3815625Sgblack@eecs.umich.edu
3825625Sgblack@eecs.umich.edu    dmaWriteFullPages
3835625Sgblack@eecs.umich.edu        .name(name() + ".dma_write_full_pages")
3845625Sgblack@eecs.umich.edu        .desc("Number of full page size DMA writes.")
3858852Sandreas.hansson@arm.com        ;
3865625Sgblack@eecs.umich.edu    dmaWriteBytes
3878706Sandreas.hansson@arm.com        .name(name() + ".dma_write_bytes")
3888706Sandreas.hansson@arm.com        .desc("Number of bytes transfered via DMA writes.")
3898706Sandreas.hansson@arm.com        ;
3908706Sandreas.hansson@arm.com    dmaWriteTxs
3918706Sandreas.hansson@arm.com        .name(name() + ".dma_write_txs")
3925625Sgblack@eecs.umich.edu        .desc("Number of DMA write transactions.")
3935625Sgblack@eecs.umich.edu        ;
3945625Sgblack@eecs.umich.edu}
3955625Sgblack@eecs.umich.edu
3965625Sgblack@eecs.umich.eduvoid
3975625Sgblack@eecs.umich.eduIdeDisk::doDmaRead()
3985625Sgblack@eecs.umich.edu{
3995625Sgblack@eecs.umich.edu
4005625Sgblack@eecs.umich.edu    if (!dmaReadCG) {
4015625Sgblack@eecs.umich.edu        // clear out the data buffer
4025625Sgblack@eecs.umich.edu        memset(dataBuffer, 0, MAX_DMA_SIZE);
4035625Sgblack@eecs.umich.edu        dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(),
4045625Sgblack@eecs.umich.edu                curPrd.getByteCount(), TheISA::PageBytes);
4055625Sgblack@eecs.umich.edu
4065625Sgblack@eecs.umich.edu    }
4075625Sgblack@eecs.umich.edu    if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
4085625Sgblack@eecs.umich.edu        schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
4098852Sandreas.hansson@arm.com        return;
4105625Sgblack@eecs.umich.edu    } else if (!dmaReadCG->done()) {
4118706Sandreas.hansson@arm.com        assert(dmaReadCG->complete() < MAX_DMA_SIZE);
4128706Sandreas.hansson@arm.com        ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
4138706Sandreas.hansson@arm.com                &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
4148706Sandreas.hansson@arm.com        dmaReadBytes += dmaReadCG->size();
4155625Sgblack@eecs.umich.edu        dmaReadTxs++;
4165625Sgblack@eecs.umich.edu        if (dmaReadCG->size() == TheISA::PageBytes)
41714010Sgabeblack@google.com            dmaReadFullPages++;
4185625Sgblack@eecs.umich.edu        dmaReadCG->next();
4195625Sgblack@eecs.umich.edu    } else {
4205625Sgblack@eecs.umich.edu        assert(dmaReadCG->done());
4215625Sgblack@eecs.umich.edu        delete dmaReadCG;
4225625Sgblack@eecs.umich.edu        dmaReadCG = NULL;
4235625Sgblack@eecs.umich.edu        dmaReadDone();
4245625Sgblack@eecs.umich.edu    }
4255625Sgblack@eecs.umich.edu}
4265625Sgblack@eecs.umich.edu
4275625Sgblack@eecs.umich.eduvoid
4285625Sgblack@eecs.umich.eduIdeDisk::dmaReadDone()
4295625Sgblack@eecs.umich.edu{
4305625Sgblack@eecs.umich.edu
4315625Sgblack@eecs.umich.edu    uint32_t bytesWritten = 0;
4325625Sgblack@eecs.umich.edu
4335625Sgblack@eecs.umich.edu
4345625Sgblack@eecs.umich.edu    // write the data to the disk image
4355625Sgblack@eecs.umich.edu    for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
4365625Sgblack@eecs.umich.edu         bytesWritten += SectorSize) {
4375625Sgblack@eecs.umich.edu
4388852Sandreas.hansson@arm.com        cmdBytesLeft -= SectorSize;
4395625Sgblack@eecs.umich.edu        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
4408706Sandreas.hansson@arm.com    }
4418706Sandreas.hansson@arm.com
4428706Sandreas.hansson@arm.com    // check for the EOT
4438706Sandreas.hansson@arm.com    if (curPrd.getEOT()) {
4445625Sgblack@eecs.umich.edu        assert(cmdBytesLeft == 0);
4455625Sgblack@eecs.umich.edu        dmaState = Dma_Idle;
4465625Sgblack@eecs.umich.edu        updateState(ACT_DMA_DONE);
4475625Sgblack@eecs.umich.edu    } else {
4485625Sgblack@eecs.umich.edu        doDmaTransfer();
4495625Sgblack@eecs.umich.edu    }
4505625Sgblack@eecs.umich.edu}
4515625Sgblack@eecs.umich.edu
4525625Sgblack@eecs.umich.eduvoid
4535625Sgblack@eecs.umich.eduIdeDisk::doDmaDataWrite()
4545625Sgblack@eecs.umich.edu{
4555625Sgblack@eecs.umich.edu    /** @todo we need to figure out what the delay actually will be */
4565625Sgblack@eecs.umich.edu    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
4575625Sgblack@eecs.umich.edu    uint32_t bytesRead = 0;
4585625Sgblack@eecs.umich.edu
4595625Sgblack@eecs.umich.edu    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
460            diskDelay, totalDiskDelay);
461
462    memset(dataBuffer, 0, MAX_DMA_SIZE);
463    assert(cmdBytesLeft <= MAX_DMA_SIZE);
464    while (bytesRead < curPrd.getByteCount()) {
465        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
466        bytesRead += SectorSize;
467        cmdBytesLeft -= SectorSize;
468    }
469    DPRINTF(IdeDisk, "doDmaWrite, bytesRead: %d cmdBytesLeft: %d\n",
470            bytesRead, cmdBytesLeft);
471
472    schedule(dmaWriteWaitEvent, curTick() + totalDiskDelay);
473}
474
475void
476IdeDisk::doDmaWrite()
477{
478    DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
479    if (!dmaWriteCG) {
480        // clear out the data buffer
481        dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
482                curPrd.getByteCount(), TheISA::PageBytes);
483    }
484    if (ctrl->dmaPending() || ctrl->getDrainState() != Drainable::Running) {
485        schedule(dmaWriteWaitEvent, curTick() + DMA_BACKOFF_PERIOD);
486        DPRINTF(IdeDisk, "doDmaWrite: rescheduling\n");
487        return;
488    } else if (!dmaWriteCG->done()) {
489        assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
490        ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
491                &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
492        DPRINTF(IdeDisk, "doDmaWrite: not done curPrd byte count %d, eot %#x\n",
493                curPrd.getByteCount(), curPrd.getEOT());
494        dmaWriteBytes += dmaWriteCG->size();
495        dmaWriteTxs++;
496        if (dmaWriteCG->size() == TheISA::PageBytes)
497            dmaWriteFullPages++;
498        dmaWriteCG->next();
499    } else {
500        DPRINTF(IdeDisk, "doDmaWrite: done curPrd byte count %d, eot %#x\n",
501                curPrd.getByteCount(), curPrd.getEOT());
502        assert(dmaWriteCG->done());
503        delete dmaWriteCG;
504        dmaWriteCG = NULL;
505        dmaWriteDone();
506    }
507}
508
509void
510IdeDisk::dmaWriteDone()
511{
512    DPRINTF(IdeDisk, "doWriteDone: curPrd byte count %d, eot %#x cmd bytes left:%d\n",
513                curPrd.getByteCount(), curPrd.getEOT(), cmdBytesLeft);
514    // check for the EOT
515    if (curPrd.getEOT()) {
516        assert(cmdBytesLeft == 0);
517        dmaState = Dma_Idle;
518        updateState(ACT_DMA_DONE);
519    } else {
520        doDmaTransfer();
521    }
522}
523
524////
525// Disk utility routines
526///
527
528void
529IdeDisk::readDisk(uint32_t sector, uint8_t *data)
530{
531    uint32_t bytesRead = image->read(data, sector);
532
533    if (bytesRead != SectorSize)
534        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
535              name(), bytesRead, SectorSize, errno);
536}
537
538void
539IdeDisk::writeDisk(uint32_t sector, uint8_t *data)
540{
541    uint32_t bytesWritten = image->write(data, sector);
542
543    if (bytesWritten != SectorSize)
544        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
545              name(), bytesWritten, SectorSize, errno);
546}
547
548////
549// Setup and handle commands
550////
551
552void
553IdeDisk::startDma(const uint32_t &prdTableBase)
554{
555    if (dmaState != Dma_Start)
556        panic("Inconsistent DMA state, should be in Dma_Start!\n");
557
558    if (devState != Transfer_Data_Dma)
559        panic("Inconsistent device state for DMA start!\n");
560
561    // PRD base address is given by bits 31:2
562    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
563
564    dmaState = Dma_Transfer;
565
566    // schedule dma transfer (doDmaTransfer)
567    schedule(dmaTransferEvent, curTick() + 1);
568}
569
570void
571IdeDisk::abortDma()
572{
573    if (dmaState == Dma_Idle)
574        panic("Inconsistent DMA state, should be Start or Transfer!");
575
576    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
577        panic("Inconsistent device state, should be Transfer or Prepare!\n");
578
579    updateState(ACT_CMD_ERROR);
580}
581
582void
583IdeDisk::startCommand()
584{
585    DevAction_t action = ACT_NONE;
586    uint32_t size = 0;
587    dmaRead = false;
588
589    // Decode commands
590    switch (cmdReg.command) {
591        // Supported non-data commands
592      case WDSF_READ_NATIVE_MAX:
593        size = (uint32_t)image->size() - 1;
594        cmdReg.sec_num = (size & 0xff);
595        cmdReg.cyl_low = ((size & 0xff00) >> 8);
596        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
597        cmdReg.head = ((size & 0xf000000) >> 24);
598
599        devState = Command_Execution;
600        action = ACT_CMD_COMPLETE;
601        break;
602
603      case WDCC_RECAL:
604      case WDCC_IDP:
605      case WDCC_STANDBY_IMMED:
606      case WDCC_FLUSHCACHE:
607      case WDSF_VERIFY:
608      case WDSF_SEEK:
609      case SET_FEATURES:
610      case WDCC_SETMULTI:
611        devState = Command_Execution;
612        action = ACT_CMD_COMPLETE;
613        break;
614
615        // Supported PIO data-in commands
616      case WDCC_IDENTIFY:
617        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
618        devState = Prepare_Data_In;
619        action = ACT_DATA_READY;
620        break;
621
622      case WDCC_READMULTI:
623      case WDCC_READ:
624        if (!(cmdReg.drive & DRIVE_LBA_BIT))
625            panic("Attempt to perform CHS access, only supports LBA\n");
626
627        if (cmdReg.sec_count == 0)
628            cmdBytes = cmdBytesLeft = (256 * SectorSize);
629        else
630            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
631
632        curSector = getLBABase();
633
634        /** @todo make this a scheduled event to simulate disk delay */
635        devState = Prepare_Data_In;
636        action = ACT_DATA_READY;
637        break;
638
639        // Supported PIO data-out commands
640      case WDCC_WRITEMULTI:
641      case WDCC_WRITE:
642        if (!(cmdReg.drive & DRIVE_LBA_BIT))
643            panic("Attempt to perform CHS access, only supports LBA\n");
644
645        if (cmdReg.sec_count == 0)
646            cmdBytes = cmdBytesLeft = (256 * SectorSize);
647        else
648            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
649        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d\n", cmdBytesLeft);
650        curSector = getLBABase();
651
652        devState = Prepare_Data_Out;
653        action = ACT_DATA_READY;
654        break;
655
656        // Supported DMA commands
657      case WDCC_WRITEDMA:
658        dmaRead = true;  // a write to the disk is a DMA read from memory
659      case WDCC_READDMA:
660        if (!(cmdReg.drive & DRIVE_LBA_BIT))
661            panic("Attempt to perform CHS access, only supports LBA\n");
662
663        if (cmdReg.sec_count == 0)
664            cmdBytes = cmdBytesLeft = (256 * SectorSize);
665        else
666            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
667        DPRINTF(IdeDisk, "Setting cmdBytesLeft to %d in readdma\n", cmdBytesLeft);
668
669        curSector = getLBABase();
670
671        devState = Prepare_Data_Dma;
672        action = ACT_DMA_READY;
673        break;
674
675      default:
676        panic("Unsupported ATA command: %#x\n", cmdReg.command);
677    }
678
679    if (action != ACT_NONE) {
680        // set the BSY bit
681        status |= STATUS_BSY_BIT;
682        // clear the DRQ bit
683        status &= ~STATUS_DRQ_BIT;
684        // clear the DF bit
685        status &= ~STATUS_DF_BIT;
686
687        updateState(action);
688    }
689}
690
691////
692// Handle setting and clearing interrupts
693////
694
695void
696IdeDisk::intrPost()
697{
698    DPRINTF(IdeDisk, "Posting Interrupt\n");
699    if (intrPending)
700        panic("Attempt to post an interrupt with one pending\n");
701
702    intrPending = true;
703
704    // talk to controller to set interrupt
705    if (ctrl) {
706        ctrl->intrPost();
707    }
708}
709
710void
711IdeDisk::intrClear()
712{
713    DPRINTF(IdeDisk, "Clearing Interrupt\n");
714    if (!intrPending)
715        panic("Attempt to clear a non-pending interrupt\n");
716
717    intrPending = false;
718
719    // talk to controller to clear interrupt
720    if (ctrl)
721        ctrl->intrClear();
722}
723
724////
725// Manage the device internal state machine
726////
727
728void
729IdeDisk::updateState(DevAction_t action)
730{
731    switch (devState) {
732      case Device_Srst:
733        if (action == ACT_SRST_SET) {
734            // set the BSY bit
735            status |= STATUS_BSY_BIT;
736        } else if (action == ACT_SRST_CLEAR) {
737            // clear the BSY bit
738            status &= ~STATUS_BSY_BIT;
739
740            // reset the device state
741            reset(devID);
742        }
743        break;
744
745      case Device_Idle_S:
746        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
747            devState = Device_Idle_NS;
748        } else if (action == ACT_CMD_WRITE) {
749            startCommand();
750        }
751
752        break;
753
754      case Device_Idle_SI:
755        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
756            devState = Device_Idle_NS;
757            intrClear();
758        } else if (action == ACT_STAT_READ || isIENSet()) {
759            devState = Device_Idle_S;
760            intrClear();
761        } else if (action == ACT_CMD_WRITE) {
762            intrClear();
763            startCommand();
764        }
765
766        break;
767
768      case Device_Idle_NS:
769        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
770            if (!isIENSet() && intrPending) {
771                devState = Device_Idle_SI;
772                intrPost();
773            }
774            if (isIENSet() || !intrPending) {
775                devState = Device_Idle_S;
776            }
777        }
778        break;
779
780      case Command_Execution:
781        if (action == ACT_CMD_COMPLETE) {
782            // clear the BSY bit
783            setComplete();
784
785            if (!isIENSet()) {
786                devState = Device_Idle_SI;
787                intrPost();
788            } else {
789                devState = Device_Idle_S;
790            }
791        }
792        break;
793
794      case Prepare_Data_In:
795        if (action == ACT_CMD_ERROR) {
796            // clear the BSY bit
797            setComplete();
798
799            if (!isIENSet()) {
800                devState = Device_Idle_SI;
801                intrPost();
802            } else {
803                devState = Device_Idle_S;
804            }
805        } else if (action == ACT_DATA_READY) {
806            // clear the BSY bit
807            status &= ~STATUS_BSY_BIT;
808            // set the DRQ bit
809            status |= STATUS_DRQ_BIT;
810
811            // copy the data into the data buffer
812            if (cmdReg.command == WDCC_IDENTIFY) {
813                // Reset the drqBytes for this block
814                drqBytesLeft = sizeof(struct ataparams);
815
816                memcpy((void *)dataBuffer, (void *)&driveID,
817                       sizeof(struct ataparams));
818            } else {
819                // Reset the drqBytes for this block
820                drqBytesLeft = SectorSize;
821
822                readDisk(curSector++, dataBuffer);
823            }
824
825            // put the first two bytes into the data register
826            memcpy((void *)&cmdReg.data, (void *)dataBuffer,
827                   sizeof(uint16_t));
828
829            if (!isIENSet()) {
830                devState = Data_Ready_INTRQ_In;
831                intrPost();
832            } else {
833                devState = Transfer_Data_In;
834            }
835        }
836        break;
837
838      case Data_Ready_INTRQ_In:
839        if (action == ACT_STAT_READ) {
840            devState = Transfer_Data_In;
841            intrClear();
842        }
843        break;
844
845      case Transfer_Data_In:
846        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
847            if (action == ACT_DATA_READ_BYTE) {
848                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
849            } else {
850                drqBytesLeft -= 2;
851                cmdBytesLeft -= 2;
852
853                // copy next short into data registers
854                if (drqBytesLeft)
855                    memcpy((void *)&cmdReg.data,
856                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
857                           sizeof(uint16_t));
858            }
859
860            if (drqBytesLeft == 0) {
861                if (cmdBytesLeft == 0) {
862                    // Clear the BSY bit
863                    setComplete();
864                    devState = Device_Idle_S;
865                } else {
866                    devState = Prepare_Data_In;
867                    // set the BSY_BIT
868                    status |= STATUS_BSY_BIT;
869                    // clear the DRQ_BIT
870                    status &= ~STATUS_DRQ_BIT;
871
872                    /** @todo change this to a scheduled event to simulate
873                        disk delay */
874                    updateState(ACT_DATA_READY);
875                }
876            }
877        }
878        break;
879
880      case Prepare_Data_Out:
881        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
882            // clear the BSY bit
883            setComplete();
884
885            if (!isIENSet()) {
886                devState = Device_Idle_SI;
887                intrPost();
888            } else {
889                devState = Device_Idle_S;
890            }
891        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
892            // clear the BSY bit
893            status &= ~STATUS_BSY_BIT;
894            // set the DRQ bit
895            status |= STATUS_DRQ_BIT;
896
897            // clear the data buffer to get it ready for writes
898            memset(dataBuffer, 0, MAX_DMA_SIZE);
899
900            // reset the drqBytes for this block
901            drqBytesLeft = SectorSize;
902
903            if (cmdBytesLeft == cmdBytes || isIENSet()) {
904                devState = Transfer_Data_Out;
905            } else {
906                devState = Data_Ready_INTRQ_Out;
907                intrPost();
908            }
909        }
910        break;
911
912      case Data_Ready_INTRQ_Out:
913        if (action == ACT_STAT_READ) {
914            devState = Transfer_Data_Out;
915            intrClear();
916        }
917        break;
918
919      case Transfer_Data_Out:
920        if (action == ACT_DATA_WRITE_BYTE ||
921            action == ACT_DATA_WRITE_SHORT) {
922
923            if (action == ACT_DATA_READ_BYTE) {
924                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
925            } else {
926                // copy the latest short into the data buffer
927                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
928                       (void *)&cmdReg.data,
929                       sizeof(uint16_t));
930
931                drqBytesLeft -= 2;
932                cmdBytesLeft -= 2;
933            }
934
935            if (drqBytesLeft == 0) {
936                // copy the block to the disk
937                writeDisk(curSector++, dataBuffer);
938
939                // set the BSY bit
940                status |= STATUS_BSY_BIT;
941                // set the seek bit
942                status |= STATUS_SEEK_BIT;
943                // clear the DRQ bit
944                status &= ~STATUS_DRQ_BIT;
945
946                devState = Prepare_Data_Out;
947
948                /** @todo change this to a scheduled event to simulate
949                    disk delay */
950                updateState(ACT_DATA_READY);
951            }
952        }
953        break;
954
955      case Prepare_Data_Dma:
956        if (action == ACT_CMD_ERROR) {
957            // clear the BSY bit
958            setComplete();
959
960            if (!isIENSet()) {
961                devState = Device_Idle_SI;
962                intrPost();
963            } else {
964                devState = Device_Idle_S;
965            }
966        } else if (action == ACT_DMA_READY) {
967            // clear the BSY bit
968            status &= ~STATUS_BSY_BIT;
969            // set the DRQ bit
970            status |= STATUS_DRQ_BIT;
971
972            devState = Transfer_Data_Dma;
973
974            if (dmaState != Dma_Idle)
975                panic("Inconsistent DMA state, should be Dma_Idle\n");
976
977            dmaState = Dma_Start;
978            // wait for the write to the DMA start bit
979        }
980        break;
981
982      case Transfer_Data_Dma:
983        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
984            // clear the BSY bit
985            setComplete();
986            // set the seek bit
987            status |= STATUS_SEEK_BIT;
988            // clear the controller state for DMA transfer
989            ctrl->setDmaComplete(this);
990
991            if (!isIENSet()) {
992                devState = Device_Idle_SI;
993                intrPost();
994            } else {
995                devState = Device_Idle_S;
996            }
997        }
998        break;
999
1000      default:
1001        panic("Unknown IDE device state: %#x\n", devState);
1002    }
1003}
1004
1005void
1006IdeDisk::serialize(ostream &os)
1007{
1008    // Check all outstanding events to see if they are scheduled
1009    // these are all mutually exclusive
1010    Tick reschedule = 0;
1011    Events_t event = None;
1012
1013    int eventCount = 0;
1014
1015    if (dmaTransferEvent.scheduled()) {
1016        reschedule = dmaTransferEvent.when();
1017        event = Transfer;
1018        eventCount++;
1019    }
1020    if (dmaReadWaitEvent.scheduled()) {
1021        reschedule = dmaReadWaitEvent.when();
1022        event = ReadWait;
1023        eventCount++;
1024    }
1025    if (dmaWriteWaitEvent.scheduled()) {
1026        reschedule = dmaWriteWaitEvent.when();
1027        event = WriteWait;
1028        eventCount++;
1029    }
1030    if (dmaPrdReadEvent.scheduled()) {
1031        reschedule = dmaPrdReadEvent.when();
1032        event = PrdRead;
1033        eventCount++;
1034    }
1035    if (dmaReadEvent.scheduled()) {
1036        reschedule = dmaReadEvent.when();
1037        event = DmaRead;
1038        eventCount++;
1039    }
1040    if (dmaWriteEvent.scheduled()) {
1041        reschedule = dmaWriteEvent.when();
1042        event = DmaWrite;
1043        eventCount++;
1044    }
1045
1046    assert(eventCount <= 1);
1047
1048    SERIALIZE_SCALAR(reschedule);
1049    SERIALIZE_ENUM(event);
1050
1051    // Serialize device registers
1052    SERIALIZE_SCALAR(cmdReg.data);
1053    SERIALIZE_SCALAR(cmdReg.sec_count);
1054    SERIALIZE_SCALAR(cmdReg.sec_num);
1055    SERIALIZE_SCALAR(cmdReg.cyl_low);
1056    SERIALIZE_SCALAR(cmdReg.cyl_high);
1057    SERIALIZE_SCALAR(cmdReg.drive);
1058    SERIALIZE_SCALAR(cmdReg.command);
1059    SERIALIZE_SCALAR(status);
1060    SERIALIZE_SCALAR(nIENBit);
1061    SERIALIZE_SCALAR(devID);
1062
1063    // Serialize the PRD related information
1064    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
1065    SERIALIZE_SCALAR(curPrd.entry.byteCount);
1066    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
1067    SERIALIZE_SCALAR(curPrdAddr);
1068
1069    /** @todo need to serialized chunk generator stuff!! */
1070    // Serialize current transfer related information
1071    SERIALIZE_SCALAR(cmdBytesLeft);
1072    SERIALIZE_SCALAR(cmdBytes);
1073    SERIALIZE_SCALAR(drqBytesLeft);
1074    SERIALIZE_SCALAR(curSector);
1075    SERIALIZE_SCALAR(dmaRead);
1076    SERIALIZE_SCALAR(intrPending);
1077    SERIALIZE_ENUM(devState);
1078    SERIALIZE_ENUM(dmaState);
1079    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1080}
1081
1082void
1083IdeDisk::unserialize(Checkpoint *cp, const string &section)
1084{
1085    // Reschedule events that were outstanding
1086    // these are all mutually exclusive
1087    Tick reschedule = 0;
1088    Events_t event = None;
1089
1090    UNSERIALIZE_SCALAR(reschedule);
1091    UNSERIALIZE_ENUM(event);
1092
1093    switch (event) {
1094      case None : break;
1095      case Transfer : schedule(dmaTransferEvent, reschedule); break;
1096      case ReadWait : schedule(dmaReadWaitEvent, reschedule); break;
1097      case WriteWait : schedule(dmaWriteWaitEvent, reschedule); break;
1098      case PrdRead : schedule(dmaPrdReadEvent, reschedule); break;
1099      case DmaRead : schedule(dmaReadEvent, reschedule); break;
1100      case DmaWrite : schedule(dmaWriteEvent, reschedule); break;
1101    }
1102
1103    // Unserialize device registers
1104    UNSERIALIZE_SCALAR(cmdReg.data);
1105    UNSERIALIZE_SCALAR(cmdReg.sec_count);
1106    UNSERIALIZE_SCALAR(cmdReg.sec_num);
1107    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
1108    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
1109    UNSERIALIZE_SCALAR(cmdReg.drive);
1110    UNSERIALIZE_SCALAR(cmdReg.command);
1111    UNSERIALIZE_SCALAR(status);
1112    UNSERIALIZE_SCALAR(nIENBit);
1113    UNSERIALIZE_SCALAR(devID);
1114
1115    // Unserialize the PRD related information
1116    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
1117    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
1118    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
1119    UNSERIALIZE_SCALAR(curPrdAddr);
1120
1121    /** @todo need to serialized chunk generator stuff!! */
1122    // Unserialize current transfer related information
1123    UNSERIALIZE_SCALAR(cmdBytes);
1124    UNSERIALIZE_SCALAR(cmdBytesLeft);
1125    UNSERIALIZE_SCALAR(drqBytesLeft);
1126    UNSERIALIZE_SCALAR(curSector);
1127    UNSERIALIZE_SCALAR(dmaRead);
1128    UNSERIALIZE_SCALAR(intrPending);
1129    UNSERIALIZE_ENUM(devState);
1130    UNSERIALIZE_ENUM(dmaState);
1131    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
1132}
1133
1134IdeDisk *
1135IdeDiskParams::create()
1136{
1137    return new IdeDisk(this);
1138}
1139