ide_disk.cc revision 8522
110244Satgutier@umich.edu/* 210244Satgutier@umich.edu * Copyright (c) 2004-2005 The Regents of The University of Michigan 310244Satgutier@umich.edu * All rights reserved. 410244Satgutier@umich.edu * 510244Satgutier@umich.edu * Redistribution and use in source and binary forms, with or without 610244Satgutier@umich.edu * modification, are permitted provided that the following conditions are 710244Satgutier@umich.edu * met: redistributions of source code must retain the above copyright 810244Satgutier@umich.edu * notice, this list of conditions and the following disclaimer; 910244Satgutier@umich.edu * redistributions in binary form must reproduce the above copyright 1010244Satgutier@umich.edu * notice, this list of conditions and the following disclaimer in the 1110244Satgutier@umich.edu * documentation and/or other materials provided with the distribution; 1210244Satgutier@umich.edu * neither the name of the copyright holders nor the names of its 1310244Satgutier@umich.edu * contributors may be used to endorse or promote products derived from 1410244Satgutier@umich.edu * this software without specific prior written permission. 1510244Satgutier@umich.edu * 1610244Satgutier@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1710244Satgutier@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1810244Satgutier@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1910244Satgutier@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2010244Satgutier@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2110244Satgutier@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2210244Satgutier@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2310244Satgutier@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2410244Satgutier@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2510244Satgutier@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2610244Satgutier@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2710244Satgutier@umich.edu * 2810244Satgutier@umich.edu * Authors: Andrew Schultz 2910244Satgutier@umich.edu * Ali Saidi 3010244Satgutier@umich.edu */ 3110244Satgutier@umich.edu 3210244Satgutier@umich.edu/** @file 3310244Satgutier@umich.edu * Device model implementation for an IDE disk 3410244Satgutier@umich.edu */ 3510244Satgutier@umich.edu 3610244Satgutier@umich.edu#include <cerrno> 3710244Satgutier@umich.edu#include <cstring> 3813960Sodanrc@yahoo.com.br#include <deque> 3910244Satgutier@umich.edu#include <string> 4010785Sgope@wisc.edu 4110244Satgutier@umich.edu#include "arch/isa_traits.hh" 4210244Satgutier@umich.edu#include "base/chunk_generator.hh" 4310244Satgutier@umich.edu#include "base/cprintf.hh" // csprintf 4410244Satgutier@umich.edu#include "base/trace.hh" 4510244Satgutier@umich.edu#include "config/the_isa.hh" 4610244Satgutier@umich.edu#include "debug/IdeDisk.hh" 4710244Satgutier@umich.edu#include "dev/disk_image.hh" 4810244Satgutier@umich.edu#include "dev/ide_ctrl.hh" 4910244Satgutier@umich.edu#include "dev/ide_disk.hh" 5010244Satgutier@umich.edu#include "sim/core.hh" 5110244Satgutier@umich.edu#include "sim/sim_object.hh" 5210244Satgutier@umich.edu 5310244Satgutier@umich.eduusing namespace std; 5410244Satgutier@umich.eduusing namespace TheISA; 5510244Satgutier@umich.edu 5610244Satgutier@umich.eduIdeDisk::IdeDisk(const Params *p) 5710244Satgutier@umich.edu : SimObject(p), ctrl(NULL), image(p->image), diskDelay(p->delay), 5810244Satgutier@umich.edu dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this), 5910785Sgope@wisc.edu dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this), 6011434Smitch.hayenga@arm.com dmaReadEvent(this), dmaWriteEvent(this) 6111434Smitch.hayenga@arm.com{ 6211434Smitch.hayenga@arm.com // Reset the device state 6311434Smitch.hayenga@arm.com reset(p->driveID); 6411434Smitch.hayenga@arm.com 6513626Sjairo.balart@metempsy.com // fill out the drive ID structure 6610244Satgutier@umich.edu memset(&driveID, 0, sizeof(struct ataparams)); 6710244Satgutier@umich.edu 6811434Smitch.hayenga@arm.com // Calculate LBA and C/H/S values 6910244Satgutier@umich.edu uint16_t cylinders; 7010244Satgutier@umich.edu uint8_t heads; 7110244Satgutier@umich.edu uint8_t sectors; 7210244Satgutier@umich.edu 7310244Satgutier@umich.edu uint32_t lba_size = image->size(); 7410244Satgutier@umich.edu if (lba_size >= 16383*16*63) { 7510244Satgutier@umich.edu cylinders = 16383; 7610244Satgutier@umich.edu heads = 16; 7710244Satgutier@umich.edu sectors = 63; 7810244Satgutier@umich.edu } else { 7910244Satgutier@umich.edu if (lba_size >= 63) 8010244Satgutier@umich.edu sectors = 63; 8110244Satgutier@umich.edu else 8210244Satgutier@umich.edu sectors = lba_size; 8310244Satgutier@umich.edu 8410244Satgutier@umich.edu if ((lba_size / sectors) >= 16) 8510244Satgutier@umich.edu heads = 16; 8610244Satgutier@umich.edu else 8710244Satgutier@umich.edu heads = (lba_size / sectors); 8810244Satgutier@umich.edu 8910244Satgutier@umich.edu cylinders = lba_size / (heads * sectors); 9011434Smitch.hayenga@arm.com } 9110244Satgutier@umich.edu 9210244Satgutier@umich.edu // Setup the model name 9310244Satgutier@umich.edu strncpy((char *)driveID.atap_model, "5MI EDD si k", 9410244Satgutier@umich.edu sizeof(driveID.atap_model)); 9510244Satgutier@umich.edu // Set the maximum multisector transfer size 9610244Satgutier@umich.edu driveID.atap_multi = MAX_MULTSECT; 9710244Satgutier@umich.edu // IORDY supported, IORDY disabled, LBA enabled, DMA enabled 9810244Satgutier@umich.edu driveID.atap_capabilities1 = 0x7; 9910244Satgutier@umich.edu // UDMA support, EIDE support 10010244Satgutier@umich.edu driveID.atap_extensions = 0x6; 10113959Sodanrc@yahoo.com.br // Setup default C/H/S settings 10213959Sodanrc@yahoo.com.br driveID.atap_cylinders = cylinders; 10313959Sodanrc@yahoo.com.br driveID.atap_sectors = sectors; 10413959Sodanrc@yahoo.com.br driveID.atap_heads = heads; 10513959Sodanrc@yahoo.com.br // Setup the current multisector transfer size 10613959Sodanrc@yahoo.com.br driveID.atap_curmulti = MAX_MULTSECT; 10713959Sodanrc@yahoo.com.br driveID.atap_curmulti_valid = 0x1; 10810244Satgutier@umich.edu // Number of sectors on disk 10910244Satgutier@umich.edu driveID.atap_capacity = lba_size; 11010244Satgutier@umich.edu // Multiword DMA mode 2 and below supported 11110244Satgutier@umich.edu driveID.atap_dmamode_supp = 0x4; 11210244Satgutier@umich.edu // Set PIO mode 4 and 3 supported 11310244Satgutier@umich.edu driveID.atap_piomode_supp = 0x3; 114 // Set DMA mode 4 and below supported 115 driveID.atap_udmamode_supp = 0x1f; 116 // Statically set hardware config word 117 driveID.atap_hwreset_res = 0x4001; 118 119 //arbitrary for now... 120 driveID.atap_ata_major = WDC_VER_ATA7; 121} 122 123IdeDisk::~IdeDisk() 124{ 125 // destroy the data buffer 126 delete [] dataBuffer; 127} 128 129void 130IdeDisk::reset(int id) 131{ 132 // initialize the data buffer and shadow registers 133 dataBuffer = new uint8_t[MAX_DMA_SIZE]; 134 135 memset(dataBuffer, 0, MAX_DMA_SIZE); 136 memset(&cmdReg, 0, sizeof(CommandReg_t)); 137 memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); 138 139 curPrdAddr = 0; 140 curSector = 0; 141 cmdBytes = 0; 142 cmdBytesLeft = 0; 143 drqBytesLeft = 0; 144 dmaRead = false; 145 intrPending = false; 146 147 // set the device state to idle 148 dmaState = Dma_Idle; 149 150 if (id == DEV0) { 151 devState = Device_Idle_S; 152 devID = DEV0; 153 } else if (id == DEV1) { 154 devState = Device_Idle_NS; 155 devID = DEV1; 156 } else { 157 panic("Invalid device ID: %#x\n", id); 158 } 159 160 // set the device ready bit 161 status = STATUS_DRDY_BIT; 162 163 /* The error register must be set to 0x1 on start-up to 164 indicate that no diagnostic error was detected */ 165 cmdReg.error = 0x1; 166} 167 168//// 169// Utility functions 170//// 171 172bool 173IdeDisk::isDEVSelect() 174{ 175 return ctrl->isDiskSelected(this); 176} 177 178Addr 179IdeDisk::pciToDma(Addr pciAddr) 180{ 181 if (ctrl) 182 return ctrl->pciToDma(pciAddr); 183 else 184 panic("Access to unset controller!\n"); 185} 186 187//// 188// Device registers read/write 189//// 190 191void 192IdeDisk::readCommand(const Addr offset, int size, uint8_t *data) 193{ 194 if (offset == DATA_OFFSET) { 195 if (size == sizeof(uint16_t)) { 196 *(uint16_t *)data = cmdReg.data; 197 } else if (size == sizeof(uint32_t)) { 198 *(uint16_t *)data = cmdReg.data; 199 updateState(ACT_DATA_READ_SHORT); 200 *((uint16_t *)data + 1) = cmdReg.data; 201 } else { 202 panic("Data read of unsupported size %d.\n", size); 203 } 204 updateState(ACT_DATA_READ_SHORT); 205 return; 206 } 207 assert(size == sizeof(uint8_t)); 208 switch (offset) { 209 case ERROR_OFFSET: 210 *data = cmdReg.error; 211 break; 212 case NSECTOR_OFFSET: 213 *data = cmdReg.sec_count; 214 break; 215 case SECTOR_OFFSET: 216 *data = cmdReg.sec_num; 217 break; 218 case LCYL_OFFSET: 219 *data = cmdReg.cyl_low; 220 break; 221 case HCYL_OFFSET: 222 *data = cmdReg.cyl_high; 223 break; 224 case DRIVE_OFFSET: 225 *data = cmdReg.drive; 226 break; 227 case STATUS_OFFSET: 228 *data = status; 229 updateState(ACT_STAT_READ); 230 break; 231 default: 232 panic("Invalid IDE command register offset: %#x\n", offset); 233 } 234 DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data); 235} 236 237void 238IdeDisk::readControl(const Addr offset, int size, uint8_t *data) 239{ 240 assert(size == sizeof(uint8_t)); 241 *data = status; 242 if (offset != ALTSTAT_OFFSET) 243 panic("Invalid IDE control register offset: %#x\n", offset); 244 DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, *data); 245} 246 247void 248IdeDisk::writeCommand(const Addr offset, int size, const uint8_t *data) 249{ 250 if (offset == DATA_OFFSET) { 251 if (size == sizeof(uint16_t)) { 252 cmdReg.data = *(const uint16_t *)data; 253 } else if (size == sizeof(uint32_t)) { 254 cmdReg.data = *(const uint16_t *)data; 255 updateState(ACT_DATA_WRITE_SHORT); 256 cmdReg.data = *((const uint16_t *)data + 1); 257 } else { 258 panic("Data write of unsupported size %d.\n", size); 259 } 260 updateState(ACT_DATA_WRITE_SHORT); 261 return; 262 } 263 264 assert(size == sizeof(uint8_t)); 265 switch (offset) { 266 case FEATURES_OFFSET: 267 break; 268 case NSECTOR_OFFSET: 269 cmdReg.sec_count = *data; 270 break; 271 case SECTOR_OFFSET: 272 cmdReg.sec_num = *data; 273 break; 274 case LCYL_OFFSET: 275 cmdReg.cyl_low = *data; 276 break; 277 case HCYL_OFFSET: 278 cmdReg.cyl_high = *data; 279 break; 280 case DRIVE_OFFSET: 281 cmdReg.drive = *data; 282 updateState(ACT_SELECT_WRITE); 283 break; 284 case COMMAND_OFFSET: 285 cmdReg.command = *data; 286 updateState(ACT_CMD_WRITE); 287 break; 288 default: 289 panic("Invalid IDE command register offset: %#x\n", offset); 290 } 291 DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset, 292 (uint32_t)*data); 293} 294 295void 296IdeDisk::writeControl(const Addr offset, int size, const uint8_t *data) 297{ 298 if (offset != CONTROL_OFFSET) 299 panic("Invalid IDE control register offset: %#x\n", offset); 300 301 if (*data & CONTROL_RST_BIT) { 302 // force the device into the reset state 303 devState = Device_Srst; 304 updateState(ACT_SRST_SET); 305 } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) { 306 updateState(ACT_SRST_CLEAR); 307 } 308 309 nIENBit = *data & CONTROL_IEN_BIT; 310 311 DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset, 312 (uint32_t)*data); 313} 314 315//// 316// Perform DMA transactions 317//// 318 319void 320IdeDisk::doDmaTransfer() 321{ 322 if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) 323 panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n", 324 dmaState, devState); 325 326 if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { 327 schedule(dmaTransferEvent, curTick() + DMA_BACKOFF_PERIOD); 328 return; 329 } else 330 ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent, 331 (uint8_t*)&curPrd.entry); 332} 333 334void 335IdeDisk::dmaPrdReadDone() 336{ 337 338 DPRINTF(IdeDisk, 339 "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n", 340 curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()), 341 curPrd.getByteCount(), (cmdBytesLeft/SectorSize), 342 curPrd.getEOT(), curSector); 343 344 // the prd pointer has already been translated, so just do an increment 345 curPrdAddr = curPrdAddr + sizeof(PrdEntry_t); 346 347 if (dmaRead) 348 doDmaDataRead(); 349 else 350 doDmaDataWrite(); 351} 352 353void 354IdeDisk::doDmaDataRead() 355{ 356 /** @todo we need to figure out what the delay actually will be */ 357 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 358 359 DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n", 360 diskDelay, totalDiskDelay); 361 362 schedule(dmaReadWaitEvent, curTick() + totalDiskDelay); 363} 364 365void 366IdeDisk::regStats() 367{ 368 using namespace Stats; 369 dmaReadFullPages 370 .name(name() + ".dma_read_full_pages") 371 .desc("Number of full page size DMA reads (not PRD).") 372 ; 373 dmaReadBytes 374 .name(name() + ".dma_read_bytes") 375 .desc("Number of bytes transfered via DMA reads (not PRD).") 376 ; 377 dmaReadTxs 378 .name(name() + ".dma_read_txs") 379 .desc("Number of DMA read transactions (not PRD).") 380 ; 381 382 dmaWriteFullPages 383 .name(name() + ".dma_write_full_pages") 384 .desc("Number of full page size DMA writes.") 385 ; 386 dmaWriteBytes 387 .name(name() + ".dma_write_bytes") 388 .desc("Number of bytes transfered via DMA writes.") 389 ; 390 dmaWriteTxs 391 .name(name() + ".dma_write_txs") 392 .desc("Number of DMA write transactions.") 393 ; 394} 395 396void 397IdeDisk::doDmaRead() 398{ 399 400 if (!dmaReadCG) { 401 // clear out the data buffer 402 memset(dataBuffer, 0, MAX_DMA_SIZE); 403 dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(), 404 curPrd.getByteCount(), TheISA::PageBytes); 405 406 } 407 if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { 408 schedule(dmaReadWaitEvent, curTick() + DMA_BACKOFF_PERIOD); 409 return; 410 } else if (!dmaReadCG->done()) { 411 assert(dmaReadCG->complete() < MAX_DMA_SIZE); 412 ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(), 413 &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete()); 414 dmaReadBytes += dmaReadCG->size(); 415 dmaReadTxs++; 416 if (dmaReadCG->size() == TheISA::PageBytes) 417 dmaReadFullPages++; 418 dmaReadCG->next(); 419 } else { 420 assert(dmaReadCG->done()); 421 delete dmaReadCG; 422 dmaReadCG = NULL; 423 dmaReadDone(); 424 } 425} 426 427void 428IdeDisk::dmaReadDone() 429{ 430 431 uint32_t bytesWritten = 0; 432 433 434 // write the data to the disk image 435 for (bytesWritten = 0; bytesWritten < curPrd.getByteCount(); 436 bytesWritten += SectorSize) { 437 438 cmdBytesLeft -= SectorSize; 439 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); 440 } 441 442 // check for the EOT 443 if (curPrd.getEOT()) { 444 assert(cmdBytesLeft == 0); 445 dmaState = Dma_Idle; 446 updateState(ACT_DMA_DONE); 447 } else { 448 doDmaTransfer(); 449 } 450} 451 452void 453IdeDisk::doDmaDataWrite() 454{ 455 /** @todo we need to figure out what the delay actually will be */ 456 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 457 uint32_t bytesRead = 0; 458 459 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->getState() != SimObject::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 = 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