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