vgic.cc revision 13230:2988dc5d1d6f
1/* 2 * Copyright (c) 2013 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Matt Evans 38 */ 39 40#include "dev/arm/vgic.hh" 41 42#include "base/trace.hh" 43#include "debug/Checkpoint.hh" 44#include "debug/VGIC.hh" 45#include "dev/arm/base_gic.hh" 46#include "mem/packet.hh" 47#include "mem/packet_access.hh" 48 49VGic::VGic(const Params *p) 50 : PioDevice(p), platform(p->platform), gic(p->gic), vcpuAddr(p->vcpu_addr), 51 hvAddr(p->hv_addr), pioDelay(p->pio_delay), 52 maintInt(p->ppint) 53{ 54 for (int x = 0; x < VGIC_CPU_MAX; x++) { 55 postVIntEvent[x] = new EventFunctionWrapper( 56 [this, x]{ processPostVIntEvent(x); }, 57 "Post VInterrupt to CPU"); 58 maintIntPosted[x] = false; 59 vIntPosted[x] = false; 60 } 61 assert(sys->numRunningContexts() <= VGIC_CPU_MAX); 62} 63 64VGic::~VGic() 65{ 66 for (int x = 0; x < VGIC_CPU_MAX; x++) 67 delete postVIntEvent[x]; 68} 69 70Tick 71VGic::read(PacketPtr pkt) 72{ 73 Addr addr = pkt->getAddr(); 74 75 if (addr >= vcpuAddr && addr < vcpuAddr + GICV_SIZE) 76 return readVCpu(pkt); 77 else if (addr >= hvAddr && addr < hvAddr + GICH_REG_SIZE) 78 return readCtrl(pkt); 79 else 80 panic("Read to unknown address %#x\n", pkt->getAddr()); 81} 82 83Tick 84VGic::write(PacketPtr pkt) 85{ 86 Addr addr = pkt->getAddr(); 87 88 if (addr >= vcpuAddr && addr < vcpuAddr + GICV_SIZE) 89 return writeVCpu(pkt); 90 else if (addr >= hvAddr && addr < hvAddr + GICH_REG_SIZE) 91 return writeCtrl(pkt); 92 else 93 panic("Write to unknown address %#x\n", pkt->getAddr()); 94} 95 96Tick 97VGic::readVCpu(PacketPtr pkt) 98{ 99 Addr daddr = pkt->getAddr() - vcpuAddr; 100 101 ContextID ctx_id = pkt->req->contextId(); 102 assert(ctx_id < VGIC_CPU_MAX); 103 struct vcpuIntData *vid = &vcpuData[ctx_id]; 104 105 DPRINTF(VGIC, "VGIC VCPU read register %#x\n", daddr); 106 107 switch (daddr) { 108 case GICV_CTLR: 109 pkt->setLE<uint32_t>(vid->vctrl); 110 break; 111 case GICV_IAR: { 112 int i = findHighestPendingLR(vid); 113 if (i < 0 || !vid->vctrl.En) { 114 pkt->setLE<uint32_t>(1023); // "No int" marker 115 } else { 116 ListReg *lr = &vid->LR[i]; 117 118 pkt->setLE<uint32_t>(lr->VirtualID | 119 (((int)lr->CpuID) << 10)); 120 // We don't support auto-EOI of HW interrupts via real GIC! 121 // Fortunately, KVM doesn't use this. How about Xen...? Ulp! 122 if (lr->HW) 123 panic("VGIC does not support 'HW' List Register feature (LR %#x)!\n", 124 *lr); 125 lr->State = LR_ACTIVE; 126 DPRINTF(VGIC, "Consumed interrupt %d (cpu%d) from LR%d (EOI%d)\n", 127 lr->VirtualID, lr->CpuID, i, lr->EOI); 128 } 129 } break; 130 default: 131 panic("VGIC VCPU read of bad address %#x\n", daddr); 132 } 133 134 updateIntState(ctx_id); 135 136 pkt->makeAtomicResponse(); 137 return pioDelay; 138} 139 140Tick 141VGic::readCtrl(PacketPtr pkt) 142{ 143 Addr daddr = pkt->getAddr() - hvAddr; 144 145 ContextID ctx_id = pkt->req->contextId(); 146 147 DPRINTF(VGIC, "VGIC HVCtrl read register %#x\n", daddr); 148 149 /* Munge the address: 0-0xfff is the usual space banked by requester CPU. 150 * Anything > that is 0x200-sized slices of 'per CPU' regs. 151 */ 152 if (daddr & ~0x1ff) { 153 ctx_id = (daddr >> 9); 154 if (ctx_id > 8) 155 panic("VGIC: Weird unbanked hv ctrl address %#x!\n", daddr); 156 daddr &= ~0x1ff; 157 } 158 assert(ctx_id < VGIC_CPU_MAX); 159 struct vcpuIntData *vid = &vcpuData[ctx_id]; 160 161 switch (daddr) { 162 case GICH_HCR: 163 pkt->setLE<uint32_t>(vid->hcr); 164 break; 165 166 case GICH_VTR: 167 pkt->setLE<uint32_t>(0x44000000 | (NUM_LR - 1)); 168 break; 169 170 case GICH_VMCR: 171 pkt->setLE<uint32_t>( 172 ((uint32_t)vid->VMPriMask << 27) | 173 ((uint32_t)vid->VMBP << 21) | 174 ((uint32_t)vid->VMABP << 18) | 175 ((uint32_t)vid->VEM << 9) | 176 ((uint32_t)vid->VMCBPR << 4) | 177 ((uint32_t)vid->VMFiqEn << 3) | 178 ((uint32_t)vid->VMAckCtl << 2) | 179 ((uint32_t)vid->VMGrp1En << 1) | 180 ((uint32_t)vid->VMGrp0En << 0) 181 ); 182 break; 183 184 case GICH_MISR: 185 pkt->setLE<uint32_t>(getMISR(vid)); 186 break; 187 188 case GICH_EISR0: 189 pkt->setLE<uint32_t>(vid->eisr & 0xffffffff); 190 break; 191 192 case GICH_EISR1: 193 pkt->setLE<uint32_t>(vid->eisr >> 32); 194 break; 195 196 case GICH_ELSR0: { 197 uint32_t bm = 0; 198 for (int i = 0; i < ((NUM_LR < 32) ? NUM_LR : 32); i++) { 199 if (!vid->LR[i].State) 200 bm |= 1 << i; 201 } 202 pkt->setLE<uint32_t>(bm); 203 } break; 204 205 case GICH_ELSR1: { 206 uint32_t bm = 0; 207 for (int i = 32; i < NUM_LR; i++) { 208 if (!vid->LR[i].State) 209 bm |= 1 << (i-32); 210 } 211 pkt->setLE<uint32_t>(bm); 212 } break; 213 214 case GICH_APR0: 215 warn_once("VGIC GICH_APR read!\n"); 216 pkt->setLE<uint32_t>(0); 217 break; 218 219 case GICH_LR0: 220 case GICH_LR1: 221 case GICH_LR2: 222 case GICH_LR3: 223 pkt->setLE<uint32_t>(vid->LR[(daddr - GICH_LR0) >> 2]); 224 break; 225 226 default: 227 panic("VGIC HVCtrl read of bad address %#x\n", daddr); 228 } 229 230 pkt->makeAtomicResponse(); 231 return pioDelay; 232} 233 234Tick 235VGic::writeVCpu(PacketPtr pkt) 236{ 237 Addr daddr = pkt->getAddr() - vcpuAddr; 238 239 ContextID ctx_id = pkt->req->contextId(); 240 assert(ctx_id < VGIC_CPU_MAX); 241 struct vcpuIntData *vid = &vcpuData[ctx_id]; 242 243 DPRINTF(VGIC, "VGIC VCPU write register %#x <= %#x\n", 244 daddr, pkt->getLE<uint32_t>()); 245 246 switch (daddr) { 247 case GICV_CTLR: 248 vid->vctrl = pkt->getLE<uint32_t>(); 249 break; 250 case GICV_PMR: 251 vid->VMPriMask = pkt->getLE<uint32_t>(); 252 break; 253 case GICV_EOIR: { 254 // We don't handle the split EOI-then-DIR mode. Linux (guest) 255 // doesn't need it though. 256 assert(!vid->vctrl.EOImode); 257 uint32_t w = pkt->getLE<uint32_t>(); 258 unsigned int virq = w & 0x3ff; 259 unsigned int vcpu = (w >> 10) & 7; 260 int i = findLRForVIRQ(vid, virq, vcpu); 261 if (i < 0) { 262 DPRINTF(VGIC, "EOIR: No LR for irq %d(cpu%d)\n", virq, vcpu); 263 } else { 264 DPRINTF(VGIC, "EOIR: Found LR%d for irq %d(cpu%d)\n", i, virq, vcpu); 265 ListReg *lr = &vid->LR[i]; 266 lr->State = 0; 267 // Maintenance interrupt -- via eisr -- is flagged when 268 // LRs have EOI=1 and State=INVALID! 269 } 270 } break; 271 default: 272 panic("VGIC VCPU write %#x to unk address %#x\n", 273 pkt->getLE<uint32_t>(), daddr); 274 } 275 276 // This updates the EISRs and flags IRQs: 277 updateIntState(ctx_id); 278 279 pkt->makeAtomicResponse(); 280 return pioDelay; 281} 282 283Tick 284VGic::writeCtrl(PacketPtr pkt) 285{ 286 Addr daddr = pkt->getAddr() - hvAddr; 287 288 ContextID ctx_id = pkt->req->contextId(); 289 290 DPRINTF(VGIC, "VGIC HVCtrl write register %#x <= %#x\n", 291 daddr, pkt->getLE<uint32_t>()); 292 293 /* Munge the address: 0-0xfff is the usual space banked by requester CPU. 294 * Anything > that is 0x200-sized slices of 'per CPU' regs. 295 */ 296 if (daddr & ~0x1ff) { 297 ctx_id = (daddr >> 9); 298 if (ctx_id > 8) 299 panic("VGIC: Weird unbanked hv ctrl address %#x!\n", daddr); 300 daddr &= ~0x1ff; 301 } 302 assert(ctx_id < VGIC_CPU_MAX); 303 struct vcpuIntData *vid = &vcpuData[ctx_id]; 304 305 switch (daddr) { 306 case GICH_HCR: 307 vid->hcr = pkt->getLE<uint32_t>(); 308 // update int state 309 break; 310 311 case GICH_VMCR: { 312 uint32_t d = pkt->getLE<uint32_t>(); 313 vid->VMPriMask = d >> 27; 314 vid->VMBP = (d >> 21) & 7; 315 vid->VMABP = (d >> 18) & 7; 316 vid->VEM = (d >> 9) & 1; 317 vid->VMCBPR = (d >> 4) & 1; 318 vid->VMFiqEn = (d >> 3) & 1; 319 vid->VMAckCtl = (d >> 2) & 1; 320 vid->VMGrp1En = (d >> 1) & 1; 321 vid->VMGrp0En = d & 1; 322 } break; 323 324 case GICH_APR0: 325 warn_once("VGIC GICH_APR0 written, ignored\n"); 326 break; 327 328 case GICH_LR0: 329 case GICH_LR1: 330 case GICH_LR2: 331 case GICH_LR3: 332 vid->LR[(daddr - GICH_LR0) >> 2] = pkt->getLE<uint32_t>(); 333 // update int state 334 break; 335 336 default: 337 panic("VGIC HVCtrl write to bad address %#x\n", daddr); 338 } 339 340 updateIntState(ctx_id); 341 342 pkt->makeAtomicResponse(); 343 return pioDelay; 344} 345 346 347uint32_t 348VGic::getMISR(struct vcpuIntData *vid) 349{ 350 return (!!vid->hcr.VGrp1DIE && !vid->VMGrp1En ? 0x80 : 0) | 351 (!!vid->hcr.VGrp1EIE && vid->VMGrp1En ? 0x40 : 0) | 352 (!!vid->hcr.VGrp0DIE && !vid->VMGrp0En ? 0x20 : 0) | 353 (!!vid->hcr.VGrp0EIE && vid->VMGrp0En ? 0x10 : 0) | 354 (!!vid->hcr.NPIE && !lrPending(vid) ? 0x08 : 0) | 355 (!!vid->hcr.LRENPIE && vid->hcr.EOICount ? 0x04 : 0) | 356 (!!vid->hcr.UIE && lrValid(vid) <= 1 ? 0x02 : 0) | 357 (vid->eisr ? 0x01 : 0); 358} 359 360void 361VGic::postVInt(uint32_t cpu, Tick when) 362{ 363 DPRINTF(VGIC, "Posting VIRQ to %d\n", cpu); 364 if (!(postVIntEvent[cpu]->scheduled())) 365 eventq->schedule(postVIntEvent[cpu], when); 366} 367 368void 369VGic::unPostVInt(uint32_t cpu) 370{ 371 DPRINTF(VGIC, "Unposting VIRQ to %d\n", cpu); 372 platform->intrctrl->clear(cpu, ArmISA::INT_VIRT_IRQ, 0); 373} 374 375void 376VGic::processPostVIntEvent(uint32_t cpu) 377{ 378 platform->intrctrl->post(cpu, ArmISA::INT_VIRT_IRQ, 0); 379} 380 381 382void 383VGic::postMaintInt(uint32_t cpu) 384{ 385 DPRINTF(VGIC, "Posting maintenance PPI to GIC/cpu%d\n", cpu); 386 // Linux DT configures this as Level. 387 gic->sendPPInt(maintInt, cpu); 388} 389 390void 391VGic::unPostMaintInt(uint32_t cpu) 392{ 393 DPRINTF(VGIC, "Unposting maintenance PPI to GIC/cpu%d\n", cpu); 394 gic->clearPPInt(maintInt, cpu); 395} 396 397/* Update state (in general); something concerned with ctx_id has changed. 398 * This may raise a maintenance interrupt. 399 */ 400void 401VGic::updateIntState(ContextID ctx_id) 402{ 403 // @todo This should update APRs! 404 405 // Build EISR contents: 406 // (Cached so that regs can read them without messing about again) 407 struct vcpuIntData *tvid = &vcpuData[ctx_id]; 408 409 tvid->eisr = 0; 410 for (int i = 0; i < NUM_LR; i++) { 411 if (!tvid->LR[i].State && tvid->LR[i].EOI) { 412 tvid->eisr |= 1 << i; 413 } 414 } 415 416 assert(sys->numRunningContexts() <= VGIC_CPU_MAX); 417 for (int i = 0; i < sys->numRunningContexts(); i++) { 418 struct vcpuIntData *vid = &vcpuData[i]; 419 // Are any LRs active that weren't before? 420 if (!vIntPosted[i]) { 421 if (lrPending(vid) && vid->vctrl.En) { 422 vIntPosted[i] = true; 423 postVInt(i, curTick() + 1); 424 } 425 } else if (!lrPending(vid)) { 426 vIntPosted[i] = false; 427 unPostVInt(i); 428 } 429 430 // Any maintenance ints to send? 431 if (!maintIntPosted[i]) { 432 if (vid->hcr.En && getMISR(vid)) { 433 maintIntPosted[i] = true; 434 postMaintInt(i); 435 } 436 } else { 437 if (!vid->hcr.En || !getMISR(vid)) { 438 unPostMaintInt(i); 439 maintIntPosted[i] = false; 440 } 441 } 442 } 443} 444 445AddrRangeList 446VGic::getAddrRanges() const 447{ 448 AddrRangeList ranges; 449 ranges.push_back(RangeSize(hvAddr, GICH_REG_SIZE)); 450 ranges.push_back(RangeSize(vcpuAddr, GICV_SIZE)); 451 return ranges; 452} 453 454void 455VGic::serialize(CheckpointOut &cp) const 456{ 457 Tick interrupt_time[VGIC_CPU_MAX]; 458 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) { 459 interrupt_time[cpu] = 0; 460 if (postVIntEvent[cpu]->scheduled()) { 461 interrupt_time[cpu] = postVIntEvent[cpu]->when(); 462 } 463 } 464 465 DPRINTF(Checkpoint, "Serializing VGIC\n"); 466 467 SERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX); 468 SERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX); 469 SERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX); 470 SERIALIZE_SCALAR(vcpuAddr); 471 SERIALIZE_SCALAR(hvAddr); 472 SERIALIZE_SCALAR(pioDelay); 473 SERIALIZE_SCALAR(maintInt); 474 475 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) 476 vcpuData[cpu].serializeSection(cp, csprintf("vcpuData%d", cpu)); 477} 478 479void 480VGic::vcpuIntData::serialize(CheckpointOut &cp) const 481{ 482 uint32_t vctrl_val = vctrl; 483 SERIALIZE_SCALAR(vctrl_val); 484 uint32_t hcr_val = hcr; 485 SERIALIZE_SCALAR(hcr_val); 486 uint64_t eisr_val = eisr; 487 SERIALIZE_SCALAR(eisr_val); 488 uint8_t VMGrp0En_val = VMGrp0En; 489 SERIALIZE_SCALAR(VMGrp0En_val); 490 uint8_t VMGrp1En_val = VMGrp1En; 491 SERIALIZE_SCALAR(VMGrp1En_val); 492 uint8_t VMAckCtl_val = VMAckCtl; 493 SERIALIZE_SCALAR(VMAckCtl_val); 494 uint8_t VMFiqEn_val = VMFiqEn; 495 SERIALIZE_SCALAR(VMFiqEn_val); 496 uint8_t VMCBPR_val = VMCBPR; 497 SERIALIZE_SCALAR(VMCBPR_val); 498 uint8_t VEM_val = VEM; 499 SERIALIZE_SCALAR(VEM_val); 500 uint8_t VMABP_val = VMABP; 501 SERIALIZE_SCALAR(VMABP_val); 502 uint8_t VMBP_val = VMBP; 503 SERIALIZE_SCALAR(VMBP_val); 504 uint8_t VMPriMask_val = VMPriMask; 505 SERIALIZE_SCALAR(VMPriMask_val); 506 507 for (int i = 0; i < NUM_LR; i++) { 508 ScopedCheckpointSection sec_lr(cp, csprintf("LR%d", i)); 509 paramOut(cp, "lr", LR[i]); 510 } 511} 512 513void VGic::unserialize(CheckpointIn &cp) 514{ 515 DPRINTF(Checkpoint, "Unserializing Arm GIC\n"); 516 517 Tick interrupt_time[VGIC_CPU_MAX]; 518 UNSERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX); 519 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) { 520 if (interrupt_time[cpu]) 521 schedule(postVIntEvent[cpu], interrupt_time[cpu]); 522 523 vcpuData[cpu].unserializeSection(cp, csprintf("vcpuData%d", cpu)); 524 } 525 UNSERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX); 526 UNSERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX); 527 UNSERIALIZE_SCALAR(vcpuAddr); 528 UNSERIALIZE_SCALAR(hvAddr); 529 UNSERIALIZE_SCALAR(pioDelay); 530 UNSERIALIZE_SCALAR(maintInt); 531} 532 533void 534VGic::vcpuIntData::unserialize(CheckpointIn &cp) 535{ 536 paramIn(cp, "vctrl_val", vctrl); 537 paramIn(cp, "hcr_val", hcr); 538 paramIn(cp, "eisr_val", eisr); 539 paramIn(cp, "VMGrp0En_val", VMGrp0En); 540 paramIn(cp, "VMGrp1En_val", VMGrp1En); 541 paramIn(cp, "VMAckCtl_val", VMAckCtl); 542 paramIn(cp, "VMFiqEn_val", VMFiqEn); 543 paramIn(cp, "VMCBPR_val", VMCBPR); 544 paramIn(cp, "VEM_val", VEM); 545 paramIn(cp, "VMABP_val", VMABP); 546 paramIn(cp, "VMPriMask_val", VMPriMask); 547 548 for (int i = 0; i < NUM_LR; i++) { 549 ScopedCheckpointSection sec_lr(cp, csprintf("LR%d", i)); 550 paramIn(cp, "lr", LR[i]); 551 } 552} 553 554VGic * 555VGicParams::create() 556{ 557 return new VGic(this); 558} 559