ide_disk.cc revision 2989
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 = 0x4; 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() || ctrl->getState() != SimObject::Running) { 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() || ctrl->getState() != SimObject::Running) { 402 dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 403 return; 404 } else if (!dmaReadCG->done()) { 405 assert(dmaReadCG->complete() < MAX_DMA_SIZE); 406 ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(), 407 &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete()); 408 dmaReadBytes += dmaReadCG->size(); 409 dmaReadTxs++; 410 if (dmaReadCG->size() == TheISA::PageBytes) 411 dmaReadFullPages++; 412 dmaReadCG->next(); 413 } else { 414 assert(dmaReadCG->done()); 415 delete dmaReadCG; 416 dmaReadCG = NULL; 417 dmaReadDone(); 418 } 419} 420 421void 422IdeDisk::dmaReadDone() 423{ 424 425 uint32_t bytesWritten = 0; 426 427 428 // write the data to the disk image 429 for (bytesWritten = 0; bytesWritten < curPrd.getByteCount(); 430 bytesWritten += SectorSize) { 431 432 cmdBytesLeft -= SectorSize; 433 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); 434 } 435 436 // check for the EOT 437 if (curPrd.getEOT()) { 438 assert(cmdBytesLeft == 0); 439 dmaState = Dma_Idle; 440 updateState(ACT_DMA_DONE); 441 } else { 442 doDmaTransfer(); 443 } 444} 445 446void 447IdeDisk::doDmaDataWrite() 448{ 449 /** @todo we need to figure out what the delay actually will be */ 450 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 451 uint32_t bytesRead = 0; 452 453 DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n", 454 diskDelay, totalDiskDelay); 455 456 memset(dataBuffer, 0, MAX_DMA_SIZE); 457 assert(cmdBytesLeft <= MAX_DMA_SIZE); 458 while (bytesRead < curPrd.getByteCount()) { 459 readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead)); 460 bytesRead += SectorSize; 461 cmdBytesLeft -= SectorSize; 462 } 463 464 dmaWriteWaitEvent.schedule(curTick + totalDiskDelay); 465} 466 467void 468IdeDisk::doDmaWrite() 469{ 470 471 if (!dmaWriteCG) { 472 // clear out the data buffer 473 dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(), 474 curPrd.getByteCount(), TheISA::PageBytes); 475 } 476 if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { 477 dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 478 return; 479 } else if (!dmaWriteCG->done()) { 480 assert(dmaWriteCG->complete() < MAX_DMA_SIZE); 481 ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(), 482 &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete()); 483 dmaWriteBytes += dmaWriteCG->size(); 484 dmaWriteTxs++; 485 if (dmaWriteCG->size() == TheISA::PageBytes) 486 dmaWriteFullPages++; 487 dmaWriteCG->next(); 488 } else { 489 assert(dmaWriteCG->done()); 490 delete dmaWriteCG; 491 dmaWriteCG = NULL; 492 dmaWriteDone(); 493 } 494} 495 496void 497IdeDisk::dmaWriteDone() 498{ 499 // check for the EOT 500 if (curPrd.getEOT()) { 501 assert(cmdBytesLeft == 0); 502 dmaState = Dma_Idle; 503 updateState(ACT_DMA_DONE); 504 } else { 505 doDmaTransfer(); 506 } 507} 508 509//// 510// Disk utility routines 511/// 512 513void 514IdeDisk::readDisk(uint32_t sector, uint8_t *data) 515{ 516 uint32_t bytesRead = image->read(data, sector); 517 518 if (bytesRead != SectorSize) 519 panic("Can't read from %s. Only %d of %d read. errno=%d\n", 520 name(), bytesRead, SectorSize, errno); 521} 522 523void 524IdeDisk::writeDisk(uint32_t sector, uint8_t *data) 525{ 526 uint32_t bytesWritten = image->write(data, sector); 527 528 if (bytesWritten != SectorSize) 529 panic("Can't write to %s. Only %d of %d written. errno=%d\n", 530 name(), bytesWritten, SectorSize, errno); 531} 532 533//// 534// Setup and handle commands 535//// 536 537void 538IdeDisk::startDma(const uint32_t &prdTableBase) 539{ 540 if (dmaState != Dma_Start) 541 panic("Inconsistent DMA state, should be in Dma_Start!\n"); 542 543 if (devState != Transfer_Data_Dma) 544 panic("Inconsistent device state for DMA start!\n"); 545 546 // PRD base address is given by bits 31:2 547 curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3))); 548 549 dmaState = Dma_Transfer; 550 551 // schedule dma transfer (doDmaTransfer) 552 dmaTransferEvent.schedule(curTick + 1); 553} 554 555void 556IdeDisk::abortDma() 557{ 558 if (dmaState == Dma_Idle) 559 panic("Inconsistent DMA state, should be Start or Transfer!"); 560 561 if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma) 562 panic("Inconsistent device state, should be Transfer or Prepare!\n"); 563 564 updateState(ACT_CMD_ERROR); 565} 566 567void 568IdeDisk::startCommand() 569{ 570 DevAction_t action = ACT_NONE; 571 uint32_t size = 0; 572 dmaRead = false; 573 574 // Decode commands 575 switch (cmdReg.command) { 576 // Supported non-data commands 577 case WDSF_READ_NATIVE_MAX: 578 size = image->size() - 1; 579 cmdReg.sec_num = (size & 0xff); 580 cmdReg.cyl_low = ((size & 0xff00) >> 8); 581 cmdReg.cyl_high = ((size & 0xff0000) >> 16); 582 cmdReg.head = ((size & 0xf000000) >> 24); 583 584 devState = Command_Execution; 585 action = ACT_CMD_COMPLETE; 586 break; 587 588 case WDCC_RECAL: 589 case WDCC_IDP: 590 case WDCC_STANDBY_IMMED: 591 case WDCC_FLUSHCACHE: 592 case WDSF_VERIFY: 593 case WDSF_SEEK: 594 case SET_FEATURES: 595 case WDCC_SETMULTI: 596 devState = Command_Execution; 597 action = ACT_CMD_COMPLETE; 598 break; 599 600 // Supported PIO data-in commands 601 case WDCC_IDENTIFY: 602 cmdBytes = cmdBytesLeft = sizeof(struct ataparams); 603 devState = Prepare_Data_In; 604 action = ACT_DATA_READY; 605 break; 606 607 case WDCC_READMULTI: 608 case WDCC_READ: 609 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 610 panic("Attempt to perform CHS access, only supports LBA\n"); 611 612 if (cmdReg.sec_count == 0) 613 cmdBytes = cmdBytesLeft = (256 * SectorSize); 614 else 615 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 616 617 curSector = getLBABase(); 618 619 /** @todo make this a scheduled event to simulate disk delay */ 620 devState = Prepare_Data_In; 621 action = ACT_DATA_READY; 622 break; 623 624 // Supported PIO data-out commands 625 case WDCC_WRITEMULTI: 626 case WDCC_WRITE: 627 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 628 panic("Attempt to perform CHS access, only supports LBA\n"); 629 630 if (cmdReg.sec_count == 0) 631 cmdBytes = cmdBytesLeft = (256 * SectorSize); 632 else 633 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 634 635 curSector = getLBABase(); 636 637 devState = Prepare_Data_Out; 638 action = ACT_DATA_READY; 639 break; 640 641 // Supported DMA commands 642 case WDCC_WRITEDMA: 643 dmaRead = true; // a write to the disk is a DMA read from memory 644 case WDCC_READDMA: 645 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 646 panic("Attempt to perform CHS access, only supports LBA\n"); 647 648 if (cmdReg.sec_count == 0) 649 cmdBytes = cmdBytesLeft = (256 * SectorSize); 650 else 651 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 652 653 curSector = getLBABase(); 654 655 devState = Prepare_Data_Dma; 656 action = ACT_DMA_READY; 657 break; 658 659 default: 660 panic("Unsupported ATA command: %#x\n", cmdReg.command); 661 } 662 663 if (action != ACT_NONE) { 664 // set the BSY bit 665 status |= STATUS_BSY_BIT; 666 // clear the DRQ bit 667 status &= ~STATUS_DRQ_BIT; 668 // clear the DF bit 669 status &= ~STATUS_DF_BIT; 670 671 updateState(action); 672 } 673} 674 675//// 676// Handle setting and clearing interrupts 677//// 678 679void 680IdeDisk::intrPost() 681{ 682 DPRINTF(IdeDisk, "Posting Interrupt\n"); 683 if (intrPending) 684 panic("Attempt to post an interrupt with one pending\n"); 685 686 intrPending = true; 687 688 // talk to controller to set interrupt 689 if (ctrl) { 690 ctrl->bmi_regs.bmis0 |= IDEINTS; 691 ctrl->intrPost(); 692 } 693} 694 695void 696IdeDisk::intrClear() 697{ 698 DPRINTF(IdeDisk, "Clearing Interrupt\n"); 699 if (!intrPending) 700 panic("Attempt to clear a non-pending interrupt\n"); 701 702 intrPending = false; 703 704 // talk to controller to clear interrupt 705 if (ctrl) 706 ctrl->intrClear(); 707} 708 709//// 710// Manage the device internal state machine 711//// 712 713void 714IdeDisk::updateState(DevAction_t action) 715{ 716 switch (devState) { 717 case Device_Srst: 718 if (action == ACT_SRST_SET) { 719 // set the BSY bit 720 status |= STATUS_BSY_BIT; 721 } else if (action == ACT_SRST_CLEAR) { 722 // clear the BSY bit 723 status &= ~STATUS_BSY_BIT; 724 725 // reset the device state 726 reset(devID); 727 } 728 break; 729 730 case Device_Idle_S: 731 if (action == ACT_SELECT_WRITE && !isDEVSelect()) { 732 devState = Device_Idle_NS; 733 } else if (action == ACT_CMD_WRITE) { 734 startCommand(); 735 } 736 737 break; 738 739 case Device_Idle_SI: 740 if (action == ACT_SELECT_WRITE && !isDEVSelect()) { 741 devState = Device_Idle_NS; 742 intrClear(); 743 } else if (action == ACT_STAT_READ || isIENSet()) { 744 devState = Device_Idle_S; 745 intrClear(); 746 } else if (action == ACT_CMD_WRITE) { 747 intrClear(); 748 startCommand(); 749 } 750 751 break; 752 753 case Device_Idle_NS: 754 if (action == ACT_SELECT_WRITE && isDEVSelect()) { 755 if (!isIENSet() && intrPending) { 756 devState = Device_Idle_SI; 757 intrPost(); 758 } 759 if (isIENSet() || !intrPending) { 760 devState = Device_Idle_S; 761 } 762 } 763 break; 764 765 case Command_Execution: 766 if (action == ACT_CMD_COMPLETE) { 767 // clear the BSY bit 768 setComplete(); 769 770 if (!isIENSet()) { 771 devState = Device_Idle_SI; 772 intrPost(); 773 } else { 774 devState = Device_Idle_S; 775 } 776 } 777 break; 778 779 case Prepare_Data_In: 780 if (action == ACT_CMD_ERROR) { 781 // clear the BSY bit 782 setComplete(); 783 784 if (!isIENSet()) { 785 devState = Device_Idle_SI; 786 intrPost(); 787 } else { 788 devState = Device_Idle_S; 789 } 790 } else if (action == ACT_DATA_READY) { 791 // clear the BSY bit 792 status &= ~STATUS_BSY_BIT; 793 // set the DRQ bit 794 status |= STATUS_DRQ_BIT; 795 796 // copy the data into the data buffer 797 if (cmdReg.command == WDCC_IDENTIFY) { 798 // Reset the drqBytes for this block 799 drqBytesLeft = sizeof(struct ataparams); 800 801 memcpy((void *)dataBuffer, (void *)&driveID, 802 sizeof(struct ataparams)); 803 } else { 804 // Reset the drqBytes for this block 805 drqBytesLeft = SectorSize; 806 807 readDisk(curSector++, dataBuffer); 808 } 809 810 // put the first two bytes into the data register 811 memcpy((void *)&cmdReg.data, (void *)dataBuffer, 812 sizeof(uint16_t)); 813 814 if (!isIENSet()) { 815 devState = Data_Ready_INTRQ_In; 816 intrPost(); 817 } else { 818 devState = Transfer_Data_In; 819 } 820 } 821 break; 822 823 case Data_Ready_INTRQ_In: 824 if (action == ACT_STAT_READ) { 825 devState = Transfer_Data_In; 826 intrClear(); 827 } 828 break; 829 830 case Transfer_Data_In: 831 if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) { 832 if (action == ACT_DATA_READ_BYTE) { 833 panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n"); 834 } else { 835 drqBytesLeft -= 2; 836 cmdBytesLeft -= 2; 837 838 // copy next short into data registers 839 if (drqBytesLeft) 840 memcpy((void *)&cmdReg.data, 841 (void *)&dataBuffer[SectorSize - drqBytesLeft], 842 sizeof(uint16_t)); 843 } 844 845 if (drqBytesLeft == 0) { 846 if (cmdBytesLeft == 0) { 847 // Clear the BSY bit 848 setComplete(); 849 devState = Device_Idle_S; 850 } else { 851 devState = Prepare_Data_In; 852 // set the BSY_BIT 853 status |= STATUS_BSY_BIT; 854 // clear the DRQ_BIT 855 status &= ~STATUS_DRQ_BIT; 856 857 /** @todo change this to a scheduled event to simulate 858 disk delay */ 859 updateState(ACT_DATA_READY); 860 } 861 } 862 } 863 break; 864 865 case Prepare_Data_Out: 866 if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) { 867 // clear the BSY bit 868 setComplete(); 869 870 if (!isIENSet()) { 871 devState = Device_Idle_SI; 872 intrPost(); 873 } else { 874 devState = Device_Idle_S; 875 } 876 } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) { 877 // clear the BSY bit 878 status &= ~STATUS_BSY_BIT; 879 // set the DRQ bit 880 status |= STATUS_DRQ_BIT; 881 882 // clear the data buffer to get it ready for writes 883 memset(dataBuffer, 0, MAX_DMA_SIZE); 884 885 // reset the drqBytes for this block 886 drqBytesLeft = SectorSize; 887 888 if (cmdBytesLeft == cmdBytes || isIENSet()) { 889 devState = Transfer_Data_Out; 890 } else { 891 devState = Data_Ready_INTRQ_Out; 892 intrPost(); 893 } 894 } 895 break; 896 897 case Data_Ready_INTRQ_Out: 898 if (action == ACT_STAT_READ) { 899 devState = Transfer_Data_Out; 900 intrClear(); 901 } 902 break; 903 904 case Transfer_Data_Out: 905 if (action == ACT_DATA_WRITE_BYTE || 906 action == ACT_DATA_WRITE_SHORT) { 907 908 if (action == ACT_DATA_READ_BYTE) { 909 panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n"); 910 } else { 911 // copy the latest short into the data buffer 912 memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft], 913 (void *)&cmdReg.data, 914 sizeof(uint16_t)); 915 916 drqBytesLeft -= 2; 917 cmdBytesLeft -= 2; 918 } 919 920 if (drqBytesLeft == 0) { 921 // copy the block to the disk 922 writeDisk(curSector++, dataBuffer); 923 924 // set the BSY bit 925 status |= STATUS_BSY_BIT; 926 // set the seek bit 927 status |= STATUS_SEEK_BIT; 928 // clear the DRQ bit 929 status &= ~STATUS_DRQ_BIT; 930 931 devState = Prepare_Data_Out; 932 933 /** @todo change this to a scheduled event to simulate 934 disk delay */ 935 updateState(ACT_DATA_READY); 936 } 937 } 938 break; 939 940 case Prepare_Data_Dma: 941 if (action == ACT_CMD_ERROR) { 942 // clear the BSY bit 943 setComplete(); 944 945 if (!isIENSet()) { 946 devState = Device_Idle_SI; 947 intrPost(); 948 } else { 949 devState = Device_Idle_S; 950 } 951 } else if (action == ACT_DMA_READY) { 952 // clear the BSY bit 953 status &= ~STATUS_BSY_BIT; 954 // set the DRQ bit 955 status |= STATUS_DRQ_BIT; 956 957 devState = Transfer_Data_Dma; 958 959 if (dmaState != Dma_Idle) 960 panic("Inconsistent DMA state, should be Dma_Idle\n"); 961 962 dmaState = Dma_Start; 963 // wait for the write to the DMA start bit 964 } 965 break; 966 967 case Transfer_Data_Dma: 968 if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) { 969 // clear the BSY bit 970 setComplete(); 971 // set the seek bit 972 status |= STATUS_SEEK_BIT; 973 // clear the controller state for DMA transfer 974 ctrl->setDmaComplete(this); 975 976 if (!isIENSet()) { 977 devState = Device_Idle_SI; 978 intrPost(); 979 } else { 980 devState = Device_Idle_S; 981 } 982 } 983 break; 984 985 default: 986 panic("Unknown IDE device state: %#x\n", devState); 987 } 988} 989 990void 991IdeDisk::serialize(ostream &os) 992{ 993 // Check all outstanding events to see if they are scheduled 994 // these are all mutually exclusive 995 Tick reschedule = 0; 996 Events_t event = None; 997 998 int eventCount = 0; 999 1000 if (dmaTransferEvent.scheduled()) { 1001 reschedule = dmaTransferEvent.when(); 1002 event = Transfer; 1003 eventCount++; 1004 } 1005 if (dmaReadWaitEvent.scheduled()) { 1006 reschedule = dmaReadWaitEvent.when(); 1007 event = ReadWait; 1008 eventCount++; 1009 } 1010 if (dmaWriteWaitEvent.scheduled()) { 1011 reschedule = dmaWriteWaitEvent.when(); 1012 event = WriteWait; 1013 eventCount++; 1014 } 1015 if (dmaPrdReadEvent.scheduled()) { 1016 reschedule = dmaPrdReadEvent.when(); 1017 event = PrdRead; 1018 eventCount++; 1019 } 1020 if (dmaReadEvent.scheduled()) { 1021 reschedule = dmaReadEvent.when(); 1022 event = DmaRead; 1023 eventCount++; 1024 } 1025 if (dmaWriteEvent.scheduled()) { 1026 reschedule = dmaWriteEvent.when(); 1027 event = DmaWrite; 1028 eventCount++; 1029 } 1030 1031 assert(eventCount <= 1); 1032 1033 SERIALIZE_SCALAR(reschedule); 1034 SERIALIZE_ENUM(event); 1035 1036 // Serialize device registers 1037 SERIALIZE_SCALAR(cmdReg.data); 1038 SERIALIZE_SCALAR(cmdReg.sec_count); 1039 SERIALIZE_SCALAR(cmdReg.sec_num); 1040 SERIALIZE_SCALAR(cmdReg.cyl_low); 1041 SERIALIZE_SCALAR(cmdReg.cyl_high); 1042 SERIALIZE_SCALAR(cmdReg.drive); 1043 SERIALIZE_SCALAR(cmdReg.command); 1044 SERIALIZE_SCALAR(status); 1045 SERIALIZE_SCALAR(nIENBit); 1046 SERIALIZE_SCALAR(devID); 1047 1048 // Serialize the PRD related information 1049 SERIALIZE_SCALAR(curPrd.entry.baseAddr); 1050 SERIALIZE_SCALAR(curPrd.entry.byteCount); 1051 SERIALIZE_SCALAR(curPrd.entry.endOfTable); 1052 SERIALIZE_SCALAR(curPrdAddr); 1053 1054 /** @todo need to serialized chunk generator stuff!! */ 1055 // Serialize current transfer related information 1056 SERIALIZE_SCALAR(cmdBytesLeft); 1057 SERIALIZE_SCALAR(cmdBytes); 1058 SERIALIZE_SCALAR(drqBytesLeft); 1059 SERIALIZE_SCALAR(curSector); 1060 SERIALIZE_SCALAR(dmaRead); 1061 SERIALIZE_SCALAR(intrPending); 1062 SERIALIZE_ENUM(devState); 1063 SERIALIZE_ENUM(dmaState); 1064 SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); 1065} 1066 1067void 1068IdeDisk::unserialize(Checkpoint *cp, const string §ion) 1069{ 1070 // Reschedule events that were outstanding 1071 // these are all mutually exclusive 1072 Tick reschedule = 0; 1073 Events_t event = None; 1074 1075 UNSERIALIZE_SCALAR(reschedule); 1076 UNSERIALIZE_ENUM(event); 1077 1078 switch (event) { 1079 case None : break; 1080 case Transfer : dmaTransferEvent.schedule(reschedule); break; 1081 case ReadWait : dmaReadWaitEvent.schedule(reschedule); break; 1082 case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break; 1083 case PrdRead : dmaPrdReadEvent.schedule(reschedule); break; 1084 case DmaRead : dmaReadEvent.schedule(reschedule); break; 1085 case DmaWrite : dmaWriteEvent.schedule(reschedule); break; 1086 } 1087 1088 // Unserialize device registers 1089 UNSERIALIZE_SCALAR(cmdReg.data); 1090 UNSERIALIZE_SCALAR(cmdReg.sec_count); 1091 UNSERIALIZE_SCALAR(cmdReg.sec_num); 1092 UNSERIALIZE_SCALAR(cmdReg.cyl_low); 1093 UNSERIALIZE_SCALAR(cmdReg.cyl_high); 1094 UNSERIALIZE_SCALAR(cmdReg.drive); 1095 UNSERIALIZE_SCALAR(cmdReg.command); 1096 UNSERIALIZE_SCALAR(status); 1097 UNSERIALIZE_SCALAR(nIENBit); 1098 UNSERIALIZE_SCALAR(devID); 1099 1100 // Unserialize the PRD related information 1101 UNSERIALIZE_SCALAR(curPrd.entry.baseAddr); 1102 UNSERIALIZE_SCALAR(curPrd.entry.byteCount); 1103 UNSERIALIZE_SCALAR(curPrd.entry.endOfTable); 1104 UNSERIALIZE_SCALAR(curPrdAddr); 1105 1106 /** @todo need to serialized chunk generator stuff!! */ 1107 // Unserialize current transfer related information 1108 UNSERIALIZE_SCALAR(cmdBytes); 1109 UNSERIALIZE_SCALAR(cmdBytesLeft); 1110 UNSERIALIZE_SCALAR(drqBytesLeft); 1111 UNSERIALIZE_SCALAR(curSector); 1112 UNSERIALIZE_SCALAR(dmaRead); 1113 UNSERIALIZE_SCALAR(intrPending); 1114 UNSERIALIZE_ENUM(devState); 1115 UNSERIALIZE_ENUM(dmaState); 1116 UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); 1117} 1118 1119#ifndef DOXYGEN_SHOULD_SKIP_THIS 1120 1121enum DriveID { master, slave }; 1122static const char *DriveID_strings[] = { "master", "slave" }; 1123BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) 1124 1125 SimObjectParam<DiskImage *> image; 1126 SimpleEnumParam<DriveID> driveID; 1127 Param<int> delay; 1128 1129END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) 1130 1131BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk) 1132 1133 INIT_PARAM(image, "Disk image"), 1134 INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings), 1135 INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1) 1136 1137END_INIT_SIM_OBJECT_PARAMS(IdeDisk) 1138 1139 1140CREATE_SIM_OBJECT(IdeDisk) 1141{ 1142 return new IdeDisk(getInstanceName(), image, driveID, delay); 1143} 1144 1145REGISTER_SIM_OBJECT("IdeDisk", IdeDisk) 1146 1147#endif //DOXYGEN_SHOULD_SKIP_THIS 1148