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