ide_disk.cc revision 2565
1955SN/A/*
2955SN/A * Copyright (c) 2004-2005 The Regents of The University of Michigan
313576Sciro.santilli@arm.com * All rights reserved.
413576Sciro.santilli@arm.com *
513576Sciro.santilli@arm.com * Redistribution and use in source and binary forms, with or without
613576Sciro.santilli@arm.com * modification, are permitted provided that the following conditions are
713576Sciro.santilli@arm.com * met: redistributions of source code must retain the above copyright
813576Sciro.santilli@arm.com * notice, this list of conditions and the following disclaimer;
913576Sciro.santilli@arm.com * redistributions in binary form must reproduce the above copyright
1013576Sciro.santilli@arm.com * notice, this list of conditions and the following disclaimer in the
1113576Sciro.santilli@arm.com * documentation and/or other materials provided with the distribution;
1213576Sciro.santilli@arm.com * neither the name of the copyright holders nor the names of its
1313576Sciro.santilli@arm.com * contributors may be used to endorse or promote products derived from
141762SN/A * this software without specific prior written permission.
15955SN/A *
16955SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17955SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18955SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19955SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20955SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21955SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22955SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23955SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24955SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25955SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26955SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27955SN/A */
28955SN/A
29955SN/A/** @file
30955SN/A * Device model implementation for an IDE disk
31955SN/A */
32955SN/A
33955SN/A#include <cerrno>
34955SN/A#include <cstring>
35955SN/A#include <deque>
36955SN/A#include <string>
37955SN/A
38955SN/A#include "base/chunk_generator.hh"
392665Ssaidi@eecs.umich.edu#include "base/cprintf.hh" // csprintf
404762Snate@binkert.org#include "base/trace.hh"
41955SN/A#include "dev/disk_image.hh"
4212563Sgabeblack@google.com#include "dev/ide_disk.hh"
4312563Sgabeblack@google.com#include "dev/ide_ctrl.hh"
445522Snate@binkert.org#include "dev/tsunami.hh"
456143Snate@binkert.org#include "dev/tsunami_pchip.hh"
4612371Sgabeblack@google.com#include "mem/packet.hh"
474762Snate@binkert.org#include "sim/builder.hh"
485522Snate@binkert.org#include "sim/sim_object.hh"
49955SN/A#include "sim/root.hh"
505522Snate@binkert.org#include "arch/isa_traits.hh"
5111974Sgabeblack@google.com
52955SN/Ausing namespace std;
535522Snate@binkert.orgusing namespace TheISA;
544202Sbinkertn@umich.edu
555742Snate@binkert.orgIdeDisk::IdeDisk(const string &name, DiskImage *img,
56955SN/A                 int id, Tick delay)
574381Sbinkertn@umich.edu    : SimObject(name), ctrl(NULL), image(img), diskDelay(delay),
584381Sbinkertn@umich.edu      dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this),
5912246Sgabeblack@google.com      dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this),
6012246Sgabeblack@google.com      dmaReadEvent(this), dmaWriteEvent(this)
618334Snate@binkert.org{
62955SN/A    // Reset the device state
63955SN/A    reset(id);
644202Sbinkertn@umich.edu
65955SN/A    // fill out the drive ID structure
664382Sbinkertn@umich.edu    memset(&driveID, 0, sizeof(struct ataparams));
674382Sbinkertn@umich.edu
684382Sbinkertn@umich.edu    // Calculate LBA and C/H/S values
696654Snate@binkert.org    uint16_t cylinders;
705517Snate@binkert.org    uint8_t heads;
718614Sgblack@eecs.umich.edu    uint8_t sectors;
727674Snate@binkert.org
736143Snate@binkert.org    uint32_t lba_size = image->size();
746143Snate@binkert.org    if (lba_size >= 16383*16*63) {
756143Snate@binkert.org        cylinders = 16383;
7612302Sgabeblack@google.com        heads = 16;
7712302Sgabeblack@google.com        sectors = 63;
7812302Sgabeblack@google.com    } else {
7912371Sgabeblack@google.com        if (lba_size >= 63)
8012371Sgabeblack@google.com            sectors = 63;
8112371Sgabeblack@google.com        else
8212371Sgabeblack@google.com            sectors = lba_size;
8312371Sgabeblack@google.com
8412371Sgabeblack@google.com        if ((lba_size / sectors) >= 16)
8512371Sgabeblack@google.com            heads = 16;
8612371Sgabeblack@google.com        else
8712371Sgabeblack@google.com            heads = (lba_size / sectors);
8812371Sgabeblack@google.com
8912371Sgabeblack@google.com        cylinders = lba_size / (heads * sectors);
9012371Sgabeblack@google.com    }
9112371Sgabeblack@google.com
9212371Sgabeblack@google.com    // Setup the model name
9312371Sgabeblack@google.com    strncpy((char *)driveID.atap_model, "5MI EDD si k",
9412371Sgabeblack@google.com            sizeof(driveID.atap_model));
9512371Sgabeblack@google.com    // Set the maximum multisector transfer size
9612371Sgabeblack@google.com    driveID.atap_multi = MAX_MULTSECT;
9712371Sgabeblack@google.com    // IORDY supported, IORDY disabled, LBA enabled, DMA enabled
9812371Sgabeblack@google.com    driveID.atap_capabilities1 = 0x7;
9912371Sgabeblack@google.com    // UDMA support, EIDE support
10012371Sgabeblack@google.com    driveID.atap_extensions = 0x6;
10112371Sgabeblack@google.com    // Setup default C/H/S settings
10212371Sgabeblack@google.com    driveID.atap_cylinders = cylinders;
10312371Sgabeblack@google.com    driveID.atap_sectors = sectors;
10412371Sgabeblack@google.com    driveID.atap_heads = heads;
10512371Sgabeblack@google.com    // Setup the current multisector transfer size
10612371Sgabeblack@google.com    driveID.atap_curmulti = MAX_MULTSECT;
10712371Sgabeblack@google.com    driveID.atap_curmulti_valid = 0x1;
10812371Sgabeblack@google.com    // Number of sectors on disk
10912371Sgabeblack@google.com    driveID.atap_capacity = lba_size;
11012371Sgabeblack@google.com    // Multiword DMA mode 2 and below supported
11112371Sgabeblack@google.com    driveID.atap_dmamode_supp = 0x400;
11212371Sgabeblack@google.com    // Set PIO mode 4 and 3 supported
11312371Sgabeblack@google.com    driveID.atap_piomode_supp = 0x3;
11412371Sgabeblack@google.com    // Set DMA mode 4 and below supported
11512371Sgabeblack@google.com    driveID.atap_udmamode_supp = 0x1f;
11612371Sgabeblack@google.com    // Statically set hardware config word
11712371Sgabeblack@google.com    driveID.atap_hwreset_res = 0x4001;
11812371Sgabeblack@google.com
11912371Sgabeblack@google.com    //arbitrary for now...
12012371Sgabeblack@google.com    driveID.atap_ata_major = WDC_VER_ATA7;
12112371Sgabeblack@google.com}
12212371Sgabeblack@google.com
12312371Sgabeblack@google.comIdeDisk::~IdeDisk()
12412371Sgabeblack@google.com{
12512371Sgabeblack@google.com    // destroy the data buffer
12612302Sgabeblack@google.com    delete [] dataBuffer;
12712371Sgabeblack@google.com}
12812302Sgabeblack@google.com
12912371Sgabeblack@google.comvoid
13012302Sgabeblack@google.comIdeDisk::reset(int id)
13112302Sgabeblack@google.com{
13212371Sgabeblack@google.com    // initialize the data buffer and shadow registers
13312371Sgabeblack@google.com    dataBuffer = new uint8_t[MAX_DMA_SIZE];
13412371Sgabeblack@google.com
13512371Sgabeblack@google.com    memset(dataBuffer, 0, MAX_DMA_SIZE);
13612302Sgabeblack@google.com    memset(&cmdReg, 0, sizeof(CommandReg_t));
13712371Sgabeblack@google.com    memset(&curPrd.entry, 0, sizeof(PrdEntry_t));
13812371Sgabeblack@google.com
13912371Sgabeblack@google.com    curPrdAddr = 0;
14012371Sgabeblack@google.com    curSector = 0;
14111983Sgabeblack@google.com    cmdBytes = 0;
1426143Snate@binkert.org    cmdBytesLeft = 0;
1438233Snate@binkert.org    drqBytesLeft = 0;
14412302Sgabeblack@google.com    dmaRead = false;
1456143Snate@binkert.org    intrPending = false;
1466143Snate@binkert.org
14712302Sgabeblack@google.com    // set the device state to idle
1484762Snate@binkert.org    dmaState = Dma_Idle;
1496143Snate@binkert.org
1508233Snate@binkert.org    if (id == DEV0) {
1518233Snate@binkert.org        devState = Device_Idle_S;
15212302Sgabeblack@google.com        devID = DEV0;
15312302Sgabeblack@google.com    } else if (id == DEV1) {
1546143Snate@binkert.org        devState = Device_Idle_NS;
15512362Sgabeblack@google.com        devID = DEV1;
15612362Sgabeblack@google.com    } else {
15712362Sgabeblack@google.com        panic("Invalid device ID: %#x\n", id);
15812362Sgabeblack@google.com    }
15912302Sgabeblack@google.com
16012302Sgabeblack@google.com    // set the device ready bit
16112302Sgabeblack@google.com    status = STATUS_DRDY_BIT;
16212302Sgabeblack@google.com
16312302Sgabeblack@google.com    /* The error register must be set to 0x1 on start-up to
16412363Sgabeblack@google.com       indicate that no diagnostic error was detected */
16512363Sgabeblack@google.com    cmdReg.error = 0x1;
16612363Sgabeblack@google.com}
16712363Sgabeblack@google.com
16812302Sgabeblack@google.com////
16912363Sgabeblack@google.com// Utility functions
17012363Sgabeblack@google.com////
17112363Sgabeblack@google.com
17212363Sgabeblack@google.combool
17312363Sgabeblack@google.comIdeDisk::isDEVSelect()
1748233Snate@binkert.org{
1756143Snate@binkert.org    return ctrl->isDiskSelected(this);
1766143Snate@binkert.org}
1776143Snate@binkert.org
1786143Snate@binkert.orgAddr
1796143Snate@binkert.orgIdeDisk::pciToDma(Addr pciAddr)
1806143Snate@binkert.org{
1816143Snate@binkert.org    if (ctrl)
1826143Snate@binkert.org        return ctrl->plat->pciToDma(pciAddr);
1836143Snate@binkert.org    else
1847065Snate@binkert.org        panic("Access to unset controller!\n");
1856143Snate@binkert.org}
18612362Sgabeblack@google.com
18712362Sgabeblack@google.com////
18812362Sgabeblack@google.com// Device registers read/write
18912362Sgabeblack@google.com////
19012362Sgabeblack@google.com
19112362Sgabeblack@google.comvoid
19212362Sgabeblack@google.comIdeDisk::read(const Addr &offset, IdeRegType reg_type, uint8_t *data)
19312362Sgabeblack@google.com{
19412362Sgabeblack@google.com    DevAction_t action = ACT_NONE;
19512362Sgabeblack@google.com
19612362Sgabeblack@google.com    switch (reg_type) {
19712362Sgabeblack@google.com      case COMMAND_BLOCK:
1988233Snate@binkert.org        switch (offset) {
1998233Snate@binkert.org          // Data transfers occur two bytes at a time
2008233Snate@binkert.org          case DATA_OFFSET:
2018233Snate@binkert.org            *(uint16_t*)data = cmdReg.data;
2028233Snate@binkert.org            action = ACT_DATA_READ_SHORT;
2038233Snate@binkert.org            break;
2048233Snate@binkert.org          case ERROR_OFFSET:
2058233Snate@binkert.org            *data = cmdReg.error;
2068233Snate@binkert.org            break;
2078233Snate@binkert.org          case NSECTOR_OFFSET:
2088233Snate@binkert.org            *data = cmdReg.sec_count;
2098233Snate@binkert.org            break;
2108233Snate@binkert.org          case SECTOR_OFFSET:
2118233Snate@binkert.org            *data = cmdReg.sec_num;
2128233Snate@binkert.org            break;
2138233Snate@binkert.org          case LCYL_OFFSET:
2148233Snate@binkert.org            *data = cmdReg.cyl_low;
2158233Snate@binkert.org            break;
2168233Snate@binkert.org          case HCYL_OFFSET:
2178233Snate@binkert.org            *data = cmdReg.cyl_high;
2188233Snate@binkert.org            break;
2196143Snate@binkert.org          case DRIVE_OFFSET:
2206143Snate@binkert.org            *data = cmdReg.drive;
2216143Snate@binkert.org            break;
2226143Snate@binkert.org          case STATUS_OFFSET:
2236143Snate@binkert.org            *data = status;
2246143Snate@binkert.org            action = ACT_STAT_READ;
2259982Satgutier@umich.edu            break;
22613576Sciro.santilli@arm.com          default:
22713576Sciro.santilli@arm.com            panic("Invalid IDE command register offset: %#x\n", offset);
22813576Sciro.santilli@arm.com        }
22913576Sciro.santilli@arm.com        break;
23013576Sciro.santilli@arm.com      case CONTROL_BLOCK:
23113576Sciro.santilli@arm.com        if (offset == ALTSTAT_OFFSET)
23213576Sciro.santilli@arm.com            *data = status;
23313576Sciro.santilli@arm.com        else
23413576Sciro.santilli@arm.com            panic("Invalid IDE control register offset: %#x\n", offset);
23513576Sciro.santilli@arm.com        break;
23613576Sciro.santilli@arm.com      default:
23713576Sciro.santilli@arm.com        panic("Unknown register block!\n");
23813576Sciro.santilli@arm.com    }
23913576Sciro.santilli@arm.com
24013576Sciro.santilli@arm.com    if (action != ACT_NONE)
24113576Sciro.santilli@arm.com        updateState(action);
24213576Sciro.santilli@arm.com}
24313576Sciro.santilli@arm.com
24413576Sciro.santilli@arm.comvoid
24513576Sciro.santilli@arm.comIdeDisk::write(const Addr &offset, IdeRegType reg_type, const uint8_t *data)
24613576Sciro.santilli@arm.com{
24713576Sciro.santilli@arm.com    DevAction_t action = ACT_NONE;
24813576Sciro.santilli@arm.com
24913576Sciro.santilli@arm.com    switch (reg_type) {
25013576Sciro.santilli@arm.com      case COMMAND_BLOCK:
25113576Sciro.santilli@arm.com        switch (offset) {
25213576Sciro.santilli@arm.com          case DATA_OFFSET:
25313576Sciro.santilli@arm.com            cmdReg.data = *(uint16_t*)data;
25413576Sciro.santilli@arm.com            action = ACT_DATA_WRITE_SHORT;
25513576Sciro.santilli@arm.com            break;
25613576Sciro.santilli@arm.com          case FEATURES_OFFSET:
25713576Sciro.santilli@arm.com            break;
25813630Sciro.santilli@arm.com          case NSECTOR_OFFSET:
25913630Sciro.santilli@arm.com            cmdReg.sec_count = *data;
26013576Sciro.santilli@arm.com            break;
26113576Sciro.santilli@arm.com          case SECTOR_OFFSET:
26213576Sciro.santilli@arm.com            cmdReg.sec_num = *data;
26313576Sciro.santilli@arm.com            break;
26413576Sciro.santilli@arm.com          case LCYL_OFFSET:
26513576Sciro.santilli@arm.com            cmdReg.cyl_low = *data;
26613576Sciro.santilli@arm.com            break;
26713576Sciro.santilli@arm.com          case HCYL_OFFSET:
26813576Sciro.santilli@arm.com            cmdReg.cyl_high = *data;
26913576Sciro.santilli@arm.com            break;
27013576Sciro.santilli@arm.com          case DRIVE_OFFSET:
27113576Sciro.santilli@arm.com            cmdReg.drive = *data;
27213576Sciro.santilli@arm.com            action = ACT_SELECT_WRITE;
27313576Sciro.santilli@arm.com            break;
27413576Sciro.santilli@arm.com          case COMMAND_OFFSET:
27513576Sciro.santilli@arm.com            cmdReg.command = *data;
27613576Sciro.santilli@arm.com            action = ACT_CMD_WRITE;
27713576Sciro.santilli@arm.com            break;
27813576Sciro.santilli@arm.com          default:
27913576Sciro.santilli@arm.com            panic("Invalid IDE command register offset: %#x\n", offset);
28013576Sciro.santilli@arm.com        }
28113576Sciro.santilli@arm.com        break;
28213576Sciro.santilli@arm.com      case CONTROL_BLOCK:
28313576Sciro.santilli@arm.com        if (offset == CONTROL_OFFSET) {
28413576Sciro.santilli@arm.com            if (*data & CONTROL_RST_BIT) {
28513576Sciro.santilli@arm.com                // force the device into the reset state
28613576Sciro.santilli@arm.com                devState = Device_Srst;
28713576Sciro.santilli@arm.com                action = ACT_SRST_SET;
28813576Sciro.santilli@arm.com            } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT))
28913576Sciro.santilli@arm.com                action = ACT_SRST_CLEAR;
29013576Sciro.santilli@arm.com
29113576Sciro.santilli@arm.com            nIENBit = (*data & CONTROL_IEN_BIT) ? true : false;
29213576Sciro.santilli@arm.com        }
29313576Sciro.santilli@arm.com        else
29413576Sciro.santilli@arm.com            panic("Invalid IDE control register offset: %#x\n", offset);
29513576Sciro.santilli@arm.com        break;
29613576Sciro.santilli@arm.com      default:
29713577Sciro.santilli@arm.com        panic("Unknown register block!\n");
29813577Sciro.santilli@arm.com    }
29913577Sciro.santilli@arm.com
3006143Snate@binkert.org    if (action != ACT_NONE)
30112302Sgabeblack@google.com        updateState(action);
30212302Sgabeblack@google.com}
30312302Sgabeblack@google.com
30412302Sgabeblack@google.com////
30512302Sgabeblack@google.com// Perform DMA transactions
30612302Sgabeblack@google.com////
30712302Sgabeblack@google.com
30812302Sgabeblack@google.comvoid
30911983Sgabeblack@google.comIdeDisk::doDmaTransfer()
31011983Sgabeblack@google.com{
31111983Sgabeblack@google.com    if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma)
31212302Sgabeblack@google.com        panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n",
31312302Sgabeblack@google.com              dmaState, devState);
31412302Sgabeblack@google.com
31512302Sgabeblack@google.com    if (ctrl->dmaPending()) {
31612302Sgabeblack@google.com        dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
31712302Sgabeblack@google.com        return;
31811983Sgabeblack@google.com    } else
3196143Snate@binkert.org        ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent,
32012305Sgabeblack@google.com                (uint8_t*)&curPrd.entry);
32112302Sgabeblack@google.com}
32212302Sgabeblack@google.com
32312302Sgabeblack@google.comvoid
3246143Snate@binkert.orgIdeDisk::dmaPrdReadDone()
3256143Snate@binkert.org{
3266143Snate@binkert.org    DPRINTF(IdeDisk,
3275522Snate@binkert.org            "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n",
3286143Snate@binkert.org            curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()),
3296143Snate@binkert.org            curPrd.getByteCount(), (cmdBytesLeft/SectorSize),
3306143Snate@binkert.org            curPrd.getEOT(), curSector);
3319982Satgutier@umich.edu
33212302Sgabeblack@google.com    // the prd pointer has already been translated, so just do an increment
33312302Sgabeblack@google.com    curPrdAddr = curPrdAddr + sizeof(PrdEntry_t);
33412302Sgabeblack@google.com
3356143Snate@binkert.org    if (dmaRead)
3366143Snate@binkert.org        doDmaDataRead();
3376143Snate@binkert.org    else
3386143Snate@binkert.org        doDmaDataWrite();
3395522Snate@binkert.org}
3405522Snate@binkert.org
3415522Snate@binkert.orgvoid
3425522Snate@binkert.orgIdeDisk::doDmaDataRead()
3435604Snate@binkert.org{
3445604Snate@binkert.org    /** @todo we need to figure out what the delay actually will be */
3456143Snate@binkert.org    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
3466143Snate@binkert.org
3474762Snate@binkert.org    DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n",
3484762Snate@binkert.org            diskDelay, totalDiskDelay);
3496143Snate@binkert.org
3506727Ssteve.reinhardt@amd.com    dmaReadWaitEvent.schedule(curTick + totalDiskDelay);
3516727Ssteve.reinhardt@amd.com}
3526727Ssteve.reinhardt@amd.com
3534762Snate@binkert.org
3546143Snate@binkert.orgvoid
3556143Snate@binkert.orgIdeDisk::doDmaRead()
3566143Snate@binkert.org{
3576143Snate@binkert.org
3586727Ssteve.reinhardt@amd.com    if (!dmaReadCG) {
3596143Snate@binkert.org        // clear out the data buffer
3607674Snate@binkert.org        memset(dataBuffer, 0, MAX_DMA_SIZE);
3617674Snate@binkert.org        dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(),
3625604Snate@binkert.org                curPrd.getByteCount(), TheISA::PageBytes);
3636143Snate@binkert.org
3646143Snate@binkert.org    }
3656143Snate@binkert.org    if (ctrl->dmaPending()) {
3664762Snate@binkert.org        panic("shouldn't be reentant??");
3676143Snate@binkert.org        dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
3684762Snate@binkert.org        return;
3694762Snate@binkert.org    } else if (!dmaReadCG->done()) {
3704762Snate@binkert.org        assert(dmaReadCG->complete() < MAX_DMA_SIZE);
3716143Snate@binkert.org        ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(),
3726143Snate@binkert.org                &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete());
3734762Snate@binkert.org        dmaReadCG->next();
37412302Sgabeblack@google.com    } else {
37512302Sgabeblack@google.com        assert(dmaReadCG->done());
3768233Snate@binkert.org        delete dmaReadCG;
37712302Sgabeblack@google.com        dmaReadCG = NULL;
3786143Snate@binkert.org        dmaReadDone();
3796143Snate@binkert.org    }
3804762Snate@binkert.org}
3816143Snate@binkert.org
3824762Snate@binkert.orgvoid
3839396Sandreas.hansson@arm.comIdeDisk::dmaReadDone()
3849396Sandreas.hansson@arm.com{
3859396Sandreas.hansson@arm.com
38612302Sgabeblack@google.com    uint32_t bytesWritten = 0;
38712302Sgabeblack@google.com
38812302Sgabeblack@google.com
3899396Sandreas.hansson@arm.com    // write the data to the disk image
3909396Sandreas.hansson@arm.com    for (bytesWritten = 0; bytesWritten < curPrd.getByteCount();
3919396Sandreas.hansson@arm.com         bytesWritten += SectorSize) {
3929396Sandreas.hansson@arm.com
3939396Sandreas.hansson@arm.com        cmdBytesLeft -= SectorSize;
3949396Sandreas.hansson@arm.com        writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten));
3959396Sandreas.hansson@arm.com    }
3969930Sandreas.hansson@arm.com
3979930Sandreas.hansson@arm.com    // check for the EOT
3989396Sandreas.hansson@arm.com    if (curPrd.getEOT()) {
3996143Snate@binkert.org        assert(cmdBytesLeft == 0);
40012797Sgabeblack@google.com        dmaState = Dma_Idle;
40112797Sgabeblack@google.com        updateState(ACT_DMA_DONE);
40212797Sgabeblack@google.com    } else {
4038235Snate@binkert.org        doDmaTransfer();
40412797Sgabeblack@google.com    }
40512797Sgabeblack@google.com}
40612797Sgabeblack@google.com
40712797Sgabeblack@google.comvoid
40812797Sgabeblack@google.comIdeDisk::doDmaDataWrite()
40912797Sgabeblack@google.com{
41012797Sgabeblack@google.com    /** @todo we need to figure out what the delay actually will be */
41112797Sgabeblack@google.com    Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize);
41212797Sgabeblack@google.com    uint32_t bytesRead = 0;
41312797Sgabeblack@google.com
41412797Sgabeblack@google.com    DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n",
41512797Sgabeblack@google.com            diskDelay, totalDiskDelay);
41612797Sgabeblack@google.com
41712797Sgabeblack@google.com    memset(dataBuffer, 0, MAX_DMA_SIZE);
41812797Sgabeblack@google.com    assert(cmdBytesLeft <= MAX_DMA_SIZE);
41912757Sgabeblack@google.com    while (bytesRead < curPrd.getByteCount()) {
42012757Sgabeblack@google.com        readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead));
42112797Sgabeblack@google.com        bytesRead += SectorSize;
42212797Sgabeblack@google.com        cmdBytesLeft -= SectorSize;
42312797Sgabeblack@google.com    }
42412757Sgabeblack@google.com
42512757Sgabeblack@google.com    dmaWriteWaitEvent.schedule(curTick + totalDiskDelay);
42612757Sgabeblack@google.com}
42712757Sgabeblack@google.com
4288235Snate@binkert.orgvoid
42912302Sgabeblack@google.comIdeDisk::doDmaWrite()
4308235Snate@binkert.org{
4318235Snate@binkert.org
43212757Sgabeblack@google.com    if (!dmaWriteCG) {
4338235Snate@binkert.org        // clear out the data buffer
4348235Snate@binkert.org        dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(),
4358235Snate@binkert.org                curPrd.getByteCount(), TheISA::PageBytes);
43612757Sgabeblack@google.com    }
43712313Sgabeblack@google.com    if (ctrl->dmaPending()) {
43812797Sgabeblack@google.com        panic("shouldn't be reentant??");
43912797Sgabeblack@google.com        dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD);
44012797Sgabeblack@google.com        return;
44112797Sgabeblack@google.com    } else if (!dmaWriteCG->done()) {
44212797Sgabeblack@google.com        assert(dmaWriteCG->complete() < MAX_DMA_SIZE);
44312797Sgabeblack@google.com        ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(),
44412797Sgabeblack@google.com                &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete());
44512797Sgabeblack@google.com        dmaWriteCG->next();
44612797Sgabeblack@google.com    } else {
44712797Sgabeblack@google.com        assert(dmaWriteCG->done());
44812797Sgabeblack@google.com        delete dmaWriteCG;
44912797Sgabeblack@google.com        dmaWriteCG = NULL;
45012797Sgabeblack@google.com        dmaWriteDone();
45112797Sgabeblack@google.com    }
45212797Sgabeblack@google.com}
45312797Sgabeblack@google.com
45412797Sgabeblack@google.comvoid
45512797Sgabeblack@google.comIdeDisk::dmaWriteDone()
45612797Sgabeblack@google.com{
45712797Sgabeblack@google.com    // check for the EOT
45812797Sgabeblack@google.com    if (curPrd.getEOT()) {
45912797Sgabeblack@google.com        assert(cmdBytesLeft == 0);
46012797Sgabeblack@google.com        dmaState = Dma_Idle;
46112797Sgabeblack@google.com        updateState(ACT_DMA_DONE);
46212797Sgabeblack@google.com    } else {
46312797Sgabeblack@google.com        doDmaTransfer();
46412797Sgabeblack@google.com    }
46512797Sgabeblack@google.com}
46612797Sgabeblack@google.com
46712797Sgabeblack@google.com////
46812797Sgabeblack@google.com// Disk utility routines
46912797Sgabeblack@google.com///
47012797Sgabeblack@google.com
47112797Sgabeblack@google.comvoid
47212797Sgabeblack@google.comIdeDisk::readDisk(uint32_t sector, uint8_t *data)
47312797Sgabeblack@google.com{
47412797Sgabeblack@google.com    uint32_t bytesRead = image->read(data, sector);
47513656Sgabeblack@google.com
47612797Sgabeblack@google.com    if (bytesRead != SectorSize)
47712797Sgabeblack@google.com        panic("Can't read from %s. Only %d of %d read. errno=%d\n",
47812797Sgabeblack@google.com              name(), bytesRead, SectorSize, errno);
47912797Sgabeblack@google.com}
48012797Sgabeblack@google.com
48112797Sgabeblack@google.comvoid
48212313Sgabeblack@google.comIdeDisk::writeDisk(uint32_t sector, uint8_t *data)
48312313Sgabeblack@google.com{
48412797Sgabeblack@google.com    uint32_t bytesWritten = image->write(data, sector);
48512797Sgabeblack@google.com
48612797Sgabeblack@google.com    if (bytesWritten != SectorSize)
48712371Sgabeblack@google.com        panic("Can't write to %s. Only %d of %d written. errno=%d\n",
4885584Snate@binkert.org              name(), bytesWritten, SectorSize, errno);
48912797Sgabeblack@google.com}
49012797Sgabeblack@google.com
49112797Sgabeblack@google.com////
49212797Sgabeblack@google.com// Setup and handle commands
49312797Sgabeblack@google.com////
49412797Sgabeblack@google.com
49512797Sgabeblack@google.comvoid
49612797Sgabeblack@google.comIdeDisk::startDma(const uint32_t &prdTableBase)
49712797Sgabeblack@google.com{
49812797Sgabeblack@google.com    if (dmaState != Dma_Start)
49912797Sgabeblack@google.com        panic("Inconsistent DMA state, should be in Dma_Start!\n");
50012797Sgabeblack@google.com
50112797Sgabeblack@google.com    if (devState != Transfer_Data_Dma)
50212797Sgabeblack@google.com        panic("Inconsistent device state for DMA start!\n");
50312797Sgabeblack@google.com
50412797Sgabeblack@google.com    // PRD base address is given by bits 31:2
50512797Sgabeblack@google.com    curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3)));
50612797Sgabeblack@google.com
50712797Sgabeblack@google.com    dmaState = Dma_Transfer;
50812797Sgabeblack@google.com
50912797Sgabeblack@google.com    // schedule dma transfer (doDmaTransfer)
51012797Sgabeblack@google.com    dmaTransferEvent.schedule(curTick + 1);
51112797Sgabeblack@google.com}
51212797Sgabeblack@google.com
51312797Sgabeblack@google.comvoid
51412797Sgabeblack@google.comIdeDisk::abortDma()
51512797Sgabeblack@google.com{
51612797Sgabeblack@google.com    if (dmaState == Dma_Idle)
51712797Sgabeblack@google.com        panic("Inconsistent DMA state, should be Start or Transfer!");
51812797Sgabeblack@google.com
51912797Sgabeblack@google.com    if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma)
52012797Sgabeblack@google.com        panic("Inconsistent device state, should be Transfer or Prepare!\n");
52112797Sgabeblack@google.com
52212797Sgabeblack@google.com    updateState(ACT_CMD_ERROR);
52312797Sgabeblack@google.com}
52412797Sgabeblack@google.com
52512797Sgabeblack@google.comvoid
52612797Sgabeblack@google.comIdeDisk::startCommand()
5274382Sbinkertn@umich.edu{
52813576Sciro.santilli@arm.com    DevAction_t action = ACT_NONE;
52913577Sciro.santilli@arm.com    uint32_t size = 0;
5304202Sbinkertn@umich.edu    dmaRead = false;
5314382Sbinkertn@umich.edu
5324382Sbinkertn@umich.edu    // Decode commands
5339396Sandreas.hansson@arm.com    switch (cmdReg.command) {
53412797Sgabeblack@google.com        // Supported non-data commands
5355584Snate@binkert.org      case WDSF_READ_NATIVE_MAX:
53612313Sgabeblack@google.com        size = image->size() - 1;
5374382Sbinkertn@umich.edu        cmdReg.sec_num = (size & 0xff);
5384382Sbinkertn@umich.edu        cmdReg.cyl_low = ((size & 0xff00) >> 8);
5394382Sbinkertn@umich.edu        cmdReg.cyl_high = ((size & 0xff0000) >> 16);
5408232Snate@binkert.org        cmdReg.head = ((size & 0xf000000) >> 24);
5415192Ssaidi@eecs.umich.edu
5428232Snate@binkert.org        devState = Command_Execution;
5438232Snate@binkert.org        action = ACT_CMD_COMPLETE;
5448232Snate@binkert.org        break;
5455192Ssaidi@eecs.umich.edu
5468232Snate@binkert.org      case WDCC_RECAL:
5475192Ssaidi@eecs.umich.edu      case WDCC_IDP:
5485799Snate@binkert.org      case WDCC_STANDBY_IMMED:
5498232Snate@binkert.org      case WDCC_FLUSHCACHE:
5505192Ssaidi@eecs.umich.edu      case WDSF_VERIFY:
5515192Ssaidi@eecs.umich.edu      case WDSF_SEEK:
5525192Ssaidi@eecs.umich.edu      case SET_FEATURES:
5538232Snate@binkert.org      case WDCC_SETMULTI:
5545192Ssaidi@eecs.umich.edu        devState = Command_Execution;
5558232Snate@binkert.org        action = ACT_CMD_COMPLETE;
5565192Ssaidi@eecs.umich.edu        break;
5575192Ssaidi@eecs.umich.edu
5585192Ssaidi@eecs.umich.edu        // Supported PIO data-in commands
5595192Ssaidi@eecs.umich.edu      case WDCC_IDENTIFY:
5604382Sbinkertn@umich.edu        cmdBytes = cmdBytesLeft = sizeof(struct ataparams);
5614382Sbinkertn@umich.edu        devState = Prepare_Data_In;
5624382Sbinkertn@umich.edu        action = ACT_DATA_READY;
5632667Sstever@eecs.umich.edu        break;
5642667Sstever@eecs.umich.edu
5652667Sstever@eecs.umich.edu      case WDCC_READMULTI:
5662667Sstever@eecs.umich.edu      case WDCC_READ:
5672667Sstever@eecs.umich.edu        if (!(cmdReg.drive & DRIVE_LBA_BIT))
5682667Sstever@eecs.umich.edu            panic("Attempt to perform CHS access, only supports LBA\n");
5695742Snate@binkert.org
5705742Snate@binkert.org        if (cmdReg.sec_count == 0)
5715742Snate@binkert.org            cmdBytes = cmdBytesLeft = (256 * SectorSize);
5725793Snate@binkert.org        else
5738334Snate@binkert.org            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
5745793Snate@binkert.org
5755793Snate@binkert.org        curSector = getLBABase();
5765793Snate@binkert.org
5774382Sbinkertn@umich.edu        /** @todo make this a scheduled event to simulate disk delay */
5784762Snate@binkert.org        devState = Prepare_Data_In;
5795344Sstever@gmail.com        action = ACT_DATA_READY;
5804382Sbinkertn@umich.edu        break;
5815341Sstever@gmail.com
5825742Snate@binkert.org        // Supported PIO data-out commands
5835742Snate@binkert.org      case WDCC_WRITEMULTI:
5845742Snate@binkert.org      case WDCC_WRITE:
5855742Snate@binkert.org        if (!(cmdReg.drive & DRIVE_LBA_BIT))
5865742Snate@binkert.org            panic("Attempt to perform CHS access, only supports LBA\n");
5874762Snate@binkert.org
5885742Snate@binkert.org        if (cmdReg.sec_count == 0)
5895742Snate@binkert.org            cmdBytes = cmdBytesLeft = (256 * SectorSize);
59011984Sgabeblack@google.com        else
5917722Sgblack@eecs.umich.edu            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
5925742Snate@binkert.org
5935742Snate@binkert.org        curSector = getLBABase();
5945742Snate@binkert.org
5959930Sandreas.hansson@arm.com        devState = Prepare_Data_Out;
5969930Sandreas.hansson@arm.com        action = ACT_DATA_READY;
5979930Sandreas.hansson@arm.com        break;
5989930Sandreas.hansson@arm.com
5999930Sandreas.hansson@arm.com        // Supported DMA commands
6005742Snate@binkert.org      case WDCC_WRITEDMA:
6018242Sbradley.danofsky@amd.com        dmaRead = true;  // a write to the disk is a DMA read from memory
6028242Sbradley.danofsky@amd.com      case WDCC_READDMA:
6038242Sbradley.danofsky@amd.com        if (!(cmdReg.drive & DRIVE_LBA_BIT))
6048242Sbradley.danofsky@amd.com            panic("Attempt to perform CHS access, only supports LBA\n");
6055341Sstever@gmail.com
6065742Snate@binkert.org        if (cmdReg.sec_count == 0)
6077722Sgblack@eecs.umich.edu            cmdBytes = cmdBytesLeft = (256 * SectorSize);
6084773Snate@binkert.org        else
6096108Snate@binkert.org            cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize);
6101858SN/A
6111085SN/A        curSector = getLBABase();
6126658Snate@binkert.org
6136658Snate@binkert.org        devState = Prepare_Data_Dma;
6147673Snate@binkert.org        action = ACT_DMA_READY;
6156658Snate@binkert.org        break;
6166658Snate@binkert.org
61711308Santhony.gutierrez@amd.com      default:
6186658Snate@binkert.org        panic("Unsupported ATA command: %#x\n", cmdReg.command);
61911308Santhony.gutierrez@amd.com    }
6206658Snate@binkert.org
6216658Snate@binkert.org    if (action != ACT_NONE) {
6227673Snate@binkert.org        // set the BSY bit
6237673Snate@binkert.org        status |= STATUS_BSY_BIT;
6247673Snate@binkert.org        // clear the DRQ bit
6257673Snate@binkert.org        status &= ~STATUS_DRQ_BIT;
6267673Snate@binkert.org        // clear the DF bit
6277673Snate@binkert.org        status &= ~STATUS_DF_BIT;
6287673Snate@binkert.org
62910467Sandreas.hansson@arm.com        updateState(action);
6306658Snate@binkert.org    }
6317673Snate@binkert.org}
63210467Sandreas.hansson@arm.com
63310467Sandreas.hansson@arm.com////
63410467Sandreas.hansson@arm.com// Handle setting and clearing interrupts
63510467Sandreas.hansson@arm.com////
63610467Sandreas.hansson@arm.com
63710467Sandreas.hansson@arm.comvoid
63810467Sandreas.hansson@arm.comIdeDisk::intrPost()
63910467Sandreas.hansson@arm.com{
64010467Sandreas.hansson@arm.com    DPRINTF(IdeDisk, "Posting Interrupt\n");
64110467Sandreas.hansson@arm.com    if (intrPending)
64210467Sandreas.hansson@arm.com        panic("Attempt to post an interrupt with one pending\n");
6437673Snate@binkert.org
6447673Snate@binkert.org    intrPending = true;
6457673Snate@binkert.org
6467673Snate@binkert.org    // talk to controller to set interrupt
6477673Snate@binkert.org    if (ctrl) {
6489048SAli.Saidi@ARM.com        ctrl->bmi_regs.bmis0 |= IDEINTS;
6497673Snate@binkert.org        ctrl->intrPost();
6507673Snate@binkert.org    }
6517673Snate@binkert.org}
6527673Snate@binkert.org
6536658Snate@binkert.orgvoid
6547756SAli.Saidi@ARM.comIdeDisk::intrClear()
6557816Ssteve.reinhardt@amd.com{
6566658Snate@binkert.org    DPRINTF(IdeDisk, "Clearing Interrupt\n");
65711308Santhony.gutierrez@amd.com    if (!intrPending)
65811308Santhony.gutierrez@amd.com        panic("Attempt to clear a non-pending interrupt\n");
65911308Santhony.gutierrez@amd.com
66011308Santhony.gutierrez@amd.com    intrPending = false;
66111308Santhony.gutierrez@amd.com
66211308Santhony.gutierrez@amd.com    // talk to controller to clear interrupt
66311308Santhony.gutierrez@amd.com    if (ctrl)
66411308Santhony.gutierrez@amd.com        ctrl->intrClear();
66511308Santhony.gutierrez@amd.com}
66611308Santhony.gutierrez@amd.com
66711308Santhony.gutierrez@amd.com////
66811308Santhony.gutierrez@amd.com// Manage the device internal state machine
66911308Santhony.gutierrez@amd.com////
67011308Santhony.gutierrez@amd.com
67111308Santhony.gutierrez@amd.comvoid
67211308Santhony.gutierrez@amd.comIdeDisk::updateState(DevAction_t action)
67311308Santhony.gutierrez@amd.com{
67411308Santhony.gutierrez@amd.com    switch (devState) {
67511308Santhony.gutierrez@amd.com      case Device_Srst:
67611308Santhony.gutierrez@amd.com        if (action == ACT_SRST_SET) {
67711308Santhony.gutierrez@amd.com            // set the BSY bit
67811308Santhony.gutierrez@amd.com            status |= STATUS_BSY_BIT;
67911308Santhony.gutierrez@amd.com        } else if (action == ACT_SRST_CLEAR) {
68011308Santhony.gutierrez@amd.com            // clear the BSY bit
68111308Santhony.gutierrez@amd.com            status &= ~STATUS_BSY_BIT;
68211308Santhony.gutierrez@amd.com
68311308Santhony.gutierrez@amd.com            // reset the device state
68411308Santhony.gutierrez@amd.com            reset(devID);
68511308Santhony.gutierrez@amd.com        }
68611308Santhony.gutierrez@amd.com        break;
68711308Santhony.gutierrez@amd.com
68811308Santhony.gutierrez@amd.com      case Device_Idle_S:
68911308Santhony.gutierrez@amd.com        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
69011308Santhony.gutierrez@amd.com            devState = Device_Idle_NS;
69111308Santhony.gutierrez@amd.com        } else if (action == ACT_CMD_WRITE) {
69211308Santhony.gutierrez@amd.com            startCommand();
69311308Santhony.gutierrez@amd.com        }
69411308Santhony.gutierrez@amd.com
69511308Santhony.gutierrez@amd.com        break;
69611308Santhony.gutierrez@amd.com
69711308Santhony.gutierrez@amd.com      case Device_Idle_SI:
69811308Santhony.gutierrez@amd.com        if (action == ACT_SELECT_WRITE && !isDEVSelect()) {
69911308Santhony.gutierrez@amd.com            devState = Device_Idle_NS;
70011308Santhony.gutierrez@amd.com            intrClear();
70111308Santhony.gutierrez@amd.com        } else if (action == ACT_STAT_READ || isIENSet()) {
7024382Sbinkertn@umich.edu            devState = Device_Idle_S;
7034382Sbinkertn@umich.edu            intrClear();
7044762Snate@binkert.org        } else if (action == ACT_CMD_WRITE) {
7054762Snate@binkert.org            intrClear();
7064762Snate@binkert.org            startCommand();
7076654Snate@binkert.org        }
7086654Snate@binkert.org
7095517Snate@binkert.org        break;
7105517Snate@binkert.org
7115517Snate@binkert.org      case Device_Idle_NS:
7125517Snate@binkert.org        if (action == ACT_SELECT_WRITE && isDEVSelect()) {
7135517Snate@binkert.org            if (!isIENSet() && intrPending) {
7145517Snate@binkert.org                devState = Device_Idle_SI;
7155517Snate@binkert.org                intrPost();
7165517Snate@binkert.org            }
7175517Snate@binkert.org            if (isIENSet() || !intrPending) {
7185517Snate@binkert.org                devState = Device_Idle_S;
7195517Snate@binkert.org            }
7205517Snate@binkert.org        }
7215517Snate@binkert.org        break;
7225517Snate@binkert.org
7235517Snate@binkert.org      case Command_Execution:
7245517Snate@binkert.org        if (action == ACT_CMD_COMPLETE) {
7255517Snate@binkert.org            // clear the BSY bit
7266654Snate@binkert.org            setComplete();
7275517Snate@binkert.org
7285517Snate@binkert.org            if (!isIENSet()) {
7295517Snate@binkert.org                devState = Device_Idle_SI;
7305517Snate@binkert.org                intrPost();
7315517Snate@binkert.org            } else {
73211802Sandreas.sandberg@arm.com                devState = Device_Idle_S;
7335517Snate@binkert.org            }
7345517Snate@binkert.org        }
7356143Snate@binkert.org        break;
7366654Snate@binkert.org
7375517Snate@binkert.org      case Prepare_Data_In:
7385517Snate@binkert.org        if (action == ACT_CMD_ERROR) {
7395517Snate@binkert.org            // clear the BSY bit
7405517Snate@binkert.org            setComplete();
7415517Snate@binkert.org
7425517Snate@binkert.org            if (!isIENSet()) {
7435517Snate@binkert.org                devState = Device_Idle_SI;
7445517Snate@binkert.org                intrPost();
7455517Snate@binkert.org            } else {
7465517Snate@binkert.org                devState = Device_Idle_S;
7475517Snate@binkert.org            }
7485517Snate@binkert.org        } else if (action == ACT_DATA_READY) {
7495517Snate@binkert.org            // clear the BSY bit
7505517Snate@binkert.org            status &= ~STATUS_BSY_BIT;
7516654Snate@binkert.org            // set the DRQ bit
7526654Snate@binkert.org            status |= STATUS_DRQ_BIT;
7535517Snate@binkert.org
7545517Snate@binkert.org            // copy the data into the data buffer
7556143Snate@binkert.org            if (cmdReg.command == WDCC_IDENTIFY) {
7566143Snate@binkert.org                // Reset the drqBytes for this block
7576143Snate@binkert.org                drqBytesLeft = sizeof(struct ataparams);
7586727Ssteve.reinhardt@amd.com
7595517Snate@binkert.org                memcpy((void *)dataBuffer, (void *)&driveID,
7606727Ssteve.reinhardt@amd.com                       sizeof(struct ataparams));
7615517Snate@binkert.org            } else {
7625517Snate@binkert.org                // Reset the drqBytes for this block
7635517Snate@binkert.org                drqBytesLeft = SectorSize;
7646654Snate@binkert.org
7656654Snate@binkert.org                readDisk(curSector++, dataBuffer);
7667673Snate@binkert.org            }
7676654Snate@binkert.org
7686654Snate@binkert.org            // put the first two bytes into the data register
7696654Snate@binkert.org            memcpy((void *)&cmdReg.data, (void *)dataBuffer,
7706654Snate@binkert.org                   sizeof(uint16_t));
7715517Snate@binkert.org
7725517Snate@binkert.org            if (!isIENSet()) {
7735517Snate@binkert.org                devState = Data_Ready_INTRQ_In;
7746143Snate@binkert.org                intrPost();
7755517Snate@binkert.org            } else {
7764762Snate@binkert.org                devState = Transfer_Data_In;
7775517Snate@binkert.org            }
7785517Snate@binkert.org        }
7796143Snate@binkert.org        break;
7806143Snate@binkert.org
7815517Snate@binkert.org      case Data_Ready_INTRQ_In:
7825517Snate@binkert.org        if (action == ACT_STAT_READ) {
7835517Snate@binkert.org            devState = Transfer_Data_In;
7845517Snate@binkert.org            intrClear();
7855517Snate@binkert.org        }
7865517Snate@binkert.org        break;
7875517Snate@binkert.org
7885517Snate@binkert.org      case Transfer_Data_In:
7895517Snate@binkert.org        if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) {
7906143Snate@binkert.org            if (action == ACT_DATA_READ_BYTE) {
7915517Snate@binkert.org                panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n");
7926654Snate@binkert.org            } else {
7936654Snate@binkert.org                drqBytesLeft -= 2;
7946654Snate@binkert.org                cmdBytesLeft -= 2;
7956654Snate@binkert.org
7966654Snate@binkert.org                // copy next short into data registers
7976654Snate@binkert.org                if (drqBytesLeft)
7984762Snate@binkert.org                    memcpy((void *)&cmdReg.data,
7994762Snate@binkert.org                           (void *)&dataBuffer[SectorSize - drqBytesLeft],
8004762Snate@binkert.org                           sizeof(uint16_t));
8014762Snate@binkert.org            }
8024762Snate@binkert.org
8037675Snate@binkert.org            if (drqBytesLeft == 0) {
80410584Sandreas.hansson@arm.com                if (cmdBytesLeft == 0) {
8054762Snate@binkert.org                    // Clear the BSY bit
8064762Snate@binkert.org                    setComplete();
8074762Snate@binkert.org                    devState = Device_Idle_S;
8084762Snate@binkert.org                } else {
8094382Sbinkertn@umich.edu                    devState = Prepare_Data_In;
8104382Sbinkertn@umich.edu                    // set the BSY_BIT
8115517Snate@binkert.org                    status |= STATUS_BSY_BIT;
8126654Snate@binkert.org                    // clear the DRQ_BIT
8135517Snate@binkert.org                    status &= ~STATUS_DRQ_BIT;
8148126Sgblack@eecs.umich.edu
8156654Snate@binkert.org                    /** @todo change this to a scheduled event to simulate
8167673Snate@binkert.org                        disk delay */
8176654Snate@binkert.org                    updateState(ACT_DATA_READY);
81811802Sandreas.sandberg@arm.com                }
8196654Snate@binkert.org            }
8206654Snate@binkert.org        }
8216654Snate@binkert.org        break;
8226654Snate@binkert.org
82311802Sandreas.sandberg@arm.com      case Prepare_Data_Out:
8246669Snate@binkert.org        if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) {
82511802Sandreas.sandberg@arm.com            // clear the BSY bit
8266669Snate@binkert.org            setComplete();
8276669Snate@binkert.org
8286669Snate@binkert.org            if (!isIENSet()) {
8296669Snate@binkert.org                devState = Device_Idle_SI;
8306654Snate@binkert.org                intrPost();
8317673Snate@binkert.org            } else {
8325517Snate@binkert.org                devState = Device_Idle_S;
8338126Sgblack@eecs.umich.edu            }
8345798Snate@binkert.org        } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) {
8357756SAli.Saidi@ARM.com            // clear the BSY bit
8367816Ssteve.reinhardt@amd.com            status &= ~STATUS_BSY_BIT;
8375798Snate@binkert.org            // set the DRQ bit
8385798Snate@binkert.org            status |= STATUS_DRQ_BIT;
8395517Snate@binkert.org
8405517Snate@binkert.org            // clear the data buffer to get it ready for writes
8417673Snate@binkert.org            memset(dataBuffer, 0, MAX_DMA_SIZE);
8425517Snate@binkert.org
8435517Snate@binkert.org            // reset the drqBytes for this block
8447673Snate@binkert.org            drqBytesLeft = SectorSize;
8457673Snate@binkert.org
8465517Snate@binkert.org            if (cmdBytesLeft == cmdBytes || isIENSet()) {
8475798Snate@binkert.org                devState = Transfer_Data_Out;
8485798Snate@binkert.org            } else {
8498333Snate@binkert.org                devState = Data_Ready_INTRQ_Out;
8507816Ssteve.reinhardt@amd.com                intrPost();
8515798Snate@binkert.org            }
8525798Snate@binkert.org        }
8534762Snate@binkert.org        break;
8544762Snate@binkert.org
8554762Snate@binkert.org      case Data_Ready_INTRQ_Out:
8564762Snate@binkert.org        if (action == ACT_STAT_READ) {
8574762Snate@binkert.org            devState = Transfer_Data_Out;
8588596Ssteve.reinhardt@amd.com            intrClear();
8595517Snate@binkert.org        }
8605517Snate@binkert.org        break;
86111997Sgabeblack@google.com
8625517Snate@binkert.org      case Transfer_Data_Out:
8635517Snate@binkert.org        if (action == ACT_DATA_WRITE_BYTE ||
8647673Snate@binkert.org            action == ACT_DATA_WRITE_SHORT) {
8658596Ssteve.reinhardt@amd.com
8667673Snate@binkert.org            if (action == ACT_DATA_READ_BYTE) {
8675517Snate@binkert.org                panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n");
86810458Sandreas.hansson@arm.com            } else {
86910458Sandreas.hansson@arm.com                // copy the latest short into the data buffer
87010458Sandreas.hansson@arm.com                memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft],
87110458Sandreas.hansson@arm.com                       (void *)&cmdReg.data,
87210458Sandreas.hansson@arm.com                       sizeof(uint16_t));
87310458Sandreas.hansson@arm.com
87410458Sandreas.hansson@arm.com                drqBytesLeft -= 2;
87510458Sandreas.hansson@arm.com                cmdBytesLeft -= 2;
87610458Sandreas.hansson@arm.com            }
87710458Sandreas.hansson@arm.com
87810458Sandreas.hansson@arm.com            if (drqBytesLeft == 0) {
87910458Sandreas.hansson@arm.com                // copy the block to the disk
8805517Snate@binkert.org                writeDisk(curSector++, dataBuffer);
88111996Sgabeblack@google.com
8825517Snate@binkert.org                // set the BSY bit
88311997Sgabeblack@google.com                status |= STATUS_BSY_BIT;
88411996Sgabeblack@google.com                // set the seek bit
8855517Snate@binkert.org                status |= STATUS_SEEK_BIT;
8865517Snate@binkert.org                // clear the DRQ bit
8877673Snate@binkert.org                status &= ~STATUS_DRQ_BIT;
8887673Snate@binkert.org
88911996Sgabeblack@google.com                devState = Prepare_Data_Out;
89011988Sandreas.sandberg@arm.com
8917673Snate@binkert.org                /** @todo change this to a scheduled event to simulate
8925517Snate@binkert.org                    disk delay */
8938596Ssteve.reinhardt@amd.com                updateState(ACT_DATA_READY);
8945517Snate@binkert.org            }
8955517Snate@binkert.org        }
89611997Sgabeblack@google.com        break;
8975517Snate@binkert.org
8985517Snate@binkert.org      case Prepare_Data_Dma:
8997673Snate@binkert.org        if (action == ACT_CMD_ERROR) {
9007673Snate@binkert.org            // clear the BSY bit
9017673Snate@binkert.org            setComplete();
9025517Snate@binkert.org
90311988Sandreas.sandberg@arm.com            if (!isIENSet()) {
90411997Sgabeblack@google.com                devState = Device_Idle_SI;
9058596Ssteve.reinhardt@amd.com                intrPost();
9068596Ssteve.reinhardt@amd.com            } else {
9078596Ssteve.reinhardt@amd.com                devState = Device_Idle_S;
90811988Sandreas.sandberg@arm.com            }
9098596Ssteve.reinhardt@amd.com        } else if (action == ACT_DMA_READY) {
9108596Ssteve.reinhardt@amd.com            // clear the BSY bit
9118596Ssteve.reinhardt@amd.com            status &= ~STATUS_BSY_BIT;
9124762Snate@binkert.org            // set the DRQ bit
9136143Snate@binkert.org            status |= STATUS_DRQ_BIT;
9146143Snate@binkert.org
9156143Snate@binkert.org            devState = Transfer_Data_Dma;
9164762Snate@binkert.org
9174762Snate@binkert.org            if (dmaState != Dma_Idle)
9184762Snate@binkert.org                panic("Inconsistent DMA state, should be Dma_Idle\n");
9197756SAli.Saidi@ARM.com
9208596Ssteve.reinhardt@amd.com            dmaState = Dma_Start;
9214762Snate@binkert.org            // wait for the write to the DMA start bit
9224762Snate@binkert.org        }
92310458Sandreas.hansson@arm.com        break;
92410458Sandreas.hansson@arm.com
92510458Sandreas.hansson@arm.com      case Transfer_Data_Dma:
92610458Sandreas.hansson@arm.com        if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) {
92710458Sandreas.hansson@arm.com            // clear the BSY bit
92810458Sandreas.hansson@arm.com            setComplete();
92910458Sandreas.hansson@arm.com            // set the seek bit
93010458Sandreas.hansson@arm.com            status |= STATUS_SEEK_BIT;
93110458Sandreas.hansson@arm.com            // clear the controller state for DMA transfer
93210458Sandreas.hansson@arm.com            ctrl->setDmaComplete(this);
93310458Sandreas.hansson@arm.com
93410458Sandreas.hansson@arm.com            if (!isIENSet()) {
93510458Sandreas.hansson@arm.com                devState = Device_Idle_SI;
93610458Sandreas.hansson@arm.com                intrPost();
93710458Sandreas.hansson@arm.com            } else {
93810458Sandreas.hansson@arm.com                devState = Device_Idle_S;
93910458Sandreas.hansson@arm.com            }
94010458Sandreas.hansson@arm.com        }
94110458Sandreas.hansson@arm.com        break;
94210458Sandreas.hansson@arm.com
94310458Sandreas.hansson@arm.com      default:
94410458Sandreas.hansson@arm.com        panic("Unknown IDE device state: %#x\n", devState);
94510458Sandreas.hansson@arm.com    }
94610458Sandreas.hansson@arm.com}
94710458Sandreas.hansson@arm.com
94810458Sandreas.hansson@arm.comvoid
94910458Sandreas.hansson@arm.comIdeDisk::serialize(ostream &os)
95010458Sandreas.hansson@arm.com{
95110458Sandreas.hansson@arm.com    // Check all outstanding events to see if they are scheduled
95210458Sandreas.hansson@arm.com    // these are all mutually exclusive
95310458Sandreas.hansson@arm.com    Tick reschedule = 0;
95410458Sandreas.hansson@arm.com    Events_t event = None;
95510458Sandreas.hansson@arm.com
95610458Sandreas.hansson@arm.com    int eventCount = 0;
95710458Sandreas.hansson@arm.com
95810458Sandreas.hansson@arm.com    if (dmaTransferEvent.scheduled()) {
95910458Sandreas.hansson@arm.com        reschedule = dmaTransferEvent.when();
96010458Sandreas.hansson@arm.com        event = Transfer;
96110458Sandreas.hansson@arm.com        eventCount++;
96210458Sandreas.hansson@arm.com    }
96310458Sandreas.hansson@arm.com    if (dmaReadWaitEvent.scheduled()) {
96410458Sandreas.hansson@arm.com        reschedule = dmaReadWaitEvent.when();
96510458Sandreas.hansson@arm.com        event = ReadWait;
96610458Sandreas.hansson@arm.com        eventCount++;
96710458Sandreas.hansson@arm.com    }
96810458Sandreas.hansson@arm.com    if (dmaWriteWaitEvent.scheduled()) {
96910458Sandreas.hansson@arm.com        reschedule = dmaWriteWaitEvent.when();
97010458Sandreas.hansson@arm.com        event = WriteWait;
97110458Sandreas.hansson@arm.com        eventCount++;
97210584Sandreas.hansson@arm.com    }
97310458Sandreas.hansson@arm.com    if (dmaPrdReadEvent.scheduled()) {
97410458Sandreas.hansson@arm.com        reschedule = dmaPrdReadEvent.when();
97510458Sandreas.hansson@arm.com        event = PrdRead;
97610458Sandreas.hansson@arm.com        eventCount++;
97710458Sandreas.hansson@arm.com    }
9784762Snate@binkert.org    if (dmaReadEvent.scheduled()) {
9796143Snate@binkert.org        reschedule = dmaReadEvent.when();
9806143Snate@binkert.org        event = DmaRead;
9816143Snate@binkert.org        eventCount++;
9824762Snate@binkert.org    }
9834762Snate@binkert.org    if (dmaWriteEvent.scheduled()) {
98411996Sgabeblack@google.com        reschedule = dmaWriteEvent.when();
9857816Ssteve.reinhardt@amd.com        event = DmaWrite;
9864762Snate@binkert.org        eventCount++;
9874762Snate@binkert.org    }
9884762Snate@binkert.org
9894762Snate@binkert.org    assert(eventCount <= 1);
9907756SAli.Saidi@ARM.com
9918596Ssteve.reinhardt@amd.com    SERIALIZE_SCALAR(reschedule);
9924762Snate@binkert.org    SERIALIZE_ENUM(event);
9934762Snate@binkert.org
99411988Sandreas.sandberg@arm.com    // Serialize device registers
99511988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.data);
99611988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.sec_count);
99711988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.sec_num);
99811988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.cyl_low);
99911988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.cyl_high);
100011988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.drive);
100111988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(cmdReg.command);
100211988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(status);
100311988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(nIENBit);
100411988Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(devID);
10054382Sbinkertn@umich.edu
10069396Sandreas.hansson@arm.com    // Serialize the PRD related information
10079396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(curPrd.entry.baseAddr);
10089396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(curPrd.entry.byteCount);
10099396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(curPrd.entry.endOfTable);
10109396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(curPrdAddr);
10119396Sandreas.hansson@arm.com
10129396Sandreas.hansson@arm.com    /** @todo need to serialized chunk generator stuff!! */
10139396Sandreas.hansson@arm.com    // Serialize current transfer related information
10149396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(cmdBytesLeft);
10159396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(cmdBytes);
10169396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(drqBytesLeft);
10179396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(curSector);
10189396Sandreas.hansson@arm.com    SERIALIZE_SCALAR(dmaRead);
101912302Sgabeblack@google.com    SERIALIZE_SCALAR(intrPending);
10209396Sandreas.hansson@arm.com    SERIALIZE_ENUM(devState);
102112563Sgabeblack@google.com    SERIALIZE_ENUM(dmaState);
10229396Sandreas.hansson@arm.com    SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
10239396Sandreas.hansson@arm.com}
10248232Snate@binkert.org
10258232Snate@binkert.orgvoid
10268232Snate@binkert.orgIdeDisk::unserialize(Checkpoint *cp, const string &section)
10278232Snate@binkert.org{
10288232Snate@binkert.org    // Reschedule events that were outstanding
10296229Snate@binkert.org    // these are all mutually exclusive
103010455SCurtis.Dunham@arm.com    Tick reschedule = 0;
10316229Snate@binkert.org    Events_t event = None;
103210455SCurtis.Dunham@arm.com
103310455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(reschedule);
103410455SCurtis.Dunham@arm.com    UNSERIALIZE_ENUM(event);
10355517Snate@binkert.org
10365517Snate@binkert.org    switch (event) {
10377673Snate@binkert.org      case None : break;
10385517Snate@binkert.org      case Transfer : dmaTransferEvent.schedule(reschedule); break;
103910455SCurtis.Dunham@arm.com      case ReadWait : dmaReadWaitEvent.schedule(reschedule); break;
10405517Snate@binkert.org      case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break;
10415517Snate@binkert.org      case PrdRead : dmaPrdReadEvent.schedule(reschedule); break;
10428232Snate@binkert.org      case DmaRead : dmaReadEvent.schedule(reschedule); break;
104310455SCurtis.Dunham@arm.com      case DmaWrite : dmaWriteEvent.schedule(reschedule); break;
104410455SCurtis.Dunham@arm.com    }
104510455SCurtis.Dunham@arm.com
10467673Snate@binkert.org    // Unserialize device registers
10477673Snate@binkert.org    UNSERIALIZE_SCALAR(cmdReg.data);
104810455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(cmdReg.sec_count);
104910455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(cmdReg.sec_num);
105010455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(cmdReg.cyl_low);
10515517Snate@binkert.org    UNSERIALIZE_SCALAR(cmdReg.cyl_high);
105210455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(cmdReg.drive);
105310455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(cmdReg.command);
105410455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(status);
105510455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(nIENBit);
105610455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(devID);
105710455SCurtis.Dunham@arm.com
105810455SCurtis.Dunham@arm.com    // Unserialize the PRD related information
105910455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(curPrd.entry.baseAddr);
106010685Sandreas.hansson@arm.com    UNSERIALIZE_SCALAR(curPrd.entry.byteCount);
106110455SCurtis.Dunham@arm.com    UNSERIALIZE_SCALAR(curPrd.entry.endOfTable);
106210685Sandreas.hansson@arm.com    UNSERIALIZE_SCALAR(curPrdAddr);
106310455SCurtis.Dunham@arm.com
10645517Snate@binkert.org    /** @todo need to serialized chunk generator stuff!! */
106510455SCurtis.Dunham@arm.com    // Unserialize current transfer related information
10668232Snate@binkert.org    UNSERIALIZE_SCALAR(cmdBytes);
10678232Snate@binkert.org    UNSERIALIZE_SCALAR(cmdBytesLeft);
10685517Snate@binkert.org    UNSERIALIZE_SCALAR(drqBytesLeft);
10697673Snate@binkert.org    UNSERIALIZE_SCALAR(curSector);
10705517Snate@binkert.org    UNSERIALIZE_SCALAR(dmaRead);
10718232Snate@binkert.org    UNSERIALIZE_SCALAR(intrPending);
10728232Snate@binkert.org    UNSERIALIZE_ENUM(devState);
10735517Snate@binkert.org    UNSERIALIZE_ENUM(dmaState);
10748232Snate@binkert.org    UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE);
10758232Snate@binkert.org}
10768232Snate@binkert.org
10777673Snate@binkert.org#ifndef DOXYGEN_SHOULD_SKIP_THIS
10785517Snate@binkert.org
10795517Snate@binkert.orgenum DriveID { master, slave };
10807673Snate@binkert.orgstatic const char *DriveID_strings[] = { "master", "slave" };
10815517Snate@binkert.orgBEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
108210455SCurtis.Dunham@arm.com
10835517Snate@binkert.org    SimObjectParam<DiskImage *> image;
10845517Snate@binkert.org    SimpleEnumParam<DriveID> driveID;
10858232Snate@binkert.org    Param<int> delay;
10868232Snate@binkert.org
10875517Snate@binkert.orgEND_DECLARE_SIM_OBJECT_PARAMS(IdeDisk)
10888232Snate@binkert.org
10898232Snate@binkert.orgBEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk)
10905517Snate@binkert.org
10918232Snate@binkert.org    INIT_PARAM(image, "Disk image"),
10928232Snate@binkert.org    INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings),
10938232Snate@binkert.org    INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1)
10945517Snate@binkert.org
10958232Snate@binkert.orgEND_INIT_SIM_OBJECT_PARAMS(IdeDisk)
10968232Snate@binkert.org
10978232Snate@binkert.org
10988232Snate@binkert.orgCREATE_SIM_OBJECT(IdeDisk)
10998232Snate@binkert.org{
11008232Snate@binkert.org    return new IdeDisk(getInstanceName(), image, driveID, delay);
11015517Snate@binkert.org}
11028232Snate@binkert.org
11038232Snate@binkert.orgREGISTER_SIM_OBJECT("IdeDisk", IdeDisk)
11045517Snate@binkert.org
11058232Snate@binkert.org#endif //DOXYGEN_SHOULD_SKIP_THIS
11067673Snate@binkert.org