arm: Add support for ARMv8 (AArch64 & AArch32)
[gem5.git] / src / dev / arm / vgic.cc
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
49 VGic::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
65 Tick
66 VGic::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
78 Tick
79 VGic::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
91 Tick
92 VGic::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
136 Tick
137 VGic::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
231 Tick
232 VGic::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
279 Tick
280 VGic::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
343 uint32_t
344 VGic::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
356 void
357 VGic::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
364 void
365 VGic::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
371 void
372 VGic::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
379 void
380 VGic::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 */
389 void
390 VGic::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
434 AddrRangeList
435 VGic::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
443 void
444 VGic::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
499 void VGic::unserialize(Checkpoint *cp, const std::string &section)
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
549 VGic *
550 VGicParams::create()
551 {
552 return new VGic(this);
553 }