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