From 6fa936b021ac4d3030d20de5d037f1e7dd902cd9 Mon Sep 17 00:00:00 2001 From: Karthik Sangaiah Date: Fri, 18 Sep 2015 16:49:28 +0100 Subject: [PATCH] dev, arm: Add gem5 extensions to support more than 8 cores Previous ARM-based simulations were limited to 8 cores due to limitations in GICv2 and earlier. This changeset adds a set of gem5-specific extensions that enable support for up to 256 cores. When the gem5 extensions are enabled, the GIC uses CPU IDs instead of a CPU bitmask in the GIC's register interface. To OS can enable the extensions by setting bit 0x200 in ICDICTR. This changeset is based on previous work by Matt Evans. --- src/dev/arm/gic_pl390.cc | 186 ++++++++++++++++++------- src/dev/arm/gic_pl390.hh | 13 +- util/cpt_upgraders/arm-gem5-gic-ext.py | 77 ++++++++++ 3 files changed, 220 insertions(+), 56 deletions(-) create mode 100644 util/cpt_upgraders/arm-gem5-gic-ext.py diff --git a/src/dev/arm/gic_pl390.cc b/src/dev/arm/gic_pl390.cc index 0de8a86c4..a6827ffe3 100644 --- a/src/dev/arm/gic_pl390.cc +++ b/src/dev/arm/gic_pl390.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013 ARM Limited + * Copyright (c) 2010, 2013, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -93,6 +93,8 @@ Pl390::Pl390(const Params *p) for (int x = 0; x < CPU_MAX; x++) { cpuPpiActive[x] = 0; cpuPpiPending[x] = 0; + cpuSgiActiveExt[x] = 0; + cpuSgiPendingExt[x] = 0; } for (int i = 0; i < CPU_MAX; i++) { @@ -100,6 +102,8 @@ Pl390::Pl390(const Params *p) bankedIntPriority[i][j] = 0; } } + + gem5ExtensionsEnabled = false; } Tick @@ -226,8 +230,13 @@ Pl390::readDistributor(PacketPtr pkt) } } else { assert(ctx_id < sys->numRunningContexts()); + uint32_t ctx_mask; + if (gem5ExtensionsEnabled) { + ctx_mask = ctx_id; + } else { // convert the CPU id number into a bit mask - uint32_t ctx_mask = power(2, ctx_id); + ctx_mask = power(2, ctx_id); + } // replicate the 8-bit mask 4 times in a 32-bit word ctx_mask |= ctx_mask << 8; ctx_mask |= ctx_mask << 16; @@ -252,7 +261,12 @@ Pl390::readDistributor(PacketPtr pkt) case ICDICTR: uint32_t tmp; tmp = ((sys->numRunningContexts() - 1) << 5) | - (itLines/INT_BITS_MAX -1); + (itLines/INT_BITS_MAX -1) | + 0x100; + /* The 0x100 is a made-up flag to show that gem5 extensions + * are available, + * write 0x200 to this register to enable it. + */ pkt->set(tmp); break; default: @@ -297,21 +311,27 @@ Pl390::readCpu(PacketPtr pkt) 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", + if (!gem5ExtensionsEnabled) { + panic_if(!cpuSgiPending[active_int], + "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; + 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_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 { + uint64_t sgi_num = ULL(1) << iar.ack_id; + cpuSgiActiveExt[ctx_id] |= sgi_num; + cpuSgiPendingExt[ctx_id] &= ~sgi_num; } - 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; @@ -478,6 +498,13 @@ Pl390::writeDistributor(PacketPtr pkt) enabled = pkt->get(); DPRINTF(Interrupt, "Distributor enable flag set to = %d\n", enabled); break; + case ICDICTR: + /* 0x200 is a made-up flag to enable gem5 extension functionality. + * This reg is not normally written. + */ + gem5ExtensionsEnabled = !!(pkt->get() & 0x200); + DPRINTF(GIC, "gem5 extensions %s\n", gem5ExtensionsEnabled ? "enabled" : "disabled"); + break; case ICDSGIR: softInt(ctx_id, pkt->get()); break; @@ -518,13 +545,18 @@ Pl390::writeCpu(PacketPtr pkt) 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)) + if (!(cpuSgiActive[iar.ack_id] & clr_int) && + !(cpuSgiActiveExt[ctx_id] & (1 << iar.ack_id))) panic("Done handling a SGI that isn't active?\n"); - cpuSgiActive[iar.ack_id] &= ~clr_int; + if (gem5ExtensionsEnabled) + cpuSgiActiveExt[ctx_id] &= ~(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_id] & int_num)) - panic("CPU %d Done handling a PPI interrupt that isn't active?\n", ctx_id); + 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); @@ -549,31 +581,69 @@ Pl390::writeCpu(PacketPtr pkt) void Pl390::softInt(ContextID 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 - } + 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_id, 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_id, ctx_id); + if (cpuEnabled[ctx_id]) { + cpuSgiPendingExt[ctx_id] |= (1 << swi.sgi_id); + DPRINTF(IPI, "SGI[%d]=%#x\n", ctx_id, + cpuSgiPendingExt[ctx_id]); + } + } 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_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]); + DPRINTF(IPI, "Generating softIRQ from CPU %d for %#x\n", ctx_id, + 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_id); + DPRINTF(IPI, "SGI[%d]=%#x\n", swi.sgi_id, + cpuSgiPending[swi.sgi_id]); + } } updateIntState(-1); } @@ -581,7 +651,7 @@ Pl390::softInt(ContextID ctx_id, SWI swi) uint64_t Pl390::genSwiMask(int cpu) { - if (cpu > 7) + if (cpu > sys->numContexts()) panic("Invalid CPU ID\n"); return ULL(0x0101010101010101) << cpu; } @@ -589,11 +659,9 @@ Pl390::genSwiMask(int cpu) void Pl390::updateIntState(int hint) { - for (int cpu = 0; cpu < CPU_MAX; cpu++) { + for (int cpu = 0; cpu < sys->numContexts(); cpu++) { if (!cpuEnabled[cpu]) continue; - if (cpu >= sys->numContexts()) - break; /*@todo use hint to do less work. */ int highest_int = SPURIOUS_INT; @@ -602,9 +670,10 @@ Pl390::updateIntState(int hint) // Check SGIs for (int swi = 0; swi < SGI_MAX; swi++) { - if (!cpuSgiPending[swi]) + if (!cpuSgiPending[swi] && !cpuSgiPendingExt[cpu]) continue; - if (cpuSgiPending[swi] & genSwiMask(cpu)) + if ((cpuSgiPending[swi] & genSwiMask(cpu)) || + (cpuSgiPendingExt[cpu] & (1 << swi))) if (highest_pri > bankedIntPriority[cpu][swi]) { highest_pri = bankedIntPriority[cpu][swi]; highest_int = swi; @@ -635,7 +704,10 @@ Pl390::updateIntState(int hint) */ if ((bits(intEnabled[x], y) & bits(pendingInt[x], y)) && (intPriority[int_nm] < highest_pri)) - if ( (!mp_sys) || (cpuTarget[int_nm] & (1 << cpu))) { + if ( (!mp_sys) || + (!gem5ExtensionsEnabled && (cpuTarget[int_nm] & (1 << cpu))) || + (gem5ExtensionsEnabled && (cpuTarget[int_nm] == cpu)) + ) { highest_pri = intPriority[int_nm]; highest_int = int_nm; } @@ -664,13 +736,14 @@ Pl390::updateIntState(int hint) void Pl390::updateRunPri() { - for (int cpu = 0; cpu < CPU_MAX; cpu++) { + 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)) && + if (((cpuSgiActive[i] & genSwiMask(cpu)) || + (cpuSgiActiveExt[cpu] & (1 << i))) && (bankedIntPriority[cpu][i] < maxPriority)) maxPriority = bankedIntPriority[cpu][i]; } else if (i < (SGI_MAX + PPI_MAX)) { @@ -693,7 +766,7 @@ Pl390::sendInt(uint32_t num) { DPRINTF(Interrupt, "Received Interupt number %d, cpuTarget %#x: \n", num, cpuTarget[num]); - if (cpuTarget[num] & (cpuTarget[num] - 1)) + if ((cpuTarget[num] & (cpuTarget[num] - 1)) && !gem5ExtensionsEnabled) panic("Multiple targets for peripheral interrupts is not supported\n"); pendingInt[intNumToWord(num)] |= 1 << intNumToBit(num); updateIntState(intNumToWord(num)); @@ -766,6 +839,8 @@ Pl390::serialize(CheckpointOut &cp) const 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_ARRAY(*bankedIntPriority, CPU_MAX * (SGI_MAX + PPI_MAX)); @@ -778,7 +853,7 @@ Pl390::serialize(CheckpointOut &cp) const } } SERIALIZE_ARRAY(interrupt_time, CPU_MAX); - + SERIALIZE_SCALAR(gem5ExtensionsEnabled); } void @@ -806,6 +881,8 @@ Pl390::unserialize(CheckpointIn &cp) 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); UNSERIALIZE_ARRAY(*bankedIntPriority, CPU_MAX * (SGI_MAX + PPI_MAX)); @@ -818,7 +895,8 @@ Pl390::unserialize(CheckpointIn &cp) if (interrupt_time[cpu]) schedule(postIntEvent[cpu], interrupt_time[cpu]); } - + if (!UNSERIALIZE_OPT_SCALAR(gem5ExtensionsEnabled)) + gem5ExtensionsEnabled = false; } Pl390 * diff --git a/src/dev/arm/gic_pl390.hh b/src/dev/arm/gic_pl390.hh index bc7bd8c81..cddd0d912 100644 --- a/src/dev/arm/gic_pl390.hh +++ b/src/dev/arm/gic_pl390.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013 ARM Limited + * Copyright (c) 2010, 2013, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -108,7 +108,7 @@ class Pl390 : public BaseGic /** Mask for bits that config N:N mode in ICDICFR's */ static const int NN_CONFIG_MASK = 0x55555555; - static const int CPU_MAX = 8; // Max number of supported CPU interfaces + static const int CPU_MAX = 256; // Max number of supported CPU interfaces static const int SPURIOUS_INT = 1023; static const int INT_BITS_MAX = 32; static const int INT_LINES_MAX = 1020; @@ -143,6 +143,9 @@ class Pl390 : public BaseGic /** Gic enabled */ bool enabled; + /** gem5 many-core extension enabled by driver */ + bool gem5ExtensionsEnabled; + /** Number of itLines enabled */ uint32_t itLines; @@ -196,6 +199,12 @@ class Pl390 : public BaseGic uint64_t cpuSgiPending[SGI_MAX]; uint64_t cpuSgiActive[SGI_MAX]; + /** SGI pending arrays for gem5 GIC extension mode, which instead keeps + * 16 SGI pending bits for each of the (large number of) CPUs. + */ + uint32_t cpuSgiPendingExt[CPU_MAX]; + uint32_t cpuSgiActiveExt[CPU_MAX]; + /** One bit per private peripheral interrupt. Only upper 16 bits * will be used since PPI interrupts are numberred from 16 to 32 */ uint32_t cpuPpiPending[CPU_MAX]; diff --git a/util/cpt_upgraders/arm-gem5-gic-ext.py b/util/cpt_upgraders/arm-gem5-gic-ext.py new file mode 100644 index 000000000..b71dbf882 --- /dev/null +++ b/util/cpt_upgraders/arm-gem5-gic-ext.py @@ -0,0 +1,77 @@ +# Copyright (c) 2015 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. +# +# 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: Andreas Sandberg +# + +def upgrader(cpt): + """The gem5 GIC extensions change the size of many GIC data + structures. Resize them to match the new GIC.""" + + import re + if cpt.get('root','isa') != 'arm': + return + + old_cpu_max = 8 + new_cpu_max = 256 + sgi_max = 16 + ppi_max = 16 + per_cpu_regs = ( + ("iccrpr", [ "0xff", ]), + ("cpuEnabled", [ "false", ]), + ("cpuPriority", [ "0xff", ]), + ("cpuBpr", [ "0", ]), + ("cpuHighestInt", [ "1023", ]), + ("cpuPpiPending", [ "0", ]), + ("cpuPpiActive", [ "0", ] ), + ("interrupt_time", [ "0", ]), + ("*bankedIntPriority", ["0", ] * (sgi_max + ppi_max)), + ) + new_per_cpu_regs = ( + ("cpuSgiPendingExt", "0"), + ("cpuSgiActiveExt", "0"), + ) + + for sec in cpt.sections(): + if re.search('.*\.gic$', sec): + for reg, default in per_cpu_regs: + value = cpt.get(sec, reg).split(" ") + assert len(value) / len(default) == old_cpu_max, \ + "GIC register size mismatch" + value += [ " ".join(default), ] * (new_cpu_max - old_cpu_max) + cpt.set(sec, reg, " ".join(value)) + + for reg, default in new_per_cpu_regs: + cpt.set(sec, reg, " ".join([ default, ] * new_cpu_max))