Compare commits

...

10 Commits

Author SHA1 Message Date
Andreas Sandberg c7f92c43da scons: Fix hook installation error caused by stale cache
Due to the way SCons caches some file system state internally, it
sometimes "remembers" that a file or directory didn't exist at some
point. The git hook installation script sometimes needs to create a
hooks directory in the repository. Due to the cached state of the
hooks directory, the build system tries to create it twice. The second
mkdir operation leads to an error since the directory already exists.

Fix this issue by clearing the cached state of the hooks directory
after creating it.

Change-Id: I3f67f75c06ef928b439a0742f7619b7e92ed093b
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2660
Reviewed-by: Gabe Black <gabeblack@google.com>
2017-04-05 10:28:55 +00:00
Curtis Dunham 567a9b0a08 arm, kvm: implement GIC state transfer
This also allows checkpointing of a Kvm GIC via the Pl390 model.

Change-Id: Ic85d81cfefad630617491b732398f5e6a5f34c0b
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2444
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:51:46 +00:00
Curtis Dunham 60075068ea arm, dev: add basic support for GICC_BPR register
The Binary Point Register (BPR) specifies which bits belong to the
group priority field (which are used for preemption) and which to the
subpriority field (which are ignored for preemption).

Change-Id: If51e669d23b49047b69b82ab363dd01a936cc93b
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2443
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:51:46 +00:00
Curtis Dunham 2f14baaabc arm, dev: refactor GIC Pl390 GICD_ITARGETSRn handling
The aforementioned registers (Interrupt Processor Targets Registers) are
banked per-CPU, but are read-only.  This patch eliminates the per-CPU
storage of these values that are simply computed.

Change-Id: I52cafc2f58e87dd54239a71326c01f4923544689
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2442
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:51:46 +00:00
Curtis Dunham bbdd34d628 arm: refactor packet processing in Pl390 GIC
Change-Id: I696703418506522ba90df5c2c4ca45c95a6efbea
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2441
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:51:46 +00:00
Nikos Nikoleris 3384caf0fe arm: Don't panic when checking coprocessor read/write permissions
Instructions that use the coprocessor interface check the current
program status to determine whether the current context has the
priviledges to read from/write to the coprocessor. Some modes allow
the execution of coprocessor instructions, some others do not allow it,
while some other modes are unexpected (e.g., executing an AArch32 
instruction while being in an AArch64 mode).

Previously we would unconditionally trigger a panic if we were in an 
unexpected mode. This change removes the panic and replaces it
with an Undefined Instruction fault that triggers if and when a 
coprocessor instruction commits in an unexpected mode. This allows
speculative coprocessor instructions from unexpected modes to execute 
but prevents them from gettting committed.

Change-Id: If2776d5bae2471cdbaf76d0e1ae655f501bfbf01
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Rekai Gonzalez Alberquilla <rekai.gonzalezalberquilla@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2281
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:39:47 +00:00
Nikos Nikoleris 4b164f8382 arm: Treat Write-Through Normal memory as Non-Cacheable
A completed write to a memory location that is Write-Through Cacheable
has to be visible to an external observer without the need of explicit
cache maintenance. This change adds support for Write-Through
Cacheable Normal memory and treats it as Non-cacheable. This incurs a
small penalty as accesses to the memory do not fill in the cache but
does not violate the properties of the memory type.

Change-Id: Iee17ef9d952a550be9ad660b1e60e9f6c4ef2c2d
Reviewed-by: Giacomo Gabrielli <giacomo.gabrielli@arm.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2280
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
2017-04-03 16:39:00 +00:00
Andreas Sandberg 31199bba53 sim: Handle cases where Drainable::resume() creates objects
There are cases where Drainable objects need to create new objects in
Drainable::resume(). In such cases, the local drain state will be
inherited from the DrainManager. We currently set the state to Running
as soon as we start resuming the simulator. This means that new
objects are created in the Running state rather than the Drained
state, which the resume code assumes. Depending on the traversal order
in DrainManager::resume(), this sometimes triggers a panic because the
object being resumed is in the wrong state.

This change introduces a new drain state, Resuming, that the
DrainManager enters as soon as it starts resuming the
simulator. Objects that are created while resuming are created in this
state. Such objects are then resumed in a subsequent pass over the
list of Drainable objects that need to be resumed. Once all objects
have been resumed, the simulator enters the Running state.

Change-Id: Ieee8645351ffbdec477e9cd2ff86fc795e459617
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Curtis Dunham <curtis.dunham@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2600
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:38:26 +00:00
Andreas Sandberg 9a13acaa36 config, arm: Add multi-core KVM support to bL config
Add support for KVM in the big.LITTLE(tm) example configuration. This
replaces the --atomic option with a --cpu-type option that can be used
to switch between atomic, kvm, and timing simulation.

When running in KVM mode, the simulation script automatically assigns
separate event queues (threads) to each of the simulated CPUs. All
simulated devices, including CPU child devices (e.g., interrupt
controllers and caches), are assigned to event queue 0.

Change-Id: Ic9a3f564db91f5a3d3cb754c5a02fdd5c17d5fdf
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Curtis Dunham <curtis.dunham@arm.com>
Reviewed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Reviewed-by: Gabor Dozsa <gabor.dozsa@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2561
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:37:55 +00:00
Andreas Sandberg 3547af6e44 config, arm: Unify checkpoint path handling in bL configs
The vanilla bL configuration file and the dist-gem5 configuration file
use slightly different code paths when restoring from
checkpoints. Unify this by passing the parsed options to the
instantiate() method and adding an optional checkpoint keyword
argument for checkpoint directories (only used by the dist-gem5
script).

Change-Id: I9943ec10bd7a256465e29c8de571142ec3fbaa0e
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Curtis Dunham <curtis.dunham@arm.com>
Reviewed-by: Sascha Bischoff <sascha.bischoff@arm.com>
Reviewed-by: Gabor Dozsa <gabor.dozsa@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/2560
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Weiping Liao <weipingliao@google.com>
2017-04-03 16:37:55 +00:00
19 changed files with 744 additions and 260 deletions

View File

