ide_disk.cc revision 929
12SN/A/* 21762SN/A * Copyright (c) 2004 The Regents of The University of Michigan 32SN/A * All rights reserved. 42SN/A * 52SN/A * Redistribution and use in source and binary forms, with or without 62SN/A * modification, are permitted provided that the following conditions are 72SN/A * met: redistributions of source code must retain the above copyright 82SN/A * notice, this list of conditions and the following disclaimer; 92SN/A * redistributions in binary form must reproduce the above copyright 102SN/A * notice, this list of conditions and the following disclaimer in the 112SN/A * documentation and/or other materials provided with the distribution; 122SN/A * neither the name of the copyright holders nor the names of its 132SN/A * contributors may be used to endorse or promote products derived from 142SN/A * this software without specific prior written permission. 152SN/A * 162SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 172SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 182SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 192SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 202SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 222SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 232SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 242SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 252SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 262SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 272665Ssaidi@eecs.umich.edu */ 282665Ssaidi@eecs.umich.edu 292665Ssaidi@eecs.umich.edu/** @file 302665Ssaidi@eecs.umich.edu * Device model implementation for an IDE disk 312SN/A */ 322SN/A 332SN/A#include <cerrno> 342SN/A#include <cstring> 352SN/A#include <deque> 362SN/A#include <string> 3775SN/A 382SN/A#include "arch/alpha/pmap.h" 392439SN/A#include "base/cprintf.hh" // csprintf 402439SN/A#include "base/trace.hh" 41603SN/A#include "dev/disk_image.hh" 42603SN/A#include "dev/ide_disk.hh" 432520SN/A#include "dev/ide_ctrl.hh" 442378SN/A#include "dev/tsunami.hh" 452378SN/A#include "dev/tsunami_pchip.hh" 46722SN/A#include "mem/functional_mem/physical_memory.hh" 472521SN/A#include "mem/bus/bus.hh" 482378SN/A#include "mem/bus/dma_interface.hh" 49312SN/A#include "mem/bus/pio_interface.hh" 501634SN/A#include "mem/bus/pio_interface_impl.hh" 512680Sktlim@umich.edu#include "sim/builder.hh" 521634SN/A#include "sim/sim_object.hh" 532521SN/A#include "sim/universe.hh" 542378SN/A 552378SN/Ausing namespace std; 56803SN/A 572378SN/AIdeDisk::IdeDisk(const string &name, DiskImage *img, PhysicalMemory *phys, 582SN/A int id, int delay) 592378SN/A : SimObject(name), ctrl(NULL), image(img), physmem(phys), 602SN/A dmaTransferEvent(this), dmaReadWaitEvent(this), 612SN/A dmaWriteWaitEvent(this), dmaPrdReadEvent(this), 622SN/A dmaReadEvent(this), dmaWriteEvent(this) 63603SN/A{ 642901Ssaidi@eecs.umich.edu // Reset the device state 652901Ssaidi@eecs.umich.edu reset(id); 662901Ssaidi@eecs.umich.edu 672901Ssaidi@eecs.umich.edu // calculate disk delay in microseconds 682901Ssaidi@eecs.umich.edu diskDelay = (delay * ticksPerSecond / 100000); 692901Ssaidi@eecs.umich.edu 702902Ssaidi@eecs.umich.edu // fill out the drive ID structure 712902Ssaidi@eecs.umich.edu memset(&driveID, 0, sizeof(struct hd_driveid)); 722901Ssaidi@eecs.umich.edu 732901Ssaidi@eecs.umich.edu // Calculate LBA and C/H/S values 742901Ssaidi@eecs.umich.edu uint16_t cylinders; 752901Ssaidi@eecs.umich.edu uint8_t heads; 762901Ssaidi@eecs.umich.edu uint8_t sectors; 772901Ssaidi@eecs.umich.edu 782901Ssaidi@eecs.umich.edu uint32_t lba_size = image->size(); 792901Ssaidi@eecs.umich.edu if (lba_size >= 16383*16*63) { 802901Ssaidi@eecs.umich.edu cylinders = 16383; 812521SN/A heads = 16; 822SN/A sectors = 63; 832SN/A } else { 842680Sktlim@umich.edu if (lba_size >= 63) 851806SN/A sectors = 63; 861806SN/A else 871806SN/A sectors = lba_size; 881806SN/A 892680Sktlim@umich.edu if ((lba_size / sectors) >= 16) 901806SN/A heads = 16; 911806SN/A else 921806SN/A heads = (lba_size / sectors); 931806SN/A 94180SN/A cylinders = lba_size / (heads * sectors); 952378SN/A } 962378SN/A 972378SN/A // Setup the model name 982378SN/A sprintf((char *)driveID.model, "5MI EDD si k"); 992520SN/A // Set the maximum multisector transfer size 1002520SN/A driveID.max_multsect = MAX_MULTSECT; 1012520SN/A // IORDY supported, IORDY disabled, LBA enabled, DMA enabled 1022521SN/A driveID.capability = 0x7; 1032520SN/A // UDMA support, EIDE support 1041885SN/A driveID.field_valid = 0x6; 1051070SN/A // Setup default C/H/S settings 106954SN/A driveID.cyls = cylinders; 1071070SN/A driveID.sectors = sectors; 1081070SN/A driveID.heads = heads; 1091070SN/A // Setup the current multisector transfer size 1101070SN/A driveID.multsect = MAX_MULTSECT; 1111070SN/A driveID.multsect_valid = 0x1; 1121070SN/A // Number of sectors on disk 1131070SN/A driveID.lba_capacity = lba_size; 1141070SN/A // Multiword DMA mode 2 and below supported 1151070SN/A driveID.dma_mword = 0x400; 1161070SN/A // Set PIO mode 4 and 3 supported 1171070SN/A driveID.eide_pio_modes = 0x3; 1181070SN/A // Set DMA mode 4 and below supported 1192378SN/A driveID.dma_ultra = 0x10; 1202378SN/A // Statically set hardware config word 1212378SN/A driveID.hw_config = 0x4001; 1222378SN/A} 1232378SN/A 1242378SN/AIdeDisk::~IdeDisk() 1252378SN/A{ 1261885SN/A // destroy the data buffer 1271885SN/A delete [] dataBuffer; 1282901Ssaidi@eecs.umich.edu} 1292901Ssaidi@eecs.umich.edu 1302424SN/Avoid 1311885SN/AIdeDisk::reset(int id) 1321885SN/A{ 1331885SN/A // initialize the data buffer and shadow registers 1341885SN/A dataBuffer = new uint8_t[MAX_DMA_SIZE]; 1351885SN/A 1362158SN/A memset(dataBuffer, 0, MAX_DMA_SIZE); 1371885SN/A memset(&cmdReg, 0, sizeof(CommandReg_t)); 1381885SN/A memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); 1391885SN/A 1401885SN/A dmaInterfaceBytes = 0; 1411885SN/A curPrdAddr = 0; 1421885SN/A curSector = 0; 1431885SN/A cmdBytes = 0; 1441885SN/A cmdBytesLeft = 0; 1451913SN/A drqBytesLeft = 0; 1461885SN/A dmaRead = false; 1471885SN/A intrPending = false; 1481885SN/A 1491885SN/A // set the device state to idle 1501885SN/A dmaState = Dma_Idle; 1511885SN/A 1521885SN/A if (id == DEV0) { 1531885SN/A devState = Device_Idle_S; 1541885SN/A devID = DEV0; 1551885SN/A } else if (id == DEV1) { 1561885SN/A devState = Device_Idle_NS; 1571885SN/A devID = DEV1; 1581885SN/A } else { 1591885SN/A panic("Invalid device ID: %#x\n", id); 1601885SN/A } 1611885SN/A 1622378SN/A // set the device ready bit 16377SN/A status = STATUS_DRDY_BIT; 1642378SN/A} 1651070SN/A 1661070SN/A//// 1672158SN/A// Utility functions 1682378SN/A//// 1691070SN/A 1701070SN/Abool 1711070SN/AIdeDisk::isDEVSelect() 1721070SN/A{ 1731070SN/A return ctrl->isDiskSelected(this); 1742521SN/A} 1752902Ssaidi@eecs.umich.edu 1762378SN/AAddr 1772378SN/AIdeDisk::pciToDma(Addr pciAddr) 1781634SN/A{ 1792567SN/A if (ctrl) 1801070SN/A return ctrl->tsunami->pchip->translatePciToDma(pciAddr); 1811070SN/A else 1821070SN/A panic("Access to unset controller!\n"); 1832158SN/A} 1842378SN/A 1852158SN/Auint32_t 1861070SN/AIdeDisk::bytesInDmaPage(Addr curAddr, uint32_t bytesLeft) 1872158SN/A{ 1882158SN/A uint32_t bytesInPage = 0; 1891070SN/A 1902158SN/A // First calculate how many bytes could be in the page 1911070SN/A if (bytesLeft > ALPHA_PGBYTES) 1922SN/A bytesInPage = ALPHA_PGBYTES; 1932SN/A else 1941129SN/A bytesInPage = bytesLeft; 1951129SN/A 1962158SN/A // Next, see if we have crossed a page boundary, and adjust 1972158SN/A Addr upperBound = curAddr + bytesInPage; 1981070SN/A Addr pageBound = alpha_trunc_page(curAddr) + ALPHA_PGBYTES; 1992378SN/A 2002378SN/A assert(upperBound >= curAddr && "DMA read wraps around address space!\n"); 2011070SN/A 2021070SN/A if (upperBound >= pageBound) 2031070SN/A bytesInPage = pageBound - curAddr; 2041070SN/A 2051070SN/A return bytesInPage; 2061070SN/A} 2071070SN/A 2081070SN/A//// 2091070SN/A// Device registers read/write 2101070SN/A//// 2111070SN/A 2121070SN/Avoid 2131070SN/AIdeDisk::read(const Addr &offset, bool byte, bool cmdBlk, uint8_t *data) 2141070SN/A{ 2151070SN/A DevAction_t action = ACT_NONE; 2161070SN/A 2171070SN/A if (cmdBlk) { 2181070SN/A if (offset < 0 || offset > sizeof(CommandReg_t)) 2192378SN/A panic("Invalid disk command register offset: %#x\n", offset); 2202378SN/A 2212378SN/A if (!byte && offset != DATA_OFFSET) 2222378SN/A panic("Invalid 16-bit read, only allowed on data reg\n"); 2232378SN/A 2242378SN/A if (!byte) 2252680Sktlim@umich.edu *(uint16_t *)data = *(uint16_t *)&cmdReg.data0; 2262680Sktlim@umich.edu else 2271070SN/A *data = ((uint8_t *)&cmdReg)[offset]; 2281070SN/A 2291070SN/A // determine if an action needs to be taken on the state machine 2302SN/A if (offset == STATUS_OFFSET) { 23177SN/A action = ACT_STAT_READ; 2322SN/A *data = status; // status is in a shadow, explicity copy 2332SN/A } else if (offset == DATA_OFFSET) { 2342SN/A if (byte) 2352SN/A action = ACT_DATA_READ_BYTE; 2362SN/A else 2372SN/A action = ACT_DATA_READ_SHORT; 2382SN/A } 2392SN/A 2402SN/A } else { 2412SN/A if (offset != ALTSTAT_OFFSET) 2422158SN/A panic("Invalid disk control register offset: %#x\n", offset); 2432158SN/A 2442SN/A if (!byte) 2452SN/A panic("Invalid 16-bit read from control block\n"); 2462SN/A 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 // make sure the new curPrdAddr is properly translated from PCI to system 345 curPrdAddr = pciToDma(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