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