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