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