arch-arm, configs: Treat the bootloader rom as cacheable memory
[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 "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
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 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
64 VGic::~VGic()
65 {
66 for (int x = 0; x < VGIC_CPU_MAX; x++)
67 delete postVIntEvent[x];
68 }
69
70 Tick
71 VGic::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
83 Tick
84 VGic::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
96 Tick
97 VGic::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->set<uint32_t>(vid->vctrl);
110 break;
111 case GICV_IAR: {
112 int i = findHighestPendingLR(vid);
113 if (i < 0 || !vid->vctrl.En) {
114 pkt->set<uint32_t>(1023); // "No int" marker
115 } else {
116 ListReg *lr = &vid->LR[i];
117
118 pkt->set<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
140 Tick
141 VGic::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->set<uint32_t>(vid->hcr);
164 break;
165
166 case GICH_VTR:
167 pkt->set<uint32_t>(0x44000000 | (NUM_LR - 1));
168 break;
169
170 case GICH_VMCR:
171 pkt->set<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->set<uint32_t>(getMISR(vid));
186 break;
187
188 case GICH_EISR0:
189 pkt->set<uint32_t>(vid->eisr & 0xffffffff);
190 break;
191
192 case GICH_EISR1:
193 pkt->set<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->set<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->set<uint32_t>(bm);
212 } break;
213
214 case GICH_APR0:
215 warn_once("VGIC GICH_APR read!\n");
216 pkt->set<uint32_t>(0);
217 break;
218
219 case GICH_LR0:
220 case GICH_LR1:
221 case GICH_LR2:
222 case GICH_LR3:
223 pkt->set<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
234 Tick
235 VGic::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", daddr, pkt->get<uint32_t>());
244
245 switch (daddr) {
246 case GICV_CTLR:
247 vid->vctrl = pkt->get<uint32_t>();
248 break;
249 case GICV_PMR:
250 vid->VMPriMask = pkt->get<uint32_t>();
251 break;
252 case GICV_EOIR: {
253 // We don't handle the split EOI-then-DIR mode. Linux (guest)
254 // doesn't need it though.
255 assert(!vid->vctrl.EOImode);
256 uint32_t w = pkt->get<uint32_t>();
257 unsigned int virq = w & 0x3ff;
258 unsigned int vcpu = (w >> 10) & 7;
259 int i = findLRForVIRQ(vid, virq, vcpu);
260 if (i < 0) {
261 DPRINTF(VGIC, "EOIR: No LR for irq %d(cpu%d)\n", virq, vcpu);
262 } else {
263 DPRINTF(VGIC, "EOIR: Found LR%d for irq %d(cpu%d)\n", i, virq, vcpu);
264 ListReg *lr = &vid->LR[i];
265 lr->State = 0;
266 // Maintenance interrupt -- via eisr -- is flagged when
267 // LRs have EOI=1 and State=INVALID!
268 }
269 } break;
270 default:
271 panic("VGIC VCPU write %#x to unk address %#x\n", pkt->get<uint32_t>(), daddr);
272 }
273
274 // This updates the EISRs and flags IRQs:
275 updateIntState(ctx_id);
276
277 pkt->makeAtomicResponse();
278 return pioDelay;
279 }
280
281 Tick
282 VGic::writeCtrl(PacketPtr pkt)
283 {
284 Addr daddr = pkt->getAddr() - hvAddr;
285
286 ContextID ctx_id = pkt->req->contextId();
287
288 DPRINTF(VGIC, "VGIC HVCtrl write register %#x <= %#x\n", daddr, pkt->get<uint32_t>());
289
290 /* Munge the address: 0-0xfff is the usual space banked by requester CPU.
291 * Anything > that is 0x200-sized slices of 'per CPU' regs.
292 */
293 if (daddr & ~0x1ff) {
294 ctx_id = (daddr >> 9);
295 if (ctx_id > 8)
296 panic("VGIC: Weird unbanked hv ctrl address %#x!\n", daddr);
297 daddr &= ~0x1ff;
298 }
299 assert(ctx_id < VGIC_CPU_MAX);
300 struct vcpuIntData *vid = &vcpuData[ctx_id];
301
302 switch (daddr) {
303 case GICH_HCR:
304 vid->hcr = pkt->get<uint32_t>();
305 // update int state
306 break;
307
308 case GICH_VMCR: {
309 uint32_t d = pkt->get<uint32_t>();
310 vid->VMPriMask = d >> 27;
311 vid->VMBP = (d >> 21) & 7;
312 vid->VMABP = (d >> 18) & 7;
313 vid->VEM = (d >> 9) & 1;
314 vid->VMCBPR = (d >> 4) & 1;
315 vid->VMFiqEn = (d >> 3) & 1;
316 vid->VMAckCtl = (d >> 2) & 1;
317 vid->VMGrp1En = (d >> 1) & 1;
318 vid->VMGrp0En = d & 1;
319 } break;
320
321 case GICH_APR0:
322 warn_once("VGIC GICH_APR0 written, ignored\n");
323 break;
324
325 case GICH_LR0:
326 case GICH_LR1:
327 case GICH_LR2:
328 case GICH_LR3:
329 vid->LR[(daddr - GICH_LR0) >> 2] = pkt->get<uint32_t>();
330 // update int state
331 break;
332
333 default:
334 panic("VGIC HVCtrl write to bad address %#x\n", daddr);
335 }
336
337 updateIntState(ctx_id);
338
339 pkt->makeAtomicResponse();
340 return pioDelay;
341 }
342
343
344 uint32_t
345 VGic::getMISR(struct vcpuIntData *vid)
346 {
347 return (!!vid->hcr.VGrp1DIE && !vid->VMGrp1En ? 0x80 : 0) |
348 (!!vid->hcr.VGrp1EIE && vid->VMGrp1En ? 0x40 : 0) |
349 (!!vid->hcr.VGrp0DIE && !vid->VMGrp0En ? 0x20 : 0) |
350 (!!vid->hcr.VGrp0EIE && vid->VMGrp0En ? 0x10 : 0) |
351 (!!vid->hcr.NPIE && !lrPending(vid) ? 0x08 : 0) |
352 (!!vid->hcr.LRENPIE && vid->hcr.EOICount ? 0x04 : 0) |
353 (!!vid->hcr.UIE && lrValid(vid) <= 1 ? 0x02 : 0) |
354 (vid->eisr ? 0x01 : 0);
355 }
356
357 void
358 VGic::postVInt(uint32_t cpu, Tick when)
359 {
360 DPRINTF(VGIC, "Posting VIRQ to %d\n", cpu);
361 if (!(postVIntEvent[cpu]->scheduled()))
362 eventq->schedule(postVIntEvent[cpu], when);
363 }
364
365 void
366 VGic::unPostVInt(uint32_t cpu)
367 {
368 DPRINTF(VGIC, "Unposting VIRQ to %d\n", cpu);
369 platform->intrctrl->clear(cpu, ArmISA::INT_VIRT_IRQ, 0);
370 }
371
372 void
373 VGic::processPostVIntEvent(uint32_t cpu)
374 {
375 platform->intrctrl->post(cpu, ArmISA::INT_VIRT_IRQ, 0);
376 }
377
378
379 void
380 VGic::postMaintInt(uint32_t cpu)
381 {
382 DPRINTF(VGIC, "Posting maintenance PPI to GIC/cpu%d\n", cpu);
383 // Linux DT configures this as Level.
384 gic->sendPPInt(maintInt, cpu);
385 }
386
387 void
388 VGic::unPostMaintInt(uint32_t cpu)
389 {
390 DPRINTF(VGIC, "Unposting maintenance PPI to GIC/cpu%d\n", cpu);
391 gic->clearPPInt(maintInt, cpu);
392 }
393
394 /* Update state (in general); something concerned with ctx_id has changed.
395 * This may raise a maintenance interrupt.
396 */
397 void
398 VGic::updateIntState(ContextID ctx_id)
399 {
400 // @todo This should update APRs!
401
402 // Build EISR contents:
403 // (Cached so that regs can read them without messing about again)
404 struct vcpuIntData *tvid = &vcpuData[ctx_id];
405
406 tvid->eisr = 0;
407 for (int i = 0; i < NUM_LR; i++) {
408 if (!tvid->LR[i].State && tvid->LR[i].EOI) {
409 tvid->eisr |= 1 << i;
410 }
411 }
412
413 assert(sys->numRunningContexts() <= VGIC_CPU_MAX);
414 for (int i = 0; i < sys->numRunningContexts(); i++) {
415 struct vcpuIntData *vid = &vcpuData[i];
416 // Are any LRs active that weren't before?
417 if (!vIntPosted[i]) {
418 if (lrPending(vid) && vid->vctrl.En) {
419 vIntPosted[i] = true;
420 postVInt(i, curTick() + 1);
421 }
422 } else if (!lrPending(vid)) {
423 vIntPosted[i] = false;
424 unPostVInt(i);
425 }
426
427 // Any maintenance ints to send?
428 if (!maintIntPosted[i]) {
429 if (vid->hcr.En && getMISR(vid)) {
430 maintIntPosted[i] = true;
431 postMaintInt(i);
432 }
433 } else {
434 if (!vid->hcr.En || !getMISR(vid)) {
435 unPostMaintInt(i);
436 maintIntPosted[i] = false;
437 }
438 }
439 }
440 }
441
442 AddrRangeList
443 VGic::getAddrRanges() const
444 {
445 AddrRangeList ranges;
446 ranges.push_back(RangeSize(hvAddr, GICH_REG_SIZE));
447 ranges.push_back(RangeSize(vcpuAddr, GICV_SIZE));
448 return ranges;
449 }
450
451 void
452 VGic::serialize(CheckpointOut &cp) const
453 {
454 Tick interrupt_time[VGIC_CPU_MAX];
455 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) {
456 interrupt_time[cpu] = 0;
457 if (postVIntEvent[cpu]->scheduled()) {
458 interrupt_time[cpu] = postVIntEvent[cpu]->when();
459 }
460 }
461
462 DPRINTF(Checkpoint, "Serializing VGIC\n");
463
464 SERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX);
465 SERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX);
466 SERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX);
467 SERIALIZE_SCALAR(vcpuAddr);
468 SERIALIZE_SCALAR(hvAddr);
469 SERIALIZE_SCALAR(pioDelay);
470 SERIALIZE_SCALAR(maintInt);
471
472 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++)
473 vcpuData[cpu].serializeSection(cp, csprintf("vcpuData%d", cpu));
474 }
475
476 void
477 VGic::vcpuIntData::serialize(CheckpointOut &cp) const
478 {
479 uint32_t vctrl_val = vctrl;
480 SERIALIZE_SCALAR(vctrl_val);
481 uint32_t hcr_val = hcr;
482 SERIALIZE_SCALAR(hcr_val);
483 uint64_t eisr_val = eisr;
484 SERIALIZE_SCALAR(eisr_val);
485 uint8_t VMGrp0En_val = VMGrp0En;
486 SERIALIZE_SCALAR(VMGrp0En_val);
487 uint8_t VMGrp1En_val = VMGrp1En;
488 SERIALIZE_SCALAR(VMGrp1En_val);
489 uint8_t VMAckCtl_val = VMAckCtl;
490 SERIALIZE_SCALAR(VMAckCtl_val);
491 uint8_t VMFiqEn_val = VMFiqEn;
492 SERIALIZE_SCALAR(VMFiqEn_val);
493 uint8_t VMCBPR_val = VMCBPR;
494 SERIALIZE_SCALAR(VMCBPR_val);
495 uint8_t VEM_val = VEM;
496 SERIALIZE_SCALAR(VEM_val);
497 uint8_t VMABP_val = VMABP;
498 SERIALIZE_SCALAR(VMABP_val);
499 uint8_t VMBP_val = VMBP;
500 SERIALIZE_SCALAR(VMBP_val);
501 uint8_t VMPriMask_val = VMPriMask;
502 SERIALIZE_SCALAR(VMPriMask_val);
503
504 for (int i = 0; i < NUM_LR; i++) {
505 ScopedCheckpointSection sec_lr(cp, csprintf("LR%d", i));
506 paramOut(cp, "lr", LR[i]);
507 }
508 }
509
510 void VGic::unserialize(CheckpointIn &cp)
511 {
512 DPRINTF(Checkpoint, "Unserializing Arm GIC\n");
513
514 Tick interrupt_time[VGIC_CPU_MAX];
515 UNSERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX);
516 for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) {
517 if (interrupt_time[cpu])
518 schedule(postVIntEvent[cpu], interrupt_time[cpu]);
519
520 vcpuData[cpu].unserializeSection(cp, csprintf("vcpuData%d", cpu));
521 }
522 UNSERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX);
523 UNSERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX);
524 UNSERIALIZE_SCALAR(vcpuAddr);
525 UNSERIALIZE_SCALAR(hvAddr);
526 UNSERIALIZE_SCALAR(pioDelay);
527 UNSERIALIZE_SCALAR(maintInt);
528 }
529
530 void
531 VGic::vcpuIntData::unserialize(CheckpointIn &cp)
532 {
533 paramIn(cp, "vctrl_val", vctrl);
534 paramIn(cp, "hcr_val", hcr);
535 paramIn(cp, "eisr_val", eisr);
536 paramIn(cp, "VMGrp0En_val", VMGrp0En);
537 paramIn(cp, "VMGrp1En_val", VMGrp1En);
538 paramIn(cp, "VMAckCtl_val", VMAckCtl);
539 paramIn(cp, "VMFiqEn_val", VMFiqEn);
540 paramIn(cp, "VMCBPR_val", VMCBPR);
541 paramIn(cp, "VEM_val", VEM);
542 paramIn(cp, "VMABP_val", VMABP);
543 paramIn(cp, "VMPriMask_val", VMPriMask);
544
545 for (int i = 0; i < NUM_LR; i++) {
546 ScopedCheckpointSection sec_lr(cp, csprintf("LR%d", i));
547 paramIn(cp, "lr", LR[i]);
548 }
549 }
550
551 VGic *
552 VGicParams::create()
553 {
554 return new VGic(this);
555 }