ide_disk.cc revision 2665
1/* 2 * Copyright (c) 2004-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Andrew Schultz 29 * Ali Saidi 30 */ 31 32/** @file 33 * Device model implementation for an IDE disk 34 */ 35 36#include <cerrno> 37#include <cstring> 38#include <deque> 39#include <string> 40 41#include "base/chunk_generator.hh" 42#include "base/cprintf.hh" // csprintf 43#include "base/trace.hh" 44#include "dev/disk_image.hh" 45#include "dev/ide_disk.hh" 46#include "dev/ide_ctrl.hh" 47#include "dev/tsunami.hh" 48#include "dev/tsunami_pchip.hh" 49#include "sim/builder.hh" 50#include "sim/sim_object.hh" 51#include "sim/root.hh" 52#include "arch/isa_traits.hh" 53 54using namespace std; 55using namespace TheISA; 56 57IdeDisk::IdeDisk(const string &name, DiskImage *img, 58 int id, Tick delay) 59 : SimObject(name), ctrl(NULL), image(img), diskDelay(delay), 60 dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this), 61 dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this), 62 dmaReadEvent(this), dmaWriteEvent(this) 63{ 64 // Reset the device state 65 reset(id); 66 67 // fill out the drive ID structure 68 memset(&driveID, 0, sizeof(struct ataparams)); 69 70 // Calculate LBA and C/H/S values 71 uint16_t cylinders; 72 uint8_t heads; 73 uint8_t sectors; 74 75 uint32_t lba_size = image->size(); 76 if (lba_size >= 16383*16*63) { 77 cylinders = 16383; 78 heads = 16; 79 sectors = 63; 80 } else { 81 if (lba_size >= 63) 82 sectors = 63; 83 else 84 sectors = lba_size; 85 86 if ((lba_size / sectors) >= 16) 87 heads = 16; 88 else 89 heads = (lba_size / sectors); 90 91 cylinders = lba_size / (heads * sectors); 92 } 93 94 // Setup the model name 95 strncpy((char *)driveID.atap_model, "5MI EDD si k", 96 sizeof(driveID.atap_model)); 97 // Set the maximum multisector transfer size 98 driveID.atap_multi = MAX_MULTSECT; 99 // IORDY supported, IORDY disabled, LBA enabled, DMA enabled 100 driveID.atap_capabilities1 = 0x7; 101 // UDMA support, EIDE support 102 driveID.atap_extensions = 0x6; 103 // Setup default C/H/S settings 104 driveID.atap_cylinders = cylinders; 105 driveID.atap_sectors = sectors; 106 driveID.atap_heads = heads; 107 // Setup the current multisector transfer size 108 driveID.atap_curmulti = MAX_MULTSECT; 109 driveID.atap_curmulti_valid = 0x1; 110 // Number of sectors on disk 111 driveID.atap_capacity = lba_size; 112 // Multiword DMA mode 2 and below supported 113 driveID.atap_dmamode_supp = 0x400; 114 // Set PIO mode 4 and 3 supported 115 driveID.atap_piomode_supp = 0x3; 116 // Set DMA mode 4 and below supported 117 driveID.atap_udmamode_supp = 0x1f; 118 // Statically set hardware config word 119 driveID.atap_hwreset_res = 0x4001; 120 121 //arbitrary for now... 122 driveID.atap_ata_major = WDC_VER_ATA7; 123} 124 125IdeDisk::~IdeDisk() 126{ 127 // destroy the data buffer 128 delete [] dataBuffer; 129} 130 131void 132IdeDisk::reset(int id) 133{ 134 // initialize the data buffer and shadow registers 135 dataBuffer = new uint8_t[MAX_DMA_SIZE]; 136 137 memset(dataBuffer, 0, MAX_DMA_SIZE); 138 memset(&cmdReg, 0, sizeof(CommandReg_t)); 139 memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); 140 141 curPrdAddr = 0; 142 curSector = 0; 143 cmdBytes = 0; 144 cmdBytesLeft = 0; 145 drqBytesLeft = 0; 146 dmaRead = false; 147 intrPending = false; 148 149 // set the device state to idle 150 dmaState = Dma_Idle; 151 152 if (id == DEV0) { 153 devState = Device_Idle_S; 154 devID = DEV0; 155 } else if (id == DEV1) { 156 devState = Device_Idle_NS; 157 devID = DEV1; 158 } else { 159 panic("Invalid device ID: %#x\n", id); 160 } 161 162 // set the device ready bit 163 status = STATUS_DRDY_BIT; 164 165 /* The error register must be set to 0x1 on start-up to 166 indicate that no diagnostic error was detected */ 167 cmdReg.error = 0x1; 168} 169 170//// 171// Utility functions 172//// 173 174bool 175IdeDisk::isDEVSelect() 176{ 177 return ctrl->isDiskSelected(this); 178} 179 180Addr 181IdeDisk::pciToDma(Addr pciAddr) 182{ 183 if (ctrl) 184 return ctrl->plat->pciToDma(pciAddr); 185 else 186 panic("Access to unset controller!\n"); 187} 188 189//// 190// Device registers read/write 191//// 192 193void 194IdeDisk::read(const Addr &offset, IdeRegType reg_type, uint8_t *data) 195{ 196 DevAction_t action = ACT_NONE; 197 198 switch (reg_type) { 199 case COMMAND_BLOCK: 200 switch (offset) { 201 // Data transfers occur two bytes at a time 202 case DATA_OFFSET: 203 *(uint16_t*)data = cmdReg.data; 204 action = ACT_DATA_READ_SHORT; 205 break; 206 case ERROR_OFFSET: 207 *data = cmdReg.error; 208 break; 209 case NSECTOR_OFFSET: 210 *data = cmdReg.sec_count; 211 break; 212 case SECTOR_OFFSET: 213 *data = cmdReg.sec_num; 214 break; 215 case LCYL_OFFSET: 216 *data = cmdReg.cyl_low; 217 break; 218 case HCYL_OFFSET: 219 *data = cmdReg.cyl_high; 220 break; 221 case DRIVE_OFFSET: 222 *data = cmdReg.drive; 223 break; 224 case STATUS_OFFSET: 225 *data = status; 226 action = ACT_STAT_READ; 227 break; 228 default: 229 panic("Invalid IDE command register offset: %#x\n", offset); 230 } 231 break; 232 case CONTROL_BLOCK: 233 if (offset == ALTSTAT_OFFSET) 234 *data = status; 235 else 236 panic("Invalid IDE control register offset: %#x\n", offset); 237 break; 238 default: 239 panic("Unknown register block!\n"); 240 } 241 DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, 242 (uint32_t)*data); 243 244 if (action != ACT_NONE) 245 updateState(action); 246} 247 248void 249IdeDisk::write(const Addr &offset, IdeRegType reg_type, const uint8_t *data) 250{ 251 DevAction_t action = ACT_NONE; 252 253 switch (reg_type) { 254 case COMMAND_BLOCK: 255 switch (offset) { 256 case DATA_OFFSET: 257 cmdReg.data = *(uint16_t*)data; 258 action = ACT_DATA_WRITE_SHORT; 259 break; 260 case FEATURES_OFFSET: 261 break; 262 case NSECTOR_OFFSET: 263 cmdReg.sec_count = *data; 264 break; 265 case SECTOR_OFFSET: 266 cmdReg.sec_num = *data; 267 break; 268 case LCYL_OFFSET: 269 cmdReg.cyl_low = *data; 270 break; 271 case HCYL_OFFSET: 272 cmdReg.cyl_high = *data; 273 break; 274 case DRIVE_OFFSET: 275 cmdReg.drive = *data; 276 action = ACT_SELECT_WRITE; 277 break; 278 case COMMAND_OFFSET: 279 cmdReg.command = *data; 280 action = ACT_CMD_WRITE; 281 break; 282 default: 283 panic("Invalid IDE command register offset: %#x\n", offset); 284 } 285 break; 286 case CONTROL_BLOCK: 287 if (offset == CONTROL_OFFSET) { 288 if (*data & CONTROL_RST_BIT) { 289 // force the device into the reset state 290 devState = Device_Srst; 291 action = ACT_SRST_SET; 292 } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) 293 action = ACT_SRST_CLEAR; 294 295 nIENBit = (*data & CONTROL_IEN_BIT) ? true : false; 296 } 297 else 298 panic("Invalid IDE control register offset: %#x\n", offset); 299 break; 300 default: 301 panic("Unknown register block!\n"); 302 } 303 304 DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset, 305 (uint32_t)*data); 306 if (action != ACT_NONE) 307 updateState(action); 308} 309 310//// 311// Perform DMA transactions 312//// 313 314void 315IdeDisk::doDmaTransfer() 316{ 317 if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) 318 panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n", 319 dmaState, devState); 320 321 if (ctrl->dmaPending()) { 322 dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 323 return; 324 } else 325 ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent, 326 (uint8_t*)&curPrd.entry); 327} 328 329void 330IdeDisk::dmaPrdReadDone() 331{ 332 DPRINTF(IdeDisk, 333 "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n", 334 curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()), 335 curPrd.getByteCount(), (cmdBytesLeft/SectorSize), 336 curPrd.getEOT(), curSector); 337 338 // the prd pointer has already been translated, so just do an increment 339 curPrdAddr = curPrdAddr + sizeof(PrdEntry_t); 340 341 if (dmaRead) 342 doDmaDataRead(); 343 else 344 doDmaDataWrite(); 345} 346 347void 348IdeDisk::doDmaDataRead() 349{ 350 /** @todo we need to figure out what the delay actually will be */ 351 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 352 353 DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n", 354 diskDelay, totalDiskDelay); 355 356 dmaReadWaitEvent.schedule(curTick + totalDiskDelay); 357} 358 359void 360IdeDisk::regStats() 361{ 362 using namespace Stats; 363 dmaReadFullPages 364 .name(name() + ".dma_read_full_pages") 365 .desc("Number of full page size DMA reads (not PRD).") 366 ; 367 dmaReadBytes 368 .name(name() + ".dma_read_bytes") 369 .desc("Number of bytes transfered via DMA reads (not PRD).") 370 ; 371 dmaReadTxs 372 .name(name() + ".dma_read_txs") 373 .desc("Number of DMA read transactions (not PRD).") 374 ; 375 376 dmaWriteFullPages 377 .name(name() + ".dma_write_full_pages") 378 .desc("Number of full page size DMA writes.") 379 ; 380 dmaWriteBytes 381 .name(name() + ".dma_write_bytes") 382 .desc("Number of bytes transfered via DMA writes.") 383 ; 384 dmaWriteTxs 385 .name(name() + ".dma_write_txs") 386 .desc("Number of DMA write transactions.") 387 ; 388} 389 390void 391IdeDisk::doDmaRead() 392{ 393 394 if (!dmaReadCG) { 395 // clear out the data buffer 396 memset(dataBuffer, 0, MAX_DMA_SIZE); 397 dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(), 398 curPrd.getByteCount(), TheISA::PageBytes); 399 400 } 401 if (ctrl->dmaPending()) { 402 panic("shouldn't be reentant??"); 403 dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 404 return; 405 } else if (!dmaReadCG->done()) { 406 assert(dmaReadCG->complete() < MAX_DMA_SIZE); 407 ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(), 408 &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete()); 409 dmaReadBytes += dmaReadCG->size(); 410 dmaReadTxs++; 411 if (dmaReadCG->size() == TheISA::PageBytes) 412 dmaReadFullPages++; 413 dmaReadCG->next(); 414 } else { 415 assert(dmaReadCG->done()); 416 delete dmaReadCG; 417 dmaReadCG = NULL; 418 dmaReadDone(); 419 } 420} 421 422void 423IdeDisk::dmaReadDone() 424{ 425 426 uint32_t bytesWritten = 0; 427 428 429 // write the data to the disk image 430 for (bytesWritten = 0; bytesWritten < curPrd.getByteCount(); 431 bytesWritten += SectorSize) { 432 433 cmdBytesLeft -= SectorSize; 434 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); 435 } 436 437 // check for the EOT 438 if (curPrd.getEOT()) { 439 assert(cmdBytesLeft == 0); 440 dmaState = Dma_Idle; 441 updateState(ACT_DMA_DONE); 442 } else { 443 doDmaTransfer(); 444 } 445} 446 447void 448IdeDisk::doDmaDataWrite() 449{ 450 /** @todo we need to figure out what the delay actually will be */ 451 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 452 uint32_t bytesRead = 0; 453 454 DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n", 455 diskDelay, totalDiskDelay); 456 457 memset(dataBuffer, 0, MAX_DMA_SIZE); 458 assert(cmdBytesLeft <= MAX_DMA_SIZE); 459 while (bytesRead < curPrd.getByteCount()) { 460 readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead)); 461 bytesRead += SectorSize; 462 cmdBytesLeft -= SectorSize; 463 } 464 465 dmaWriteWaitEvent.schedule(curTick + totalDiskDelay); 466} 467 468void 469IdeDisk::doDmaWrite() 470{ 471 472 if (!dmaWriteCG) { 473 // clear out the data buffer 474 dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(), 475 curPrd.getByteCount(), TheISA::PageBytes); 476 } 477 if (ctrl->dmaPending()) { 478 panic("shouldn't be reentant??"); 479 dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 480 return; 481 } else if (!dmaWriteCG->done()) { 482 assert(dmaWriteCG->complete() < MAX_DMA_SIZE); 483 ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(), 484 &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete()); 485 dmaWriteBytes += dmaWriteCG->size(); 486 dmaWriteTxs++; 487 if (dmaWriteCG->size() == TheISA::PageBytes) 488 dmaWriteFullPages++; 489 dmaWriteCG->next(); 490 } else { 491 assert(dmaWriteCG->done()); 492 delete dmaWriteCG; 493 dmaWriteCG = NULL; 494 dmaWriteDone(); 495 } 496} 497 498void 499IdeDisk::dmaWriteDone() 500{ 501 // check for the EOT 502 if (curPrd.getEOT()) { 503 assert(cmdBytesLeft == 0); 504 dmaState = Dma_Idle; 505 updateState(ACT_DMA_DONE); 506 } else { 507 doDmaTransfer(); 508 } 509} 510 511//// 512// Disk utility routines 513/// 514 515void 516IdeDisk::readDisk(uint32_t sector, uint8_t *data) 517{ 518 uint32_t bytesRead = image->read(data, sector); 519 520 if (bytesRead != SectorSize) 521 panic("Can't read from %s. Only %d of %d read. errno=%d\n", 522 name(), bytesRead, SectorSize, errno); 523} 524 525void 526IdeDisk::writeDisk(uint32_t sector, uint8_t *data) 527{ 528 uint32_t bytesWritten = image->write(data, sector); 529 530 if (bytesWritten != SectorSize) 531 panic("Can't write to %s. Only %d of %d written. errno=%d\n", 532 name(), bytesWritten, SectorSize, errno); 533} 534 535//// 536// Setup and handle commands 537//// 538 539void 540IdeDisk::startDma(const uint32_t &prdTableBase) 541{ 542 if (dmaState != Dma_Start) 543 panic("Inconsistent DMA state, should be in Dma_Start!\n"); 544 545 if (devState != Transfer_Data_Dma) 546 panic("Inconsistent device state for DMA start!\n"); 547 548 // PRD base address is given by bits 31:2 549 curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3))); 550 551 dmaState = Dma_Transfer; 552 553 // schedule dma transfer (doDmaTransfer) 554 dmaTransferEvent.schedule(curTick + 1); 555} 556 557void 558IdeDisk::abortDma() 559{ 560 if (dmaState == Dma_Idle) 561 panic("Inconsistent DMA state, should be Start or Transfer!"); 562 563 if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma) 564 panic("Inconsistent device state, should be Transfer or Prepare!\n"); 565 566 updateState(ACT_CMD_ERROR); 567} 568 569void 570IdeDisk::startCommand() 571{ 572 DevAction_t action = ACT_NONE; 573 uint32_t size = 0; 574 dmaRead = false; 575 576 // Decode commands 577 switch (cmdReg.command) { 578 // Supported non-data commands 579 case WDSF_READ_NATIVE_MAX: 580 size = image->size() - 1; 581 cmdReg.sec_num = (size & 0xff); 582 cmdReg.cyl_low = ((size & 0xff00) >> 8); 583 cmdReg.cyl_high = ((size & 0xff0000) >> 16); 584 cmdReg.head = ((size & 0xf000000) >> 24); 585 586 devState = Command_Execution; 587 action = ACT_CMD_COMPLETE; 588 break; 589 590 case WDCC_RECAL: 591 case WDCC_IDP: 592 case WDCC_STANDBY_IMMED: 593 case WDCC_FLUSHCACHE: 594 case WDSF_VERIFY: 595 case WDSF_SEEK: 596 case SET_FEATURES: 597 case WDCC_SETMULTI: 598 devState = Command_Execution; 599 action = ACT_CMD_COMPLETE; 600 break; 601 602 // Supported PIO data-in commands 603 case WDCC_IDENTIFY: 604 cmdBytes = cmdBytesLeft = sizeof(struct ataparams); 605 devState = Prepare_Data_In; 606 action = ACT_DATA_READY; 607 break; 608 609 case WDCC_READMULTI: 610 case WDCC_READ: 611 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 612 panic("Attempt to perform CHS access, only supports LBA\n"); 613 614 if (cmdReg.sec_count == 0) 615 cmdBytes = cmdBytesLeft = (256 * SectorSize); 616 else 617 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 618 619 curSector = getLBABase(); 620 621 /** @todo make this a scheduled event to simulate disk delay */ 622 devState = Prepare_Data_In; 623 action = ACT_DATA_READY; 624 break; 625 626 // Supported PIO data-out commands 627 case WDCC_WRITEMULTI: 628 case WDCC_WRITE: 629 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 630 panic("Attempt to perform CHS access, only supports LBA\n"); 631 632 if (cmdReg.sec_count == 0) 633 cmdBytes = cmdBytesLeft = (256 * SectorSize); 634 else 635 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 636 637 curSector = getLBABase(); 638 639 devState = Prepare_Data_Out; 640 action = ACT_DATA_READY; 641 break; 642 643 // Supported DMA commands 644 case WDCC_WRITEDMA: 645 dmaRead = true; // a write to the disk is a DMA read from memory 646 case WDCC_READDMA: 647 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 648 panic("Attempt to perform CHS access, only supports LBA\n"); 649 650 if (cmdReg.sec_count == 0) 651 cmdBytes = cmdBytesLeft = (256 * SectorSize); 652 else 653 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 654 655 curSector = getLBABase(); 656 657 devState = Prepare_Data_Dma; 658 action = ACT_DMA_READY; 659 break; 660 661 default: 662 panic("Unsupported ATA command: %#x\n", cmdReg.command); 663 } 664 665 if (action != ACT_NONE) { 666 // set the BSY bit 667 status |= STATUS_BSY_BIT; 668 // clear the DRQ bit 669 status &= ~STATUS_DRQ_BIT; 670 // clear the DF bit 671 status &= ~STATUS_DF_BIT; 672 673 updateState(action); 674 } 675} 676 677//// 678// Handle setting and clearing interrupts 679//// 680 681void 682IdeDisk::intrPost() 683{ 684 DPRINTF(IdeDisk, "Posting Interrupt\n"); 685 if (intrPending) 686 panic("Attempt to post an interrupt with one pending\n"); 687 688 intrPending = true; 689 690 // talk to controller to set interrupt 691 if (ctrl) { 692 ctrl->bmi_regs.bmis0 |= IDEINTS; 693 ctrl->intrPost(); 694 } 695} 696 697void 698IdeDisk::intrClear() 699{ 700 DPRINTF(IdeDisk, "Clearing Interrupt\n"); 701 if (!intrPending) 702 panic("Attempt to clear a non-pending interrupt\n"); 703 704 intrPending = false; 705 706 // talk to controller to clear interrupt 707 if (ctrl) 708 ctrl->intrClear(); 709} 710 711//// 712// Manage the device internal state machine 713//// 714 715void 716IdeDisk::updateState(DevAction_t action) 717{ 718 switch (devState) { 719 case Device_Srst: 720 if (action == ACT_SRST_SET) { 721 // set the BSY bit 722 status |= STATUS_BSY_BIT; 723 } else if (action == ACT_SRST_CLEAR) { 724 // clear the BSY bit 725 status &= ~STATUS_BSY_BIT; 726 727 // reset the device state 728 reset(devID); 729 } 730 break; 731 732 case Device_Idle_S: 733 if (action == ACT_SELECT_WRITE && !isDEVSelect()) { 734 devState = Device_Idle_NS; 735 } else if (action == ACT_CMD_WRITE) { 736 startCommand(); 737 } 738 739 break; 740 741 case Device_Idle_SI: 742 if (action == ACT_SELECT_WRITE && !isDEVSelect()) { 743 devState = Device_Idle_NS; 744 intrClear(); 745 } else if (action == ACT_STAT_READ || isIENSet()) { 746 devState = Device_Idle_S; 747 intrClear(); 748 } else if (action == ACT_CMD_WRITE) { 749 intrClear(); 750 startCommand(); 751 } 752 753 break; 754 755 case Device_Idle_NS: 756 if (action == ACT_SELECT_WRITE && isDEVSelect()) { 757 if (!isIENSet() && intrPending) { 758 devState = Device_Idle_SI; 759 intrPost(); 760 } 761 if (isIENSet() || !intrPending) { 762 devState = Device_Idle_S; 763 } 764 } 765 break; 766 767 case Command_Execution: 768 if (action == ACT_CMD_COMPLETE) { 769 // clear the BSY bit 770 setComplete(); 771 772 if (!isIENSet()) { 773 devState = Device_Idle_SI; 774 intrPost(); 775 } else { 776 devState = Device_Idle_S; 777 } 778 } 779 break; 780 781 case Prepare_Data_In: 782 if (action == ACT_CMD_ERROR) { 783 // clear the BSY bit 784 setComplete(); 785 786 if (!isIENSet()) { 787 devState = Device_Idle_SI; 788 intrPost(); 789 } else { 790 devState = Device_Idle_S; 791 } 792 } else if (action == ACT_DATA_READY) { 793 // clear the BSY bit 794 status &= ~STATUS_BSY_BIT; 795 // set the DRQ bit 796 status |= STATUS_DRQ_BIT; 797 798 // copy the data into the data buffer 799 if (cmdReg.command == WDCC_IDENTIFY) { 800 // Reset the drqBytes for this block 801 drqBytesLeft = sizeof(struct ataparams); 802 803 memcpy((void *)dataBuffer, (void *)&driveID, 804 sizeof(struct ataparams)); 805 } else { 806 // Reset the drqBytes for this block 807 drqBytesLeft = SectorSize; 808 809 readDisk(curSector++, dataBuffer); 810 } 811 812 // put the first two bytes into the data register 813 memcpy((void *)&cmdReg.data, (void *)dataBuffer, 814 sizeof(uint16_t)); 815 816 if (!isIENSet()) { 817 devState = Data_Ready_INTRQ_In; 818 intrPost(); 819 } else { 820 devState = Transfer_Data_In; 821 } 822 } 823 break; 824 825 case Data_Ready_INTRQ_In: 826 if (action == ACT_STAT_READ) { 827 devState = Transfer_Data_In; 828 intrClear(); 829 } 830 break; 831 832 case Transfer_Data_In: 833 if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) { 834 if (action == ACT_DATA_READ_BYTE) { 835 panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n"); 836 } else { 837 drqBytesLeft -= 2; 838 cmdBytesLeft -= 2; 839 840 // copy next short into data registers 841 if (drqBytesLeft) 842 memcpy((void *)&cmdReg.data, 843 (void *)&dataBuffer[SectorSize - drqBytesLeft], 844 sizeof(uint16_t)); 845 } 846 847 if (drqBytesLeft == 0) { 848 if (cmdBytesLeft == 0) { 849 // Clear the BSY bit 850 setComplete(); 851 devState = Device_Idle_S; 852 } else { 853 devState = Prepare_Data_In; 854 // set the BSY_BIT 855 status |= STATUS_BSY_BIT; 856 // clear the DRQ_BIT 857 status &= ~STATUS_DRQ_BIT; 858 859 /** @todo change this to a scheduled event to simulate 860 disk delay */ 861 updateState(ACT_DATA_READY); 862 } 863 } 864 } 865 break; 866 867 case Prepare_Data_Out: 868 if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) { 869 // clear the BSY bit 870 setComplete(); 871 872 if (!isIENSet()) { 873 devState = Device_Idle_SI; 874 intrPost(); 875 } else { 876 devState = Device_Idle_S; 877 } 878 } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) { 879 // clear the BSY bit 880 status &= ~STATUS_BSY_BIT; 881 // set the DRQ bit 882 status |= STATUS_DRQ_BIT; 883 884 // clear the data buffer to get it ready for writes 885 memset(dataBuffer, 0, MAX_DMA_SIZE); 886 887 // reset the drqBytes for this block 888 drqBytesLeft = SectorSize; 889 890 if (cmdBytesLeft == cmdBytes || isIENSet()) { 891 devState = Transfer_Data_Out; 892 } else { 893 devState = Data_Ready_INTRQ_Out; 894 intrPost(); 895 } 896 } 897 break; 898 899 case Data_Ready_INTRQ_Out: 900 if (action == ACT_STAT_READ) { 901 devState = Transfer_Data_Out; 902 intrClear(); 903 } 904 break; 905 906 case Transfer_Data_Out: 907 if (action == ACT_DATA_WRITE_BYTE || 908 action == ACT_DATA_WRITE_SHORT) { 909 910 if (action == ACT_DATA_READ_BYTE) { 911 panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n"); 912 } else { 913 // copy the latest short into the data buffer 914 memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft], 915 (void *)&cmdReg.data, 916 sizeof(uint16_t)); 917 918 drqBytesLeft -= 2; 919 cmdBytesLeft -= 2; 920 } 921 922 if (drqBytesLeft == 0) { 923 // copy the block to the disk 924 writeDisk(curSector++, dataBuffer); 925 926 // set the BSY bit 927 status |= STATUS_BSY_BIT; 928 // set the seek bit 929 status |= STATUS_SEEK_BIT; 930 // clear the DRQ bit 931 status &= ~STATUS_DRQ_BIT; 932 933 devState = Prepare_Data_Out; 934 935 /** @todo change this to a scheduled event to simulate 936 disk delay */ 937 updateState(ACT_DATA_READY); 938 } 939 } 940 break; 941 942 case Prepare_Data_Dma: 943 if (action == ACT_CMD_ERROR) { 944 // clear the BSY bit 945 setComplete(); 946 947 if (!isIENSet()) { 948 devState = Device_Idle_SI; 949 intrPost(); 950 } else { 951 devState = Device_Idle_S; 952 } 953 } else if (action == ACT_DMA_READY) { 954 // clear the BSY bit 955 status &= ~STATUS_BSY_BIT; 956 // set the DRQ bit 957 status |= STATUS_DRQ_BIT; 958 959 devState = Transfer_Data_Dma; 960 961 if (dmaState != Dma_Idle) 962 panic("Inconsistent DMA state, should be Dma_Idle\n"); 963 964 dmaState = Dma_Start; 965 // wait for the write to the DMA start bit 966 } 967 break; 968 969 case Transfer_Data_Dma: 970 if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) { 971 // clear the BSY bit 972 setComplete(); 973 // set the seek bit 974 status |= STATUS_SEEK_BIT; 975 // clear the controller state for DMA transfer 976 ctrl->setDmaComplete(this); 977 978 if (!isIENSet()) { 979 devState = Device_Idle_SI; 980 intrPost(); 981 } else { 982 devState = Device_Idle_S; 983 } 984 } 985 break; 986 987 default: 988 panic("Unknown IDE device state: %#x\n", devState); 989 } 990} 991 992void 993IdeDisk::serialize(ostream &os) 994{ 995 // Check all outstanding events to see if they are scheduled 996 // these are all mutually exclusive 997 Tick reschedule = 0; 998 Events_t event = None; 999 1000 int eventCount = 0; 1001 1002 if (dmaTransferEvent.scheduled()) { 1003 reschedule = dmaTransferEvent.when(); 1004 event = Transfer; 1005 eventCount++; 1006 } 1007 if (dmaReadWaitEvent.scheduled()) { 1008 reschedule = dmaReadWaitEvent.when(); 1009 event = ReadWait; 1010 eventCount++; 1011 } 1012 if (dmaWriteWaitEvent.scheduled()) { 1013 reschedule = dmaWriteWaitEvent.when(); 1014 event = WriteWait; 1015 eventCount++; 1016 } 1017 if (dmaPrdReadEvent.scheduled()) { 1018 reschedule = dmaPrdReadEvent.when(); 1019 event = PrdRead; 1020 eventCount++; 1021 } 1022 if (dmaReadEvent.scheduled()) { 1023 reschedule = dmaReadEvent.when(); 1024 event = DmaRead; 1025 eventCount++; 1026 } 1027 if (dmaWriteEvent.scheduled()) { 1028 reschedule = dmaWriteEvent.when(); 1029 event = DmaWrite; 1030 eventCount++; 1031 } 1032 1033 assert(eventCount <= 1); 1034 1035 SERIALIZE_SCALAR(reschedule); 1036 SERIALIZE_ENUM(event); 1037 1038 // Serialize device registers 1039 SERIALIZE_SCALAR(cmdReg.data); 1040 SERIALIZE_SCALAR(cmdReg.sec_count); 1041 SERIALIZE_SCALAR(cmdReg.sec_num); 1042 SERIALIZE_SCALAR(cmdReg.cyl_low); 1043 SERIALIZE_SCALAR(cmdReg.cyl_high); 1044 SERIALIZE_SCALAR(cmdReg.drive); 1045 SERIALIZE_SCALAR(cmdReg.command); 1046 SERIALIZE_SCALAR(status); 1047 SERIALIZE_SCALAR(nIENBit); 1048 SERIALIZE_SCALAR(devID); 1049 1050 // Serialize the PRD related information 1051 SERIALIZE_SCALAR(curPrd.entry.baseAddr); 1052 SERIALIZE_SCALAR(curPrd.entry.byteCount); 1053 SERIALIZE_SCALAR(curPrd.entry.endOfTable); 1054 SERIALIZE_SCALAR(curPrdAddr); 1055 1056 /** @todo need to serialized chunk generator stuff!! */ 1057 // Serialize current transfer related information 1058 SERIALIZE_SCALAR(cmdBytesLeft); 1059 SERIALIZE_SCALAR(cmdBytes); 1060 SERIALIZE_SCALAR(drqBytesLeft); 1061 SERIALIZE_SCALAR(curSector); 1062 SERIALIZE_SCALAR(dmaRead); 1063 SERIALIZE_SCALAR(intrPending); 1064 SERIALIZE_ENUM(devState); 1065 SERIALIZE_ENUM(dmaState); 1066 SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); 1067} 1068 1069void 1070IdeDisk::unserialize(Checkpoint *cp, const string §ion) 1071{ 1072 // Reschedule events that were outstanding 1073 // these are all mutually exclusive 1074 Tick reschedule = 0; 1075 Events_t event = None; 1076 1077 UNSERIALIZE_SCALAR(reschedule); 1078 UNSERIALIZE_ENUM(event); 1079 1080 switch (event) { 1081 case None : break; 1082 case Transfer : dmaTransferEvent.schedule(reschedule); break; 1083 case ReadWait : dmaReadWaitEvent.schedule(reschedule); break; 1084 case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break; 1085 case PrdRead : dmaPrdReadEvent.schedule(reschedule); break; 1086 case DmaRead : dmaReadEvent.schedule(reschedule); break; 1087 case DmaWrite : dmaWriteEvent.schedule(reschedule); break; 1088 } 1089 1090 // Unserialize device registers 1091 UNSERIALIZE_SCALAR(cmdReg.data); 1092 UNSERIALIZE_SCALAR(cmdReg.sec_count); 1093 UNSERIALIZE_SCALAR(cmdReg.sec_num); 1094 UNSERIALIZE_SCALAR(cmdReg.cyl_low); 1095 UNSERIALIZE_SCALAR(cmdReg.cyl_high); 1096 UNSERIALIZE_SCALAR(cmdReg.drive); 1097 UNSERIALIZE_SCALAR(cmdReg.command); 1098 UNSERIALIZE_SCALAR(status); 1099 UNSERIALIZE_SCALAR(nIENBit); 1100 UNSERIALIZE_SCALAR(devID); 1101 1102 // Unserialize the PRD related information 1103 UNSERIALIZE_SCALAR(curPrd.entry.baseAddr); 1104 UNSERIALIZE_SCALAR(curPrd.entry.byteCount); 1105 UNSERIALIZE_SCALAR(curPrd.entry.endOfTable); 1106 UNSERIALIZE_SCALAR(curPrdAddr); 1107 1108 /** @todo need to serialized chunk generator stuff!! */ 1109 // Unserialize current transfer related information 1110 UNSERIALIZE_SCALAR(cmdBytes); 1111 UNSERIALIZE_SCALAR(cmdBytesLeft); 1112 UNSERIALIZE_SCALAR(drqBytesLeft); 1113 UNSERIALIZE_SCALAR(curSector); 1114 UNSERIALIZE_SCALAR(dmaRead); 1115 UNSERIALIZE_SCALAR(intrPending); 1116 UNSERIALIZE_ENUM(devState); 1117 UNSERIALIZE_ENUM(dmaState); 1118 UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); 1119} 1120 1121#ifndef DOXYGEN_SHOULD_SKIP_THIS 1122 1123enum DriveID { master, slave }; 1124static const char *DriveID_strings[] = { "master", "slave" }; 1125BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) 1126 1127 SimObjectParam<DiskImage *> image; 1128 SimpleEnumParam<DriveID> driveID; 1129 Param<int> delay; 1130 1131END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) 1132 1133BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk) 1134 1135 INIT_PARAM(image, "Disk image"), 1136 INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings), 1137 INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1) 1138 1139END_INIT_SIM_OBJECT_PARAMS(IdeDisk) 1140 1141 1142CREATE_SIM_OBJECT(IdeDisk) 1143{ 1144 return new IdeDisk(getInstanceName(), image, driveID, delay); 1145} 1146 1147REGISTER_SIM_OBJECT("IdeDisk", IdeDisk) 1148 1149#endif //DOXYGEN_SHOULD_SKIP_THIS 1150