838 lines
26 KiB
C++
838 lines
26 KiB
C++
/*
|
|
* Copyright (c) 2010 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 "base/trace.hh"
|
|
#include "debug/Checkpoint.hh"
|
|
#include "debug/GIC.hh"
|
|
#include "debug/IPI.hh"
|
|
#include "dev/arm/gic.hh"
|
|
#include "dev/arm/realview.hh"
|
|
#include "dev/terminal.hh"
|
|
#include "mem/packet.hh"
|
|
#include "mem/packet_access.hh"
|
|
|
|
Gic::Gic(const Params *p)
|
|
: PioDevice(p),distAddr(p->dist_addr), cpuAddr(p->cpu_addr),
|
|
distPioDelay(p->dist_pio_delay), cpuPioDelay(p->cpu_pio_delay),
|
|
intLatency(p->int_latency), enabled(false), itLines(p->it_lines)
|
|
{
|
|
itLinesLog2 = ceilLog2(itLines);
|
|
|
|
for (int x = 0; x < CPU_MAX; x++) {
|
|
cpuEnabled[x] = false;
|
|
cpuPriority[x] = 0xff;
|
|
cpuBpr[x] = 0;
|
|
// Initialize cpu highest int
|
|
cpuHighestInt[x] = SPURIOUS_INT;
|
|
postIntEvent[x] = new PostIntEvent(x, p->platform);
|
|
}
|
|
DPRINTF(Interrupt, "cpuEnabled[0]=%d cpuEnabled[1]=%d\n", cpuEnabled[0],
|
|
cpuEnabled[1]);
|
|
|
|
for (int x = 0; x < INT_BITS_MAX; x++) {
|
|
intEnabled[x] = 0;
|
|
pendingInt[x] = 0;
|
|
activeInt[x] = 0;
|
|
}
|
|
|
|
for (int x = 0; x < INT_LINES_MAX; x++) {
|
|
intPriority[x] = 0;
|
|
cpuTarget[x] = 0;
|
|
}
|
|
|
|
for (int x = 0; x < INT_BITS_MAX*2; x++) {
|
|
intConfig[x] = 0;
|
|
}
|
|
|
|
for (int x = 0; x < SGI_MAX; x++) {
|
|
cpuSgiActive[x] = 0;
|
|
cpuSgiPending[x] = 0;
|
|
}
|
|
for (int x = 0; x < CPU_MAX; x++) {
|
|
cpuPpiActive[x] = 0;
|
|
cpuPpiPending[x] = 0;
|
|
}
|
|
|
|
for (int i = 0; i < CPU_MAX; i++) {
|
|
for (int j = 0; j < (SGI_MAX + PPI_MAX); j++) {
|
|
bankedIntPriority[i][j] = 0;
|
|
}
|
|
}
|
|
|
|
RealView *rv = dynamic_cast<RealView*>(p->platform);
|
|
assert(rv);
|
|
rv->setGic(this);
|
|
|
|
}
|
|
|
|
Tick
|
|
Gic::read(PacketPtr pkt)
|
|
{
|
|
|
|
Addr addr = pkt->getAddr();
|
|
|
|
if (addr >= distAddr && addr < distAddr + DIST_SIZE)
|
|
return readDistributor(pkt);
|
|
else if (addr >= cpuAddr && addr < cpuAddr + CPU_SIZE)
|
|
return readCpu(pkt);
|
|
else
|
|
panic("Read to unknown address %#x\n", pkt->getAddr());
|
|
}
|
|
|
|
|
|
Tick
|
|
Gic::write(PacketPtr pkt)
|
|
{
|
|
|
|
Addr addr = pkt->getAddr();
|
|
|
|
if (addr >= distAddr && addr < distAddr + DIST_SIZE)
|
|
return writeDistributor(pkt);
|
|
else if (addr >= cpuAddr && addr < cpuAddr + CPU_SIZE)
|
|
return writeCpu(pkt);
|
|
else
|
|
panic("Write to unknown address %#x\n", pkt->getAddr());
|
|
}
|
|
|
|
Tick
|
|
Gic::readDistributor(PacketPtr pkt)
|
|
{
|
|
Addr daddr = pkt->getAddr() - distAddr;
|
|
pkt->allocate();
|
|
|
|
int ctx_id = pkt->req->contextId();
|
|
|
|
DPRINTF(GIC, "gic distributor read register %#x\n", daddr);
|
|
|
|
if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) {
|
|
assert((daddr-ICDISER_ST) >> 2 < 32);
|
|
pkt->set<uint32_t>(intEnabled[(daddr-ICDISER_ST)>>2]);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDICER_ST && daddr < ICDICER_ED + 4) {
|
|
assert((daddr-ICDICER_ST) >> 2 < 32);
|
|
pkt->set<uint32_t>(intEnabled[(daddr-ICDICER_ST)>>2]);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDISPR_ST && daddr < ICDISPR_ED + 4) {
|
|
assert((daddr-ICDISPR_ST) >> 2 < 32);
|
|
pkt->set<uint32_t>(pendingInt[(daddr-ICDISPR_ST)>>2]);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDICPR_ST && daddr < ICDICPR_ED + 4) {
|
|
assert((daddr-ICDICPR_ST) >> 2 < 32);
|
|
pkt->set<uint32_t>(pendingInt[(daddr-ICDICPR_ST)>>2]);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDABR_ST && daddr < ICDABR_ED + 4) {
|
|
assert((daddr-ICDABR_ST) >> 2 < 32);
|
|
pkt->set<uint32_t>(activeInt[(daddr-ICDABR_ST)>>2]);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
|
|
Addr int_num;
|
|
int_num = daddr - ICDIPR_ST;
|
|
assert(int_num < INT_LINES_MAX);
|
|
DPRINTF(Interrupt, "Reading interrupt priority at int# %#x \n",int_num);
|
|
|
|
uint8_t* int_p;
|
|
if (int_num < (SGI_MAX + PPI_MAX))
|
|
int_p = bankedIntPriority[ctx_id];
|
|
else
|
|
int_p = intPriority;
|
|
|
|
switch (pkt->getSize()) {
|
|
case 1:
|
|
pkt->set<uint8_t>(int_p[int_num]);
|
|
break;
|
|
case 2:
|
|
pkt->set<uint16_t>(int_p[int_num] |
|
|
int_p[int_num+1] << 8);
|
|
break;
|
|
case 4:
|
|
pkt->set<uint32_t>(int_p[int_num] |
|
|
int_p[int_num+1] << 8 |
|
|
int_p[int_num+2] << 16 |
|
|
int_p[int_num+3] << 24);
|
|
break;
|
|
default:
|
|
panic("Invalid size while reading priority regs in GIC: %d\n",
|
|
pkt->getSize());
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDIPTR_ST && daddr < ICDIPTR_ED + 4) {
|
|
Addr int_num;
|
|
int_num = (daddr-ICDIPTR_ST) ;
|
|
DPRINTF(GIC, "Reading processor target register for int# %#x \n",
|
|
int_num);
|
|
assert(int_num < INT_LINES_MAX);
|
|
|
|
// First 31 interrupts only target single processor (SGI)
|
|
if (int_num > 31) {
|
|
if (pkt->getSize() == 1) {
|
|
pkt->set<uint8_t>(cpuTarget[int_num]);
|
|
} else {
|
|
assert(pkt->getSize() == 4);
|
|
int_num = mbits(int_num, 31, 2);
|
|
pkt->set<uint32_t>(cpuTarget[int_num] |
|
|
cpuTarget[int_num+1] << 8 |
|
|
cpuTarget[int_num+2] << 16 |
|
|
cpuTarget[int_num+3] << 24) ;
|
|
}
|
|
} else {
|
|
int ctx_id = pkt->req->contextId();
|
|
assert(ctx_id < sys->numRunningContexts());
|
|
pkt->set<uint32_t>(ctx_id);
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDICFR_ST && daddr < ICDICFR_ED + 4) {
|
|
assert((daddr-ICDICFR_ST) >> 2 < 64);
|
|
/** @todo software generated interrutps and PPIs
|
|
* can't be configured in some ways
|
|
*/
|
|
pkt->set<uint32_t>(intConfig[(daddr-ICDICFR_ST)>>2]);
|
|
goto done;
|
|
}
|
|
|
|
switch(daddr) {
|
|
case ICDDCR:
|
|
pkt->set<uint32_t>(enabled);
|
|
break;
|
|
case ICDICTR:
|
|
uint32_t tmp;
|
|
tmp = ((sys->numRunningContexts() - 1) << 5) |
|
|
(itLines/INT_BITS_MAX -1);
|
|
pkt->set<uint32_t>(tmp);
|
|
break;
|
|
default:
|
|
panic("Tried to read Gic distributor at offset %#x\n", daddr);
|
|
break;
|
|
}
|
|
done:
|
|
pkt->makeAtomicResponse();
|
|
return distPioDelay;
|
|
}
|
|
|
|
Tick
|
|
Gic::readCpu(PacketPtr pkt)
|
|
{
|
|
Addr daddr = pkt->getAddr() - cpuAddr;
|
|
pkt->allocate();
|
|
|
|
assert(pkt->req->hasContextId());
|
|
int ctx_id = pkt->req->contextId();
|
|
assert(ctx_id < sys->numRunningContexts());
|
|
|
|
DPRINTF(GIC, "gic cpu read register %#x cpu context: %d\n", daddr,
|
|
ctx_id);
|
|
|
|
switch(daddr) {
|
|
case ICCICR:
|
|
pkt->set<uint32_t>(cpuEnabled[ctx_id]);
|
|
break;
|
|
case ICCPMR:
|
|
pkt->set<uint32_t>(cpuPriority[ctx_id]);
|
|
break;
|
|
case ICCBPR:
|
|
pkt->set<uint32_t>(cpuBpr[ctx_id]);
|
|
break;
|
|
case ICCIAR:
|
|
if (enabled && cpuEnabled[ctx_id]) {
|
|
int active_int = cpuHighestInt[ctx_id];
|
|
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 (!cpuSgiPending[active_int])
|
|
panic("Interrupt %d active but no CPU generated it?\n",
|
|
active_int);
|
|
for (int x = 0; x < CPU_MAX; x++) {
|
|
// See which CPU generated the interrupt
|
|
uint8_t cpugen =
|
|
bits(cpuSgiPending[active_int], 7 + 8 * x, 8 * x);
|
|
if (cpugen & (1 << ctx_id)) {
|
|
iar.cpu_id = x;
|
|
break;
|
|
}
|
|
}
|
|
uint64_t sgi_num = ULL(1) << (ctx_id + 8 * iar.cpu_id);
|
|
cpuSgiActive[iar.ack_id] |= sgi_num;
|
|
cpuSgiPending[iar.ack_id] &= ~sgi_num;
|
|
} else if (active_int < (SGI_MAX + PPI_MAX) ) {
|
|
uint32_t int_num = 1 << (cpuHighestInt[ctx_id] - SGI_MAX);
|
|
cpuPpiActive[ctx_id] |= int_num;
|
|
updateRunPri();
|
|
cpuPpiPending[ctx_id] &= ~int_num;
|
|
|
|
} else {
|
|
uint32_t int_num = 1 << intNumToBit(cpuHighestInt[ctx_id]);
|
|
activeInt[intNumToWord(cpuHighestInt[ctx_id])] |= int_num;
|
|
updateRunPri();
|
|
pendingInt[intNumToWord(cpuHighestInt[ctx_id])] &= ~int_num;
|
|
}
|
|
|
|
DPRINTF(Interrupt,"CPU %d reading IAR.id=%d IAR.cpu=%d, iar=0x%x\n",
|
|
ctx_id, iar.ack_id, iar.cpu_id, iar);
|
|
cpuHighestInt[ctx_id] = SPURIOUS_INT;
|
|
updateIntState(-1);
|
|
pkt->set<uint32_t>(iar);
|
|
platform->intrctrl->clear(ctx_id, ArmISA::INT_IRQ, 0);
|
|
} else {
|
|
pkt->set<uint32_t>(SPURIOUS_INT);
|
|
}
|
|
|
|
break;
|
|
case ICCRPR:
|
|
pkt->set<uint32_t>(iccrpr[0]);
|
|
break;
|
|
case ICCHPIR:
|
|
pkt->set<uint32_t>(0);
|
|
panic("Need to implement HPIR");
|
|
break;
|
|
default:
|
|
panic("Tried to read Gic cpu at offset %#x\n", daddr);
|
|
break;
|
|
}
|
|
pkt->makeAtomicResponse();
|
|
return cpuPioDelay;
|
|
}
|
|
|
|
|
|
Tick
|
|
Gic::writeDistributor(PacketPtr pkt)
|
|
{
|
|
Addr daddr = pkt->getAddr() - distAddr;
|
|
pkt->allocate();
|
|
|
|
assert(pkt->req->hasContextId());
|
|
int ctx_id = pkt->req->contextId();
|
|
|
|
DPRINTF(GIC, "gic distributor write register %#x size %#x value %#x \n",
|
|
daddr, pkt->getSize(), pkt->get<uint32_t>());
|
|
|
|
if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) {
|
|
assert((daddr-ICDISER_ST) >> 2 < 32);
|
|
intEnabled[(daddr-ICDISER_ST) >> 2] |= pkt->get<uint32_t>();
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDICER_ST && daddr < ICDICER_ED + 4) {
|
|
assert((daddr-ICDICER_ST) >> 2 < 32);
|
|
intEnabled[(daddr-ICDICER_ST) >> 2] &= ~pkt->get<uint32_t>();
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDISPR_ST && daddr < ICDISPR_ED + 4) {
|
|
assert((daddr-ICDISPR_ST) >> 2 < 32);
|
|
pendingInt[(daddr-ICDISPR_ST) >> 2] |= pkt->get<uint32_t>();
|
|
pendingInt[0] &= SGI_MASK; // Don't allow SGIs to be changed
|
|
updateIntState((daddr-ICDISPR_ST) >> 2);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDICPR_ST && daddr < ICDICPR_ED + 4) {
|
|
assert((daddr-ICDICPR_ST) >> 2 < 32);
|
|
pendingInt[(daddr-ICDICPR_ST) >> 2] &= ~pkt->get<uint32_t>();
|
|
pendingInt[0] &= SGI_MASK; // Don't allow SGIs to be changed
|
|
updateIntState((daddr-ICDICPR_ST) >> 2);
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
|
|
Addr int_num = daddr - ICDIPR_ST;
|
|
assert(int_num < INT_LINES_MAX);
|
|
uint8_t* int_p;
|
|
if (int_num < (SGI_MAX + PPI_MAX))
|
|
int_p = bankedIntPriority[ctx_id];
|
|
else
|
|
int_p = intPriority;
|
|
uint32_t tmp;
|
|
switch(pkt->getSize()) {
|
|
case 1:
|
|
tmp = pkt->get<uint8_t>();
|
|
int_p[int_num] = bits(tmp, 7, 0);
|
|
break;
|
|
case 2:
|
|
tmp = pkt->get<uint16_t>();
|
|
int_p[int_num] = bits(tmp, 7, 0);
|
|
int_p[int_num + 1] = bits(tmp, 15, 8);
|
|
break;
|
|
case 4:
|
|
tmp = pkt->get<uint32_t>();
|
|
int_p[int_num] = bits(tmp, 7, 0);
|
|
int_p[int_num + 1] = bits(tmp, 15, 8);
|
|
int_p[int_num + 2] = bits(tmp, 23, 16);
|
|
int_p[int_num + 3] = bits(tmp, 31, 24);
|
|
break;
|
|
default:
|
|
panic("Invalid size when writing to priority regs in Gic: %d\n",
|
|
pkt->getSize());
|
|
}
|
|
|
|
updateIntState(-1);
|
|
updateRunPri();
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDIPTR_ST && daddr < ICDIPTR_ED + 4) {
|
|
Addr int_num = (daddr-ICDIPTR_ST) ;
|
|
assert(int_num < INT_LINES_MAX);
|
|
// First 31 interrupts only target single processor
|
|
if (int_num >= SGI_MAX) {
|
|
if (pkt->getSize() == 1) {
|
|
uint8_t tmp = pkt->get<uint8_t>();
|
|
cpuTarget[int_num] = tmp & 0xff;
|
|
} else {
|
|
assert (pkt->getSize() == 4);
|
|
int_num = mbits(int_num, 31, 2);
|
|
uint32_t tmp = pkt->get<uint32_t>();
|
|
cpuTarget[int_num] = bits(tmp, 7, 0);
|
|
cpuTarget[int_num+1] = bits(tmp, 15, 8);
|
|
cpuTarget[int_num+2] = bits(tmp, 23, 16);
|
|
cpuTarget[int_num+3] = bits(tmp, 31, 24);
|
|
}
|
|
updateIntState((daddr-ICDIPTR_ST)>>2);
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
if (daddr >= ICDICFR_ST && daddr < ICDICFR_ED + 4) {
|
|
assert((daddr-ICDICFR_ST) >> 2 < 64);
|
|
intConfig[(daddr-ICDICFR_ST)>>2] = pkt->get<uint32_t>();
|
|
if (pkt->get<uint32_t>() & NN_CONFIG_MASK)
|
|
warn("GIC N:N mode selected and not supported at this time\n");
|
|
goto done;
|
|
}
|
|
|
|
switch(daddr) {
|
|
case ICDDCR:
|
|
enabled = pkt->get<uint32_t>();
|
|
DPRINTF(Interrupt, "Distributor enable flag set to = %d\n", enabled);
|
|
break;
|
|
case ICDSGIR:
|
|
softInt(ctx_id, pkt->get<uint32_t>());
|
|
break;
|
|
default:
|
|
panic("Tried to write Gic distributor at offset %#x\n", daddr);
|
|
break;
|
|
}
|
|
|
|
done:
|
|
pkt->makeAtomicResponse();
|
|
return distPioDelay;
|
|
}
|
|
|
|
Tick
|
|
Gic::writeCpu(PacketPtr pkt)
|
|
{
|
|
Addr daddr = pkt->getAddr() - cpuAddr;
|
|
pkt->allocate();
|
|
|
|
assert(pkt->req->hasContextId());
|
|
int ctx_id = pkt->req->contextId();
|
|
IAR iar;
|
|
|
|
DPRINTF(GIC, "gic cpu write register cpu:%d %#x val: %#x\n",
|
|
ctx_id, daddr, pkt->get<uint32_t>());
|
|
|
|
switch(daddr) {
|
|
case ICCICR:
|
|
cpuEnabled[ctx_id] = pkt->get<uint32_t>();
|
|
break;
|
|
case ICCPMR:
|
|
cpuPriority[ctx_id] = pkt->get<uint32_t>();
|
|
break;
|
|
case ICCBPR:
|
|
cpuBpr[ctx_id] = pkt->get<uint32_t>();
|
|
break;
|
|
case ICCEOIR:
|
|
iar = pkt->get<uint32_t>();
|
|
if (iar.ack_id < SGI_MAX) {
|
|
// Clear out the bit that corrseponds to the cleared int
|
|
uint64_t clr_int = ULL(1) << (ctx_id + 8 * iar.cpu_id);
|
|
if (!(cpuSgiActive[iar.ack_id] & clr_int))
|
|
panic("Done handling a SGI that isn't active?\n");
|
|
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_id] & int_num))
|
|
panic("CPU %d Done handling a PPI interrupt that isn't active?\n", ctx_id);
|
|
cpuPpiActive[ctx_id] &= ~int_num;
|
|
} else {
|
|
uint32_t int_num = 1 << intNumToBit(iar.ack_id);
|
|
if (!(activeInt[intNumToWord(iar.ack_id)] & int_num))
|
|
panic("Done handling interrupt that isn't active?\n");
|
|
activeInt[intNumToWord(iar.ack_id)] &= ~int_num;
|
|
}
|
|
updateRunPri();
|
|
DPRINTF(Interrupt, "CPU %d done handling intr IAR = %d from cpu %d\n",
|
|
ctx_id, iar.ack_id, iar.cpu_id);
|
|
break;
|
|
default:
|
|
panic("Tried to write Gic cpu at offset %#x\n", daddr);
|
|
break;
|
|
}
|
|
if (cpuEnabled[ctx_id]) updateIntState(-1);
|
|
pkt->makeAtomicResponse();
|
|
return cpuPioDelay;
|
|
}
|
|
|
|
void
|
|
Gic::softInt(int ctx_id, SWI swi)
|
|
{
|
|
switch (swi.list_type) {
|
|
case 1:
|
|
// interrupt all
|
|
uint8_t cpu_list;
|
|
cpu_list = 0;
|
|
for (int x = 0; x < CPU_MAX; 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_id;
|
|
break;
|
|
// else interrupt cpus specified
|
|
}
|
|
|
|
DPRINTF(IPI, "Generating softIRQ from CPU %d for %#x\n", ctx_id,
|
|
swi.cpu_list);
|
|
for (int i = 0; i < CPU_MAX; 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_id);
|
|
DPRINTF(IPI, "SGI[%d]=%#x\n", swi.sgi_id, cpuSgiPending[swi.sgi_id]);
|
|
}
|
|
updateIntState(-1);
|
|
}
|
|
|
|
uint64_t
|
|
Gic::genSwiMask(int cpu)
|
|
{
|
|
if (cpu > 7)
|
|
panic("Invalid CPU ID\n");
|
|
return ULL(0x0101010101010101) << cpu;
|
|
}
|
|
|
|
void
|
|
Gic::updateIntState(int hint)
|
|
{
|
|
for (int cpu = 0; cpu < CPU_MAX; cpu++) {
|
|
if (!cpuEnabled[cpu])
|
|
continue;
|
|
if (cpu >= sys->numContexts())
|
|
break;
|
|
|
|
/*@todo use hint to do less work. */
|
|
int highest_int = SPURIOUS_INT;
|
|
// Priorities below that set in ICCPMR can be ignored
|
|
uint8_t highest_pri = cpuPriority[cpu];
|
|
|
|
// Check SGIs
|
|
for (int swi = 0; swi < SGI_MAX; swi++) {
|
|
if (!cpuSgiPending[swi])
|
|
continue;
|
|
if (cpuSgiPending[swi] & genSwiMask(cpu))
|
|
if (highest_pri > bankedIntPriority[cpu][swi]) {
|
|
highest_pri = bankedIntPriority[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 > bankedIntPriority[cpu][SGI_MAX + ppi]) {
|
|
highest_pri = bankedIntPriority[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 (intEnabled[x] & pendingInt[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 prioirty
|
|
and if currrent cpu is the target (for mp configs only)
|
|
*/
|
|
if ((bits(intEnabled[x], y) & bits(pendingInt[x], y)) &&
|
|
(intPriority[int_nm] < highest_pri))
|
|
if ( (!mp_sys) || (cpuTarget[int_nm] & (1 << cpu))) {
|
|
highest_pri = intPriority[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 < cpuPriority[cpu]) &&
|
|
!(activeInt[intNumToWord(highest_int)]
|
|
& (1 << intNumToBit(highest_int)))) {
|
|
|
|
DPRINTF(Interrupt, "Posting interrupt %d to cpu%d\n", highest_int,
|
|
cpu);
|
|
postInt(cpu, curTick() + intLatency);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Gic::updateRunPri()
|
|
{
|
|
for (int cpu = 0; cpu < CPU_MAX; 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)) &&
|
|
(bankedIntPriority[cpu][i] < maxPriority))
|
|
maxPriority = bankedIntPriority[cpu][i];
|
|
} else if (i < (SGI_MAX + PPI_MAX)) {
|
|
if ((cpuPpiActive[cpu] & ( 1 << (i - SGI_MAX))) &&
|
|
(bankedIntPriority[cpu][i] < maxPriority))
|
|
maxPriority = bankedIntPriority[cpu][i];
|
|
|
|
} else {
|
|
if (activeInt[intNumToWord(i)] & (1 << intNumToBit(i)))
|
|
if (intPriority[i] < maxPriority)
|
|
maxPriority = intPriority[i];
|
|
}
|
|
}
|
|
iccrpr[cpu] = maxPriority;
|
|
}
|
|
}
|
|
|
|
void
|
|
Gic::sendInt(uint32_t num)
|
|
{
|
|
DPRINTF(Interrupt, "Received Interupt number %d, cpuTarget %#x: \n",
|
|
num, cpuTarget[num]);
|
|
if (cpuTarget[num] & (cpuTarget[num] - 1))
|
|
panic("Multiple targets for peripheral interrupts is not supported\n");
|
|
pendingInt[intNumToWord(num)] |= 1 << intNumToBit(num);
|
|
updateIntState(intNumToWord(num));
|
|
|
|
}
|
|
|
|
void
|
|
Gic::sendPPInt(uint32_t num, uint32_t cpu)
|
|
{
|
|
DPRINTF(Interrupt, "Received Interrupt number %d, cpuTarget %#x: \n",
|
|
num, cpu);
|
|
cpuPpiPending[cpu] |= 1 << (num - SGI_MAX);
|
|
updateIntState(intNumToWord(num));
|
|
}
|
|
|
|
void
|
|
Gic::clearInt(uint32_t number)
|
|
{
|
|
/* @todo assume edge triggered only at the moment. Nothing to do. */
|
|
}
|
|
|
|
void
|
|
Gic::postInt(uint32_t cpu, Tick when)
|
|
{
|
|
if (!(postIntEvent[cpu]->scheduled()))
|
|
eventq->schedule(postIntEvent[cpu], when);
|
|
}
|
|
|
|
void
|
|
Gic::addressRanges(AddrRangeList &range_list)
|
|
{
|
|
range_list.clear();
|
|
range_list.push_back(RangeSize(distAddr, DIST_SIZE));
|
|
range_list.push_back(RangeSize(cpuAddr, CPU_SIZE));
|
|
}
|
|
|
|
|
|
void
|
|
Gic::serialize(std::ostream &os)
|
|
{
|
|
DPRINTF(Checkpoint, "Serializing Arm GIC\n");
|
|
|
|
SERIALIZE_SCALAR(distAddr);
|
|
SERIALIZE_SCALAR(cpuAddr);
|
|
SERIALIZE_SCALAR(distPioDelay);
|
|
SERIALIZE_SCALAR(cpuPioDelay);
|
|
SERIALIZE_SCALAR(enabled);
|
|
SERIALIZE_SCALAR(itLines);
|
|
SERIALIZE_SCALAR(itLinesLog2);
|
|
SERIALIZE_ARRAY(intEnabled, INT_BITS_MAX);
|
|
SERIALIZE_ARRAY(pendingInt, INT_BITS_MAX);
|
|
SERIALIZE_ARRAY(activeInt, INT_BITS_MAX);
|
|
SERIALIZE_ARRAY(iccrpr, CPU_MAX);
|
|
SERIALIZE_ARRAY(intPriority, INT_LINES_MAX);
|
|
SERIALIZE_ARRAY(cpuTarget, INT_LINES_MAX);
|
|
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(cpuPpiActive, CPU_MAX);
|
|
SERIALIZE_ARRAY(cpuPpiPending, CPU_MAX);
|
|
SERIALIZE_ARRAY(*bankedIntPriority, CPU_MAX * (SGI_MAX + PPI_MAX));
|
|
SERIALIZE_SCALAR(irqEnable);
|
|
Tick interrupt_time[CPU_MAX];
|
|
for (uint32_t cpu = 0; cpu < CPU_MAX; cpu++) {
|
|
interrupt_time[cpu] = 0;
|
|
if (postIntEvent[cpu]->scheduled()) {
|
|
interrupt_time[cpu] = postIntEvent[cpu]->when();
|
|
}
|
|
}
|
|
SERIALIZE_ARRAY(interrupt_time, CPU_MAX);
|
|
|
|
}
|
|
|
|
void
|
|
Gic::unserialize(Checkpoint *cp, const std::string §ion)
|
|
{
|
|
DPRINTF(Checkpoint, "Unserializing Arm GIC\n");
|
|
|
|
UNSERIALIZE_SCALAR(distAddr);
|
|
UNSERIALIZE_SCALAR(cpuAddr);
|
|
UNSERIALIZE_SCALAR(distPioDelay);
|
|
UNSERIALIZE_SCALAR(cpuPioDelay);
|
|
UNSERIALIZE_SCALAR(enabled);
|
|
UNSERIALIZE_SCALAR(itLines);
|
|
UNSERIALIZE_SCALAR(itLinesLog2);
|
|
UNSERIALIZE_ARRAY(intEnabled, INT_BITS_MAX);
|
|
UNSERIALIZE_ARRAY(pendingInt, INT_BITS_MAX);
|
|
UNSERIALIZE_ARRAY(activeInt, INT_BITS_MAX);
|
|
UNSERIALIZE_ARRAY(iccrpr, CPU_MAX);
|
|
UNSERIALIZE_ARRAY(intPriority, INT_LINES_MAX);
|
|
UNSERIALIZE_ARRAY(cpuTarget, INT_LINES_MAX);
|
|
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(cpuPpiActive, CPU_MAX);
|
|
UNSERIALIZE_ARRAY(cpuPpiPending, CPU_MAX);
|
|
UNSERIALIZE_ARRAY(*bankedIntPriority, CPU_MAX * (SGI_MAX + PPI_MAX));
|
|
UNSERIALIZE_SCALAR(irqEnable);
|
|
|
|
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]);
|
|
}
|
|
|
|
}
|
|
|
|
Gic *
|
|
GicParams::create()
|
|
{
|
|
return new Gic(this);
|
|
}
|
|
|
|
/* Functions for debugging and testing */
|
|
void
|
|
Gic::driveSPI(unsigned int spiVect)
|
|
{
|
|
DPRINTF(GIC, "Received SPI Vector:%x Enable: %d\n", spiVect, irqEnable);
|
|
pendingInt[1] |= spiVect;
|
|
if (irqEnable && enabled) {
|
|
updateIntState(-1);
|
|
}
|
|
}
|
|
|
|
void
|
|
Gic::driveIrqEn( bool state)
|
|
{
|
|
irqEnable = state;
|
|
DPRINTF(GIC, " Enabling Irq\n");
|
|
updateIntState(-1);
|
|
}
|
|
|
|
void
|
|
Gic::driveLegIRQ(bool state)
|
|
{
|
|
if (irqEnable && !(!enabled && cpuEnabled[0])) {
|
|
if (state) {
|
|
DPRINTF(GIC, "Driving Legacy Irq\n");
|
|
platform->intrctrl->post(0, ArmISA::INT_IRQ, 0);
|
|
}
|
|
else platform->intrctrl->clear(0, ArmISA::INT_IRQ, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
Gic::driveLegFIQ(bool state)
|
|
{
|
|
if (state)
|
|
platform->intrctrl->post(0, ArmISA::INT_FIQ, 0);
|
|
else platform->intrctrl->clear(0, ArmISA::INT_FIQ, 0);
|
|
}
|