ide_disk.cc revision 896
1/* 2 * Copyright (c) 2003 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 "arch/alpha/pmap.h" 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/functional_mem/physical_memory.hh" 47#include "mem/bus/bus.hh" 48#include "mem/bus/dma_interface.hh" 49#include "mem/bus/pio_interface.hh" 50#include "mem/bus/pio_interface_impl.hh" 51#include "sim/builder.hh" 52#include "sim/sim_object.hh" 53#include "sim/universe.hh" 54 55using namespace std; 56 57IdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, 58 int id, int delay) 59 : SimObject(name), ctrl(NULL), image(img), physmem(phys), 60 dmaTransferEvent(this), dmaReadWaitEvent(this), 61 dmaWriteWaitEvent(this), dmaPrdReadEvent(this), 62 dmaReadEvent(this), dmaWriteEvent(this) 63{ 64 // Reset the device state 65 reset(id); 66 67 // calculate disk delay in microseconds 68 diskDelay = (delay * ticksPerSecond / 100000); 69 70 // fill out the drive ID structure 71 memset(&driveID, 0, sizeof(struct hd_driveid)); 72 73 // Calculate LBA and C/H/S values 74 uint16_t cylinders; 75 uint8_t heads; 76 uint8_t sectors; 77 78 uint32_t lba_size = image->size(); 79 if (lba_size >= 16383*16*63) { 80 cylinders = 16383; 81 heads = 16; 82 sectors = 63; 83 } else { 84 if (lba_size >= 63) 85 sectors = 63; 86 else 87 sectors = lba_size; 88 89 if ((lba_size / sectors) >= 16) 90 heads = 16; 91 else 92 heads = (lba_size / sectors); 93 94 cylinders = lba_size / (heads * sectors); 95 } 96 97 // Setup the model name 98 sprintf((char *)driveID.model, "5MI EDD si k"); 99 // Set the maximum multisector transfer size 100 driveID.max_multsect = MAX_MULTSECT; 101 // IORDY supported, IORDY disabled, LBA enabled, DMA enabled 102 driveID.capability = 0x7; 103 // UDMA support, EIDE support 104 driveID.field_valid = 0x6; 105 // Setup default C/H/S settings 106 driveID.cyls = cylinders; 107 driveID.sectors = sectors; 108 driveID.heads = heads; 109 // Setup the current multisector transfer size 110 driveID.multsect = MAX_MULTSECT; 111 driveID.multsect_valid = 0x1; 112 // Number of sectors on disk 113 driveID.lba_capacity = lba_size; 114 // Multiword DMA mode 2 and below supported 115 driveID.dma_mword = 0x400; 116 // Set PIO mode 4 and 3 supported 117 driveID.eide_pio_modes = 0x3; 118 // Set DMA mode 4 and below supported 119 driveID.dma_ultra = 0x10; 120 // Statically set hardware config word 121 driveID.hw_config = 0x4001; 122} 123 124IdeDisk::~IdeDisk() 125{ 126 // destroy the data buffer 127 delete [] dataBuffer; 128} 129 130void 131IdeDisk::reset(int id) 132{ 133 // initialize the data buffer and shadow registers 134 dataBuffer = new uint8_t[MAX_DMA_SIZE]; 135 136 memset(dataBuffer, 0, MAX_DMA_SIZE); 137 memset(&cmdReg, 0, sizeof(CommandReg_t)); 138 memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); 139 140 dmaInterfaceBytes = 0; 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 166//// 167// Utility functions 168//// 169 170Addr 171IdeDisk::pciToDma(Addr pciAddr) 172{ 173 if (ctrl) 174 return ctrl->tsunami->pchip->translatePciToDma(pciAddr); 175 else 176 panic("Access to unset controller!\n"); 177} 178 179uint32_t 180IdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft) 181{ 182 uint32_t bytesInPage = 0; 183 184 // First calculate how many bytes could be in the page 185 if (bytesLeft > ALPHA_PGBYTES) 186 bytesInPage = ALPHA_PGBYTES; 187 else 188 bytesInPage = bytesLeft; 189 190 // Next, see if we have crossed a page boundary, and adjust 191 Addr upperBound = curAddr + bytesInPage; 192 Addr pageBound = alpha_trunc_page(curAddr) + ALPHA_PGBYTES; 193 194 assert(upperBound >= curAddr && "DMA read wraps around address space!\n"); 195 196 if (upperBound >= pageBound) 197 bytesInPage = pageBound - curAddr; 198 199 return bytesInPage; 200} 201 202//// 203// Device registers read/write 204//// 205 206void 207IdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data) 208{ 209 DevAction_t action = ACT_NONE; 210 211 if (cmdBlk) { 212 if (offset < 0 || offset > sizeof(CommandReg_t)) 213 panic("Invalid disk command register offset: %#x\n", offset); 214 215 if (!byte && offset != DATA_OFFSET) 216 panic("Invalid 16-bit read, only allowed on data reg\n"); 217 218 if (!byte) 219 *(uint16_t *)data = *(uint16_t *)&cmdReg.data0; 220 else 221 *data = ((uint8_t *)&cmdReg)[offset]; 222 223 // determine if an action needs to be taken on the state machine 224 if (offset == STATUS_OFFSET) { 225 action = ACT_STAT_READ; 226 *data = status; // status is in a shadow, explicity copy 227 } else if (offset == DATA_OFFSET) { 228 if (byte) 229 action = ACT_DATA_READ_BYTE; 230 else 231 action = ACT_DATA_READ_SHORT; 232 } 233 234 } else { 235 if (offset != ALTSTAT_OFFSET) 236 panic("Invalid disk control register offset: %#x\n", offset); 237 238 if (!byte) 239 panic("Invalid 16-bit read from control block\n"); 240 241 *data = status; 242 } 243 244 if (action != ACT_NONE) 245 updateState(action); 246} 247 248void 249IdeDisk::write(const Addr &offset, bool byte, bool cmdBlk, const uint8_t *data) 250{ 251 DevAction_t action = ACT_NONE; 252 253 if (cmdBlk) { 254 if (offset < 0 || offset > sizeof(CommandReg_t)) 255 panic("Invalid disk command register offset: %#x\n", offset); 256 257 if (!byte && offset != DATA_OFFSET) 258 panic("Invalid 16-bit write, only allowed on data reg\n"); 259 260 if (!byte) 261 *((uint16_t *)&cmdReg.data0) = *(uint16_t *)data; 262 else 263 ((uint8_t *)&cmdReg)[offset] = *data; 264 265 // determine if an action needs to be taken on the state machine 266 if (offset == COMMAND_OFFSET) { 267 action = ACT_CMD_WRITE; 268 } else if (offset == DATA_OFFSET) { 269 if (byte) 270 action = ACT_DATA_WRITE_BYTE; 271 else 272 action = ACT_DATA_WRITE_SHORT; 273 } else if (offset == SELECT_OFFSET) { 274 action = ACT_SELECT_WRITE; 275 } 276 277 } else { 278 if (offset != CONTROL_OFFSET) 279 panic("Invalid disk control register offset: %#x\n", offset); 280 281 if (!byte) 282 panic("Invalid 16-bit write to control block\n"); 283 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 292 nIENBit = (*data & CONTROL_IEN_BIT) ? true : false; 293 } 294 295 if (action != ACT_NONE) 296 updateState(action); 297} 298 299//// 300// Perform DMA transactions 301//// 302 303void 304IdeDisk::doDmaTransfer() 305{ 306 if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) 307 panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n", 308 dmaState, devState); 309 310 // first read the current PRD 311 if (dmaInterface) { 312 if (dmaInterface->busy()) { 313 // reschedule after waiting period 314 dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 315 return; 316 } 317 318 dmaInterface->doDMA(Read, curPrdAddr, sizeof(PrdEntry_t), curTick, 319 &dmaPrdReadEvent); 320 } else { 321 dmaPrdReadDone(); 322 } 323} 324 325void 326IdeDisk::dmaPrdReadDone() 327{ 328 // actually copy the PRD from physical memory 329 memcpy((void *)&curPrd.entry, 330 physmem->dma_addr(curPrdAddr, sizeof(PrdEntry_t)), 331 sizeof(PrdEntry_t)); 332 333 DPRINTF(IdeDisk, "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 // make sure the new curPrdAddr is properly translated from PCI to system 339 curPrdAddr = pciToDma(curPrdAddr + sizeof(PrdEntry_t)); 340 341 if (dmaRead) 342 doDmaRead(); 343 else 344 doDmaWrite(); 345} 346 347void 348IdeDisk::doDmaRead() 349{ 350 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 351 352 if (dmaInterface) { 353 if (dmaInterface->busy()) { 354 // reschedule after waiting period 355 dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 356 return; 357 } 358 359 Addr dmaAddr = pciToDma(curPrd.getBaseAddr()); 360 361 uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(), 362 (uint32_t)curPrd.getByteCount()); 363 364 dmaInterfaceBytes = bytesInPage; 365 366 dmaInterface->doDMA(Read, dmaAddr, bytesInPage, 367 curTick + totalDiskDelay, &dmaReadEvent); 368 } else { 369 // schedule dmaReadEvent with sectorDelay (dmaReadDone) 370 dmaReadEvent.schedule(curTick + totalDiskDelay); 371 } 372} 373 374void 375IdeDisk::dmaReadDone() 376{ 377 378 Addr curAddr = 0, dmaAddr = 0; 379 uint32_t bytesWritten = 0, bytesInPage = 0, bytesLeft = 0; 380 381 // continue to use the DMA interface until all pages are read 382 if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) { 383 // see if the interface is busy 384 if (dmaInterface->busy()) { 385 // reschedule after waiting period 386 dmaReadEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 387 return; 388 } 389 390 uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes; 391 curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes; 392 dmaAddr = pciToDma(curAddr); 393 394 bytesInPage = bytesInDmaPage(curAddr, bytesLeft); 395 dmaInterfaceBytes += bytesInPage; 396 397 dmaInterface->doDMA(Read, dmaAddr, bytesInPage, 398 curTick, &dmaReadEvent); 399 400 return; 401 } 402 403 // set initial address 404 curAddr = curPrd.getBaseAddr(); 405 406 // clear out the data buffer 407 memset(dataBuffer, 0, MAX_DMA_SIZE); 408 409 // read the data from memory via DMA into a data buffer 410 while (bytesWritten < curPrd.getByteCount()) { 411 if (cmdBytesLeft <= 0) 412 panic("DMA data is larger than # of sectors specified\n"); 413 414 dmaAddr = pciToDma(curAddr); 415 416 // calculate how many bytes are in the current page 417 bytesLeft = curPrd.getByteCount() - bytesWritten; 418 bytesInPage = bytesInDmaPage(curAddr, bytesLeft); 419 420 // copy the data from memory into the data buffer 421 memcpy((void *)(dataBuffer + bytesWritten), 422 physmem->dma_addr(dmaAddr, bytesInPage), 423 bytesInPage); 424 425 curAddr += bytesInPage; 426 bytesWritten += bytesInPage; 427 cmdBytesLeft -= bytesInPage; 428 } 429 430 // write the data to the disk image 431 for (bytesWritten = 0; 432 bytesWritten < curPrd.getByteCount(); 433 bytesWritten += SectorSize) { 434 435 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); 436 } 437 438#if 0 439 // actually copy the data from memory to data buffer 440 Addr dmaAddr = 441 ctrl->tsunami->pchip->translatePciToDma(curPrd.getBaseAddr()); 442 memcpy((void *)dataBuffer, 443 physmem->dma_addr(dmaAddr, curPrd.getByteCount()), 444 curPrd.getByteCount()); 445 446 uint32_t bytesWritten = 0; 447 448 while (bytesWritten < curPrd.getByteCount()) { 449 if (cmdBytesLeft <= 0) 450 panic("DMA data is larger than # sectors specified\n"); 451 452 writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); 453 454 bytesWritten += SectorSize; 455 cmdBytesLeft -= SectorSize; 456 } 457#endif 458 459 // check for the EOT 460 if (curPrd.getEOT()){ 461 assert(cmdBytesLeft == 0); 462 dmaState = Dma_Idle; 463 updateState(ACT_DMA_DONE); 464 } else { 465 doDmaTransfer(); 466 } 467} 468 469void 470IdeDisk::doDmaWrite() 471{ 472 Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); 473 474 if (dmaInterface) { 475 if (dmaInterface->busy()) { 476 // reschedule after waiting period 477 dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 478 return; 479 } 480 481 Addr dmaAddr = pciToDma(curPrd.getBaseAddr()); 482 483 uint32_t bytesInPage = bytesInDmaPage(curPrd.getBaseAddr(), 484 (uint32_t)curPrd.getByteCount()); 485 486 dmaInterfaceBytes = bytesInPage; 487 488 dmaInterface->doDMA(WriteInvalidate, dmaAddr, 489 bytesInPage, curTick + totalDiskDelay, 490 &dmaWriteEvent); 491 } else { 492 // schedule event with disk delay (dmaWriteDone) 493 dmaWriteEvent.schedule(curTick + totalDiskDelay); 494 } 495} 496 497void 498IdeDisk::dmaWriteDone() 499{ 500 Addr curAddr = 0, pageAddr = 0, dmaAddr = 0; 501 uint32_t bytesRead = 0, bytesInPage = 0; 502 503 // continue to use the DMA interface until all pages are read 504 if (dmaInterface && (dmaInterfaceBytes < curPrd.getByteCount())) { 505 // see if the interface is busy 506 if (dmaInterface->busy()) { 507 // reschedule after waiting period 508 dmaWriteEvent.schedule(curTick + DMA_BACKOFF_PERIOD); 509 return; 510 } 511 512 uint32_t bytesLeft = curPrd.getByteCount() - dmaInterfaceBytes; 513 curAddr = curPrd.getBaseAddr() + dmaInterfaceBytes; 514 dmaAddr = pciToDma(curAddr); 515 516 bytesInPage = bytesInDmaPage(curAddr, bytesLeft); 517 dmaInterfaceBytes += bytesInPage; 518 519 dmaInterface->doDMA(WriteInvalidate, dmaAddr, 520 bytesInPage, curTick, 521 &dmaWriteEvent); 522 523 return; 524 } 525 526 // setup the initial page and DMA address 527 curAddr = curPrd.getBaseAddr(); 528 pageAddr = alpha_trunc_page(curAddr); 529 dmaAddr = pciToDma(curAddr); 530 531 // clear out the data buffer 532 memset(dataBuffer, 0, MAX_DMA_SIZE); 533 534 while (bytesRead < curPrd.getByteCount()) { 535 // see if we have crossed into a new page 536 if (pageAddr != alpha_trunc_page(curAddr)) { 537 // write the data to memory 538 memcpy(physmem->dma_addr(dmaAddr, bytesInPage), 539 (void *)(dataBuffer + (bytesRead - bytesInPage)), 540 bytesInPage); 541 542 // update the DMA address and page address 543 pageAddr = alpha_trunc_page(curAddr); 544 dmaAddr = pciToDma(curAddr); 545 546 bytesInPage = 0; 547 } 548 549 if (cmdBytesLeft <= 0) 550 panic("DMA requested data is larger than # sectors specified\n"); 551 552 readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead)); 553 554 curAddr += SectorSize; 555 bytesRead += SectorSize; 556 bytesInPage += SectorSize; 557 cmdBytesLeft -= SectorSize; 558 } 559 560 // write the last page worth read to memory 561 if (bytesInPage != 0) { 562 memcpy(physmem->dma_addr(dmaAddr, bytesInPage), 563 (void *)(dataBuffer + (bytesRead - bytesInPage)), 564 bytesInPage); 565 } 566 567#if 0 568 Addr dmaAddr = ctrl->tsunami->pchip-> 569 translatePciToDma(curPrd.getBaseAddr()); 570 571 memcpy(physmem->dma_addr(dmaAddr, curPrd.getByteCount()), 572 (void *)dataBuffer, curPrd.getByteCount()); 573#endif 574 575 // check for the EOT 576 if (curPrd.getEOT()) { 577 assert(cmdBytesLeft == 0); 578 dmaState = Dma_Idle; 579 updateState(ACT_DMA_DONE); 580 } else { 581 doDmaTransfer(); 582 } 583} 584 585//// 586// Disk utility routines 587/// 588 589void 590IdeDisk::readDisk(uint32_t sector, uint8_t *data) 591{ 592 uint32_t bytesRead = image->read(data, sector); 593 594 if (bytesRead != SectorSize) 595 panic("Can't read from %s. Only %d of %d read. errno=%d\n", 596 name(), bytesRead, SectorSize, errno); 597} 598 599void 600IdeDisk::writeDisk(uint32_t sector, uint8_t *data) 601{ 602 uint32_t bytesWritten = image->write(data, sector); 603 604 if (bytesWritten != SectorSize) 605 panic("Can't write to %s. Only %d of %d written. errno=%d\n", 606 name(), bytesWritten, SectorSize, errno); 607} 608 609//// 610// Setup and handle commands 611//// 612 613void 614IdeDisk::startDma(const uint32_t &prdTableBase) 615{ 616 if (dmaState != Dma_Start) 617 panic("Inconsistent DMA state, should be in Dma_Start!\n"); 618 619 if (devState != Transfer_Data_Dma) 620 panic("Inconsistent device state for DMA start!\n"); 621 622 // PRD base address is given by bits 31:2 623 curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3))); 624 625 dmaState = Dma_Transfer; 626 627 // schedule dma transfer (doDmaTransfer) 628 dmaTransferEvent.schedule(curTick + 1); 629} 630 631void 632IdeDisk::abortDma() 633{ 634 if (dmaState == Dma_Idle) 635 panic("Inconsistent DMA state, should be in Dma_Start or Dma_Transfer!\n"); 636 637 if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma) 638 panic("Inconsistent device state, should be in Transfer or Prepare!\n"); 639 640 updateState(ACT_CMD_ERROR); 641} 642 643void 644IdeDisk::startCommand() 645{ 646 DevAction_t action = ACT_NONE; 647 uint32_t size = 0; 648 dmaRead = false; 649 650 // Decode commands 651 switch (cmdReg.command) { 652 // Supported non-data commands 653 case WIN_READ_NATIVE_MAX: 654 size = image->size() - 1; 655 cmdReg.sec_num = (size & 0xff); 656 cmdReg.cyl_low = ((size & 0xff00) >> 8); 657 cmdReg.cyl_high = ((size & 0xff0000) >> 16); 658 cmdReg.head = ((size & 0xf000000) >> 24); 659 660 devState = Command_Execution; 661 action = ACT_CMD_COMPLETE; 662 break; 663 664 case WIN_RECAL: 665 case WIN_SPECIFY: 666 case WIN_STANDBYNOW1: 667 case WIN_FLUSH_CACHE: 668 case WIN_VERIFY: 669 case WIN_SEEK: 670 case WIN_SETFEATURES: 671 case WIN_SETMULT: 672 devState = Command_Execution; 673 action = ACT_CMD_COMPLETE; 674 break; 675 676 // Supported PIO data-in commands 677 case WIN_IDENTIFY: 678 cmdBytes = cmdBytesLeft = sizeof(struct hd_driveid); 679 devState = Prepare_Data_In; 680 action = ACT_DATA_READY; 681 break; 682 683 case WIN_MULTREAD: 684 case WIN_READ: 685 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 686 panic("Attempt to perform CHS access, only supports LBA\n"); 687 688 if (cmdReg.sec_count == 0) 689 cmdBytes = cmdBytesLeft = (256 * SectorSize); 690 else 691 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 692 693 curSector = getLBABase(); 694 695 /** @todo make this a scheduled event to simulate disk delay */ 696 devState = Prepare_Data_In; 697 action = ACT_DATA_READY; 698 break; 699 700 // Supported PIO data-out commands 701 case WIN_MULTWRITE: 702 case WIN_WRITE: 703 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 704 panic("Attempt to perform CHS access, only supports LBA\n"); 705 706 if (cmdReg.sec_count == 0) 707 cmdBytes = cmdBytesLeft = (256 * SectorSize); 708 else 709 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 710 711 curSector = getLBABase(); 712 713 devState = Prepare_Data_Out; 714 action = ACT_DATA_READY; 715 break; 716 717 // Supported DMA commands 718 case WIN_WRITEDMA: 719 dmaRead = true; // a write to the disk is a DMA read from memory 720 case WIN_READDMA: 721 if (!(cmdReg.drive & DRIVE_LBA_BIT)) 722 panic("Attempt to perform CHS access, only supports LBA\n"); 723 724 if (cmdReg.sec_count == 0) 725 cmdBytes = cmdBytesLeft = (256 * SectorSize); 726 else 727 cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); 728 729 curSector = getLBABase(); 730 731 devState = Prepare_Data_Dma; 732 action = ACT_DMA_READY; 733 break; 734 735 default: 736 panic("Unsupported ATA command: %#x\n", cmdReg.command); 737 } 738 739 if (action != ACT_NONE) { 740 // set the BSY bit 741 status |= STATUS_BSY_BIT; 742 // clear the DRQ bit 743 status &= ~STATUS_DRQ_BIT; 744 // clear the DF bit 745 status &= ~STATUS_DF_BIT; 746 747 updateState(action); 748 } 749} 750 751//// 752// Handle setting and clearing interrupts 753//// 754 755void 756IdeDisk::intrPost() 757{ 758 if (intrPending) 759 panic("Attempt to post an interrupt with one pending\n"); 760 761 intrPending = true; 762 763 // talk to controller to set interrupt 764 if (ctrl) 765 ctrl->intrPost(); 766} 767 768void 769IdeDisk::intrClear() 770{ 771 if (!intrPending) 772 panic("Attempt to clear a non-pending interrupt\n"); 773 774 intrPending = false; 775 776 // talk to controller to clear interrupt 777 if (ctrl) 778 ctrl->intrClear(); 779} 780 781//// 782// Manage the device internal state machine 783//// 784 785void 786IdeDisk::updateState(DevAction_t action) 787{ 788 switch (devState) { 789 case Device_Srst: 790 if (action == ACT_SRST_SET) { 791 // set the BSY bit 792 status |= STATUS_BSY_BIT; 793 } else if (action == ACT_SRST_CLEAR) { 794 // clear the BSY bit 795 status &= ~STATUS_BSY_BIT; 796 797 // reset the device state 798 reset(devID); 799 } 800 break; 801 802 case Device_Idle_S: 803 if (action == ACT_SELECT_WRITE && !isDEVSelect()) { 804 devState = Device_Idle_NS; 805 } else if (action == ACT_CMD_WRITE) { 806 startCommand(); 807 } 808 809 break; 810 811 case Device_Idle_SI: 812 if (action == ACT_SELECT_WRITE && !isDEVSelect()) { 813 devState = Device_Idle_NS; 814 intrClear(); 815 } else if (action == ACT_STAT_READ || isIENSet()) { 816 devState = Device_Idle_S; 817 intrClear(); 818 } else if (action == ACT_CMD_WRITE) { 819 intrClear(); 820 startCommand(); 821 } 822 823 break; 824 825 case Device_Idle_NS: 826 if (action == ACT_SELECT_WRITE && isDEVSelect()) { 827 if (!isIENSet() && intrPending) { 828 devState = Device_Idle_SI; 829 intrPost(); 830 } 831 if (isIENSet() || !intrPending) { 832 devState = Device_Idle_S; 833 } 834 } 835 break; 836 837 case Command_Execution: 838 if (action == ACT_CMD_COMPLETE) { 839 // clear the BSY bit 840 setComplete(); 841 842 if (!isIENSet()) { 843 devState = Device_Idle_SI; 844 intrPost(); 845 } else { 846 devState = Device_Idle_S; 847 } 848 } 849 break; 850 851 case Prepare_Data_In: 852 if (action == ACT_CMD_ERROR) { 853 // clear the BSY bit 854 setComplete(); 855 856 if (!isIENSet()) { 857 devState = Device_Idle_SI; 858 intrPost(); 859 } else { 860 devState = Device_Idle_S; 861 } 862 } else if (action == ACT_DATA_READY) { 863 // clear the BSY bit 864 status &= ~STATUS_BSY_BIT; 865 // set the DRQ bit 866 status |= STATUS_DRQ_BIT; 867 868 // copy the data into the data buffer 869 if (cmdReg.command == WIN_IDENTIFY) { 870 // Reset the drqBytes for this block 871 drqBytesLeft = sizeof(struct hd_driveid); 872 873 memcpy((void *)dataBuffer, (void *)&driveID, 874 sizeof(struct hd_driveid)); 875 } else { 876 // Reset the drqBytes for this block 877 drqBytesLeft = SectorSize; 878 879 readDisk(curSector++, dataBuffer); 880 } 881 882 // put the first two bytes into the data register 883 memcpy((void *)&cmdReg.data0, (void *)dataBuffer, 884 sizeof(uint16_t)); 885 886 if (!isIENSet()) { 887 devState = Data_Ready_INTRQ_In; 888 intrPost(); 889 } else { 890 devState = Transfer_Data_In; 891 } 892 } 893 break; 894 895 case Data_Ready_INTRQ_In: 896 if (action == ACT_STAT_READ) { 897 devState = Transfer_Data_In; 898 intrClear(); 899 } 900 break; 901 902 case Transfer_Data_In: 903 if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) { 904 if (action == ACT_DATA_READ_BYTE) { 905 panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n"); 906 } else { 907 drqBytesLeft -= 2; 908 cmdBytesLeft -= 2; 909 910 // copy next short into data registers 911 if (drqBytesLeft) 912 memcpy((void *)&cmdReg.data0, 913 (void *)&dataBuffer[SectorSize - drqBytesLeft], 914 sizeof(uint16_t)); 915 } 916 917 if (drqBytesLeft == 0) { 918 if (cmdBytesLeft == 0) { 919 // Clear the BSY bit 920 setComplete(); 921 devState = Device_Idle_S; 922 } else { 923 devState = Prepare_Data_In; 924 // set the BSY_BIT 925 status |= STATUS_BSY_BIT; 926 // clear the DRQ_BIT 927 status &= ~STATUS_DRQ_BIT; 928 929 /** @todo change this to a scheduled event to simulate 930 disk delay */ 931 updateState(ACT_DATA_READY); 932 } 933 } 934 } 935 break; 936 937 case Prepare_Data_Out: 938 if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) { 939 // clear the BSY bit 940 setComplete(); 941 942 if (!isIENSet()) { 943 devState = Device_Idle_SI; 944 intrPost(); 945 } else { 946 devState = Device_Idle_S; 947 } 948 } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) { 949 // clear the BSY bit 950 status &= ~STATUS_BSY_BIT; 951 // set the DRQ bit 952 status |= STATUS_DRQ_BIT; 953 954 // clear the data buffer to get it ready for writes 955 memset(dataBuffer, 0, MAX_DMA_SIZE); 956 957 // reset the drqBytes for this block 958 drqBytesLeft = SectorSize; 959 960 if (cmdBytesLeft == cmdBytes || isIENSet()) { 961 devState = Transfer_Data_Out; 962 } else { 963 devState = Data_Ready_INTRQ_Out; 964 intrPost(); 965 } 966 } 967 break; 968 969 case Data_Ready_INTRQ_Out: 970 if (action == ACT_STAT_READ) { 971 devState = Transfer_Data_Out; 972 intrClear(); 973 } 974 break; 975 976 case Transfer_Data_Out: 977 if (action == ACT_DATA_WRITE_BYTE || 978 action == ACT_DATA_WRITE_SHORT) { 979 980 if (action == ACT_DATA_READ_BYTE) { 981 panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n"); 982 } else { 983 // copy the latest short into the data buffer 984 memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft], 985 (void *)&cmdReg.data0, 986 sizeof(uint16_t)); 987 988 drqBytesLeft -= 2; 989 cmdBytesLeft -= 2; 990 } 991 992 if (drqBytesLeft == 0) { 993 // copy the block to the disk 994 writeDisk(curSector++, dataBuffer); 995 996 // set the BSY bit 997 status |= STATUS_BSY_BIT; 998 // set the seek bit 999 status |= STATUS_SEEK_BIT; 1000 // clear the DRQ bit 1001 status &= ~STATUS_DRQ_BIT; 1002 1003 devState = Prepare_Data_Out; 1004 1005 /** @todo change this to a scheduled event to simulate 1006 disk delay */ 1007 updateState(ACT_DATA_READY); 1008 } 1009 } 1010 break; 1011 1012 case Prepare_Data_Dma: 1013 if (action == ACT_CMD_ERROR) { 1014 // clear the BSY bit 1015 setComplete(); 1016 1017 if (!isIENSet()) { 1018 devState = Device_Idle_SI; 1019 intrPost(); 1020 } else { 1021 devState = Device_Idle_S; 1022 } 1023 } else if (action == ACT_DMA_READY) { 1024 // clear the BSY bit 1025 status &= ~STATUS_BSY_BIT; 1026 // set the DRQ bit 1027 status |= STATUS_DRQ_BIT; 1028 1029 devState = Transfer_Data_Dma; 1030 1031 if (dmaState != Dma_Idle) 1032 panic("Inconsistent DMA state, should be Dma_Idle\n"); 1033 1034 dmaState = Dma_Start; 1035 // wait for the write to the DMA start bit 1036 } 1037 break; 1038 1039 case Transfer_Data_Dma: 1040 if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) { 1041 // clear the BSY bit 1042 setComplete(); 1043 // set the seek bit 1044 status |= STATUS_SEEK_BIT; 1045 // clear the controller state for DMA transfer 1046 ctrl->setDmaComplete(this); 1047 1048 if (!isIENSet()) { 1049 devState = Device_Idle_SI; 1050 intrPost(); 1051 } else { 1052 devState = Device_Idle_S; 1053 } 1054 } 1055 break; 1056 1057 default: 1058 panic("Unknown IDE device state: %#x\n", devState); 1059 } 1060} 1061 1062void 1063IdeDisk::serialize(ostream &os) 1064{ 1065 // Check all outstanding events to see if they are scheduled 1066 // these are all mutually exclusive 1067 Tick reschedule = 0; 1068 Events_t event = None; 1069 1070 if (dmaTransferEvent.scheduled()) { 1071 reschedule = dmaTransferEvent.when(); 1072 event = Transfer; 1073 } else if (dmaReadWaitEvent.scheduled()) { 1074 reschedule = dmaReadWaitEvent.when(); 1075 event = ReadWait; 1076 } else if (dmaWriteWaitEvent.scheduled()) { 1077 reschedule = dmaWriteWaitEvent.when(); 1078 event = WriteWait; 1079 } else if (dmaPrdReadEvent.scheduled()) { 1080 reschedule = dmaPrdReadEvent.when(); 1081 event = PrdRead; 1082 } else if (dmaReadEvent.scheduled()) { 1083 reschedule = dmaReadEvent.when(); 1084 event = DmaRead; 1085 } else if (dmaWriteEvent.scheduled()) { 1086 reschedule = dmaWriteEvent.when(); 1087 event = DmaWrite; 1088 } 1089 1090 SERIALIZE_SCALAR(reschedule); 1091 SERIALIZE_ENUM(event); 1092 1093 // Serialize device registers 1094 SERIALIZE_SCALAR(cmdReg.data0); 1095 SERIALIZE_SCALAR(cmdReg.data1); 1096 SERIALIZE_SCALAR(cmdReg.sec_count); 1097 SERIALIZE_SCALAR(cmdReg.sec_num); 1098 SERIALIZE_SCALAR(cmdReg.cyl_low); 1099 SERIALIZE_SCALAR(cmdReg.cyl_high); 1100 SERIALIZE_SCALAR(cmdReg.drive); 1101 SERIALIZE_SCALAR(status); 1102 SERIALIZE_SCALAR(nIENBit); 1103 SERIALIZE_SCALAR(devID); 1104 1105 // Serialize the PRD related information 1106 SERIALIZE_SCALAR(curPrd.entry.baseAddr); 1107 SERIALIZE_SCALAR(curPrd.entry.byteCount); 1108 SERIALIZE_SCALAR(curPrd.entry.endOfTable); 1109 SERIALIZE_SCALAR(curPrdAddr); 1110 1111 // Serialize current transfer related information 1112 SERIALIZE_SCALAR(cmdBytesLeft); 1113 SERIALIZE_SCALAR(cmdBytes); 1114 SERIALIZE_SCALAR(drqBytesLeft); 1115 SERIALIZE_SCALAR(curSector); 1116 SERIALIZE_SCALAR(dmaRead); 1117 SERIALIZE_SCALAR(dmaInterfaceBytes); 1118 SERIALIZE_SCALAR(intrPending); 1119 SERIALIZE_ENUM(devState); 1120 SERIALIZE_ENUM(dmaState); 1121 SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); 1122} 1123 1124void 1125IdeDisk::unserialize(Checkpoint *cp, const string §ion) 1126{ 1127 // Reschedule events that were outstanding 1128 // these are all mutually exclusive 1129 Tick reschedule = 0; 1130 Events_t event = None; 1131 1132 UNSERIALIZE_SCALAR(reschedule); 1133 UNSERIALIZE_ENUM(event); 1134 1135 switch (event) { 1136 case None : break; 1137 case Transfer : dmaTransferEvent.schedule(reschedule); break; 1138 case ReadWait : dmaReadWaitEvent.schedule(reschedule); break; 1139 case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break; 1140 case PrdRead : dmaPrdReadEvent.schedule(reschedule); break; 1141 case DmaRead : dmaReadEvent.schedule(reschedule); break; 1142 case DmaWrite : dmaWriteEvent.schedule(reschedule); break; 1143 } 1144 1145 // Unserialize device registers 1146 UNSERIALIZE_SCALAR(cmdReg.data0); 1147 UNSERIALIZE_SCALAR(cmdReg.data1); 1148 UNSERIALIZE_SCALAR(cmdReg.sec_count); 1149 UNSERIALIZE_SCALAR(cmdReg.sec_num); 1150 UNSERIALIZE_SCALAR(cmdReg.cyl_low); 1151 UNSERIALIZE_SCALAR(cmdReg.cyl_high); 1152 UNSERIALIZE_SCALAR(cmdReg.drive); 1153 UNSERIALIZE_SCALAR(status); 1154 UNSERIALIZE_SCALAR(nIENBit); 1155 UNSERIALIZE_SCALAR(devID); 1156 1157 // Unserialize the PRD related information 1158 UNSERIALIZE_SCALAR(curPrd.entry.baseAddr); 1159 UNSERIALIZE_SCALAR(curPrd.entry.byteCount); 1160 UNSERIALIZE_SCALAR(curPrd.entry.endOfTable); 1161 UNSERIALIZE_SCALAR(curPrdAddr); 1162 1163 // Unserialize current transfer related information 1164 UNSERIALIZE_SCALAR(cmdBytes); 1165 UNSERIALIZE_SCALAR(cmdBytesLeft); 1166 UNSERIALIZE_SCALAR(drqBytesLeft); 1167 UNSERIALIZE_SCALAR(curSector); 1168 UNSERIALIZE_SCALAR(dmaRead); 1169 UNSERIALIZE_SCALAR(dmaInterfaceBytes); 1170 UNSERIALIZE_SCALAR(intrPending); 1171 UNSERIALIZE_ENUM(devState); 1172 UNSERIALIZE_ENUM(dmaState); 1173 UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); 1174} 1175 1176#ifndef DOXYGEN_SHOULD_SKIP_THIS 1177 1178BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) 1179 1180 SimObjectParam<DiskImage *> image; 1181 SimObjectParam<PhysicalMemory *> physmem; 1182 Param<int> driveID; 1183 Param<int> disk_delay; 1184 1185END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) 1186 1187BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk) 1188 1189 INIT_PARAM(image, "Disk image"), 1190 INIT_PARAM(physmem, "Physical memory"), 1191 INIT_PARAM(driveID, "Drive ID (0=master 1=slave)"), 1192 INIT_PARAM_DFLT(disk_delay, "Fixed disk delay in microseconds", 1) 1193 1194END_INIT_SIM_OBJECT_PARAMS(IdeDisk) 1195 1196 1197CREATE_SIM_OBJECT(IdeDisk) 1198{ 1199 return new IdeDisk(getInstanceName(), image, physmem, driveID, 1200 disk_delay); 1201} 1202 1203REGISTER_SIM_OBJECT("IdeDisk", IdeDisk) 1204 1205#endif //DOXYGEN_SHOULD_SKIP_THIS 1206