ide_ctrl.cc revision 9807
1/* 2 * Copyright (c) 2004-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Andrew Schultz 29 * Ali Saidi 30 * Miguel Serrano 31 */ 32 33#include <string> 34 35#include "cpu/intr_control.hh" 36#include "debug/IdeCtrl.hh" 37#include "dev/ide_ctrl.hh" 38#include "dev/ide_disk.hh" 39#include "mem/packet.hh" 40#include "mem/packet_access.hh" 41#include "params/IdeController.hh" 42#include "sim/byteswap.hh" 43 44// clang complains about std::set being overloaded with Packet::set if 45// we open up the entire namespace std 46using std::string; 47 48// Bus master IDE registers 49enum BMIRegOffset { 50 BMICommand = 0x0, 51 BMIStatus = 0x2, 52 BMIDescTablePtr = 0x4 53}; 54 55// PCI config space registers 56enum ConfRegOffset { 57 PrimaryTiming = 0x40, 58 SecondaryTiming = 0x42, 59 DeviceTiming = 0x44, 60 UDMAControl = 0x48, 61 UDMATiming = 0x4A, 62 IDEConfig = 0x54 63}; 64 65static const uint16_t timeRegWithDecodeEn = 0x8000; 66 67IdeController::Channel::Channel( 68 string newName, Addr _cmdSize, Addr _ctrlSize) : 69 _name(newName), 70 cmdAddr(0), cmdSize(_cmdSize), ctrlAddr(0), ctrlSize(_ctrlSize), 71 master(NULL), slave(NULL), selected(NULL) 72{ 73 memset(&bmiRegs, 0, sizeof(bmiRegs)); 74 bmiRegs.status.dmaCap0 = 1; 75 bmiRegs.status.dmaCap1 = 1; 76} 77 78IdeController::Channel::~Channel() 79{ 80} 81 82IdeController::IdeController(Params *p) 83 : PciDevice(p), primary(name() + ".primary", BARSize[0], BARSize[1]), 84 secondary(name() + ".secondary", BARSize[2], BARSize[3]), 85 bmiAddr(0), bmiSize(BARSize[4]), 86 primaryTiming(htole(timeRegWithDecodeEn)), 87 secondaryTiming(htole(timeRegWithDecodeEn)), 88 deviceTiming(0), udmaControl(0), udmaTiming(0), ideConfig(0), 89 ioEnabled(false), bmEnabled(false), 90 ioShift(p->io_shift), ctrlOffset(p->ctrl_offset) 91{ 92 if (params()->disks.size() > 3) 93 panic("IDE controllers support a maximum of 4 devices attached!\n"); 94 95 // Assign the disks to channels 96 int numDisks = params()->disks.size(); 97 if (numDisks > 0) 98 primary.master = params()->disks[0]; 99 if (numDisks > 1) 100 primary.slave = params()->disks[1]; 101 if (numDisks > 2) 102 secondary.master = params()->disks[2]; 103 if (numDisks > 3) 104 secondary.slave = params()->disks[3]; 105 106 for (int i = 0; i < params()->disks.size(); i++) { 107 params()->disks[i]->setController(this); 108 } 109 primary.select(false); 110 secondary.select(false); 111 112 if ((BARAddrs[0] & ~BAR_IO_MASK) && (!legacyIO[0] || ioShift)) { 113 primary.cmdAddr = BARAddrs[0]; primary.cmdSize = BARSize[0]; 114 primary.ctrlAddr = BARAddrs[1]; primary.ctrlSize = BARAddrs[1]; 115 } 116 if ((BARAddrs[2] & ~BAR_IO_MASK) && (!legacyIO[2] || ioShift)) { 117 secondary.cmdAddr = BARAddrs[2]; secondary.cmdSize = BARSize[2]; 118 secondary.ctrlAddr = BARAddrs[3]; secondary.ctrlSize = BARAddrs[3]; 119 } 120 121 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 122 bmEnabled = (config.command & htole(PCI_CMD_BME)); 123} 124 125bool 126IdeController::isDiskSelected(IdeDisk *diskPtr) 127{ 128 return (primary.selected == diskPtr || secondary.selected == diskPtr); 129} 130 131void 132IdeController::intrPost() 133{ 134 primary.bmiRegs.status.intStatus = 1; 135 PciDevice::intrPost(); 136} 137 138void 139IdeController::setDmaComplete(IdeDisk *disk) 140{ 141 Channel *channel; 142 if (disk == primary.master || disk == primary.slave) { 143 channel = &primary; 144 } else if (disk == secondary.master || disk == secondary.slave) { 145 channel = &secondary; 146 } else { 147 panic("Unable to find disk based on pointer %#x\n", disk); 148 } 149 150 channel->bmiRegs.command.startStop = 0; 151 channel->bmiRegs.status.active = 0; 152 channel->bmiRegs.status.intStatus = 1; 153} 154 155Tick 156IdeController::readConfig(PacketPtr pkt) 157{ 158 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 159 if (offset < PCI_DEVICE_SPECIFIC) { 160 return PciDevice::readConfig(pkt); 161 } 162 163 pkt->allocate(); 164 165 switch (pkt->getSize()) { 166 case sizeof(uint8_t): 167 switch (offset) { 168 case DeviceTiming: 169 pkt->set<uint8_t>(deviceTiming); 170 break; 171 case UDMAControl: 172 pkt->set<uint8_t>(udmaControl); 173 break; 174 case PrimaryTiming + 1: 175 pkt->set<uint8_t>(bits(htole(primaryTiming), 15, 8)); 176 break; 177 case SecondaryTiming + 1: 178 pkt->set<uint8_t>(bits(htole(secondaryTiming), 15, 8)); 179 break; 180 case IDEConfig: 181 pkt->set<uint8_t>(bits(htole(ideConfig), 7, 0)); 182 break; 183 case IDEConfig + 1: 184 pkt->set<uint8_t>(bits(htole(ideConfig), 15, 8)); 185 break; 186 default: 187 panic("Invalid PCI configuration read for size 1 at offset: %#x!\n", 188 offset); 189 } 190 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset, 191 (uint32_t)pkt->get<uint8_t>()); 192 break; 193 case sizeof(uint16_t): 194 switch (offset) { 195 case PrimaryTiming: 196 pkt->set<uint16_t>(primaryTiming); 197 break; 198 case SecondaryTiming: 199 pkt->set<uint16_t>(secondaryTiming); 200 break; 201 case UDMATiming: 202 pkt->set<uint16_t>(udmaTiming); 203 break; 204 case IDEConfig: 205 pkt->set<uint16_t>(ideConfig); 206 break; 207 default: 208 panic("Invalid PCI configuration read for size 2 offset: %#x!\n", 209 offset); 210 } 211 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset, 212 (uint32_t)pkt->get<uint16_t>()); 213 break; 214 case sizeof(uint32_t): 215 if (offset == IDEConfig) 216 pkt->set<uint32_t>(ideConfig); 217 else 218 panic("No 32bit reads implemented for this device."); 219 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset, 220 (uint32_t)pkt->get<uint32_t>()); 221 break; 222 default: 223 panic("invalid access size(?) for PCI configspace!\n"); 224 } 225 pkt->makeAtomicResponse(); 226 return configDelay; 227} 228 229 230Tick 231IdeController::writeConfig(PacketPtr pkt) 232{ 233 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 234 if (offset < PCI_DEVICE_SPECIFIC) { 235 PciDevice::writeConfig(pkt); 236 } else { 237 switch (pkt->getSize()) { 238 case sizeof(uint8_t): 239 switch (offset) { 240 case DeviceTiming: 241 deviceTiming = pkt->get<uint8_t>(); 242 break; 243 case UDMAControl: 244 udmaControl = pkt->get<uint8_t>(); 245 break; 246 case IDEConfig: 247 replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>()); 248 break; 249 case IDEConfig + 1: 250 replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>()); 251 break; 252 default: 253 panic("Invalid PCI configuration write " 254 "for size 1 offset: %#x!\n", offset); 255 } 256 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", 257 offset, (uint32_t)pkt->get<uint8_t>()); 258 break; 259 case sizeof(uint16_t): 260 switch (offset) { 261 case PrimaryTiming: 262 primaryTiming = pkt->get<uint16_t>(); 263 break; 264 case SecondaryTiming: 265 secondaryTiming = pkt->get<uint16_t>(); 266 break; 267 case UDMATiming: 268 udmaTiming = pkt->get<uint16_t>(); 269 break; 270 case IDEConfig: 271 ideConfig = pkt->get<uint16_t>(); 272 break; 273 default: 274 panic("Invalid PCI configuration write " 275 "for size 2 offset: %#x!\n", 276 offset); 277 } 278 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", 279 offset, (uint32_t)pkt->get<uint16_t>()); 280 break; 281 case sizeof(uint32_t): 282 if (offset == IDEConfig) 283 ideConfig = pkt->get<uint32_t>(); 284 else 285 panic("Write of unimplemented PCI config. register: %x\n", offset); 286 break; 287 default: 288 panic("invalid access size(?) for PCI configspace!\n"); 289 } 290 pkt->makeAtomicResponse(); 291 } 292 293 /* Trap command register writes and enable IO/BM as appropriate as well as 294 * BARs. */ 295 switch(offset) { 296 case PCI0_BASE_ADDR0: 297 if (BARAddrs[0] != 0) 298 primary.cmdAddr = BARAddrs[0]; 299 break; 300 301 case PCI0_BASE_ADDR1: 302 if (BARAddrs[1] != 0) 303 primary.ctrlAddr = BARAddrs[1]; 304 break; 305 306 case PCI0_BASE_ADDR2: 307 if (BARAddrs[2] != 0) 308 secondary.cmdAddr = BARAddrs[2]; 309 break; 310 311 case PCI0_BASE_ADDR3: 312 if (BARAddrs[3] != 0) 313 secondary.ctrlAddr = BARAddrs[3]; 314 break; 315 316 case PCI0_BASE_ADDR4: 317 if (BARAddrs[4] != 0) 318 bmiAddr = BARAddrs[4]; 319 break; 320 321 case PCI_COMMAND: 322 DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command); 323 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 324 bmEnabled = (config.command & htole(PCI_CMD_BME)); 325 break; 326 } 327 return configDelay; 328} 329 330void 331IdeController::Channel::accessCommand(Addr offset, 332 int size, uint8_t *data, bool read) 333{ 334 const Addr SelectOffset = 6; 335 const uint8_t SelectDevBit = 0x10; 336 337 if (!read && offset == SelectOffset) 338 select(*data & SelectDevBit); 339 340 if (selected == NULL) { 341 assert(size == sizeof(uint8_t)); 342 *data = 0; 343 } else if (read) { 344 selected->readCommand(offset, size, data); 345 } else { 346 selected->writeCommand(offset, size, data); 347 } 348} 349 350void 351IdeController::Channel::accessControl(Addr offset, 352 int size, uint8_t *data, bool read) 353{ 354 if (selected == NULL) { 355 assert(size == sizeof(uint8_t)); 356 *data = 0; 357 } else if (read) { 358 selected->readControl(offset, size, data); 359 } else { 360 selected->writeControl(offset, size, data); 361 } 362} 363 364void 365IdeController::Channel::accessBMI(Addr offset, 366 int size, uint8_t *data, bool read) 367{ 368 assert(offset + size <= sizeof(BMIRegs)); 369 if (read) { 370 memcpy(data, (uint8_t *)&bmiRegs + offset, size); 371 } else { 372 switch (offset) { 373 case BMICommand: 374 { 375 if (size != sizeof(uint8_t)) 376 panic("Invalid BMIC write size: %x\n", size); 377 378 BMICommandReg oldVal = bmiRegs.command; 379 BMICommandReg newVal = *data; 380 381 // if a DMA transfer is in progress, R/W control cannot change 382 if (oldVal.startStop && oldVal.rw != newVal.rw) 383 oldVal.rw = newVal.rw; 384 385 if (oldVal.startStop != newVal.startStop) { 386 if (selected == NULL) 387 panic("DMA start for disk which does not exist\n"); 388 389 if (oldVal.startStop) { 390 DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); 391 bmiRegs.status.active = 0; 392 393 selected->abortDma(); 394 } else { 395 DPRINTF(IdeCtrl, "Starting DMA transfer\n"); 396 bmiRegs.status.active = 1; 397 398 selected->startDma(letoh(bmiRegs.bmidtp)); 399 } 400 } 401 402 bmiRegs.command = newVal; 403 } 404 break; 405 case BMIStatus: 406 { 407 if (size != sizeof(uint8_t)) 408 panic("Invalid BMIS write size: %x\n", size); 409 410 BMIStatusReg oldVal = bmiRegs.status; 411 BMIStatusReg newVal = *data; 412 413 // the BMIDEA bit is read only 414 newVal.active = oldVal.active; 415 416 // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each 417 if (oldVal.intStatus && newVal.intStatus) 418 newVal.intStatus = 0; // clear the interrupt? 419 else 420 newVal.intStatus = oldVal.intStatus; 421 if (oldVal.dmaError && newVal.dmaError) 422 newVal.dmaError = 0; 423 else 424 newVal.dmaError = oldVal.dmaError; 425 426 bmiRegs.status = newVal; 427 } 428 break; 429 case BMIDescTablePtr: 430 if (size != sizeof(uint32_t)) 431 panic("Invalid BMIDTP write size: %x\n", size); 432 bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3); 433 break; 434 default: 435 if (size != sizeof(uint8_t) && size != sizeof(uint16_t) && 436 size != sizeof(uint32_t)) 437 panic("IDE controller write of invalid write size: %x\n", size); 438 memcpy((uint8_t *)&bmiRegs + offset, data, size); 439 } 440 } 441} 442 443void 444IdeController::dispatchAccess(PacketPtr pkt, bool read) 445{ 446 pkt->allocate(); 447 if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) 448 panic("Bad IDE read size: %d\n", pkt->getSize()); 449 450 if (!ioEnabled) { 451 pkt->makeAtomicResponse(); 452 DPRINTF(IdeCtrl, "io not enabled\n"); 453 return; 454 } 455 456 Addr addr = pkt->getAddr(); 457 int size = pkt->getSize(); 458 uint8_t *dataPtr = pkt->getPtr<uint8_t>(); 459 460 if (addr >= primary.cmdAddr && 461 addr < (primary.cmdAddr + primary.cmdSize)) { 462 addr -= primary.cmdAddr; 463 // linux may have shifted the address by ioShift, 464 // here we shift it back, similarly for ctrlOffset. 465 addr >>= ioShift; 466 primary.accessCommand(addr, size, dataPtr, read); 467 } else if (addr >= primary.ctrlAddr && 468 addr < (primary.ctrlAddr + primary.ctrlSize)) { 469 addr -= primary.ctrlAddr; 470 addr += ctrlOffset; 471 primary.accessControl(addr, size, dataPtr, read); 472 } else if (addr >= secondary.cmdAddr && 473 addr < (secondary.cmdAddr + secondary.cmdSize)) { 474 addr -= secondary.cmdAddr; 475 secondary.accessCommand(addr, size, dataPtr, read); 476 } else if (addr >= secondary.ctrlAddr && 477 addr < (secondary.ctrlAddr + secondary.ctrlSize)) { 478 addr -= secondary.ctrlAddr; 479 secondary.accessControl(addr, size, dataPtr, read); 480 } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { 481 if (!read && !bmEnabled) 482 return; 483 addr -= bmiAddr; 484 if (addr < sizeof(Channel::BMIRegs)) { 485 primary.accessBMI(addr, size, dataPtr, read); 486 } else { 487 addr -= sizeof(Channel::BMIRegs); 488 secondary.accessBMI(addr, size, dataPtr, read); 489 } 490 } else { 491 panic("IDE controller access to invalid address: %#x\n", addr); 492 } 493 494#ifndef NDEBUG 495 uint32_t data; 496 if (pkt->getSize() == 1) 497 data = pkt->get<uint8_t>(); 498 else if (pkt->getSize() == 2) 499 data = pkt->get<uint16_t>(); 500 else 501 data = pkt->get<uint32_t>(); 502 DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n", 503 read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data); 504#endif 505 506 pkt->makeAtomicResponse(); 507} 508 509Tick 510IdeController::read(PacketPtr pkt) 511{ 512 dispatchAccess(pkt, true); 513 return pioDelay; 514} 515 516Tick 517IdeController::write(PacketPtr pkt) 518{ 519 dispatchAccess(pkt, false); 520 return pioDelay; 521} 522 523void 524IdeController::serialize(std::ostream &os) 525{ 526 // Serialize the PciDevice base class 527 PciDevice::serialize(os); 528 529 // Serialize channels 530 primary.serialize("primary", os); 531 secondary.serialize("secondary", os); 532 533 // Serialize config registers 534 SERIALIZE_SCALAR(primaryTiming); 535 SERIALIZE_SCALAR(secondaryTiming); 536 SERIALIZE_SCALAR(deviceTiming); 537 SERIALIZE_SCALAR(udmaControl); 538 SERIALIZE_SCALAR(udmaTiming); 539 SERIALIZE_SCALAR(ideConfig); 540 541 // Serialize internal state 542 SERIALIZE_SCALAR(ioEnabled); 543 SERIALIZE_SCALAR(bmEnabled); 544 SERIALIZE_SCALAR(bmiAddr); 545 SERIALIZE_SCALAR(bmiSize); 546} 547 548void 549IdeController::Channel::serialize(const std::string &base, std::ostream &os) 550{ 551 paramOut(os, base + ".cmdAddr", cmdAddr); 552 paramOut(os, base + ".cmdSize", cmdSize); 553 paramOut(os, base + ".ctrlAddr", ctrlAddr); 554 paramOut(os, base + ".ctrlSize", ctrlSize); 555 uint8_t command = bmiRegs.command; 556 paramOut(os, base + ".bmiRegs.command", command); 557 paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 558 uint8_t status = bmiRegs.status; 559 paramOut(os, base + ".bmiRegs.status", status); 560 paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 561 paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 562 paramOut(os, base + ".selectBit", selectBit); 563} 564 565void 566IdeController::unserialize(Checkpoint *cp, const std::string §ion) 567{ 568 // Unserialize the PciDevice base class 569 PciDevice::unserialize(cp, section); 570 571 // Unserialize channels 572 primary.unserialize("primary", cp, section); 573 secondary.unserialize("secondary", cp, section); 574 575 // Unserialize config registers 576 UNSERIALIZE_SCALAR(primaryTiming); 577 UNSERIALIZE_SCALAR(secondaryTiming); 578 UNSERIALIZE_SCALAR(deviceTiming); 579 UNSERIALIZE_SCALAR(udmaControl); 580 UNSERIALIZE_SCALAR(udmaTiming); 581 UNSERIALIZE_SCALAR(ideConfig); 582 583 // Unserialize internal state 584 UNSERIALIZE_SCALAR(ioEnabled); 585 UNSERIALIZE_SCALAR(bmEnabled); 586 UNSERIALIZE_SCALAR(bmiAddr); 587 UNSERIALIZE_SCALAR(bmiSize); 588} 589 590void 591IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp, 592 const std::string §ion) 593{ 594 paramIn(cp, section, base + ".cmdAddr", cmdAddr); 595 paramIn(cp, section, base + ".cmdSize", cmdSize); 596 paramIn(cp, section, base + ".ctrlAddr", ctrlAddr); 597 paramIn(cp, section, base + ".ctrlSize", ctrlSize); 598 uint8_t command; 599 paramIn(cp, section, base +".bmiRegs.command", command); 600 bmiRegs.command = command; 601 paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 602 uint8_t status; 603 paramIn(cp, section, base + ".bmiRegs.status", status); 604 bmiRegs.status = status; 605 paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 606 paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 607 paramIn(cp, section, base + ".selectBit", selectBit); 608 select(selectBit); 609} 610 611IdeController * 612IdeControllerParams::create() 613{ 614 return new IdeController(this); 615} 616