summaryrefslogtreecommitdiff
path: root/src/dev/arm/gic_v2.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/dev/arm/gic_v2.cc')
-rw-r--r--src/dev/arm/gic_v2.cc1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/src/dev/arm/gic_v2.cc b/src/dev/arm/gic_v2.cc
new file mode 100644
index 000000000..6eaf06525
--- /dev/null
+++ b/src/dev/arm/gic_v2.cc
@@ -0,0 +1,1005 @@
+/*
+ * Copyright (c) 2010, 2013, 2015-2018 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2005 The Regents of The University of Michigan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Ali Saidi
+ * Prakash Ramrakhyani
+ */
+
+#include "dev/arm/gic_v2.hh"
+
+#include "base/trace.hh"
+#include "debug/Checkpoint.hh"
+#include "debug/GIC.hh"
+#include "debug/IPI.hh"
+#include "debug/Interrupt.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+
+const AddrRange GicV2::GICD_IGROUPR (0x080, 0x0ff);
+const AddrRange GicV2::GICD_ISENABLER (0x100, 0x17f);
+const AddrRange GicV2::GICD_ICENABLER (0x180, 0x1ff);
+const AddrRange GicV2::GICD_ISPENDR (0x200, 0x27f);
+const AddrRange GicV2::GICD_ICPENDR (0x280, 0x2ff);
+const AddrRange GicV2::GICD_ISACTIVER (0x300, 0x37f);
+const AddrRange GicV2::GICD_ICACTIVER (0x380, 0x3ff);
+const AddrRange GicV2::GICD_IPRIORITYR(0x400, 0x7ff);
+const AddrRange GicV2::GICD_ITARGETSR (0x800, 0xbff);
+const AddrRange GicV2::GICD_ICFGR (0xc00, 0xcff);
+
+GicV2::GicV2(const Params *p)
+ : BaseGic(p),
+ distRange(RangeSize(p->dist_addr, DIST_SIZE)),
+ cpuRange(RangeSize(p->cpu_addr, p->cpu_size)),
+ addrRanges{distRange, cpuRange},
+ distPioDelay(p->dist_pio_delay),
+ cpuPioDelay(p->cpu_pio_delay), intLatency(p->int_latency),
+ enabled(false), haveGem5Extensions(p->gem5_extensions),
+ itLines(p->it_lines),
+ intEnabled {}, pendingInt {}, activeInt {},
+ intPriority {}, cpuTarget {}, intConfig {},
+ cpuSgiPending {}, cpuSgiActive {},
+ cpuSgiPendingExt {}, cpuSgiActiveExt {},
+ cpuPpiPending {}, cpuPpiActive {},
+ pendingDelayedInterrupts(0)
+{
+ for (int x = 0; x < CPU_MAX; x++) {
+ iccrpr[x] = 0xff;
+ cpuEnabled[x] = false;
+ cpuPriority[x] = 0xff;
+ cpuBpr[x] = GICC_BPR_MINIMUM;
+ // Initialize cpu highest int
+ cpuHighestInt[x] = SPURIOUS_INT;
+ postIntEvent[x] =
+ new EventFunctionWrapper([this, x]{ postDelayedInt(x); },
+ "Post Interrupt to CPU");
+ }
+ DPRINTF(Interrupt, "cpuEnabled[0]=%d cpuEnabled[1]=%d\n", cpuEnabled[0],
+ cpuEnabled[1]);
+
+ gem5ExtensionsEnabled = false;
+}
+
+GicV2::~GicV2()
+{
+ for (int x = 0; x < CPU_MAX; x++)
+ delete postIntEvent[x];
+}
+
+Tick
+GicV2::read(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr();
+
+ if (distRange.contains(addr))
+ return readDistributor(pkt);
+ else if (cpuRange.contains(addr))
+ return readCpu(pkt);
+ else
+ panic("Read to unknown address %#x\n", pkt->getAddr());
+}
+
+
+Tick
+GicV2::write(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr();
+
+ if (distRange.contains(addr))
+ return writeDistributor(pkt);
+ else if (cpuRange.contains(addr))
+ return writeCpu(pkt);
+ else
+ panic("Write to unknown address %#x\n", pkt->getAddr());
+}
+
+Tick
+GicV2::readDistributor(PacketPtr pkt)
+{
+ const Addr daddr = pkt->getAddr() - distRange.start();
+ const ContextID ctx = pkt->req->contextId();
+
+ DPRINTF(GIC, "gic distributor read register %#x\n", daddr);
+
+ const uint32_t resp = readDistributor(ctx, daddr, pkt->getSize());
+
+ switch (pkt->getSize()) {
+ case 1:
+ pkt->set<uint8_t>(resp);
+ break;
+ case 2:
+ pkt->set<uint16_t>(resp);
+ break;
+ case 4:
+ pkt->set<uint32_t>(resp);
+ break;
+ default:
+ panic("Invalid size while reading Distributor regs in GIC: %d\n",
+ pkt->getSize());
+ }
+
+ pkt->makeAtomicResponse();
+ return distPioDelay;
+}
+
+uint32_t
+GicV2::readDistributor(ContextID ctx, Addr daddr, size_t resp_sz)
+{
+ if (GICD_IGROUPR.contains(daddr)) {
+ return 0; // unimplemented; RAZ (read as zero)
+ }
+
+ if (GICD_ISENABLER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ISENABLER.start()) >> 2;
+ assert(ix < 32);
+ return getIntEnabled(ctx, ix);
+ }
+
+ if (GICD_ICENABLER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICENABLER.start()) >> 2;
+ assert(ix < 32);
+ return getIntEnabled(ctx, ix);
+ }
+
+ if (GICD_ISPENDR.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ISPENDR.start()) >> 2;
+ assert(ix < 32);
+ return getPendingInt(ctx, ix);
+ }
+
+ if (GICD_ICPENDR.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICPENDR.start()) >> 2;
+ assert(ix < 32);
+ return getPendingInt(ctx, ix);
+ }
+
+ if (GICD_ISACTIVER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ISACTIVER.start()) >> 2;
+ assert(ix < 32);
+ return getActiveInt(ctx, ix);
+ }
+
+ if (GICD_ICACTIVER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICACTIVER.start()) >> 2;
+ assert(ix < 32);
+ return getActiveInt(ctx, ix);
+ }
+
+ if (GICD_IPRIORITYR.contains(daddr)) {
+ Addr int_num = daddr - GICD_IPRIORITYR.start();
+ assert(int_num < INT_LINES_MAX);
+ DPRINTF(Interrupt, "Reading interrupt priority at int# %#x \n",
+ int_num);
+
+ switch (resp_sz) {
+ default: // will panic() after return to caller anyway
+ case 1:
+ return getIntPriority(ctx, int_num);
+ case 2:
+ assert((int_num + 1) < INT_LINES_MAX);
+ return (getIntPriority(ctx, int_num) |
+ getIntPriority(ctx, int_num+1) << 8);
+ case 4:
+ assert((int_num + 3) < INT_LINES_MAX);
+ return (getIntPriority(ctx, int_num) |
+ getIntPriority(ctx, int_num+1) << 8 |
+ getIntPriority(ctx, int_num+2) << 16 |
+ getIntPriority(ctx, int_num+3) << 24);
+ }
+ }
+
+ if (GICD_ITARGETSR.contains(daddr)) {
+ Addr int_num = daddr - GICD_ITARGETSR.start();
+ DPRINTF(GIC, "Reading processor target register for int# %#x \n",
+ int_num);
+ assert(int_num < INT_LINES_MAX);
+
+ if (resp_sz == 1) {
+ return getCpuTarget(ctx, int_num);
+ } else {
+ assert(resp_sz == 4);
+ int_num = mbits(int_num, 31, 2);
+ return (getCpuTarget(ctx, int_num) |
+ getCpuTarget(ctx, int_num+1) << 8 |
+ getCpuTarget(ctx, int_num+2) << 16 |
+ getCpuTarget(ctx, int_num+3) << 24) ;
+ }
+ }
+
+ if (GICD_ICFGR.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICFGR.start()) >> 2;
+ assert(ix < 64);
+ /** @todo software generated interrupts and PPIs
+ * can't be configured in some ways */
+ return intConfig[ix];
+ }
+
+ switch(daddr) {
+ case GICD_CTLR:
+ return enabled;
+ case GICD_TYPER:
+ /* The 0x100 is a made-up flag to show that gem5 extensions
+ * are available,
+ * write 0x200 to this register to enable it. */
+ return (((sys->numRunningContexts() - 1) << 5) |
+ (itLines/INT_BITS_MAX -1) |
+ (haveGem5Extensions ? 0x100 : 0x0));
+ case GICD_PIDR0:
+ //ARM defined DevID
+ return (GICD_400_PIDR_VALUE & 0xFF);
+ case GICD_PIDR1:
+ return ((GICD_400_PIDR_VALUE >> 8) & 0xFF);
+ case GICD_PIDR2:
+ return ((GICD_400_PIDR_VALUE >> 16) & 0xFF);
+ case GICD_PIDR3:
+ return ((GICD_400_PIDR_VALUE >> 24) & 0xFF);
+ case GICD_IIDR:
+ /* revision id is resorted to 1 and variant to 0*/
+ return GICD_400_IIDR_VALUE;
+ default:
+ panic("Tried to read Gic distributor at offset %#x\n", daddr);
+ break;
+ }
+}
+
+Tick
+GicV2::readCpu(PacketPtr pkt)
+{
+ const Addr daddr = pkt->getAddr() - cpuRange.start();
+
+ assert(pkt->req->hasContextId());
+ const ContextID ctx = pkt->req->contextId();
+ assert(ctx < sys->numRunningContexts());
+
+ DPRINTF(GIC, "gic cpu read register %#x cpu context: %d\n", daddr,
+ ctx);
+
+ pkt->set<uint32_t>(readCpu(ctx, daddr));
+
+ pkt->makeAtomicResponse();
+ return cpuPioDelay;
+}
+
+uint32_t
+GicV2::readCpu(ContextID ctx, Addr daddr)
+{
+ switch(daddr) {
+ case GICC_IIDR:
+ return GICC_400_IIDR_VALUE;
+ case GICC_CTLR:
+ return cpuEnabled[ctx];
+ case GICC_PMR:
+ return cpuPriority[ctx];
+ case GICC_BPR:
+ return cpuBpr[ctx];
+ case GICC_IAR:
+ if (enabled && cpuEnabled[ctx]) {
+ int active_int = cpuHighestInt[ctx];
+ IAR iar = 0;
+ iar.ack_id = active_int;
+ iar.cpu_id = 0;
+ if (active_int < SGI_MAX) {
+ // this is a software interrupt from another CPU
+ if (!gem5ExtensionsEnabled) {
+ panic_if(!cpuSgiPending[active_int],
+ "Interrupt %d active but no CPU generated it?\n",
+ active_int);
+ for (int x = 0; x < sys->numRunningContexts(); x++) {
+ // See which CPU generated the interrupt
+ uint8_t cpugen =
+ bits(cpuSgiPending[active_int], 7 + 8 * x, 8 * x);
+ if (cpugen & (1 << ctx)) {
+ iar.cpu_id = x;
+ break;
+ }
+ }
+ uint64_t sgi_num = ULL(1) << (ctx + 8 * iar.cpu_id);
+ cpuSgiActive[iar.ack_id] |= sgi_num;
+ cpuSgiPending[iar.ack_id] &= ~sgi_num;
+ } else {
+ uint64_t sgi_num = ULL(1) << iar.ack_id;
+ cpuSgiActiveExt[ctx] |= sgi_num;
+ cpuSgiPendingExt[ctx] &= ~sgi_num;
+ }
+ } else if (active_int < (SGI_MAX + PPI_MAX) ) {
+ uint32_t int_num = 1 << (cpuHighestInt[ctx] - SGI_MAX);
+ cpuPpiActive[ctx] |= int_num;
+ updateRunPri();
+ cpuPpiPending[ctx] &= ~int_num;
+
+ } else {
+ uint32_t int_num = 1 << intNumToBit(cpuHighestInt[ctx]);
+ getActiveInt(ctx, intNumToWord(cpuHighestInt[ctx])) |= int_num;
+ updateRunPri();
+ getPendingInt(ctx, intNumToWord(cpuHighestInt[ctx]))
+ &= ~int_num;
+ }
+
+ DPRINTF(Interrupt,
+ "CPU %d reading IAR.id=%d IAR.cpu=%d, iar=0x%x\n",
+ ctx, iar.ack_id, iar.cpu_id, iar);
+ cpuHighestInt[ctx] = SPURIOUS_INT;
+ updateIntState(-1);
+ platform->intrctrl->clear(ctx, ArmISA::INT_IRQ, 0);
+ return iar;
+ } else {
+ return SPURIOUS_INT;
+ }
+
+ break;
+ case GICC_RPR:
+ return iccrpr[0];
+ case GICC_HPPIR:
+ panic("Need to implement HPIR");
+ break;
+ default:
+ panic("Tried to read Gic cpu at offset %#x\n", daddr);
+ break;
+ }
+}
+
+Tick
+GicV2::writeDistributor(PacketPtr pkt)
+{
+ const Addr daddr = pkt->getAddr() - distRange.start();
+
+ assert(pkt->req->hasContextId());
+ const ContextID ctx = pkt->req->contextId();
+ const size_t data_sz = pkt->getSize();
+
+ uint32_t pkt_data M5_VAR_USED;
+ switch (data_sz)
+ {
+ case 1:
+ pkt_data = pkt->get<uint8_t>();
+ break;
+ case 2:
+ pkt_data = pkt->get<uint16_t>();
+ break;
+ case 4:
+ pkt_data = pkt->get<uint32_t>();
+ break;
+ default:
+ panic("Invalid size when writing to priority regs in Gic: %d\n",
+ data_sz);
+ }
+
+ DPRINTF(GIC, "gic distributor write register %#x size %#x value %#x \n",
+ daddr, data_sz, pkt_data);
+
+ writeDistributor(ctx, daddr, pkt_data, data_sz);
+
+ pkt->makeAtomicResponse();
+ return distPioDelay;
+}
+
+void
+GicV2::writeDistributor(ContextID ctx, Addr daddr, uint32_t data,
+ size_t data_sz)
+{
+ if (GICD_IGROUPR.contains(daddr)) {
+ return; // unimplemented; WI (writes ignored)
+ }
+
+ if (GICD_ISENABLER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ISENABLER.start()) >> 2;
+ assert(ix < 32);
+ getIntEnabled(ctx, ix) |= data;
+ return;
+ }
+
+ if (GICD_ICENABLER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICENABLER.start()) >> 2;
+ assert(ix < 32);
+ getIntEnabled(ctx, ix) &= ~data;
+ return;
+ }
+
+ if (GICD_ISPENDR.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ISPENDR.start()) >> 2;
+ auto mask = data;
+ if (ix == 0) mask &= SGI_MASK; // Don't allow SGIs to be changed
+ getPendingInt(ctx, ix) |= mask;
+ updateIntState(ix);
+ return;
+ }
+
+ if (GICD_ICPENDR.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICPENDR.start()) >> 2;
+ auto mask = data;
+ if (ix == 0) mask &= SGI_MASK; // Don't allow SGIs to be changed
+ getPendingInt(ctx, ix) &= ~mask;
+ updateIntState(ix);
+ return;
+ }
+
+ if (GICD_ISACTIVER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ISACTIVER.start()) >> 2;
+ getActiveInt(ctx, ix) |= data;
+ return;
+ }
+
+ if (GICD_ICACTIVER.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICACTIVER.start()) >> 2;
+ getActiveInt(ctx, ix) &= ~data;
+ return;
+ }
+
+ if (GICD_IPRIORITYR.contains(daddr)) {
+ Addr int_num = daddr - GICD_IPRIORITYR.start();
+ switch(data_sz) {
+ case 1:
+ getIntPriority(ctx, int_num) = data;
+ break;
+ case 2: {
+ getIntPriority(ctx, int_num) = bits(data, 7, 0);
+ getIntPriority(ctx, int_num + 1) = bits(data, 15, 8);
+ break;
+ }
+ case 4: {
+ getIntPriority(ctx, int_num) = bits(data, 7, 0);
+ getIntPriority(ctx, int_num + 1) = bits(data, 15, 8);
+ getIntPriority(ctx, int_num + 2) = bits(data, 23, 16);
+ getIntPriority(ctx, int_num + 3) = bits(data, 31, 24);
+ break;
+ }
+ default:
+ panic("Invalid size when writing to priority regs in Gic: %d\n",
+ data_sz);
+ }
+
+ updateIntState(-1);
+ updateRunPri();
+ return;
+ }
+
+ if (GICD_ITARGETSR.contains(daddr)) {
+ Addr int_num = daddr - GICD_ITARGETSR.start();
+ // Interrupts 0-31 are read only
+ unsigned offset = SGI_MAX + PPI_MAX;
+ if (int_num >= offset) {
+ unsigned ix = int_num - offset; // index into cpuTarget array
+ if (data_sz == 1) {
+ cpuTarget[ix] = data & 0xff;
+ } else {
+ assert (data_sz == 4);
+ cpuTarget[ix] = bits(data, 7, 0);
+ cpuTarget[ix+1] = bits(data, 15, 8);
+ cpuTarget[ix+2] = bits(data, 23, 16);
+ cpuTarget[ix+3] = bits(data, 31, 24);
+ }
+ updateIntState(int_num >> 2);
+ }
+ return;
+ }
+
+ if (GICD_ICFGR.contains(daddr)) {
+ uint32_t ix = (daddr - GICD_ICFGR.start()) >> 2;
+ assert(ix < INT_BITS_MAX*2);
+ intConfig[ix] = data;
+ if (data & NN_CONFIG_MASK)
+ warn("GIC N:N mode selected and not supported at this time\n");
+ return;
+ }
+
+ switch(daddr) {
+ case GICD_CTLR:
+ enabled = data;
+ DPRINTF(Interrupt, "Distributor enable flag set to = %d\n", enabled);
+ break;
+ case GICD_TYPER:
+ /* 0x200 is a made-up flag to enable gem5 extension functionality.
+ * This reg is not normally written.
+ */
+ gem5ExtensionsEnabled = (data & 0x200) && haveGem5Extensions;
+ DPRINTF(GIC, "gem5 extensions %s\n",
+ gem5ExtensionsEnabled ? "enabled" : "disabled");
+ break;
+ case GICD_SGIR:
+ softInt(ctx, data);
+ break;
+ default:
+ panic("Tried to write Gic distributor at offset %#x\n", daddr);
+ break;
+ }
+}
+
+Tick
+GicV2::writeCpu(PacketPtr pkt)
+{
+ const Addr daddr = pkt->getAddr() - cpuRange.start();
+
+ assert(pkt->req->hasContextId());
+ const ContextID ctx = pkt->req->contextId();
+ const uint32_t data = pkt->get<uint32_t>();
+
+ DPRINTF(GIC, "gic cpu write register cpu:%d %#x val: %#x\n",
+ ctx, daddr, data);
+
+ writeCpu(ctx, daddr, data);
+
+ pkt->makeAtomicResponse();
+ return cpuPioDelay;
+}
+
+void
+GicV2::writeCpu(ContextID ctx, Addr daddr, uint32_t data)
+{
+ switch(daddr) {
+ case GICC_CTLR:
+ cpuEnabled[ctx] = data;
+ break;
+ case GICC_PMR:
+ cpuPriority[ctx] = data;
+ break;
+ case GICC_BPR: {
+ auto bpr = data & 0x7;
+ if (bpr < GICC_BPR_MINIMUM)
+ bpr = GICC_BPR_MINIMUM;
+ cpuBpr[ctx] = bpr;
+ break;
+ }
+ case GICC_EOIR: {
+ const IAR iar = data;
+ if (iar.ack_id < SGI_MAX) {
+ // Clear out the bit that corresponds to the cleared int
+ uint64_t clr_int = ULL(1) << (ctx + 8 * iar.cpu_id);
+ if (!(cpuSgiActive[iar.ack_id] & clr_int) &&
+ !(cpuSgiActiveExt[ctx] & (1 << iar.ack_id)))
+ panic("Done handling a SGI that isn't active?\n");
+ if (gem5ExtensionsEnabled)
+ cpuSgiActiveExt[ctx] &= ~(1 << iar.ack_id);
+ else
+ cpuSgiActive[iar.ack_id] &= ~clr_int;
+ } else if (iar.ack_id < (SGI_MAX + PPI_MAX) ) {
+ uint32_t int_num = 1 << (iar.ack_id - SGI_MAX);
+ if (!(cpuPpiActive[ctx] & int_num))
+ panic("CPU %d Done handling a PPI interrupt "
+ "that isn't active?\n", ctx);
+ cpuPpiActive[ctx] &= ~int_num;
+ } else {
+ uint32_t int_num = 1 << intNumToBit(iar.ack_id);
+ if (!(getActiveInt(ctx, intNumToWord(iar.ack_id)) & int_num))
+ warn("Done handling interrupt that isn't active: %d\n",
+ intNumToBit(iar.ack_id));
+ getActiveInt(ctx, intNumToWord(iar.ack_id)) &= ~int_num;
+ }
+ updateRunPri();
+ DPRINTF(Interrupt, "CPU %d done handling intr IAR = %d from cpu %d\n",
+ ctx, iar.ack_id, iar.cpu_id);
+ break;
+ }
+ case GICC_APR0:
+ case GICC_APR1:
+ case GICC_APR2:
+ case GICC_APR3:
+ warn("GIC APRn write ignored because not implemented: %#x\n", daddr);
+ break;
+ default:
+ panic("Tried to write Gic cpu at offset %#x\n", daddr);
+ break;
+ }
+ if (cpuEnabled[ctx]) updateIntState(-1);
+}
+
+GicV2::BankedRegs&
+GicV2::getBankedRegs(ContextID ctx) {
+ if (bankedRegs.size() <= ctx)
+ bankedRegs.resize(ctx + 1);
+
+ if (!bankedRegs[ctx])
+ bankedRegs[ctx] = new BankedRegs;
+ return *bankedRegs[ctx];
+}
+
+void
+GicV2::softInt(ContextID ctx, SWI swi)
+{
+ if (gem5ExtensionsEnabled) {
+ switch (swi.list_type) {
+ case 0: {
+ // interrupt cpus specified
+ int dest = swi.cpu_list;
+ DPRINTF(IPI, "Generating softIRQ from CPU %d for CPU %d\n",
+ ctx, dest);
+ if (cpuEnabled[dest]) {
+ cpuSgiPendingExt[dest] |= (1 << swi.sgi_id);
+ DPRINTF(IPI, "SGI[%d]=%#x\n", dest,
+ cpuSgiPendingExt[dest]);
+ }
+ } break;
+ case 1: {
+ // interrupt all
+ for (int i = 0; i < sys->numContexts(); i++) {
+ DPRINTF(IPI, "Processing CPU %d\n", i);
+ if (!cpuEnabled[i])
+ continue;
+ cpuSgiPendingExt[i] |= 1 << swi.sgi_id;
+ DPRINTF(IPI, "SGI[%d]=%#x\n", swi.sgi_id,
+ cpuSgiPendingExt[i]);
+ }
+ } break;
+ case 2: {
+ // Interrupt requesting cpu only
+ DPRINTF(IPI, "Generating softIRQ from CPU %d for CPU %d\n",
+ ctx, ctx);
+ if (cpuEnabled[ctx]) {
+ cpuSgiPendingExt[ctx] |= (1 << swi.sgi_id);
+ DPRINTF(IPI, "SGI[%d]=%#x\n", ctx,
+ cpuSgiPendingExt[ctx]);
+ }
+ } break;
+ }
+ } else {
+ switch (swi.list_type) {
+ case 1:
+ // interrupt all
+ uint8_t cpu_list;
+ cpu_list = 0;
+ for (int x = 0; x < sys->numContexts(); x++)
+ cpu_list |= cpuEnabled[x] ? 1 << x : 0;
+ swi.cpu_list = cpu_list;
+ break;
+ case 2:
+ // interrupt requesting cpu only
+ swi.cpu_list = 1 << ctx;
+ break;
+ // else interrupt cpus specified
+ }
+
+ DPRINTF(IPI, "Generating softIRQ from CPU %d for %#x\n", ctx,
+ swi.cpu_list);
+ for (int i = 0; i < sys->numContexts(); i++) {
+ DPRINTF(IPI, "Processing CPU %d\n", i);
+ if (!cpuEnabled[i])
+ continue;
+ if (swi.cpu_list & (1 << i))
+ cpuSgiPending[swi.sgi_id] |= (1 << i) << (8 * ctx);
+ DPRINTF(IPI, "SGI[%d]=%#x\n", swi.sgi_id,
+ cpuSgiPending[swi.sgi_id]);
+ }
+ }
+ updateIntState(-1);
+}
+
+uint64_t
+GicV2::genSwiMask(int cpu)
+{
+ if (cpu > sys->numContexts())
+ panic("Invalid CPU ID\n");
+ return ULL(0x0101010101010101) << cpu;
+}
+
+uint8_t
+GicV2::getCpuPriority(unsigned cpu)
+{
+ // see Table 3-2 in IHI0048B.b (GICv2)
+ // mask some low-order priority bits per BPR value
+ // NB: the GIC prioritization scheme is upside down:
+ // lower values are higher priority; masking off bits
+ // actually creates a higher priority, not lower.
+ return cpuPriority[cpu] & (0xff00 >> (7 - cpuBpr[cpu]));
+}
+
+void
+GicV2::updateIntState(int hint)
+{
+ for (int cpu = 0; cpu < sys->numContexts(); cpu++) {
+ if (!cpuEnabled[cpu])
+ continue;
+
+ /*@todo use hint to do less work. */
+ int highest_int = SPURIOUS_INT;
+ // Priorities below that set in GICC_PMR can be ignored
+ uint8_t highest_pri = getCpuPriority(cpu);
+
+ // Check SGIs
+ for (int swi = 0; swi < SGI_MAX; swi++) {
+ if (!cpuSgiPending[swi] && !cpuSgiPendingExt[cpu])
+ continue;
+ if ((cpuSgiPending[swi] & genSwiMask(cpu)) ||
+ (cpuSgiPendingExt[cpu] & (1 << swi)))
+ if (highest_pri > getIntPriority(cpu, swi)) {
+ highest_pri = getIntPriority(cpu, swi);
+ highest_int = swi;
+ }
+ }
+
+ // Check PPIs
+ if (cpuPpiPending[cpu]) {
+ for (int ppi = 0; ppi < PPI_MAX; ppi++) {
+ if (cpuPpiPending[cpu] & (1 << ppi))
+ if (highest_pri > getIntPriority(cpu, SGI_MAX + ppi)) {
+ highest_pri = getIntPriority(cpu, SGI_MAX + ppi);
+ highest_int = SGI_MAX + ppi;
+ }
+ }
+ }
+
+ bool mp_sys = sys->numRunningContexts() > 1;
+ // Check other ints
+ for (int x = 0; x < (itLines/INT_BITS_MAX); x++) {
+ if (getIntEnabled(cpu, x) & getPendingInt(cpu, x)) {
+ for (int y = 0; y < INT_BITS_MAX; y++) {
+ uint32_t int_nm = x * INT_BITS_MAX + y;
+ DPRINTF(GIC, "Checking for interrupt# %d \n",int_nm);
+ /* Set current pending int as highest int for current cpu
+ if the interrupt's priority higher than current priority
+ and if current cpu is the target (for mp configs only)
+ */
+ if ((bits(getIntEnabled(cpu, x), y)
+ &bits(getPendingInt(cpu, x), y)) &&
+ (getIntPriority(cpu, int_nm) < highest_pri))
+ if ((!mp_sys) ||
+ (gem5ExtensionsEnabled
+ ? (getCpuTarget(cpu, int_nm) == cpu)
+ : (getCpuTarget(cpu, int_nm) & (1 << cpu)))) {
+ highest_pri = getIntPriority(cpu, int_nm);
+ highest_int = int_nm;
+ }
+ }
+ }
+ }
+
+ cpuHighestInt[cpu] = highest_int;
+
+ if (highest_int == SPURIOUS_INT)
+ continue;
+
+ /* @todo make this work for more than one cpu, need to handle 1:N, N:N
+ * models */
+ if (enabled && cpuEnabled[cpu] &&
+ (highest_pri < getCpuPriority(cpu)) &&
+ !(getActiveInt(cpu, intNumToWord(highest_int))
+ & (1 << intNumToBit(highest_int)))) {
+
+ DPRINTF(Interrupt, "Posting interrupt %d to cpu%d\n", highest_int,
+ cpu);
+ postInt(cpu, curTick() + intLatency);
+ }
+ }
+}
+
+void
+GicV2::updateRunPri()
+{
+ for (int cpu = 0; cpu < sys->numContexts(); cpu++) {
+ if (!cpuEnabled[cpu])
+ continue;
+ uint8_t maxPriority = 0xff;
+ for (int i = 0; i < itLines; i++) {
+ if (i < SGI_MAX) {
+ if (((cpuSgiActive[i] & genSwiMask(cpu)) ||
+ (cpuSgiActiveExt[cpu] & (1 << i))) &&
+ (getIntPriority(cpu, i) < maxPriority))
+ maxPriority = getIntPriority(cpu, i);
+ } else if (i < (SGI_MAX + PPI_MAX)) {
+ if ((cpuPpiActive[cpu] & ( 1 << (i - SGI_MAX))) &&
+ (getIntPriority(cpu, i) < maxPriority))
+ maxPriority = getIntPriority(cpu, i);
+
+ } else {
+ if (getActiveInt(cpu, intNumToWord(i))
+ & (1 << intNumToBit(i)))
+ if (getIntPriority(cpu, i) < maxPriority)
+ maxPriority = getIntPriority(cpu, i);
+ }
+ }
+ iccrpr[cpu] = maxPriority;
+ }
+}
+
+void
+GicV2::sendInt(uint32_t num)
+{
+ uint8_t target = getCpuTarget(0, num);
+ DPRINTF(Interrupt, "Received Interrupt number %d, cpuTarget %#x: \n",
+ num, target);
+ if ((target & (target - 1)) && !gem5ExtensionsEnabled)
+ panic("Multiple targets for peripheral interrupts is not supported\n");
+ panic_if(num < SGI_MAX + PPI_MAX,
+ "sentInt() must only be used for interrupts 32 and higher");
+ getPendingInt(target, intNumToWord(num)) |= 1 << intNumToBit(num);
+ updateIntState(intNumToWord(num));
+}
+
+void
+GicV2::sendPPInt(uint32_t num, uint32_t cpu)
+{
+ DPRINTF(Interrupt, "Received PPI %d, cpuTarget %#x: \n",
+ num, cpu);
+ cpuPpiPending[cpu] |= 1 << (num - SGI_MAX);
+ updateIntState(intNumToWord(num));
+}
+
+void
+GicV2::clearInt(uint32_t number)
+{
+ /* @todo assume edge triggered only at the moment. Nothing to do. */
+}
+
+void
+GicV2::clearPPInt(uint32_t num, uint32_t cpu)
+{
+ DPRINTF(Interrupt, "Clearing PPI %d, cpuTarget %#x: \n",
+ num, cpu);
+ cpuPpiPending[cpu] &= ~(1 << (num - SGI_MAX));
+ updateIntState(intNumToWord(num));
+}
+
+void
+GicV2::postInt(uint32_t cpu, Tick when)
+{
+ if (!(postIntEvent[cpu]->scheduled())) {
+ ++pendingDelayedInterrupts;
+ eventq->schedule(postIntEvent[cpu], when);
+ }
+}
+
+void
+GicV2::postDelayedInt(uint32_t cpu)
+{
+ platform->intrctrl->post(cpu, ArmISA::INT_IRQ, 0);
+ --pendingDelayedInterrupts;
+ assert(pendingDelayedInterrupts >= 0);
+ if (pendingDelayedInterrupts == 0)
+ signalDrainDone();
+}
+
+DrainState
+GicV2::drain()
+{
+ if (pendingDelayedInterrupts == 0) {
+ return DrainState::Drained;
+ } else {
+ return DrainState::Draining;
+ }
+}
+
+
+void
+GicV2::drainResume()
+{
+ // There may be pending interrupts if checkpointed from Kvm; post them.
+ updateIntState(-1);
+}
+
+void
+GicV2::serialize(CheckpointOut &cp) const
+{
+ DPRINTF(Checkpoint, "Serializing Arm GIC\n");
+
+ SERIALIZE_SCALAR(enabled);
+ SERIALIZE_SCALAR(itLines);
+ SERIALIZE_ARRAY(intEnabled, INT_BITS_MAX-1);
+ SERIALIZE_ARRAY(pendingInt, INT_BITS_MAX-1);
+ SERIALIZE_ARRAY(activeInt, INT_BITS_MAX-1);
+ SERIALIZE_ARRAY(iccrpr, CPU_MAX);
+ SERIALIZE_ARRAY(intPriority, GLOBAL_INT_LINES);
+ SERIALIZE_ARRAY(cpuTarget, GLOBAL_INT_LINES);
+ SERIALIZE_ARRAY(intConfig, INT_BITS_MAX * 2);
+ SERIALIZE_ARRAY(cpuEnabled, CPU_MAX);
+ SERIALIZE_ARRAY(cpuPriority, CPU_MAX);
+ SERIALIZE_ARRAY(cpuBpr, CPU_MAX);
+ SERIALIZE_ARRAY(cpuHighestInt, CPU_MAX);
+ SERIALIZE_ARRAY(cpuSgiActive, SGI_MAX);
+ SERIALIZE_ARRAY(cpuSgiPending, SGI_MAX);
+ SERIALIZE_ARRAY(cpuSgiActiveExt, CPU_MAX);
+ SERIALIZE_ARRAY(cpuSgiPendingExt, CPU_MAX);
+ SERIALIZE_ARRAY(cpuPpiActive, CPU_MAX);
+ SERIALIZE_ARRAY(cpuPpiPending, CPU_MAX);
+ SERIALIZE_SCALAR(gem5ExtensionsEnabled);
+
+ for (uint32_t i=0; i < bankedRegs.size(); ++i) {
+ if (!bankedRegs[i])
+ continue;
+ bankedRegs[i]->serializeSection(cp, csprintf("bankedRegs%i", i));
+ }
+}
+
+void
+GicV2::BankedRegs::serialize(CheckpointOut &cp) const
+{
+ SERIALIZE_SCALAR(intEnabled);
+ SERIALIZE_SCALAR(pendingInt);
+ SERIALIZE_SCALAR(activeInt);
+ SERIALIZE_ARRAY(intPriority, SGI_MAX + PPI_MAX);
+}
+
+void
+GicV2::unserialize(CheckpointIn &cp)
+{
+ DPRINTF(Checkpoint, "Unserializing Arm GIC\n");
+
+ UNSERIALIZE_SCALAR(enabled);
+ UNSERIALIZE_SCALAR(itLines);
+ UNSERIALIZE_ARRAY(intEnabled, INT_BITS_MAX-1);
+ UNSERIALIZE_ARRAY(pendingInt, INT_BITS_MAX-1);
+ UNSERIALIZE_ARRAY(activeInt, INT_BITS_MAX-1);
+ UNSERIALIZE_ARRAY(iccrpr, CPU_MAX);
+ UNSERIALIZE_ARRAY(intPriority, GLOBAL_INT_LINES);
+ UNSERIALIZE_ARRAY(cpuTarget, GLOBAL_INT_LINES);
+ UNSERIALIZE_ARRAY(intConfig, INT_BITS_MAX * 2);
+ UNSERIALIZE_ARRAY(cpuEnabled, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuPriority, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuBpr, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuHighestInt, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuSgiActive, SGI_MAX);
+ UNSERIALIZE_ARRAY(cpuSgiPending, SGI_MAX);
+ UNSERIALIZE_ARRAY(cpuSgiActiveExt, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuSgiPendingExt, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuPpiActive, CPU_MAX);
+ UNSERIALIZE_ARRAY(cpuPpiPending, CPU_MAX);
+
+ // Handle checkpoints from before we drained the GIC to prevent
+ // in-flight interrupts.
+ if (cp.entryExists(Serializable::currentSection(), "interrupt_time")) {
+ Tick interrupt_time[CPU_MAX];
+ UNSERIALIZE_ARRAY(interrupt_time, CPU_MAX);
+
+ for (uint32_t cpu = 0; cpu < CPU_MAX; cpu++) {
+ if (interrupt_time[cpu])
+ schedule(postIntEvent[cpu], interrupt_time[cpu]);
+ }
+ }
+
+ if (!UNSERIALIZE_OPT_SCALAR(gem5ExtensionsEnabled))
+ gem5ExtensionsEnabled = false;
+
+ for (uint32_t i=0; i < CPU_MAX; ++i) {
+ ScopedCheckpointSection sec(cp, csprintf("bankedRegs%i", i));
+ if (cp.sectionExists(Serializable::currentSection())) {
+ getBankedRegs(i).unserialize(cp);
+ }
+ }
+}
+
+void
+GicV2::BankedRegs::unserialize(CheckpointIn &cp)
+{
+ UNSERIALIZE_SCALAR(intEnabled);
+ UNSERIALIZE_SCALAR(pendingInt);
+ UNSERIALIZE_SCALAR(activeInt);
+ UNSERIALIZE_ARRAY(intPriority, SGI_MAX + PPI_MAX);
+}
+
+GicV2 *
+GicV2Params::create()
+{
+ return new GicV2(this);
+}