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 §ion) 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