vgic.cc revision 12092
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 PostVIntEvent(x, p->platform);
57        maintIntPosted[x] = false;
58        vIntPosted[x] = false;
59    }
60    assert(sys->numRunningContexts() <= VGIC_CPU_MAX);
61}
62
63VGic::~VGic()
64{
65    for (int x = 0; x < VGIC_CPU_MAX; x++)
66        delete postVIntEvent[x];
67}
68
69Tick
70VGic::read(PacketPtr pkt)
71{
72    Addr addr = pkt->getAddr();
73
74    if (addr >= vcpuAddr && addr < vcpuAddr + GICV_SIZE)
75        return readVCpu(pkt);
76    else if (addr >= hvAddr && addr < hvAddr + GICH_REG_SIZE)
77        return readCtrl(pkt);
78    else
79        panic("Read to unknown address %#x\n", pkt->getAddr());
80}
81
82Tick
83VGic::write(PacketPtr pkt)
84{
85    Addr addr = pkt->getAddr();
86
87    if (addr >= vcpuAddr && addr < vcpuAddr + GICV_SIZE)
88        return writeVCpu(pkt);
89    else if (addr >= hvAddr && addr < hvAddr + GICH_REG_SIZE)
90        return writeCtrl(pkt);
91    else
92        panic("Write to unknown address %#x\n", pkt->getAddr());
93}
94
95Tick
96VGic::readVCpu(PacketPtr pkt)
97{
98    Addr daddr = pkt->getAddr() - vcpuAddr;
99
100    ContextID ctx_id = pkt->req->contextId();
101    assert(ctx_id < VGIC_CPU_MAX);
102    struct vcpuIntData *vid = &vcpuData[ctx_id];
103
104    DPRINTF(VGIC, "VGIC VCPU read register %#x\n", daddr);
105
106    switch (daddr) {
107      case GICV_CTLR:
108        pkt->set<uint32_t>(vid->vctrl);
109        break;
110      case GICV_IAR: {
111          int i = findHighestPendingLR(vid);
112          if (i < 0 || !vid->vctrl.En) {
113              pkt->set<uint32_t>(1023); // "No int" marker
114          } else {
115              ListReg *lr = &vid->LR[i];
116
117              pkt->set<uint32_t>(lr->VirtualID |
118                                 (((int)lr->CpuID) << 10));
119              // We don't support auto-EOI of HW interrupts via real GIC!
120              // Fortunately, KVM doesn't use this.  How about Xen...? Ulp!
121              if (lr->HW)
122                  panic("VGIC does not support 'HW' List Register feature (LR %#x)!\n",
123                        *lr);
124              lr->State = LR_ACTIVE;
125              DPRINTF(VGIC, "Consumed interrupt %d (cpu%d) from LR%d (EOI%d)\n",
126                      lr->VirtualID, lr->CpuID, i, lr->EOI);
127          }
128      } break;
129      default:
130        panic("VGIC VCPU read of bad address %#x\n", daddr);
131    }
132
133    updateIntState(ctx_id);
134
135    pkt->makeAtomicResponse();
136    return pioDelay;
137}
138
139Tick
140VGic::readCtrl(PacketPtr pkt)
141{
142    Addr daddr = pkt->getAddr() - hvAddr;
143
144    ContextID ctx_id = pkt->req->contextId();
145
146    DPRINTF(VGIC, "VGIC HVCtrl read register %#x\n", daddr);
147
148    /* Munge the address: 0-0xfff is the usual space banked by requester CPU.
149     * Anything > that is 0x200-sized slices of 'per CPU' regs.
150     */
151    if (daddr & ~0x1ff) {
152        ctx_id = (daddr >> 9);
153        if (ctx_id > 8)
154            panic("VGIC: Weird unbanked hv ctrl address %#x!\n", daddr);
155        daddr &= ~0x1ff;
156    }
157    assert(ctx_id < VGIC_CPU_MAX);
158    struct vcpuIntData *vid = &vcpuData[ctx_id];
159
160    switch (daddr) {
161      case GICH_HCR:
162        pkt->set<uint32_t>(vid->hcr);
163        break;
164
165      case GICH_VTR:
166        pkt->set<uint32_t>(0x44000000 | (NUM_LR - 1));
167        break;
168
169      case GICH_VMCR:
170        pkt->set<uint32_t>(
171            ((uint32_t)vid->VMPriMask << 27) |
172            ((uint32_t)vid->VMBP << 21) |
173            ((uint32_t)vid->VMABP << 18) |
174            ((uint32_t)vid->VEM << 9) |
175            ((uint32_t)vid->VMCBPR << 4) |
176            ((uint32_t)vid->VMFiqEn << 3) |
177            ((uint32_t)vid->VMAckCtl << 2) |
178            ((uint32_t)vid->VMGrp1En << 1) |
179            ((uint32_t)vid->VMGrp0En << 0)
180            );
181        break;
182
183      case GICH_MISR:
184        pkt->set<uint32_t>(getMISR(vid));
185        break;
186
187      case GICH_EISR0:
188        pkt->set<uint32_t>(vid->eisr & 0xffffffff);
189        break;
190
191      case GICH_EISR1:
192        pkt->set<uint32_t>(vid->eisr >> 32);
193        break;
194
195      case GICH_ELSR0: {
196          uint32_t bm = 0;
197          for (int i = 0; i < ((NUM_LR < 32) ? NUM_LR : 32); i++) {
198              if (!vid->LR[i].State)
199                  bm |= 1 << i;
200          }
201          pkt->set<uint32_t>(bm);
202      } break;
203
204      case GICH_ELSR1: {
205          uint32_t bm = 0;
206          for (int i = 32; i < NUM_LR; i++) {
207              if (!vid->LR[i].State)
208                  bm |= 1 << (i-32);
209          }
210          pkt->set<uint32_t>(bm);
211      } break;
212
213      case GICH_APR0:
214        warn_once("VGIC GICH_APR read!\n");
215        pkt->set<uint32_t>(0);
216        break;
217
218      case GICH_LR0:
219      case GICH_LR1:
220      case GICH_LR2:
221      case GICH_LR3:
222        pkt->set<uint32_t>(vid->LR[(daddr - GICH_LR0) >> 2]);
223        break;
224
225      default:
226        panic("VGIC HVCtrl read of bad address %#x\n", daddr);
227    }
228
229    pkt->makeAtomicResponse();
230    return pioDelay;
231}
232
233Tick
234VGic::writeVCpu(PacketPtr pkt)
235{
236    Addr daddr = pkt->getAddr() - vcpuAddr;
237
238    ContextID ctx_id = pkt->req->contextId();
239    assert(ctx_id < VGIC_CPU_MAX);
240    struct vcpuIntData *vid = &vcpuData[ctx_id];
241
242    DPRINTF(VGIC, "VGIC VCPU write register %#x <= %#x\n", daddr, pkt->get<uint32_t>());
243
244    switch (daddr) {
245      case GICV_CTLR:
246        vid->vctrl = pkt->get<uint32_t>();
247        break;
248      case GICV_PMR:
249        vid->VMPriMask = pkt->get<uint32_t>();
250        break;
251      case GICV_EOIR: {
252          // We don't handle the split EOI-then-DIR mode.  Linux (guest)
253          // doesn't need it though.
254          assert(!vid->vctrl.EOImode);
255          uint32_t w = pkt->get<uint32_t>();
256          unsigned int virq = w & 0x3ff;
257          unsigned int vcpu = (w >> 10) & 7;
258          int i = findLRForVIRQ(vid, virq, vcpu);
259          if (i < 0) {
260              DPRINTF(VGIC, "EOIR: No LR for irq %d(cpu%d)\n", virq, vcpu);
261          } else {
262              DPRINTF(VGIC, "EOIR: Found LR%d for irq %d(cpu%d)\n", i, virq, vcpu);
263              ListReg *lr = &vid->LR[i];
264              lr->State = 0;
265              // Maintenance interrupt -- via eisr -- is flagged when
266              // LRs have EOI=1 and State=INVALID!
267          }
268      } break;
269      default:
270        panic("VGIC VCPU write %#x to unk address %#x\n", pkt->get<uint32_t>(), daddr);
271    }
272
273    // This updates the EISRs and flags IRQs:
274    updateIntState(ctx_id);
275
276    pkt->makeAtomicResponse();
277    return pioDelay;
278}
279
280Tick
281VGic::writeCtrl(PacketPtr pkt)
282{
283    Addr daddr = pkt->getAddr() - hvAddr;
284
285    ContextID 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(ContextID 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(CheckpointOut &cp) const
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        vcpuData[cpu].serializeSection(cp, csprintf("vcpuData%d", cpu));
466}
467
468void
469VGic::vcpuIntData::serialize(CheckpointOut &cp) const
470{
471    uint32_t vctrl_val = vctrl;
472    SERIALIZE_SCALAR(vctrl_val);
473    uint32_t hcr_val = hcr;
474    SERIALIZE_SCALAR(hcr_val);
475    uint64_t eisr_val = eisr;
476    SERIALIZE_SCALAR(eisr_val);
477    uint8_t VMGrp0En_val = VMGrp0En;
478    SERIALIZE_SCALAR(VMGrp0En_val);
479    uint8_t VMGrp1En_val = VMGrp1En;
480    SERIALIZE_SCALAR(VMGrp1En_val);
481    uint8_t VMAckCtl_val = VMAckCtl;
482    SERIALIZE_SCALAR(VMAckCtl_val);
483    uint8_t VMFiqEn_val = VMFiqEn;
484    SERIALIZE_SCALAR(VMFiqEn_val);
485    uint8_t VMCBPR_val = VMCBPR;
486    SERIALIZE_SCALAR(VMCBPR_val);
487    uint8_t VEM_val = VEM;
488    SERIALIZE_SCALAR(VEM_val);
489    uint8_t VMABP_val = VMABP;
490    SERIALIZE_SCALAR(VMABP_val);
491    uint8_t VMBP_val = VMBP;
492    SERIALIZE_SCALAR(VMBP_val);
493    uint8_t VMPriMask_val = VMPriMask;
494    SERIALIZE_SCALAR(VMPriMask_val);
495
496    for (int i = 0; i < NUM_LR; i++) {
497        ScopedCheckpointSection sec_lr(cp, csprintf("LR%d", i));
498        paramOut(cp, "lr", LR[i]);
499    }
500}
501
502void VGic::unserialize(CheckpointIn &cp)
503{
504    DPRINTF(Checkpoint, "Unserializing Arm GIC\n");
505
506    Tick interrupt_time[VGIC_CPU_MAX];
507    UNSERIALIZE_ARRAY(interrupt_time, VGIC_CPU_MAX);
508    for (uint32_t cpu = 0; cpu < VGIC_CPU_MAX; cpu++) {
509        if (interrupt_time[cpu])
510            schedule(postVIntEvent[cpu], interrupt_time[cpu]);
511
512        vcpuData[cpu].unserializeSection(cp, csprintf("vcpuData%d", cpu));
513    }
514    UNSERIALIZE_ARRAY(maintIntPosted, VGIC_CPU_MAX);
515    UNSERIALIZE_ARRAY(vIntPosted, VGIC_CPU_MAX);
516    UNSERIALIZE_SCALAR(vcpuAddr);
517    UNSERIALIZE_SCALAR(hvAddr);
518    UNSERIALIZE_SCALAR(pioDelay);
519    UNSERIALIZE_SCALAR(maintInt);
520}
521
522void
523VGic::vcpuIntData::unserialize(CheckpointIn &cp)
524{
525    paramIn(cp, "vctrl_val", vctrl);
526    paramIn(cp, "hcr_val", hcr);
527    paramIn(cp, "eisr_val", eisr);
528    paramIn(cp, "VMGrp0En_val", VMGrp0En);
529    paramIn(cp, "VMGrp1En_val", VMGrp1En);
530    paramIn(cp, "VMAckCtl_val", VMAckCtl);
531    paramIn(cp, "VMFiqEn_val", VMFiqEn);
532    paramIn(cp, "VMCBPR_val", VMCBPR);
533    paramIn(cp, "VEM_val", VEM);
534    paramIn(cp, "VMABP_val", VMABP);
535    paramIn(cp, "VMPriMask_val", VMPriMask);
536
537    for (int i = 0; i < NUM_LR; i++) {
538        ScopedCheckpointSection sec_lr(cp, csprintf("LR%d", i));
539        paramIn(cp, "lr", LR[i]);
540    }
541}
542
543VGic *
544VGicParams::create()
545{
546    return new VGic(this);
547}
548