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 &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
549VGic *
550VGicParams::create()
551{
552    return new VGic(this);
553}
554