vgic.cc revision 10037
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 "base/trace.hh" 41#include "debug/Checkpoint.hh" 42#include "debug/VGIC.hh" 43#include "dev/arm/base_gic.hh" 44#include "dev/arm/vgic.hh" 45#include "dev/terminal.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 PostVIntEvent(x, p->platform); 56 maintIntPosted[x] = false; 57 vIntPosted[x] = false; 58 } 59 for (int c = 0; c < VGIC_CPU_MAX; c++) { 60 memset(&vcpuData[c], 0, sizeof(struct vcpuIntData)); 61 } 62 assert(sys->numRunningContexts() <= VGIC_CPU_MAX); 63} 64 65Tick 66VGic::read(PacketPtr pkt) 67{ 68 Addr addr = pkt->getAddr(); 69 70 if (addr >= vcpuAddr && addr < vcpuAddr + GICV_SIZE) 71 return readVCpu(pkt); 72 else if (addr >= hvAddr && addr < hvAddr + GICH_REG_SIZE) 73 return readCtrl(pkt); 74 else 75 panic("Read to unknown address %#x\n", pkt->getAddr()); 76} 77 78Tick 79VGic::write(PacketPtr pkt) 80{ 81 Addr addr = pkt->getAddr(); 82 83 if (addr >= vcpuAddr && addr < vcpuAddr + GICV_SIZE) 84 return writeVCpu(pkt); 85 else if (addr >= hvAddr && addr < hvAddr + GICH_REG_SIZE) 86 return writeCtrl(pkt); 87 else 88 panic("Write to unknown address %#x\n", pkt->getAddr()); 89} 90 91Tick 92VGic::readVCpu(PacketPtr pkt) 93{ 94 Addr daddr = pkt->getAddr() - vcpuAddr; 95 pkt->allocate(); 96 97 int ctx_id = pkt->req->contextId(); 98 assert(ctx_id < VGIC_CPU_MAX); 99 struct vcpuIntData *vid = &vcpuData[ctx_id]; 100 101 DPRINTF(VGIC, "VGIC VCPU read register %#x\n", daddr); 102 103 switch (daddr) { 104 case GICV_CTLR: 105 pkt->set<uint32_t>(vid->vctrl); 106 break; 107 case GICV_IAR: { 108 int i = findHighestPendingLR(vid); 109 if (i < 0 || !vid->vctrl.En) { 110 pkt->set<uint32_t>(1023); // "No int" marker 111 } else { 112 ListReg *lr = &vid->LR[i]; 113 114 pkt->set<uint32_t>(lr->VirtualID | 115 (((int)lr->CpuID) << 10)); 116 // We don't support auto-EOI of HW interrupts via real GIC! 117 // Fortunately, KVM doesn't use this. How about Xen...? Ulp! 118 if (lr->HW) 119 panic("VGIC does not support 'HW' List Register feature (LR %#x)!\n", 120 *lr); 121 lr->State = LR_ACTIVE; 122 DPRINTF(VGIC, "Consumed interrupt %d (cpu%d) from LR%d (EOI%d)\n", 123 lr->VirtualID, lr->CpuID, i, lr->EOI); 124 } 125 } break; 126 default: 127 panic("VGIC VCPU read of bad address %#x\n", daddr); 128 } 129 130 updateIntState(ctx_id); 131 132 pkt->makeAtomicResponse(); 133 return pioDelay; 134} 135 136Tick 137VGic::readCtrl(PacketPtr pkt) 138{ 139 Addr daddr = pkt->getAddr() - hvAddr; 140 pkt->allocate(); 141 142 int ctx_id = pkt->req->contextId(); 143 144 DPRINTF(VGIC, "VGIC HVCtrl read register %#x\n", daddr); 145 146 /* Munge the address: 0-0xfff is the usual space banked by requester CPU. 147 * Anything > that is 0x200-sized slices of 'per CPU' regs. 148 */ 149 if (daddr & ~0x1ff) { 150 ctx_id = (daddr >> 9); 151 if (ctx_id > 8) 152 panic("VGIC: Weird unbanked hv ctrl address %#x!\n", daddr); 153 daddr &= ~0x1ff; 154 } 155 assert(ctx_id < VGIC_CPU_MAX); 156 struct vcpuIntData *vid = &vcpuData[ctx_id]; 157 158 switch (daddr) { 159 case GICH_HCR: 160 pkt->set<uint32_t>(vid->hcr); 161 break; 162 163 case GICH_VTR: 164 pkt->set<uint32_t>(0x44000000 | (NUM_LR - 1)); 165 break; 166 167 case GICH_VMCR: 168 pkt->set<uint32_t>( 169 ((uint32_t)vid->VMPriMask << 27) | 170 ((uint32_t)vid->VMBP << 21) | 171 ((uint32_t)vid->VMABP << 18) | 172 ((uint32_t)vid->VEM << 9) | 173 ((uint32_t)vid->VMCBPR << 4) | 174 ((uint32_t)vid->VMFiqEn << 3) | 175 ((uint32_t)vid->VMAckCtl << 2) | 176 ((uint32_t)vid->VMGrp1En << 1) | 177 ((uint32_t)vid->VMGrp0En << 0) 178 ); 179 break; 180 181 case GICH_MISR: 182 pkt->set<uint32_t>(getMISR(vid)); 183 break; 184 185 case GICH_EISR0: 186 pkt->set<uint32_t>(vid->eisr & 0xffffffff); 187 break; 188 189 case GICH_EISR1: 190 pkt->set<uint32_t>(vid->eisr >> 32); 191 break; 192 193 case GICH_ELSR0: { 194 uint32_t bm = 0; 195 for (int i = 0; i < ((NUM_LR < 32) ? NUM_LR : 32); i++) { 196 if (!vid->LR[i].State) 197 bm |= 1 << i; 198 } 199 pkt->set<uint32_t>(bm); 200 } break; 201 202 case GICH_ELSR1: { 203 uint32_t bm = 0; 204 for (int i = 32; i < NUM_LR; i++) { 205 if (!vid->LR[i].State) 206 bm |= 1 << (i-32); 207 } 208 pkt->set<uint32_t>(bm); 209 } break; 210 211 case GICH_APR0: 212 warn_once("VGIC GICH_APR read!\n"); 213 pkt->set<uint32_t>(0); 214 break; 215 216 case GICH_LR0: 217 case GICH_LR1: 218 case GICH_LR2: 219 case GICH_LR3: 220 pkt->set<uint32_t>(vid->LR[(daddr - GICH_LR0) >> 2]); 221 break; 222 223 default: 224 panic("VGIC HVCtrl read of bad address %#x\n", daddr); 225 } 226 227 pkt->makeAtomicResponse(); 228 return pioDelay; 229} 230 231Tick 232VGic::writeVCpu(PacketPtr pkt) 233{ 234 Addr daddr = pkt->getAddr() - vcpuAddr; 235 pkt->allocate(); 236 237 int ctx_id = pkt->req->contextId(); 238 assert(ctx_id < VGIC_CPU_MAX); 239 struct vcpuIntData *vid = &vcpuData[ctx_id]; 240 241 DPRINTF(VGIC, "VGIC VCPU write register %#x <= %#x\n", daddr, pkt->get<uint32_t>()); 242 243 switch (daddr) { 244 case GICV_CTLR: 245 vid->vctrl = pkt->get<uint32_t>(); 246 break; 247 case GICV_PMR: 248 vid->VMPriMask = pkt->get<uint32_t>(); 249 break; 250 case GICV_EOIR: { 251 // We don't handle the split EOI-then-DIR mode. Linux (guest) 252 // doesn't need it though. 253 assert(!vid->vctrl.EOImode); 254 uint32_t w = pkt->get<uint32_t>(); 255 unsigned int virq = w & 0x3ff; 256 unsigned int vcpu = (w >> 10) & 7; 257 int i = findLRForVIRQ(vid, virq, vcpu); 258 if (i < 0) { 259 DPRINTF(VGIC, "EOIR: No LR for irq %d(cpu%d)\n", virq, vcpu); 260 } else { 261 DPRINTF(VGIC, "EOIR: Found LR%d for irq %d(cpu%d)\n", i, virq, vcpu); 262 ListReg *lr = &vid->LR[i]; 263 lr->State = 0; 264 // Maintenance interrupt -- via eisr -- is flagged when 265 // LRs have EOI=1 and State=INVALID! 266 } 267 } break; 268 default: 269 panic("VGIC VCPU write %#x to unk address %#x\n", pkt->get<uint32_t>(), daddr); 270 } 271 272 // This updates the EISRs and flags IRQs: 273 updateIntState(ctx_id); 274 275 pkt->makeAtomicResponse(); 276 return pioDelay; 277} 278 279Tick 280VGic::writeCtrl(PacketPtr pkt) 281{ 282 Addr daddr = pkt->getAddr() - hvAddr; 283 pkt->allocate(); 284 285 int ctx_id = pkt->req->contextId(); 286 287 DPRINTF(VGIC, "VGIC HVCtrl write register %#x <= %#x\n", daddr, pkt->get<uint32_t>()); 288 289 /* Munge the address: 0-0xfff is the usual space banked by requester CPU. 290 * Anything > that is 0x200-sized slices of 'per CPU' regs. 291 */ 292 if (daddr & ~0x1ff) { 293 ctx_id = (daddr >> 9); 294 if (ctx_id > 8) 295 panic("VGIC: Weird unbanked hv ctrl address %#x!\n", daddr); 296 daddr &= ~0x1ff; 297 } 298 assert(ctx_id < VGIC_CPU_MAX); 299 struct vcpuIntData *vid = &vcpuData[ctx_id]; 300 301 switch (daddr) { 302 case GICH_HCR: 303 vid->hcr = pkt->get<uint32_t>(); 304 // update int state 305 break; 306 307 case GICH_VMCR: { 308 uint32_t d = pkt->get<uint32_t>(); 309 vid->VMPriMask = d >> 27; 310 vid->VMBP = (d >> 21) & 7; 311 vid->VMABP = (d >> 18) & 7; 312 vid->VEM = (d >> 9) & 1; 313 vid->VMCBPR = (d >> 4) & 1; 314 vid->VMFiqEn = (d >> 3) & 1; 315 vid->VMAckCtl = (d >> 2) & 1; 316 vid->VMGrp1En = (d >> 1) & 1; 317 vid->VMGrp0En = d & 1; 318 } break; 319 320 case GICH_APR0: 321 warn_once("VGIC GICH_APR0 written, ignored\n"); 322 break; 323 324 case GICH_LR0: 325 case GICH_LR1: 326 case GICH_LR2: 327 case GICH_LR3: 328 vid->LR[(daddr - GICH_LR0) >> 2] = pkt->get<uint32_t>(); 329 // update int state 330 break; 331 332 default: 333 panic("VGIC HVCtrl write to bad address %#x\n", daddr); 334 } 335 336 updateIntState(ctx_id); 337 338 pkt->makeAtomicResponse(); 339 return pioDelay; 340} 341 342 343uint32_t 344VGic::getMISR(struct vcpuIntData *vid) 345{ 346 return (!!vid->hcr.VGrp1DIE && !vid->VMGrp1En ? 0x80 : 0) | 347 (!!vid->hcr.VGrp1EIE && vid->VMGrp1En ? 0x40 : 0) | 348 (!!vid->hcr.VGrp0DIE && !vid->VMGrp0En ? 0x20 : 0) | 349 (!!vid->hcr.VGrp0EIE && vid->VMGrp0En ? 0x10 : 0) | 350 (!!vid->hcr.NPIE && !lrPending(vid) ? 0x08 : 0) | 351 (!!vid->hcr.LRENPIE && vid->hcr.EOICount ? 0x04 : 0) | 352 (!!vid->hcr.UIE && lrValid(vid) <= 1 ? 0x02 : 0) | 353 (vid->eisr ? 0x01 : 0); 354} 355 356void 357VGic::postVInt(uint32_t cpu, Tick when) 358{ 359 DPRINTF(VGIC, "Posting VIRQ to %d\n", cpu); 360 if (!(postVIntEvent[cpu]->scheduled())) 361 eventq->schedule(postVIntEvent[cpu], when); 362} 363 364void 365VGic::unPostVInt(uint32_t cpu) 366{ 367 DPRINTF(VGIC, "Unposting VIRQ to %d\n", cpu); 368 platform->intrctrl->clear(cpu, ArmISA::INT_VIRT_IRQ, 0); 369} 370 371void 372VGic::postMaintInt(uint32_t cpu) 373{ 374 DPRINTF(VGIC, "Posting maintenance PPI to GIC/cpu%d\n", cpu); 375 // Linux DT configures this as Level. 376 gic->sendPPInt(maintInt, cpu); 377} 378 379void 380VGic::unPostMaintInt(uint32_t cpu) 381{ 382 DPRINTF(VGIC, "Unposting maintenance PPI to GIC/cpu%d\n", cpu); 383 gic->clearPPInt(maintInt, cpu); 384} 385 386/* Update state (in general); something concerned with ctx_id has changed. 387 * This may raise a maintenance interrupt. 388 */ 389void 390VGic::updateIntState(int ctx_id) 391{ 392 // @todo This should update APRs! 393 394 // Build EISR contents: 395 // (Cached so that regs can read them without messing about again) 396 struct vcpuIntData *tvid = &vcpuData[ctx_id]; 397 398 tvid->eisr = 0; 399 for (int i = 0; i < NUM_LR; i++) { 400 if (!tvid->LR[i].State && tvid->LR[i].EOI) { 401 tvid->eisr |= 1 << i; 402 } 403 } 404 405 assert(sys->numRunningContexts() <= VGIC_CPU_MAX); 406 for (int i = 0; i < sys->numRunningContexts(); i++) { 407 struct vcpuIntData *vid = &vcpuData[i]; 408 // Are any LRs active that weren't before? 409 if (!vIntPosted[i]) { 410 if (lrPending(vid) && vid->vctrl.En) { 411 vIntPosted[i] = true; 412 postVInt(i, curTick() + 1); 413 } 414 } else if (!lrPending(vid)) { 415 vIntPosted[i] = false; 416 unPostVInt(i); 417 } 418 419 // Any maintenance ints to send? 420 if (!maintIntPosted[i]) { 421 if (vid->hcr.En && getMISR(vid)) { 422 maintIntPosted[i] = true; 423 postMaintInt(i); 424 } 425 } else { 426 if (!vid->hcr.En || !getMISR(vid)) { 427 unPostMaintInt(i); 428 maintIntPosted[i] = false; 429 } 430 } 431 } 432} 433 434AddrRangeList 435VGic::getAddrRanges() const 436{ 437 AddrRangeList ranges; 438 ranges.push_back(RangeSize(hvAddr, GICH_REG_SIZE)); 439 ranges.push_back(RangeSize(vcpuAddr, GICV_SIZE)); 440 return ranges; 441} 442 443void 444VGic::serialize(std::ostream &os) 445{ 446 Tick interrupt_time[VGIC_CPU_MAX]; 447 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) { 448 interrupt_time[cpu] = 0; 449 if (postVIntEvent[cpu]->scheduled()) { 450 interrupt_time[cpu] = postVIntEvent[cpu]->when(); 451 } 452 } 453 454 DPRINTF(Checkpoint, "Serializing VGIC\n"); 455 456 SERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX); 457 SERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX); 458 SERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX); 459 SERIALIZE_SCALAR(vcpuAddr); 460 SERIALIZE_SCALAR(hvAddr); 461 SERIALIZE_SCALAR(pioDelay); 462 SERIALIZE_SCALAR(maintInt); 463 464 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) { 465 nameOut(os, csprintf("%s.vcpuData%d", name(), cpu)); 466 uint32_t vctrl_val = vcpuData[cpu].vctrl; 467 SERIALIZE_SCALAR(vctrl_val); 468 uint32_t hcr_val = vcpuData[cpu].hcr; 469 SERIALIZE_SCALAR(hcr_val); 470 uint64_t eisr_val = vcpuData[cpu].eisr; 471 SERIALIZE_SCALAR(eisr_val); 472 uint8_t VMGrp0En_val = vcpuData[cpu].VMGrp0En; 473 SERIALIZE_SCALAR(VMGrp0En_val); 474 uint8_t VMGrp1En_val = vcpuData[cpu].VMGrp1En; 475 SERIALIZE_SCALAR(VMGrp1En_val); 476 uint8_t VMAckCtl_val = vcpuData[cpu].VMAckCtl; 477 SERIALIZE_SCALAR(VMAckCtl_val); 478 uint8_t VMFiqEn_val = vcpuData[cpu].VMFiqEn; 479 SERIALIZE_SCALAR(VMFiqEn_val); 480 uint8_t VMCBPR_val = vcpuData[cpu].VMCBPR; 481 SERIALIZE_SCALAR(VMCBPR_val); 482 uint8_t VEM_val = vcpuData[cpu].VEM; 483 SERIALIZE_SCALAR(VEM_val); 484 uint8_t VMABP_val = vcpuData[cpu].VMABP; 485 SERIALIZE_SCALAR(VMABP_val); 486 uint8_t VMBP_val = vcpuData[cpu].VMBP; 487 SERIALIZE_SCALAR(VMBP_val); 488 uint8_t VMPriMask_val = vcpuData[cpu].VMPriMask; 489 SERIALIZE_SCALAR(VMPriMask_val); 490 491 for (int i = 0; i < NUM_LR; i++) { 492 uint32_t lr = vcpuData[cpu].LR[i]; 493 nameOut(os, csprintf("%s.vcpuData%d.LR%d", name(), cpu, i)); 494 SERIALIZE_SCALAR(lr); 495 } 496 } 497} 498 499void VGic::unserialize(Checkpoint *cp, const std::string §ion) 500{ 501 DPRINTF(Checkpoint, "Unserializing Arm GIC\n"); 502 503 Tick interrupt_time[VGIC_CPU_MAX]; 504 UNSERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX); 505 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) { 506 if (interrupt_time[cpu]) 507 schedule(postVIntEvent[cpu], interrupt_time[cpu]); 508 509 uint32_t tmp; 510 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 511 "vctrl_val", tmp); 512 vcpuData[cpu].vctrl = tmp; 513 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 514 "hcr_val", tmp); 515 vcpuData[cpu].hcr = tmp; 516 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 517 "eisr_val", vcpuData[cpu].eisr); 518 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 519 "VMGrp0En_val", vcpuData[cpu].VMGrp0En); 520 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 521 "VMGrp1En_val", vcpuData[cpu].VMGrp1En); 522 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 523 "VMAckCtl_val", vcpuData[cpu].VMAckCtl); 524 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 525 "VMFiqEn_val", vcpuData[cpu].VMFiqEn); 526 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 527 "VMCBPR_val", vcpuData[cpu].VMCBPR); 528 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 529 "VEM_val", vcpuData[cpu].VEM); 530 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 531 "VMABP_val", vcpuData[cpu].VMABP); 532 paramIn(cp, csprintf("%s.vcpuData%d", section, cpu), 533 "VMPriMask_val", vcpuData[cpu].VMPriMask); 534 535 for (int i = 0; i < NUM_LR; i++) { 536 paramIn(cp, csprintf("%s.vcpuData%d.LR%d", section, cpu, i), 537 "lr", tmp); 538 vcpuData[cpu].LR[i] = tmp; 539 } 540 } 541 UNSERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX); 542 UNSERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX); 543 UNSERIALIZE_SCALAR(vcpuAddr); 544 UNSERIALIZE_SCALAR(hvAddr); 545 UNSERIALIZE_SCALAR(pioDelay); 546 UNSERIALIZE_SCALAR(maintInt); 547} 548 549VGic * 550VGicParams::create() 551{ 552 return new VGic(this); 553} 554