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