ide_ctrl.cc revision 8522
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 if (offset == IDEConfig) 215 pkt->set<uint32_t>(ideConfig); 216 else 217 panic("No 32bit reads implemented for this device."); 218 DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset, 219 (uint32_t)pkt->get<uint32_t>()); 220 break; 221 default: 222 panic("invalid access size(?) for PCI configspace!\n"); 223 } 224 pkt->makeAtomicResponse(); 225 return configDelay; 226} 227 228 229Tick 230IdeController::writeConfig(PacketPtr pkt) 231{ 232 int offset = pkt->getAddr() & PCI_CONFIG_SIZE; 233 if (offset < PCI_DEVICE_SPECIFIC) { 234 PciDev::writeConfig(pkt); 235 } else { 236 switch (pkt->getSize()) { 237 case sizeof(uint8_t): 238 switch (offset) { 239 case DeviceTiming: 240 deviceTiming = pkt->get<uint8_t>(); 241 break; 242 case UDMAControl: 243 udmaControl = pkt->get<uint8_t>(); 244 break; 245 case IDEConfig: 246 replaceBits(ideConfig, 7, 0, pkt->get<uint8_t>()); 247 break; 248 case IDEConfig + 1: 249 replaceBits(ideConfig, 15, 8, pkt->get<uint8_t>()); 250 break; 251 default: 252 panic("Invalid PCI configuration write " 253 "for size 1 offset: %#x!\n", offset); 254 } 255 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", 256 offset, (uint32_t)pkt->get<uint8_t>()); 257 break; 258 case sizeof(uint16_t): 259 switch (offset) { 260 case PrimaryTiming: 261 primaryTiming = pkt->get<uint16_t>(); 262 break; 263 case SecondaryTiming: 264 secondaryTiming = pkt->get<uint16_t>(); 265 break; 266 case UDMATiming: 267 udmaTiming = pkt->get<uint16_t>(); 268 break; 269 case IDEConfig: 270 ideConfig = pkt->get<uint16_t>(); 271 break; 272 default: 273 panic("Invalid PCI configuration write " 274 "for size 2 offset: %#x!\n", 275 offset); 276 } 277 DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", 278 offset, (uint32_t)pkt->get<uint16_t>()); 279 break; 280 case sizeof(uint32_t): 281 if (offset == IDEConfig) 282 ideConfig = pkt->get<uint32_t>(); 283 else 284 panic("Write of unimplemented PCI config. register: %x\n", offset); 285 break; 286 default: 287 panic("invalid access size(?) for PCI configspace!\n"); 288 } 289 pkt->makeAtomicResponse(); 290 } 291 292 /* Trap command register writes and enable IO/BM as appropriate as well as 293 * BARs. */ 294 switch(offset) { 295 case PCI0_BASE_ADDR0: 296 if (BARAddrs[0] != 0) 297 primary.cmdAddr = BARAddrs[0]; 298 break; 299 300 case PCI0_BASE_ADDR1: 301 if (BARAddrs[1] != 0) 302 primary.ctrlAddr = BARAddrs[1]; 303 break; 304 305 case PCI0_BASE_ADDR2: 306 if (BARAddrs[2] != 0) 307 secondary.cmdAddr = BARAddrs[2]; 308 break; 309 310 case PCI0_BASE_ADDR3: 311 if (BARAddrs[3] != 0) 312 secondary.ctrlAddr = BARAddrs[3]; 313 break; 314 315 case PCI0_BASE_ADDR4: 316 if (BARAddrs[4] != 0) 317 bmiAddr = BARAddrs[4]; 318 break; 319 320 case PCI_COMMAND: 321 DPRINTF(IdeCtrl, "Writing to PCI Command val: %#x\n", config.command); 322 ioEnabled = (config.command & htole(PCI_CMD_IOSE)); 323 bmEnabled = (config.command & htole(PCI_CMD_BME)); 324 break; 325 } 326 return configDelay; 327} 328 329void 330IdeController::Channel::accessCommand(Addr offset, 331 int size, uint8_t *data, bool read) 332{ 333 const Addr SelectOffset = 6; 334 const uint8_t SelectDevBit = 0x10; 335 336 if (!read && offset == SelectOffset) 337 select(*data & SelectDevBit); 338 339 if (selected == NULL) { 340 assert(size == sizeof(uint8_t)); 341 *data = 0; 342 } else if (read) { 343 selected->readCommand(offset, size, data); 344 } else { 345 selected->writeCommand(offset, size, data); 346 } 347} 348 349void 350IdeController::Channel::accessControl(Addr offset, 351 int size, uint8_t *data, bool read) 352{ 353 if (selected == NULL) { 354 assert(size == sizeof(uint8_t)); 355 *data = 0; 356 } else if (read) { 357 selected->readControl(offset, size, data); 358 } else { 359 selected->writeControl(offset, size, data); 360 } 361} 362 363void 364IdeController::Channel::accessBMI(Addr offset, 365 int size, uint8_t *data, bool read) 366{ 367 assert(offset + size <= sizeof(BMIRegs)); 368 if (read) { 369 memcpy(data, (uint8_t *)&bmiRegs + offset, size); 370 } else { 371 switch (offset) { 372 case BMICommand: 373 { 374 if (size != sizeof(uint8_t)) 375 panic("Invalid BMIC write size: %x\n", size); 376 377 BMICommandReg oldVal = bmiRegs.command; 378 BMICommandReg newVal = *data; 379 380 // if a DMA transfer is in progress, R/W control cannot change 381 if (oldVal.startStop && oldVal.rw != newVal.rw) 382 oldVal.rw = newVal.rw; 383 384 if (oldVal.startStop != newVal.startStop) { 385 if (selected == NULL) 386 panic("DMA start for disk which does not exist\n"); 387 388 if (oldVal.startStop) { 389 DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); 390 bmiRegs.status.active = 0; 391 392 selected->abortDma(); 393 } else { 394 DPRINTF(IdeCtrl, "Starting DMA transfer\n"); 395 bmiRegs.status.active = 1; 396 397 selected->startDma(letoh(bmiRegs.bmidtp)); 398 } 399 } 400 401 bmiRegs.command = newVal; 402 } 403 break; 404 case BMIStatus: 405 { 406 if (size != sizeof(uint8_t)) 407 panic("Invalid BMIS write size: %x\n", size); 408 409 BMIStatusReg oldVal = bmiRegs.status; 410 BMIStatusReg newVal = *data; 411 412 // the BMIDEA bit is read only 413 newVal.active = oldVal.active; 414 415 // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each 416 if (oldVal.intStatus && newVal.intStatus) 417 newVal.intStatus = 0; // clear the interrupt? 418 else 419 newVal.intStatus = oldVal.intStatus; 420 if (oldVal.dmaError && newVal.dmaError) 421 newVal.dmaError = 0; 422 else 423 newVal.dmaError = oldVal.dmaError; 424 425 bmiRegs.status = newVal; 426 } 427 break; 428 case BMIDescTablePtr: 429 if (size != sizeof(uint32_t)) 430 panic("Invalid BMIDTP write size: %x\n", size); 431 bmiRegs.bmidtp = htole(*(uint32_t *)data & ~0x3); 432 break; 433 default: 434 if (size != sizeof(uint8_t) && size != sizeof(uint16_t) && 435 size != sizeof(uint32_t)) 436 panic("IDE controller write of invalid write size: %x\n", size); 437 memcpy((uint8_t *)&bmiRegs + offset, data, size); 438 } 439 } 440} 441 442void 443IdeController::dispatchAccess(PacketPtr pkt, bool read) 444{ 445 pkt->allocate(); 446 if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) 447 panic("Bad IDE read size: %d\n", pkt->getSize()); 448 449 if (!ioEnabled) { 450 pkt->makeAtomicResponse(); 451 DPRINTF(IdeCtrl, "io not enabled\n"); 452 return; 453 } 454 455 Addr addr = pkt->getAddr(); 456 int size = pkt->getSize(); 457 uint8_t *dataPtr = pkt->getPtr<uint8_t>(); 458 459 if (addr >= primary.cmdAddr && 460 addr < (primary.cmdAddr + primary.cmdSize)) { 461 addr -= primary.cmdAddr; 462 // linux may have shifted the address by ioShift, 463 // here we shift it back, similarly for ctrlOffset. 464 addr >>= ioShift; 465 primary.accessCommand(addr, size, dataPtr, read); 466 } else if (addr >= primary.ctrlAddr && 467 addr < (primary.ctrlAddr + primary.ctrlSize)) { 468 addr -= primary.ctrlAddr; 469 addr += ctrlOffset; 470 primary.accessControl(addr, size, dataPtr, read); 471 } else if (addr >= secondary.cmdAddr && 472 addr < (secondary.cmdAddr + secondary.cmdSize)) { 473 addr -= secondary.cmdAddr; 474 secondary.accessCommand(addr, size, dataPtr, read); 475 } else if (addr >= secondary.ctrlAddr && 476 addr < (secondary.ctrlAddr + secondary.ctrlSize)) { 477 addr -= secondary.ctrlAddr; 478 secondary.accessControl(addr, size, dataPtr, read); 479 } else if (addr >= bmiAddr && addr < (bmiAddr + bmiSize)) { 480 if (!read && !bmEnabled) 481 return; 482 addr -= bmiAddr; 483 if (addr < sizeof(Channel::BMIRegs)) { 484 primary.accessBMI(addr, size, dataPtr, read); 485 } else { 486 addr -= sizeof(Channel::BMIRegs); 487 secondary.accessBMI(addr, size, dataPtr, read); 488 } 489 } else { 490 panic("IDE controller access to invalid address: %#x\n", addr); 491 } 492 493 uint32_t data; 494 if (pkt->getSize() == 1) 495 data = pkt->get<uint8_t>(); 496 else if (pkt->getSize() == 2) 497 data = pkt->get<uint16_t>(); 498 else 499 data = pkt->get<uint32_t>(); 500 DPRINTF(IdeCtrl, "%s from offset: %#x size: %#x data: %#x\n", 501 read ? "Read" : "Write", pkt->getAddr(), pkt->getSize(), data); 502 503 pkt->makeAtomicResponse(); 504} 505 506Tick 507IdeController::read(PacketPtr pkt) 508{ 509 dispatchAccess(pkt, true); 510 return pioDelay; 511} 512 513Tick 514IdeController::write(PacketPtr pkt) 515{ 516 dispatchAccess(pkt, false); 517 return pioDelay; 518} 519 520void 521IdeController::serialize(std::ostream &os) 522{ 523 // Serialize the PciDev base class 524 PciDev::serialize(os); 525 526 // Serialize channels 527 primary.serialize("primary", os); 528 secondary.serialize("secondary", os); 529 530 // Serialize config registers 531 SERIALIZE_SCALAR(primaryTiming); 532 SERIALIZE_SCALAR(secondaryTiming); 533 SERIALIZE_SCALAR(deviceTiming); 534 SERIALIZE_SCALAR(udmaControl); 535 SERIALIZE_SCALAR(udmaTiming); 536 SERIALIZE_SCALAR(ideConfig); 537 538 // Serialize internal state 539 SERIALIZE_SCALAR(ioEnabled); 540 SERIALIZE_SCALAR(bmEnabled); 541 SERIALIZE_SCALAR(bmiAddr); 542 SERIALIZE_SCALAR(bmiSize); 543} 544 545void 546IdeController::Channel::serialize(const std::string &base, std::ostream &os) 547{ 548 paramOut(os, base + ".cmdAddr", cmdAddr); 549 paramOut(os, base + ".cmdSize", cmdSize); 550 paramOut(os, base + ".ctrlAddr", ctrlAddr); 551 paramOut(os, base + ".ctrlSize", ctrlSize); 552 uint8_t command = bmiRegs.command; 553 paramOut(os, base + ".bmiRegs.command", command); 554 paramOut(os, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 555 uint8_t status = bmiRegs.status; 556 paramOut(os, base + ".bmiRegs.status", status); 557 paramOut(os, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 558 paramOut(os, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 559 paramOut(os, base + ".selectBit", selectBit); 560} 561 562void 563IdeController::unserialize(Checkpoint *cp, const std::string §ion) 564{ 565 // Unserialize the PciDev base class 566 PciDev::unserialize(cp, section); 567 568 // Unserialize channels 569 primary.unserialize("primary", cp, section); 570 secondary.unserialize("secondary", cp, section); 571 572 // Unserialize config registers 573 UNSERIALIZE_SCALAR(primaryTiming); 574 UNSERIALIZE_SCALAR(secondaryTiming); 575 UNSERIALIZE_SCALAR(deviceTiming); 576 UNSERIALIZE_SCALAR(udmaControl); 577 UNSERIALIZE_SCALAR(udmaTiming); 578 UNSERIALIZE_SCALAR(ideConfig); 579 580 // Unserialize internal state 581 UNSERIALIZE_SCALAR(ioEnabled); 582 UNSERIALIZE_SCALAR(bmEnabled); 583 UNSERIALIZE_SCALAR(bmiAddr); 584 UNSERIALIZE_SCALAR(bmiSize); 585} 586 587void 588IdeController::Channel::unserialize(const std::string &base, Checkpoint *cp, 589 const std::string §ion) 590{ 591 paramIn(cp, section, base + ".cmdAddr", cmdAddr); 592 paramIn(cp, section, base + ".cmdSize", cmdSize); 593 paramIn(cp, section, base + ".ctrlAddr", ctrlAddr); 594 paramIn(cp, section, base + ".ctrlSize", ctrlSize); 595 uint8_t command; 596 paramIn(cp, section, base +".bmiRegs.command", command); 597 bmiRegs.command = command; 598 paramIn(cp, section, base + ".bmiRegs.reserved0", bmiRegs.reserved0); 599 uint8_t status; 600 paramIn(cp, section, base + ".bmiRegs.status", status); 601 bmiRegs.status = status; 602 paramIn(cp, section, base + ".bmiRegs.reserved1", bmiRegs.reserved1); 603 paramIn(cp, section, base + ".bmiRegs.bmidtp", bmiRegs.bmidtp); 604 paramIn(cp, section, base + ".selectBit", selectBit); 605 select(selectBit); 606} 607 608IdeController * 609IdeControllerParams::create() 610{ 611 return new IdeController(this); 612} 613