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