@ -399,6 +399,7 @@ def install_git_style_hooks():
if not git_hooks.exists():
mkdir(git_hooks.get_abspath())
git_hooks.clear()
abs_symlink_hooks = git_hooks.islink() and \
os.path.isabs(os.readlink(git_hooks.get_abspath()))

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016 ARM Limited
# Copyright (c) 2016-2017 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@ -44,6 +44,8 @@ m5.util.addToPath('../../')
from common.Caches import *
from common import CpuConfig
have_kvm = "kvm" in CpuConfig.cpu_names()
class L1I(L1_ICache):
tag_latency = 1
data_latency = 1
@ -170,6 +172,14 @@ class AtomicCluster(CpuCluster):
def addL1(self):
pass
class KvmCluster(CpuCluster):
def __init__(self, system, num_cpus, cpu_clock, cpu_voltage="1.0V"):
cpu_config = [ CpuConfig.get("kvm"), None, None, None, None ]
super(KvmCluster, self).__init__(system, num_cpus, cpu_clock,
cpu_voltage, *cpu_config)
def addL1(self):
pass
class SimpleSystem(LinuxArmSystem):
cache_line_size = 64

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016 ARM Limited
# Copyright (c) 2016-2017 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@ -132,12 +132,7 @@ def main():
root = bL.build(options)
addEthernet(root.system, options)
if options.restore_from:
checkpoint_path = os.path.join(options.checkpoint_dir,
options.restore_from)
else:
checkpoint_path = None
bL.instantiate(checkpoint_path)
bL.instantiate(options, checkpoint_dir=options.checkpoint_dir)
bL.run(options.checkpoint_dir)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016 ARM Limited
# Copyright (c) 2016-2017 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@ -44,6 +44,7 @@ import argparse
import os
import sys
import m5
import m5.util
from m5.objects import *
m5.util.addToPath("../../")
@ -52,6 +53,7 @@ from common import SysPaths
from common import CpuConfig
import devices
from devices import AtomicCluster, KvmCluster
default_dtb = 'armv8_gem5_v1_big_little_2_2.dtb'
@ -61,6 +63,21 @@ default_rcs = 'bootscript.rcS'
default_mem_size= "2GB"
def _to_ticks(value):
"""Helper function to convert a latency from string format to Ticks"""
return m5.ticks.fromSeconds(m5.util.convert.anyToLatency(value))
def _using_pdes(root):
"""Determine if the simulator is using multiple parallel event queues"""
for obj in root.descendants():
if not m5.proxy.isproxy(obj.eventq_index) and \
obj.eventq_index != root.eventq_index:
return True
return False
class BigCluster(devices.CpuCluster):
def __init__(self, system, num_cpus, cpu_clock,
@ -107,6 +124,15 @@ def createSystem(caches, kernel, bootscript, disks=[]):
return sys
cpu_types = {
"atomic" : (AtomicCluster, AtomicCluster),
"timing" : (BigCluster, LittleCluster),
}
# Only add the KVM CPU if it has been compiled into gem5
if devices.have_kvm:
cpu_types["kvm"] = (KvmCluster, KvmCluster)
def addOptions(parser):
parser.add_argument("--restore-from", type=str, default=None,
@ -119,8 +145,9 @@ def addOptions(parser):
help="Disks to instantiate")
parser.add_argument("--bootscript", type=str, default=default_rcs,
help="Linux bootscript")
parser.add_argument("--atomic", action="store_true", default=False,
help="Use atomic CPUs")
parser.add_argument("--cpu-type", type=str, choices=cpu_types.keys(),
default="timing",
help="CPU simulation mode. Default: %(default)s")
parser.add_argument("--kernel-init", type=str, default="/sbin/init",
help="Override init")
parser.add_argument("--big-cpus", type=int, default=1,
@ -135,9 +162,11 @@ def addOptions(parser):
help="Big CPU clock frequency")
parser.add_argument("--little-cpu-clock", type=str, default="1GHz",
help="Little CPU clock frequency")
parser.add_argument("--sim-quantum", type=str, default="1ms",
help="Simulation quantum for parallel simulation. " \
"Default: %(default)s")
return parser
def build(options):
m5.ticks.fixGlobalFrequency()
@ -165,35 +194,31 @@ def build(options):
root.system = system
system.boot_osflags = " ".join(kernel_cmd)
AtomicCluster = devices.AtomicCluster
if options.big_cpus + options.little_cpus == 0:
m5.util.panic("Empty CPU clusters")
big_model, little_model = cpu_types[options.cpu_type]
all_cpus = []
# big cluster
if options.big_cpus > 0:
if options.atomic:
system.bigCluster = AtomicCluster(system, options.big_cpus,
options.big_cpu_clock)
else:
system.bigCluster = BigCluster(system, options.big_cpus,
options.big_cpu_clock)
mem_mode = system.bigCluster.memoryMode()
system.bigCluster = big_model(system, options.big_cpus,
options.big_cpu_clock)
system.mem_mode = system.bigCluster.memoryMode()
all_cpus += system.bigCluster.cpus
# little cluster
if options.little_cpus > 0:
if options.atomic:
system.littleCluster = AtomicCluster(system, options.little_cpus,
options.little_cpu_clock)
system.littleCluster = little_model(system, options.little_cpus,
options.little_cpu_clock)
system.mem_mode = system.littleCluster.memoryMode()
all_cpus += system.littleCluster.cpus
else:
system.littleCluster = LittleCluster(system, options.little_cpus,
options.little_cpu_clock)
mem_mode = system.littleCluster.memoryMode()
# Figure out the memory mode
if options.big_cpus > 0 and options.little_cpus > 0 and \
system.littleCluster.memoryMode() != system.littleCluster.memoryMode():
m5.util.panic("Memory mode missmatch among CPU clusters")
if options.big_cpus > 0 and options.little_cpus > 0:
if system.bigCluster.memoryMode() != system.littleCluster.memoryMode():
m5.util.panic("Memory mode missmatch among CPU clusters")
system.mem_mode = mem_mode
# create caches
system.addCaches(options.caches, options.last_cache_level)
@ -203,17 +228,52 @@ def build(options):
if options.little_cpus > 0 and system.littleCluster.requireCaches():
m5.util.panic("Little CPU model requires caches")
# Create a KVM VM and do KVM-specific configuration
if issubclass(big_model, KvmCluster):
_build_kvm(system, all_cpus)
# Linux device tree
system.dtb_filename = SysPaths.binary(options.dtb)
return root
def _build_kvm(system, cpus):
system.kvm_vm = KvmVM()
# Assign KVM CPUs to their own event queues / threads. This
# has to be done after creating caches and other child objects
# since these mustn't inherit the CPU event queue.
if len(cpus) > 1:
device_eq = 0
first_cpu_eq = 1
for idx, cpu in enumerate(cpus):
# Child objects usually inherit the parent's event
# queue. Override that and use the same event queue for
# all devices.
for obj in cpu.descendants():
obj.eventq_index = device_eq
cpu.eventq_index = first_cpu_eq + idx
def instantiate(options, checkpoint_dir=None):
# Setup the simulation quantum if we are running in PDES-mode
# (e.g., when using KVM)
root = Root.getInstance()
if root and _using_pdes(root):
m5.util.inform("Running in PDES mode with a %s simulation quantum.",
options.sim_quantum)
root.sim_quantum = _to_ticks(options.sim_quantum)
def instantiate(checkpoint_path=None):
# Get and load from the chkpt or simpoint checkpoint
if checkpoint_path is not None:
m5.util.inform("Restoring from checkpoint %s", checkpoint_path)
m5.instantiate(checkpoint_path)
if options.restore_from:
if checkpoint_dir and not os.path.isabs(options.restore_from):
cpt = os.path.join(checkpoint_dir, options.restore_from)
else:
cpt = options.restore_from
m5.util.inform("Restoring from checkpoint %s", cpt)
m5.instantiate(cpt)
else:
m5.instantiate()
@ -241,7 +301,7 @@ def main():
addOptions(parser)
options = parser.parse_args()
root = build(options)
instantiate(options.restore_from)
instantiate(options)
run()

View File

@ -1,6 +1,6 @@
// -*- mode:c++ -*-
// Copyright (c) 2010-2013 ARM Limited
// Copyright (c) 2010-2013,2017 ARM Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
@ -814,7 +814,9 @@ let {{
mrc14code = '''
MiscRegIndex miscReg = (MiscRegIndex) xc->tcBase()->flattenMiscIndex(op1);
if (!canReadCoprocReg(miscReg, Scr, Cpsr, xc->tcBase())) {
bool can_read, undefined;
std::tie(can_read, undefined) = canReadCoprocReg(miscReg, Scr, Cpsr);
if (!can_read || undefined) {
return std::make_shared<UndefinedInstruction>(machInst, false,
mnemonic);
}
@ -836,7 +838,9 @@ let {{
mcr14code = '''
MiscRegIndex miscReg = (MiscRegIndex) xc->tcBase()->flattenMiscIndex(dest);
if (!canWriteCoprocReg(miscReg, Scr, Cpsr, xc->tcBase())) {
bool can_write, undefined;
std::tie(can_write, undefined) = canWriteCoprocReg(miscReg, Scr, Cpsr);
if (undefined || !can_write) {
return std::make_shared<UndefinedInstruction>(machInst, false,
mnemonic);
}
@ -861,12 +865,13 @@ let {{
xc->tcBase()->flattenMiscIndex(preFlatOp1);
bool hypTrap = mcrMrc15TrapToHyp(miscReg, Hcr, Cpsr, Scr, Hdcr, Hstr,
Hcptr, imm);
bool canRead = canReadCoprocReg(miscReg, Scr, Cpsr, xc->tcBase());
bool can_read, undefined;
std::tie(can_read, undefined) = canReadCoprocReg(miscReg, Scr, Cpsr);
// if we're in non secure PL1 mode then we can trap regargless of whether
// the register is accessable, in other modes we trap if only if the register
// IS accessable.
if (!canRead && !(hypTrap && !inUserMode(Cpsr) && !inSecureState(Scr, Cpsr))) {
if (undefined || (!can_read && !(hypTrap && !inUserMode(Cpsr) &&
!inSecureState(Scr, Cpsr)))) {
return std::make_shared<UndefinedInstruction>(machInst, false,
mnemonic);
}
@ -891,12 +896,14 @@ let {{
xc->tcBase()->flattenMiscIndex(preFlatDest);
bool hypTrap = mcrMrc15TrapToHyp(miscReg, Hcr, Cpsr, Scr, Hdcr, Hstr,
Hcptr, imm);
bool canWrite = canWriteCoprocReg(miscReg, Scr, Cpsr, xc->tcBase());
bool can_write, undefined;
std::tie(can_write, undefined) = canWriteCoprocReg(miscReg, Scr, Cpsr);
// if we're in non secure PL1 mode then we can trap regargless of whether
// the register is accessable, in other modes we trap if only if the register
// IS accessable.
if (!canWrite & !(hypTrap & !inUserMode(Cpsr) & !inSecureState(Scr, Cpsr))) {
if (undefined || (!can_write && !(hypTrap && !inUserMode(Cpsr) &&
!inSecureState(Scr, Cpsr)))) {
return std::make_shared<UndefinedInstruction>(machInst, false,
mnemonic);
}
@ -920,12 +927,13 @@ let {{
MiscRegIndex miscReg = (MiscRegIndex)
xc->tcBase()->flattenMiscIndex(preFlatOp1);
bool hypTrap = mcrrMrrc15TrapToHyp(miscReg, Cpsr, Scr, Hstr, Hcr, imm);
bool canRead = canReadCoprocReg(miscReg, Scr, Cpsr, xc->tcBase());
bool can_read, undefined;
std::tie(can_read, undefined) = canReadCoprocReg(miscReg, Scr, Cpsr);
// if we're in non secure PL1 mode then we can trap regargless of whether
// the register is accessable, in other modes we trap if only if the register
// IS accessable.
if (!canRead && !(hypTrap && !inUserMode(Cpsr) && !inSecureState(Scr, Cpsr))) {
if (undefined || (!can_read && !(hypTrap && !inUserMode(Cpsr) &&
!inSecureState(Scr, Cpsr)))) {
return std::make_shared<UndefinedInstruction>(machInst, false,
mnemonic);
}
@ -949,12 +957,14 @@ let {{
MiscRegIndex miscReg = (MiscRegIndex)
xc->tcBase()->flattenMiscIndex(preFlatDest);
bool hypTrap = mcrrMrrc15TrapToHyp(miscReg, Cpsr, Scr, Hstr, Hcr, imm);
bool canWrite = canWriteCoprocReg(miscReg, Scr, Cpsr, xc->tcBase());
bool can_write, undefined;
std::tie(can_write, undefined) = canWriteCoprocReg(miscReg, Scr, Cpsr);
// if we're in non secure PL1 mode then we can trap regargless of whether
// the register is accessable, in other modes we trap if only if the register
// IS accessable.
if (!canWrite & !(hypTrap & !inUserMode(Cpsr) & !inSecureState(Scr, Cpsr))) {
if (undefined || (!can_write && !(hypTrap && !inUserMode(Cpsr) &&
!inSecureState(Scr, Cpsr)))) {
return std::make_shared<UndefinedInstruction>(machInst, false,
mnemonic);
}

View File

@ -43,6 +43,7 @@
#include <linux/kvm.h>
#include "arch/arm/kvm/base_cpu.hh"
#include "debug/GIC.hh"
#include "debug/Interrupt.hh"
#include "params/MuxingKvmGic.hh"
@ -104,6 +105,63 @@ KvmKernelGicV2::setIntState(unsigned type, unsigned vcpu, unsigned irq,
vm.setIRQLine(line, high);
}
uint32_t
KvmKernelGicV2::getGicReg(unsigned group, unsigned vcpu, unsigned offset)
{
uint64_t reg;
assert(vcpu <= KVM_ARM_IRQ_VCPU_MASK);
const uint32_t attr(
(vcpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) |
(offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT));
kdev.getAttrPtr(group, attr, &reg);
return (uint32_t) reg;
}
void
KvmKernelGicV2::setGicReg(unsigned group, unsigned vcpu, unsigned offset,
unsigned value)
{
uint64_t reg = value;
assert(vcpu <= KVM_ARM_IRQ_VCPU_MASK);
const uint32_t attr(
(vcpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) |
(offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT));
kdev.setAttrPtr(group, attr, &reg);
}
uint32_t
KvmKernelGicV2::readDistributor(ContextID ctx, Addr daddr)
{
auto vcpu = vm.contextIdToVCpuId(ctx);
return getGicReg(KVM_DEV_ARM_VGIC_GRP_DIST_REGS, vcpu, daddr);
}
uint32_t
KvmKernelGicV2::readCpu(ContextID ctx, Addr daddr)
{
auto vcpu = vm.contextIdToVCpuId(ctx);
return getGicReg(KVM_DEV_ARM_VGIC_GRP_CPU_REGS, vcpu, daddr);
}
void
KvmKernelGicV2::writeDistributor(ContextID ctx, Addr daddr, uint32_t data)
{
auto vcpu = vm.contextIdToVCpuId(ctx);
setGicReg(KVM_DEV_ARM_VGIC_GRP_DIST_REGS, vcpu, daddr, data);
}
void
KvmKernelGicV2::writeCpu(ContextID ctx, Addr daddr, uint32_t data)
{
auto vcpu = vm.contextIdToVCpuId(ctx);
setGicReg(KVM_DEV_ARM_VGIC_GRP_CPU_REGS, vcpu, daddr, data);
}
MuxingKvmGic::MuxingKvmGic(const MuxingKvmGicParams *p)
: Pl390(p),
@ -121,21 +179,39 @@ MuxingKvmGic::~MuxingKvmGic()
{
}
void
MuxingKvmGic::loadState(CheckpointIn &cp)
{
Pl390::loadState(cp);
}
void
MuxingKvmGic::startup()
{
Pl390::startup();
usingKvm = (kernelGic != nullptr) && validKvmEnvironment();
if (usingKvm)
fromPl390ToKvm();
}
DrainState
MuxingKvmGic::drain()
{
if (usingKvm)
fromKvmToPl390();
return Pl390::drain();
}
void
MuxingKvmGic::drainResume()
{
Pl390::drainResume();
bool use_kvm = (kernelGic != nullptr) && validKvmEnvironment();
if (use_kvm != usingKvm) {
// Should only occur due to CPU switches
if (use_kvm) // from simulation to KVM emulation
fromPl390ToKvm();
else // from KVM emulation to simulation
fromKvmToPl390();
// otherwise, drain() already sync'd the state back to the Pl390
usingKvm = use_kvm;
}
@ -144,19 +220,14 @@ MuxingKvmGic::drainResume()
void
MuxingKvmGic::serialize(CheckpointOut &cp) const
{
if (!usingKvm)
return Pl390::serialize(cp);
panic("Checkpointing unsupported\n");
// drain() already ensured Pl390 updated with KvmGic state if necessary
Pl390::serialize(cp);
}
void
MuxingKvmGic::unserialize(CheckpointIn &cp)
{
if (!usingKvm)
return Pl390::unserialize(cp);
panic("Checkpointing unsupported\n");
Pl390::unserialize(cp);
}
Tick
@ -230,16 +301,150 @@ MuxingKvmGic::validKvmEnvironment() const
return true;
}
void
MuxingKvmGic::copyDistRegister(BaseGicRegisters* from, BaseGicRegisters* to,
ContextID ctx, Addr daddr)
{
auto val = from->readDistributor(ctx, daddr);
DPRINTF(GIC, "copy dist 0x%x 0x%08x\n", daddr, val);
to->writeDistributor(ctx, daddr, val);
}
void
MuxingKvmGic::copyCpuRegister(BaseGicRegisters* from, BaseGicRegisters* to,
ContextID ctx, Addr daddr)
{
auto val = from->readCpu(ctx, daddr);
DPRINTF(GIC, "copy cpu 0x%x 0x%08x\n", daddr, val);
to->writeCpu(ctx, daddr, val);
}
void
MuxingKvmGic::copyBankedDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
Addr daddr, size_t size)
{
for (int ctx = 0; ctx < system._numContexts; ++ctx)
for (auto a = daddr; a < daddr + size; a += 4)
copyDistRegister(from, to, ctx, a);
}
void
MuxingKvmGic::clearBankedDistRange(BaseGicRegisters* to,
Addr daddr, size_t size)
{
for (int ctx = 0; ctx < system._numContexts; ++ctx)
for (auto a = daddr; a < daddr + size; a += 4)
to->writeDistributor(ctx, a, 0xFFFFFFFF);
}
void
MuxingKvmGic::copyDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
Addr daddr, size_t size)
{
for (auto a = daddr; a < daddr + size; a += 4)
copyDistRegister(from, to, 0, a);
}
void
MuxingKvmGic::clearDistRange(BaseGicRegisters* to,
Addr daddr, size_t size)
{
for (auto a = daddr; a < daddr + size; a += 4)
to->writeDistributor(0, a, 0xFFFFFFFF);
}
void
MuxingKvmGic::copyGicState(BaseGicRegisters* from, BaseGicRegisters* to)
{
Addr set, clear;
size_t size;
/// CPU state (GICC_*)
// Copy CPU Interface Control Register (CTLR),
// Interrupt Priority Mask Register (PMR), and
// Binary Point Register (BPR)
for (int ctx = 0; ctx < system._numContexts; ++ctx) {
copyCpuRegister(from, to, ctx, GICC_CTLR);
copyCpuRegister(from, to, ctx, GICC_PMR);
copyCpuRegister(from, to, ctx, GICC_BPR);
}
/// Distributor state (GICD_*)
// Copy Distributor Control Register (CTLR)
copyDistRegister(from, to, 0, GICD_CTLR);
// Copy interrupt-enabled statuses (I[CS]ENABLERn; R0 is per-CPU banked)
set = Pl390::GICD_ISENABLER.start();
clear = Pl390::GICD_ICENABLER.start();
size = Pl390::itLines / 8;
clearBankedDistRange(to, clear, 4);
copyBankedDistRange(from, to, set, 4);
set += 4, clear += 4, size -= 4;
clearDistRange(to, clear, size);
copyDistRange(from, to, set, size);
// Copy pending interrupts (I[CS]PENDRn; R0 is per-CPU banked)
set = Pl390::GICD_ISPENDR.start();
clear = Pl390::GICD_ICPENDR.start();
size = Pl390::itLines / 8;
clearBankedDistRange(to, clear, 4);
copyBankedDistRange(from, to, set, 4);
set += 4, clear += 4, size -= 4;
clearDistRange(to, clear, size);
copyDistRange(from, to, set, size);
// Copy active interrupts (I[CS]ACTIVERn; R0 is per-CPU banked)
set = Pl390::GICD_ISACTIVER.start();
clear = Pl390::GICD_ICACTIVER.start();
size = Pl390::itLines / 8;
clearBankedDistRange(to, clear, 4);
copyBankedDistRange(from, to, set, 4);
set += 4, clear += 4, size -= 4;
clearDistRange(to, clear, size);
copyDistRange(from, to, set, size);
// Copy interrupt priorities (IPRIORITYRn; R0-7 are per-CPU banked)
set = Pl390::GICD_IPRIORITYR.start();
copyBankedDistRange(from, to, set, 32);
set += 32;
size = Pl390::itLines - 32;
copyDistRange(from, to, set, size);
// Copy interrupt processor target regs (ITARGETRn; R0-7 are read-only)
set = Pl390::GICD_ITARGETSR.start() + 32;
size = Pl390::itLines - 32;
copyDistRange(from, to, set, size);
// Copy interrupt configuration registers (ICFGRn)
set = Pl390::GICD_ICFGR.start();
size = Pl390::itLines / 4;
copyDistRange(from, to, set, size);
}
void
MuxingKvmGic::fromPl390ToKvm()
{
panic("Gic multiplexing not implemented.\n");
copyGicState(static_cast<Pl390*>(this), kernelGic);
}
void
MuxingKvmGic::fromKvmToPl390()
{
panic("Gic multiplexing not implemented.\n");
copyGicState(kernelGic, static_cast<Pl390*>(this));
// the values read for the Interrupt Priority Mask Register (PMR)
// have been shifted by three bits due to its having been emulated by
// a VGIC with only 5 PMR bits in its VMCR register. Presently the
// Linux kernel does not repair this inaccuracy, so we correct it here.
for (int cpu = 0; cpu < system._numContexts; ++cpu) {
cpuPriority[cpu] <<= 3;
assert((cpuPriority[cpu] & ~0xff) == 0);
}
}
MuxingKvmGic *

View File

@ -54,7 +54,7 @@
* model. It exposes an API that is similar to that of
* software-emulated GIC models in gem5.
*/
class KvmKernelGicV2
class KvmKernelGicV2 : public BaseGicRegisters
{
public:
/**
@ -117,6 +117,14 @@ class KvmKernelGicV2
/** Address range for the distributor interface */
const AddrRange distRange;
/** BaseGicRegisters interface */
uint32_t readDistributor(ContextID ctx, Addr daddr) override;
uint32_t readCpu(ContextID ctx, Addr daddr) override;
void writeDistributor(ContextID ctx, Addr daddr,
uint32_t data) override;
void writeCpu(ContextID ctx, Addr daddr, uint32_t data) override;
/* @} */
protected:
@ -130,6 +138,26 @@ class KvmKernelGicV2
*/
void setIntState(unsigned type, unsigned vcpu, unsigned irq, bool high);
/**
* Get value of GIC register "from" a cpu
*
* @param group Distributor or CPU (KVM_DEV_ARM_VGIC_GRP_{DIST,CPU}_REGS)
* @param vcpu CPU id within KVM
* @param offset register offset
*/
uint32_t getGicReg(unsigned group, unsigned vcpu, unsigned offset);
/**
* Set value of GIC register "from" a cpu
*
* @param group Distributor or CPU (KVM_DEV_ARM_VGIC_GRP_{DIST,CPU}_REGS)
* @param vcpu CPU id within KVM
* @param offset register offset
* @param value value to set register to
*/
void setGicReg(unsigned group, unsigned vcpu, unsigned offset,
unsigned value);
/** KVM VM in the parent system */
KvmVM &vm;
@ -146,7 +174,10 @@ class MuxingKvmGic : public Pl390
MuxingKvmGic(const MuxingKvmGicParams *p);
~MuxingKvmGic();
void loadState(CheckpointIn &cp) override;
void startup() override;
DrainState drain() override;
void drainResume() override;
void serialize(CheckpointOut &cp) const override;
@ -176,9 +207,25 @@ class MuxingKvmGic : public Pl390
private:
bool usingKvm;
/** Multiplexing implementation: state transfer functions */
/** Multiplexing implementation */
void fromPl390ToKvm();
void fromKvmToPl390();
void copyGicState(BaseGicRegisters* from, BaseGicRegisters* to);
void copyDistRegister(BaseGicRegisters* from, BaseGicRegisters* to,
ContextID ctx, Addr daddr);
void copyCpuRegister(BaseGicRegisters* from, BaseGicRegisters* to,
ContextID ctx, Addr daddr);
void copyBankedDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
Addr daddr, size_t size);
void clearBankedDistRange(BaseGicRegisters* to,
Addr daddr, size_t size);
void copyDistRange(BaseGicRegisters* from, BaseGicRegisters* to,
Addr daddr, size_t size);
void clearDistRange(BaseGicRegisters* to,
Addr daddr, size_t size);
};
#endif // __ARCH_ARM_KVM_GIC_HH__

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2013, 2015-2016 ARM Limited
* Copyright (c) 2010-2013, 2015-2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -41,6 +41,8 @@
#include "arch/arm/miscregs.hh"
#include <tuple>
#include "arch/arm/isa.hh"
#include "base/misc.hh"
#include "cpu/thread_context.hh"
@ -1967,11 +1969,12 @@ decodeCP15Reg64(unsigned crm, unsigned opc1)
return MISCREG_CP15_UNIMPL;
}
bool
canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr, ThreadContext *tc)
std::tuple<bool, bool>
canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr)
{
bool secure = !scr.ns;
bool canRead;
bool canRead = false;
bool undefined = false;
switch (cpsr.mode) {
case MODE_USER:
@ -1995,18 +1998,19 @@ canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr, ThreadContext *tc)
canRead = miscRegInfo[reg][MISCREG_HYP_RD];
break;
default:
panic("Unrecognized mode setting in CPSR.\n");
undefined = true;
}
// can't do permissions checkes on the root of a banked pair of regs
assert(!miscRegInfo[reg][MISCREG_BANKED]);
return canRead;
return std::make_tuple(canRead, undefined);
}
bool
canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr, ThreadContext *tc)
std::tuple<bool, bool>
canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr)
{
bool secure = !scr.ns;
bool canWrite;
bool canWrite = false;
bool undefined = false;
switch (cpsr.mode) {
case MODE_USER:
@ -2030,11 +2034,11 @@ canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr, ThreadContext *tc)
canWrite = miscRegInfo[reg][MISCREG_HYP_WR];
break;
default:
panic("Unrecognized mode setting in CPSR.\n");
undefined = true;
}
// can't do permissions checkes on the root of a banked pair of regs
assert(!miscRegInfo[reg][MISCREG_BANKED]);
return canWrite;
return std::make_tuple(canWrite, undefined);
}
int

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2016 ARM Limited
* Copyright (c) 2010-2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -44,6 +44,7 @@
#define __ARCH_ARM_MISCREGS_HH__
#include <bitset>
#include <tuple>
#include "base/bitunion.hh"
#include "base/compiler.hh"
@ -1847,13 +1848,37 @@ namespace ArmISA
EndBitUnion(CPTR)
// Checks read access permissions to coproc. registers
bool canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr,
ThreadContext *tc);
/**
* Check for permission to read coprocessor registers.
*
* Checks whether an instruction at the current program mode has
* permissions to read the coprocessor registers. This function
* returns whether the check is undefined and if not whether the
* read access is permitted.
*
* @param the misc reg indicating the coprocessor
* @param the SCR
* @param the CPSR
* @return a tuple of booleans: can_read, undefined
*/
std::tuple<bool, bool> canReadCoprocReg(MiscRegIndex reg, SCR scr,
CPSR cpsr);
// Checks write access permissions to coproc. registers
bool canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr,
ThreadContext *tc);
/**
* Check for permission to write coprocessor registers.
*
* Checks whether an instruction at the current program mode has
* permissions to write the coprocessor registers. This function
* returns whether the check is undefined and if not whether the
* write access is permitted.
*
* @param the misc reg indicating the coprocessor
* @param the SCR
* @param the CPSR
* @return a tuple of booleans: can_write, undefined
*/
std::tuple<bool, bool> canWriteCoprocReg(MiscRegIndex reg, SCR scr,
CPSR cpsr);
// Checks read access permissions to AArch64 system registers
bool canReadAArch64SysReg(MiscRegIndex reg, SCR scr, CPSR cpsr,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2012-2016 ARM Limited
* Copyright (c) 2010, 2012-2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -1342,7 +1342,10 @@ TableWalker::memAttrsAArch64(ThreadContext *tc, TlbEntry &te,
attr_hi == 2 ? 2 : 1;
te.innerAttrs = attr_lo == 1 ? 0 :
attr_lo == 2 ? 6 : 5;
te.nonCacheable = (attr_hi == 1) || (attr_lo == 1);
// Treat write-through memory as uncacheable, this is safe
// but for performance reasons not optimal.
te.nonCacheable = (attr_hi == 1) || (attr_hi == 2) ||
(attr_lo == 1) || (attr_lo == 2);
}
} else {
uint8_t attrIndx = lDescriptor.attrIndx();
@ -1377,9 +1380,25 @@ TableWalker::memAttrsAArch64(ThreadContext *tc, TlbEntry &te,
// Cacheability
te.nonCacheable = false;
if (te.mtype == TlbEntry::MemoryType::Device || // Device memory
attr_hi == 0x8 || // Normal memory, Outer Non-cacheable
attr_lo == 0x8) { // Normal memory, Inner Non-cacheable
if (te.mtype == TlbEntry::MemoryType::Device) { // Device memory
te.nonCacheable = true;
}
// Treat write-through memory as uncacheable, this is safe
// but for performance reasons not optimal.
switch (attr_hi) {
case 0x1 ... 0x3: // Normal Memory, Outer Write-through transient
case 0x4: // Normal memory, Outer Non-cacheable
case 0x8 ... 0xb: // Normal Memory, Outer Write-through non-transient
te.nonCacheable = true;
}
switch (attr_lo) {
case 0x1 ... 0x3: // Normal Memory, Inner Write-through transient
case 0x9 ... 0xb: // Normal Memory, Inner Write-through non-transient
warn_if(!attr_hi, "Unpredictable behavior");
case 0x4: // Device-nGnRE memory or
// Normal memory, Inner Non-cacheable
case 0x8: // Device-nGRE memory or
// Normal memory, Inner Write-through non-transient
te.nonCacheable = true;
}

View File

@ -106,6 +106,7 @@ class BaseKvmCPU : public BaseCPU
void deallocateContext(ThreadID thread_num);
void haltContext(ThreadID thread_num) override;
long getVCpuID() const { return vcpuID; }
ThreadContext *getContext(int tn) override;
Counter totalInsts() const override;

View File

@ -50,6 +50,7 @@
#include <cerrno>
#include <memory>
#include "cpu/kvm/base.hh"
#include "debug/Kvm.hh"
#include "params/KvmVM.hh"
#include "sim/system.hh"
@ -528,12 +529,21 @@ KvmVM::createDevice(uint32_t type, uint32_t flags)
}
void
KvmVM::setSystem(System *s) {
KvmVM::setSystem(System *s)
{
panic_if(system != nullptr, "setSystem() can only be called once");
panic_if(s == nullptr, "setSystem() called with null System*");
system = s;
}
long
KvmVM::contextIdToVCpuId(ContextID ctx) const
{
assert(system != nullptr);
return dynamic_cast<BaseKvmCPU*>
(system->getThreadContext(ctx)->getCpuPtr())->getVCpuID();
}
int
KvmVM::createVCPU(long vcpuID)
{

View File

@ -48,6 +48,7 @@
// forward declarations
struct KvmVMParams;
class BaseKvmCPU;
class System;
/**
@ -405,6 +406,11 @@ class KvmVM : public SimObject
*/
void setSystem(System *s);
/**
* Get the VCPUID for a given context
*/
long contextIdToVCpuId(ContextID ctx) const;
#if defined(__aarch64__)
public: // ARM-specific
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2013 ARM Limited
* Copyright (c) 2012-2013, 2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -92,4 +92,15 @@ class BaseGic : public PioDevice
Platform *platform;
};
class BaseGicRegisters
{
public:
virtual uint32_t readDistributor(ContextID ctx, Addr daddr) = 0;
virtual uint32_t readCpu(ContextID ctx, Addr daddr) = 0;
virtual void writeDistributor(ContextID ctx, Addr daddr,
uint32_t data) = 0;
virtual void writeCpu(ContextID ctx, Addr daddr, uint32_t data) = 0;
};
#endif

View File

@ -83,7 +83,7 @@ Pl390::Pl390(const Params *p)
iccrpr[x] = 0xff;
cpuEnabled[x] = false;
cpuPriority[x] = 0xff;
cpuBpr[x] = 0;
cpuBpr[x] = GICC_BPR_MINIMUM;
// Initialize cpu highest int
cpuHighestInt[x] = SPURIOUS_INT;
postIntEvent[x] = new PostIntEvent(*this, x);
@ -129,46 +129,64 @@ Pl390::readDistributor(PacketPtr pkt)
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
Pl390::readDistributor(ContextID ctx, Addr daddr, size_t resp_sz)
{
if (GICD_ISENABLER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISENABLER.start()) >> 2;
assert(ix < 32);
pkt->set<uint32_t>(getIntEnabled(ctx, ix));
goto done;
return getIntEnabled(ctx, ix);
}
if (GICD_ICENABLER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICENABLER.start()) >> 2;
assert(ix < 32);
pkt->set<uint32_t>(getIntEnabled(ctx, ix));
goto done;
return getIntEnabled(ctx, ix);
}
if (GICD_ISPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISPENDR.start()) >> 2;
assert(ix < 32);
pkt->set<uint32_t>(getPendingInt(ctx, ix));
goto done;
return getPendingInt(ctx, ix);
}
if (GICD_ICPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICPENDR.start()) >> 2;
assert(ix < 32);
pkt->set<uint32_t>(getPendingInt(ctx, ix));
goto done;
return getPendingInt(ctx, ix);
}
if (GICD_ISACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISACTIVER.start()) >> 2;
assert(ix < 32);
pkt->set<uint32_t>(getActiveInt(ctx, ix));
goto done;
return getActiveInt(ctx, ix);
}
if (GICD_ICACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICACTIVER.start()) >> 2;
assert(ix < 32);
pkt->set<uint32_t>(getActiveInt(ctx, ix));
goto done;
return getActiveInt(ctx, ix);
}
if (GICD_IPRIORITYR.contains(daddr)) {
@ -176,27 +194,21 @@ Pl390::readDistributor(PacketPtr pkt)
assert(int_num < INT_LINES_MAX);
DPRINTF(Interrupt, "Reading interrupt priority at int# %#x \n",int_num);
switch (pkt->getSize()) {
switch (resp_sz) {
default: // will panic() after return to caller anyway
case 1:
pkt->set<uint8_t>(getIntPriority(ctx, int_num));
break;
return getIntPriority(ctx, int_num);
case 2:
assert((int_num + 1) < INT_LINES_MAX);
pkt->set<uint16_t>(getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8);
break;
return (getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8);
case 4:
assert((int_num + 3) < INT_LINES_MAX);
pkt->set<uint32_t>(getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8 |
getIntPriority(ctx, int_num+2) << 16 |
getIntPriority(ctx, int_num+3) << 24);
break;
default:
panic("Invalid size while reading priority regs in GIC: %d\n",
pkt->getSize());
return (getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8 |
getIntPriority(ctx, int_num+2) << 16 |
getIntPriority(ctx, int_num+3) << 24);
}
goto done;
}
if (GICD_ITARGETSR.contains(daddr)) {
@ -205,33 +217,16 @@ Pl390::readDistributor(PacketPtr pkt)
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) ;
}
if (resp_sz == 1) {
return getCpuTarget(ctx, int_num);
} else {
assert(ctx < sys->numRunningContexts());
uint32_t ctx_mask;
if (gem5ExtensionsEnabled) {
ctx_mask = ctx;
} else {
// convert the CPU id number into a bit mask
ctx_mask = power(2, ctx);
}
// replicate the 8-bit mask 4 times in a 32-bit word
ctx_mask |= ctx_mask << 8;
ctx_mask |= ctx_mask << 16;
pkt->set<uint32_t>(ctx_mask);
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) ;
}
goto done;
}
if (GICD_ICFGR.contains(daddr)) {
@ -239,30 +234,23 @@ Pl390::readDistributor(PacketPtr pkt)
assert(ix < 64);
/** @todo software generated interrupts and PPIs
* can't be configured in some ways */
pkt->set<uint32_t>(intConfig[ix]);
goto done;
return intConfig[ix];
}
switch(daddr) {
case GICD_CTLR:
pkt->set<uint32_t>(enabled);
break;
case GICD_TYPER: {
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. */
uint32_t tmp = ((sys->numRunningContexts() - 1) << 5) |
(itLines/INT_BITS_MAX -1) |
(haveGem5Extensions ? 0x100 : 0x0);
pkt->set<uint32_t>(tmp);
} break;
return (((sys->numRunningContexts() - 1) << 5) |
(itLines/INT_BITS_MAX -1) |
(haveGem5Extensions ? 0x100 : 0x0));
default:
panic("Tried to read Gic distributor at offset %#x\n", daddr);
break;
}
done:
pkt->makeAtomicResponse();
return distPioDelay;
}
Tick
@ -277,19 +265,24 @@ Pl390::readCpu(PacketPtr pkt)
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
Pl390::readCpu(ContextID ctx, Addr daddr)
{
switch(daddr) {
case GICC_IIDR:
pkt->set<uint32_t>(0);
break;
return 0;
case GICC_CTLR:
pkt->set<uint32_t>(cpuEnabled[ctx]);
break;
return cpuEnabled[ctx];
case GICC_PMR:
pkt->set<uint32_t>(cpuPriority[ctx]);
break;
return cpuPriority[ctx];
case GICC_BPR:
pkt->set<uint32_t>(cpuBpr[ctx]);
break;
return cpuBpr[ctx];
case GICC_IAR:
if (enabled && cpuEnabled[ctx]) {
int active_int = cpuHighestInt[ctx];
@ -337,26 +330,22 @@ Pl390::readCpu(PacketPtr pkt)
ctx, iar.ack_id, iar.cpu_id, iar);
cpuHighestInt[ctx] = SPURIOUS_INT;
updateIntState(-1);
pkt->set<uint32_t>(iar);
platform->intrctrl->clear(ctx, ArmISA::INT_IRQ, 0);
return iar;
} else {
pkt->set<uint32_t>(SPURIOUS_INT);
return SPURIOUS_INT;
}
break;
case GICC_RPR:
pkt->set<uint32_t>(iccrpr[0]);
break;
return iccrpr[0];
case GICC_HPPIR:
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
@ -366,9 +355,10 @@ Pl390::writeDistributor(PacketPtr pkt)
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 (pkt->getSize())
switch (data_sz)
{
case 1:
pkt_data = pkt->get<uint8_t>();
@ -381,141 +371,143 @@ Pl390::writeDistributor(PacketPtr pkt)
break;
default:
panic("Invalid size when writing to priority regs in Gic: %d\n",
pkt->getSize());
data_sz);
}
DPRINTF(GIC, "gic distributor write register %#x size %#x value %#x \n",
daddr, pkt->getSize(), pkt_data);
daddr, data_sz, pkt_data);
writeDistributor(ctx, daddr, pkt_data, data_sz);
pkt->makeAtomicResponse();
return distPioDelay;
}
void
Pl390::writeDistributor(ContextID ctx, Addr daddr, uint32_t data,
size_t data_sz)
{
if (GICD_ISENABLER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISENABLER.start()) >> 2;
assert(ix < 32);
getIntEnabled(ctx, ix) |= pkt->get<uint32_t>();
goto done;
getIntEnabled(ctx, ix) |= data;
return;
}
if (GICD_ICENABLER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICENABLER.start()) >> 2;
assert(ix < 32);
getIntEnabled(ctx, ix) &= ~pkt->get<uint32_t>();
goto done;
getIntEnabled(ctx, ix) &= ~data;
return;
}
if (GICD_ISPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISPENDR.start()) >> 2;
auto mask = pkt->get<uint32_t>();
auto mask = data;
if (ix == 0) mask &= SGI_MASK; // Don't allow SGIs to be changed
getPendingInt(ctx, ix) |= mask;
updateIntState(ix);
goto done;
return;
}
if (GICD_ICPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICPENDR.start()) >> 2;
auto mask = pkt->get<uint32_t>();
auto mask = data;
if (ix == 0) mask &= SGI_MASK; // Don't allow SGIs to be changed
getPendingInt(ctx, ix) &= ~mask;
updateIntState(ix);
goto done;
return;
}
if (GICD_ISACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISACTIVER.start()) >> 2;
getActiveInt(ctx, ix) |= pkt->get<uint32_t>();
goto done;
getActiveInt(ctx, ix) |= data;
return;
}
if (GICD_ICACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICACTIVER.start()) >> 2;
getActiveInt(ctx, ix) &= ~pkt->get<uint32_t>();
goto done;
getActiveInt(ctx, ix) &= ~data;
return;
}
if (GICD_IPRIORITYR.contains(daddr)) {
Addr int_num = daddr - GICD_IPRIORITYR.start();
switch(pkt->getSize()) {
switch(data_sz) {
case 1:
getIntPriority(ctx, int_num) = pkt->get<uint8_t>();
getIntPriority(ctx, int_num) = data;
break;
case 2: {
auto tmp16 = pkt->get<uint16_t>();
getIntPriority(ctx, int_num) = bits(tmp16, 7, 0);
getIntPriority(ctx, int_num + 1) = bits(tmp16, 15, 8);
getIntPriority(ctx, int_num) = bits(data, 7, 0);
getIntPriority(ctx, int_num + 1) = bits(data, 15, 8);
break;
}
case 4: {
auto tmp32 = pkt->get<uint32_t>();
getIntPriority(ctx, int_num) = bits(tmp32, 7, 0);
getIntPriority(ctx, int_num + 1) = bits(tmp32, 15, 8);
getIntPriority(ctx, int_num + 2) = bits(tmp32, 23, 16);
getIntPriority(ctx, int_num + 3) = bits(tmp32, 31, 24);
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",
pkt->getSize());
data_sz);
}
updateIntState(-1);
updateRunPri();
goto done;
return;
}
if (GICD_ITARGETSR.contains(daddr)) {
Addr int_num = daddr - GICD_ITARGETSR.start();
// 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;
// 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 (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);
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);
}
goto done;
return;
}
if (GICD_ICFGR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICFGR.start()) >> 2;
assert(ix < INT_BITS_MAX*2);
intConfig[ix] = pkt->get<uint32_t>();
if (pkt->get<uint32_t>() & NN_CONFIG_MASK)
intConfig[ix] = data;
if (data & NN_CONFIG_MASK)
warn("GIC N:N mode selected and not supported at this time\n");
goto done;
return;
}
switch(daddr) {
case GICD_CTLR:
enabled = pkt->get<uint32_t>();
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 = (
(pkt->get<uint32_t>() & 0x200) && haveGem5Extensions);
gem5ExtensionsEnabled = (data & 0x200) && haveGem5Extensions;
DPRINTF(GIC, "gem5 extensions %s\n",
gem5ExtensionsEnabled ? "enabled" : "disabled");
break;
case GICD_SGIR:
softInt(ctx, pkt->get<uint32_t>());
softInt(ctx, data);
break;
default:
panic("Tried to write Gic distributor at offset %#x\n", daddr);
break;
}
done:
pkt->makeAtomicResponse();
return distPioDelay;
}
Tick
@ -525,23 +517,36 @@ Pl390::writeCpu(PacketPtr pkt)
assert(pkt->req->hasContextId());
const ContextID ctx = pkt->req->contextId();
IAR iar;
const uint32_t data = pkt->get<uint32_t>();
DPRINTF(GIC, "gic cpu write register cpu:%d %#x val: %#x\n",
ctx, daddr, pkt->get<uint32_t>());
ctx, daddr, data);
writeCpu(ctx, daddr, data);
pkt->makeAtomicResponse();
return cpuPioDelay;
}
void
Pl390::writeCpu(ContextID ctx, Addr daddr, uint32_t data)
{
switch(daddr) {
case GICC_CTLR:
cpuEnabled[ctx] = pkt->get<uint32_t>();
cpuEnabled[ctx] = data;
break;
case GICC_PMR:
cpuPriority[ctx] = pkt->get<uint32_t>();
cpuPriority[ctx] = data;
break;
case GICC_BPR:
cpuBpr[ctx] = pkt->get<uint32_t>();
case GICC_BPR: {
auto bpr = data & 0x7;
if (bpr < GICC_BPR_MINIMUM)
bpr = GICC_BPR_MINIMUM;
cpuBpr[ctx] = bpr;
break;
case GICC_EOIR:
iar = pkt->get<uint32_t>();
}
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);
@ -569,13 +574,12 @@ Pl390::writeCpu(PacketPtr pkt)
DPRINTF(Interrupt, "CPU %d done handling intr IAR = %d from cpu %d\n",
ctx, iar.ack_id, iar.cpu_id);
break;
}
default:
panic("Tried to write Gic cpu at offset %#x\n", daddr);
break;
}
if (cpuEnabled[ctx]) updateIntState(-1);
pkt->makeAtomicResponse();
return cpuPioDelay;
}
Pl390::BankedRegs&
@ -666,6 +670,17 @@ Pl390::genSwiMask(int cpu)
return ULL(0x0101010101010101) << cpu;
}
uint8_t
Pl390::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
Pl390::updateIntState(int hint)
{
@ -676,7 +691,7 @@ Pl390::updateIntState(int hint)
/*@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 = cpuPriority[cpu];
uint8_t highest_pri = getCpuPriority(cpu);
// Check SGIs
for (int swi = 0; swi < SGI_MAX; swi++) {
@ -717,8 +732,8 @@ Pl390::updateIntState(int hint)
(getIntPriority(cpu, int_nm) < highest_pri))
if ((!mp_sys) ||
(gem5ExtensionsEnabled
? (cpuTarget[int_nm] == cpu)
: (cpuTarget[int_nm] & (1 << cpu)))) {
? (getCpuTarget(cpu, int_nm) == cpu)
: (getCpuTarget(cpu, int_nm) & (1 << cpu)))) {
highest_pri = getIntPriority(cpu, int_nm);
highest_int = int_nm;
}
@ -733,7 +748,8 @@ Pl390::updateIntState(int hint)
/* @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]) &&
if (enabled && cpuEnabled[cpu] &&
(highest_pri < getCpuPriority(cpu)) &&
!(getActiveInt(cpu, intNumToWord(highest_int))
& (1 << intNumToBit(highest_int)))) {
@ -776,13 +792,14 @@ Pl390::updateRunPri()
void
Pl390::sendInt(uint32_t num)
{
uint8_t target = getCpuTarget(0, num);
DPRINTF(Interrupt, "Received Interrupt number %d, cpuTarget %#x: \n",
num, cpuTarget[num]);
if ((cpuTarget[num] & (cpuTarget[num] - 1)) && !gem5ExtensionsEnabled)
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(cpuTarget[num], intNumToWord(num)) |= 1 << intNumToBit(num);
getPendingInt(target, intNumToWord(num)) |= 1 << intNumToBit(num);
updateIntState(intNumToWord(num));
}
@ -880,7 +897,6 @@ Pl390::BankedRegs::serialize(CheckpointOut &cp) const
SERIALIZE_SCALAR(pendingInt);
SERIALIZE_SCALAR(activeInt);
SERIALIZE_ARRAY(intPriority, SGI_MAX + PPI_MAX);
SERIALIZE_ARRAY(cpuTarget, SGI_MAX + PPI_MAX);
}
void
@ -939,7 +955,6 @@ Pl390::BankedRegs::unserialize(CheckpointIn &cp)
UNSERIALIZE_SCALAR(pendingInt);
UNSERIALIZE_SCALAR(activeInt);
UNSERIALIZE_ARRAY(intPriority, SGI_MAX + PPI_MAX);
UNSERIALIZE_ARRAY(cpuTarget, SGI_MAX + PPI_MAX);
}
Pl390 *

View File

@ -58,7 +58,7 @@
#include "dev/platform.hh"
#include "params/Pl390.hh"
class Pl390 : public BaseGic
class Pl390 : public BaseGic, public BaseGicRegisters
{
protected:
// distributor memory addresses
@ -111,6 +111,10 @@ class Pl390 : public BaseGic
static const int INT_LINES_MAX = 1020;
static const int GLOBAL_INT_LINES = INT_LINES_MAX - SGI_MAX - PPI_MAX;
/** minimum value for Binary Point Register ("IMPLEMENTATION DEFINED");
chosen for consistency with Linux's in-kernel KVM GIC model */
static const int GICC_BPR_MINIMUM = 2;
BitUnion32(SWI)
Bitfield<3,0> sgi_id;
Bitfield<23,16> cpu_list;
@ -172,16 +176,11 @@ class Pl390 : public BaseGic
* interrupt priority for SGIs and PPIs */
uint8_t intPriority[SGI_MAX + PPI_MAX];
/** GICD_ITARGETSR{0..7}
* 8b CPU target ID for each SGI and PPI */
uint8_t cpuTarget[SGI_MAX + PPI_MAX];
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
BankedRegs() :
intEnabled(0), pendingInt(0), activeInt(0),
intPriority {0}, cpuTarget {0}
intEnabled(0), pendingInt(0), activeInt(0), intPriority {0}
{}
};
std::vector<BankedRegs*> bankedRegs;
@ -252,12 +251,23 @@ class Pl390 : public BaseGic
*/
uint8_t cpuTarget[GLOBAL_INT_LINES];
uint8_t& getCpuTarget(ContextID ctx, uint32_t ix) {
uint8_t getCpuTarget(ContextID ctx, uint32_t ix) {
assert(ctx < sys->numRunningContexts());
assert(ix < INT_LINES_MAX);
if (ix < SGI_MAX + PPI_MAX) {
return getBankedRegs(ctx).cpuTarget[ix];
// "GICD_ITARGETSR0 to GICD_ITARGETSR7 are read-only, and each
// field returns a value that corresponds only to the processor
// reading the register."
uint32_t ctx_mask;
if (gem5ExtensionsEnabled) {
ctx_mask = ctx;
} else {
// convert the CPU id number into a bit mask
ctx_mask = power(2, ctx);
}
return ctx_mask;
} else {
return cpuTarget[ix - (SGI_MAX + PPI_MAX)];
return cpuTarget[ix - 32];
}
}
@ -270,6 +280,7 @@ class Pl390 : public BaseGic
/** CPU priority */
uint8_t cpuPriority[CPU_MAX];
uint8_t getCpuPriority(unsigned cpu); // BPR-adjusted priority value
/** Binary point registers */
uint8_t cpuBpr[CPU_MAX];
@ -393,21 +404,34 @@ class Pl390 : public BaseGic
* @param pkt packet to respond to
*/
Tick readDistributor(PacketPtr pkt);
uint32_t readDistributor(ContextID ctx, Addr daddr,
size_t resp_sz);
uint32_t readDistributor(ContextID ctx, Addr daddr) override {
return readDistributor(ctx, daddr, 4);
}
/** Handle a read to the cpu portion of the GIC
* @param pkt packet to respond to
*/
Tick readCpu(PacketPtr pkt);
uint32_t readCpu(ContextID ctx, Addr daddr) override;
/** Handle a write to the distributor portion of the GIC
* @param pkt packet to respond to
*/
Tick writeDistributor(PacketPtr pkt);
void writeDistributor(ContextID ctx, Addr daddr,
uint32_t data, size_t data_sz);
void writeDistributor(ContextID ctx, Addr daddr,
uint32_t data) override {
return writeDistributor(ctx, daddr, data, 4);
}
/** Handle a write to the cpu portion of the GIC
* @param pkt packet to respond to
*/
Tick writeCpu(PacketPtr pkt);
void writeCpu(ContextID ctx, Addr daddr, uint32_t data) override;
};
#endif //__DEV_ARM_GIC_H__

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015 ARM Limited
* Copyright (c) 2012, 2015, 2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -101,14 +101,33 @@ DrainManager::resume()
"Resuming a system that isn't fully drained, this is untested and "
"likely to break\n");
panic_if(_state == DrainState::Resuming,
"Resuming a system that is already trying to resume. This should "
"never happen.\n");
panic_if(_count != 0,
"Resume called in the middle of a drain cycle. %u objects "
"left to drain.\n", _count);
DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
// At this point in time the DrainManager and all objects will be
// in the the Drained state. New objects (i.e., objects created
// while resuming) will inherit the Resuming state from the
// DrainManager, which means we have to resume objects until all
// objects are in the Running state.
_state = DrainState::Resuming;
do {
DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
for (auto *obj : _allDrainable) {
if (obj->drainState() != DrainState::Running) {
assert(obj->drainState() == DrainState::Drained ||
obj->drainState() == DrainState::Resuming);
obj->dmDrainResume();
}
}
} while (!allInState(DrainState::Running));
_state = DrainState::Running;
for (auto *obj : _allDrainable)
obj->dmDrainResume();
}
void
@ -154,6 +173,17 @@ DrainManager::unregisterDrainable(Drainable *obj)
_allDrainable.erase(o);
}
bool
DrainManager::allInState(DrainState state) const
{
for (const auto *obj : _allDrainable) {
if (obj->drainState() != state)
return false;
}
return true;
}
size_t
DrainManager::drainableCount() const
{
@ -189,7 +219,8 @@ Drainable::dmDrain()
void
Drainable::dmDrainResume()
{
panic_if(_drainState != DrainState::Drained,
panic_if(_drainState != DrainState::Drained &&
_drainState != DrainState::Resuming,
"Trying to resume an object that hasn't been drained\n");
_drainState = DrainState::Running;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015 ARM Limited
* Copyright (c) 2012, 2015, 2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -58,7 +58,11 @@ class Drainable;
* all objects have entered the Drained state.
*
* Before resuming simulation, the simulator calls resume() to
* transfer the object to the Running state.
* transfer the object to the Running state. This in turn results in a
* call to drainResume() for all Drainable objects in the
* simulator. New Drainable objects may be created while resuming. In
* such cases, the new objects will be created in the Resuming state
* and later resumed.
*
* \note Even though the state of an object (visible to the rest of
* the world through Drainable::getState()) could be used to determine
@ -68,7 +72,8 @@ class Drainable;
enum class DrainState {
Running, /** Running normally */
Draining, /** Draining buffers pending serialization/handover */
Drained /** Buffers drained, ready for serialization/handover */
Drained, /** Buffers drained, ready for serialization/handover */
Resuming, /** Transient state while the simulator is resuming */
};
#endif
@ -152,6 +157,12 @@ class DrainManager
void unregisterDrainable(Drainable *obj);
private:
/**
* Helper function to check if all Drainable objects are in a
* specific state.
*/
bool allInState(DrainState state) const;
/**
* Thread-safe helper function to get the number of Drainable
* objects in a system.
@ -261,6 +272,7 @@ class Drainable
switch (_drainState) {
case DrainState::Running:
case DrainState::Drained:
case DrainState::Resuming:
return;
case DrainState::Draining:
_drainState = DrainState::Drained;

View File

@ -52,7 +52,6 @@ def upgrader(cpt):
b_intEnabled = intEnabled[0]
b_pendingInt = pendingInt[0]
b_activeInt = activeInt[0]
b_cpuTarget = cpuTarget[0:32]
del intEnabled[0]
del pendingInt[0]
@ -78,4 +77,3 @@ def upgrader(cpt):
cpt.set(new_sec, 'pendingInt', b_pendingInt)
cpt.set(new_sec, 'activeInt', b_activeInt)
cpt.set(new_sec, 'intPriority',' '.join(intPriority))
cpt.set(new_sec, 'cpuTarget', ' '.join(b_cpuTarget))