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.
This commit is contained in:
Karthik Sangaiah 2015-09-18 16:49:28 +01:00
parent 413f3088ea
commit 6fa936b021
3 changed files with 220 additions and 56 deletions

View file

@ -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<uint32_t>(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<uint32_t>();
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<uint32_t>() & 0x200);
DPRINTF(GIC, "gem5 extensions %s\n", gem5ExtensionsEnabled ? "enabled" : "disabled");
break;
case ICDSGIR:
softInt(ctx_id, pkt->get<uint32_t>());
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 *

View file

@ -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];

View file

@ -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))