| /* |
| * 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 "debug/Interrupt.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), platform(p->platform), 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: |
| assert((int_num + 1) < INT_LINES_MAX); |
| pkt->set<uint16_t>(int_p[int_num] | |
| int_p[int_num+1] << 8); |
| break; |
| case 4: |
| assert((int_num + 3) < INT_LINES_MAX); |
| 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); |
| } |
| |
| AddrRangeList |
| Gic::getAddrRanges() |
| { |
| AddrRangeList ranges; |
| ranges.push_back(RangeSize(distAddr, DIST_SIZE)); |
| ranges.push_back(RangeSize(cpuAddr, CPU_SIZE)); |
| return ranges; |
| } |
| |
| |
| 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); |
| } |