Compare commits

..

No commits in common. "c7f92c43da5b5d96b4287c418beaab3e56a91137" and "66a1016a3548e244f4d96773bfa8985262e4d4b4" have entirely different histories.

19 changed files with 260 additions and 744 deletions

View file

@ -399,7 +399,6 @@ 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-2017 ARM Limited
# Copyright (c) 2016 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@ -44,8 +44,6 @@ 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
@ -172,14 +170,6 @@ 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-2017 ARM Limited
# Copyright (c) 2016 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@ -132,7 +132,12 @@ def main():
root = bL.build(options)
addEthernet(root.system, options)
bL.instantiate(options, checkpoint_dir=options.checkpoint_dir)
if options.restore_from:
checkpoint_path = os.path.join(options.checkpoint_dir,
options.restore_from)
else:
checkpoint_path = None
bL.instantiate(checkpoint_path)
bL.run(options.checkpoint_dir)

View file

@ -1,4 +1,4 @@
# Copyright (c) 2016-2017 ARM Limited
# Copyright (c) 2016 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@ -44,7 +44,6 @@ import argparse
import os
import sys
import m5
import m5.util
from m5.objects import *
m5.util.addToPath("../../")
@ -53,7 +52,6 @@ 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'
@ -63,21 +61,6 @@ 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,
@ -124,15 +107,6 @@ 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,
@ -145,9 +119,8 @@ def addOptions(parser):
help="Disks to instantiate")
parser.add_argument("--bootscript", type=str, default=default_rcs,
help="Linux bootscript")
parser.add_argument("--cpu-type", type=str, choices=cpu_types.keys(),
default="timing",
help="CPU simulation mode. Default: %(default)s")
parser.add_argument("--atomic", action="store_true", default=False,
help="Use atomic CPUs")
parser.add_argument("--kernel-init", type=str, default="/sbin/init",
help="Override init")
parser.add_argument("--big-cpus", type=int, default=1,
@ -162,11 +135,9 @@ 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()
@ -194,31 +165,35 @@ 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:
system.bigCluster = big_model(system, options.big_cpus,
options.big_cpu_clock)
system.mem_mode = system.bigCluster.memoryMode()
all_cpus += system.bigCluster.cpus
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()
# little cluster
if options.little_cpus > 0:
system.littleCluster = little_model(system, options.little_cpus,
options.little_cpu_clock)
system.mem_mode = system.littleCluster.memoryMode()
all_cpus += system.littleCluster.cpus
if options.atomic:
system.littleCluster = AtomicCluster(system, options.little_cpus,
options.little_cpu_clock)
# 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")
else:
system.littleCluster = LittleCluster(system, options.little_cpus,
options.little_cpu_clock)
mem_mode = system.littleCluster.memoryMode()
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)
@ -228,52 +203,17 @@ 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 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)
if checkpoint_path is not None:
m5.util.inform("Restoring from checkpoint %s", checkpoint_path)
m5.instantiate(checkpoint_path)
else:
m5.instantiate()
@ -301,7 +241,7 @@ def main():
addOptions(parser)
options = parser.parse_args()
root = build(options)
instantiate(options)
instantiate(options.restore_from)
run()

View file

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

View file

@ -43,7 +43,6 @@
#include <linux/kvm.h>
#include "arch/arm/kvm/base_cpu.hh"
#include "debug/GIC.hh"
#include "debug/Interrupt.hh"
#include "params/MuxingKvmGic.hh"
@ -105,63 +104,6 @@ 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),
@ -179,39 +121,21 @@ 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();
// otherwise, drain() already sync'd the state back to the Pl390
else // from KVM emulation to simulation
fromKvmToPl390();
usingKvm = use_kvm;
}
@ -220,14 +144,19 @@ MuxingKvmGic::drainResume()
void
MuxingKvmGic::serialize(CheckpointOut &cp) const
{
// drain() already ensured Pl390 updated with KvmGic state if necessary
Pl390::serialize(cp);
if (!usingKvm)
return Pl390::serialize(cp);
panic("Checkpointing unsupported\n");
}
void
MuxingKvmGic::unserialize(CheckpointIn &cp)
{
Pl390::unserialize(cp);
if (!usingKvm)
return Pl390::unserialize(cp);
panic("Checkpointing unsupported\n");
}
Tick
@ -301,150 +230,16 @@ 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()
{
copyGicState(static_cast<Pl390*>(this), kernelGic);
panic("Gic multiplexing not implemented.\n");
}
void
MuxingKvmGic::fromKvmToPl390()
{
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);
}
panic("Gic multiplexing not implemented.\n");
}
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 : public BaseGicRegisters
class KvmKernelGicV2
{
public:
/**
@ -117,14 +117,6 @@ class KvmKernelGicV2 : public BaseGicRegisters
/** 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:
@ -138,26 +130,6 @@ class KvmKernelGicV2 : public BaseGicRegisters
*/
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;
@ -174,10 +146,7 @@ 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;
@ -207,25 +176,9 @@ class MuxingKvmGic : public Pl390
private:
bool usingKvm;
/** Multiplexing implementation */
/** Multiplexing implementation: state transfer functions */
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-2017 ARM Limited
* Copyright (c) 2010-2013, 2015-2016 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -41,8 +41,6 @@
#include "arch/arm/miscregs.hh"
#include <tuple>
#include "arch/arm/isa.hh"
#include "base/misc.hh"
#include "cpu/thread_context.hh"
@ -1969,12 +1967,11 @@ decodeCP15Reg64(unsigned crm, unsigned opc1)
return MISCREG_CP15_UNIMPL;
}
std::tuple<bool, bool>
canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr)
bool
canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr, ThreadContext *tc)
{
bool secure = !scr.ns;
bool canRead = false;
bool undefined = false;
bool canRead;
switch (cpsr.mode) {
case MODE_USER:
@ -1998,19 +1995,18 @@ canReadCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr)
canRead = miscRegInfo[reg][MISCREG_HYP_RD];
break;
default:
undefined = true;
panic("Unrecognized mode setting in CPSR.\n");
}
// can't do permissions checkes on the root of a banked pair of regs
assert(!miscRegInfo[reg][MISCREG_BANKED]);
return std::make_tuple(canRead, undefined);
return canRead;
}
std::tuple<bool, bool>
canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr)
bool
canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr, ThreadContext *tc)
{
bool secure = !scr.ns;
bool canWrite = false;
bool undefined = false;
bool canWrite;
switch (cpsr.mode) {
case MODE_USER:
@ -2034,11 +2030,11 @@ canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr)
canWrite = miscRegInfo[reg][MISCREG_HYP_WR];
break;
default:
undefined = true;
panic("Unrecognized mode setting in CPSR.\n");
}
// can't do permissions checkes on the root of a banked pair of regs
assert(!miscRegInfo[reg][MISCREG_BANKED]);
return std::make_tuple(canWrite, undefined);
return canWrite;
}
int

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2017 ARM Limited
* Copyright (c) 2010-2016 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -44,7 +44,6 @@
#define __ARCH_ARM_MISCREGS_HH__
#include <bitset>
#include <tuple>
#include "base/bitunion.hh"
#include "base/compiler.hh"
@ -1848,37 +1847,13 @@ namespace ArmISA
EndBitUnion(CPTR)
/**
* 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 read access permissions to coproc. registers
bool canReadCoprocReg(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 write access permissions to coproc. registers
bool canWriteCoprocReg(MiscRegIndex reg, SCR scr, CPSR cpsr,
ThreadContext *tc);
// 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-2017 ARM Limited
* Copyright (c) 2010, 2012-2016 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -1342,10 +1342,7 @@ TableWalker::memAttrsAArch64(ThreadContext *tc, TlbEntry &te,
attr_hi == 2 ? 2 : 1;
te.innerAttrs = attr_lo == 1 ? 0 :
attr_lo == 2 ? 6 : 5;
// 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);
te.nonCacheable = (attr_hi == 1) || (attr_lo == 1);
}
} else {
uint8_t attrIndx = lDescriptor.attrIndx();
@ -1380,25 +1377,9 @@ TableWalker::memAttrsAArch64(ThreadContext *tc, TlbEntry &te,
// Cacheability
te.nonCacheable = false;
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
if (te.mtype == TlbEntry::MemoryType::Device || // Device memory
attr_hi == 0x8 || // Normal memory, Outer Non-cacheable
attr_lo == 0x8) { // Normal memory, Inner Non-cacheable
te.nonCacheable = true;
}

View file

@ -106,7 +106,6 @@ 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,7 +50,6 @@
#include <cerrno>
#include <memory>
#include "cpu/kvm/base.hh"
#include "debug/Kvm.hh"
#include "params/KvmVM.hh"
#include "sim/system.hh"
@ -529,21 +528,12 @@ 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,7 +48,6 @@
// forward declarations
struct KvmVMParams;
class BaseKvmCPU;
class System;
/**
@ -406,11 +405,6 @@ 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, 2017 ARM Limited
* Copyright (c) 2012-2013 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -92,15 +92,4 @@ 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] = GICC_BPR_MINIMUM;
cpuBpr[x] = 0;
// Initialize cpu highest int
cpuHighestInt[x] = SPURIOUS_INT;
postIntEvent[x] = new PostIntEvent(*this, x);
@ -129,64 +129,46 @@ 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);
return getIntEnabled(ctx, ix);
pkt->set<uint32_t>(getIntEnabled(ctx, ix));
goto done;
}
if (GICD_ICENABLER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICENABLER.start()) >> 2;
assert(ix < 32);
return getIntEnabled(ctx, ix);
pkt->set<uint32_t>(getIntEnabled(ctx, ix));
goto done;
}
if (GICD_ISPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISPENDR.start()) >> 2;
assert(ix < 32);
return getPendingInt(ctx, ix);
pkt->set<uint32_t>(getPendingInt(ctx, ix));
goto done;
}
if (GICD_ICPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICPENDR.start()) >> 2;
assert(ix < 32);
return getPendingInt(ctx, ix);
pkt->set<uint32_t>(getPendingInt(ctx, ix));
goto done;
}
if (GICD_ISACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISACTIVER.start()) >> 2;
assert(ix < 32);
return getActiveInt(ctx, ix);
pkt->set<uint32_t>(getActiveInt(ctx, ix));
goto done;
}
if (GICD_ICACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICACTIVER.start()) >> 2;
assert(ix < 32);
return getActiveInt(ctx, ix);
pkt->set<uint32_t>(getActiveInt(ctx, ix));
goto done;
}
if (GICD_IPRIORITYR.contains(daddr)) {
@ -194,21 +176,27 @@ Pl390::readDistributor(ContextID ctx, Addr daddr, size_t resp_sz)
assert(int_num < INT_LINES_MAX);
DPRINTF(Interrupt, "Reading interrupt priority at int# %#x \n",int_num);
switch (resp_sz) {
default: // will panic() after return to caller anyway
switch (pkt->getSize()) {
case 1:
return getIntPriority(ctx, int_num);
pkt->set<uint8_t>(getIntPriority(ctx, int_num));
break;
case 2:
assert((int_num + 1) < INT_LINES_MAX);
return (getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8);
pkt->set<uint16_t>(getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8);
break;
case 4:
assert((int_num + 3) < INT_LINES_MAX);
return (getIntPriority(ctx, int_num) |
getIntPriority(ctx, int_num+1) << 8 |
getIntPriority(ctx, int_num+2) << 16 |
getIntPriority(ctx, int_num+3) << 24);
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());
}
goto done;
}
if (GICD_ITARGETSR.contains(daddr)) {
@ -217,16 +205,33 @@ Pl390::readDistributor(ContextID ctx, Addr daddr, size_t resp_sz)
int_num);
assert(int_num < INT_LINES_MAX);
if (resp_sz == 1) {
return getCpuTarget(ctx, int_num);
// 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 {
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) ;
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);
}
goto done;
}
if (GICD_ICFGR.contains(daddr)) {
@ -234,23 +239,30 @@ Pl390::readDistributor(ContextID ctx, Addr daddr, size_t resp_sz)
assert(ix < 64);
/** @todo software generated interrupts and PPIs
* can't be configured in some ways */
return intConfig[ix];
pkt->set<uint32_t>(intConfig[ix]);
goto done;
}
switch(daddr) {
case GICD_CTLR:
return enabled;
case GICD_TYPER:
pkt->set<uint32_t>(enabled);
break;
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. */
return (((sys->numRunningContexts() - 1) << 5) |
(itLines/INT_BITS_MAX -1) |
(haveGem5Extensions ? 0x100 : 0x0));
uint32_t tmp = ((sys->numRunningContexts() - 1) << 5) |
(itLines/INT_BITS_MAX -1) |
(haveGem5Extensions ? 0x100 : 0x0);
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
@ -265,24 +277,19 @@ 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:
return 0;
pkt->set<uint32_t>(0);
break;
case GICC_CTLR:
return cpuEnabled[ctx];
pkt->set<uint32_t>(cpuEnabled[ctx]);
break;
case GICC_PMR:
return cpuPriority[ctx];
pkt->set<uint32_t>(cpuPriority[ctx]);
break;
case GICC_BPR:
return cpuBpr[ctx];
pkt->set<uint32_t>(cpuBpr[ctx]);
break;
case GICC_IAR:
if (enabled && cpuEnabled[ctx]) {
int active_int = cpuHighestInt[ctx];
@ -330,22 +337,26 @@ Pl390::readCpu(ContextID ctx, Addr daddr)
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 {
return SPURIOUS_INT;
pkt->set<uint32_t>(SPURIOUS_INT);
}
break;
case GICC_RPR:
return iccrpr[0];
pkt->set<uint32_t>(iccrpr[0]);
break;
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
@ -355,10 +366,9 @@ 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 (data_sz)
switch (pkt->getSize())
{
case 1:
pkt_data = pkt->get<uint8_t>();
@ -371,143 +381,141 @@ Pl390::writeDistributor(PacketPtr pkt)
break;
default:
panic("Invalid size when writing to priority regs in Gic: %d\n",
data_sz);
pkt->getSize());
}
DPRINTF(GIC, "gic distributor write register %#x size %#x value %#x \n",
daddr, data_sz, pkt_data);
daddr, pkt->getSize(), 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) |= data;
return;
getIntEnabled(ctx, ix) |= pkt->get<uint32_t>();
goto done;
}
if (GICD_ICENABLER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICENABLER.start()) >> 2;
assert(ix < 32);
getIntEnabled(ctx, ix) &= ~data;
return;
getIntEnabled(ctx, ix) &= ~pkt->get<uint32_t>();
goto done;
}
if (GICD_ISPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISPENDR.start()) >> 2;
auto mask = data;
auto mask = pkt->get<uint32_t>();
if (ix == 0) mask &= SGI_MASK; // Don't allow SGIs to be changed
getPendingInt(ctx, ix) |= mask;
updateIntState(ix);
return;
goto done;
}
if (GICD_ICPENDR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICPENDR.start()) >> 2;
auto mask = data;
auto mask = pkt->get<uint32_t>();
if (ix == 0) mask &= SGI_MASK; // Don't allow SGIs to be changed
getPendingInt(ctx, ix) &= ~mask;
updateIntState(ix);
return;
goto done;
}
if (GICD_ISACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ISACTIVER.start()) >> 2;
getActiveInt(ctx, ix) |= data;
return;
getActiveInt(ctx, ix) |= pkt->get<uint32_t>();
goto done;
}
if (GICD_ICACTIVER.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICACTIVER.start()) >> 2;
getActiveInt(ctx, ix) &= ~data;
return;
getActiveInt(ctx, ix) &= ~pkt->get<uint32_t>();
goto done;
}
if (GICD_IPRIORITYR.contains(daddr)) {
Addr int_num = daddr - GICD_IPRIORITYR.start();
switch(data_sz) {
switch(pkt->getSize()) {
case 1:
getIntPriority(ctx, int_num) = data;
getIntPriority(ctx, int_num) = pkt->get<uint8_t>();
break;
case 2: {
getIntPriority(ctx, int_num) = bits(data, 7, 0);
getIntPriority(ctx, int_num + 1) = bits(data, 15, 8);
auto tmp16 = pkt->get<uint16_t>();
getIntPriority(ctx, int_num) = bits(tmp16, 7, 0);
getIntPriority(ctx, int_num + 1) = bits(tmp16, 15, 8);
break;
}
case 4: {
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);
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);
break;
}
default:
panic("Invalid size when writing to priority regs in Gic: %d\n",
data_sz);
pkt->getSize());
}
updateIntState(-1);
updateRunPri();
return;
goto done;
}
if (GICD_ITARGETSR.contains(daddr)) {
Addr int_num = daddr - GICD_ITARGETSR.start();
// 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;
// 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 (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);
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(int_num >> 2);
}
return;
goto done;
}
if (GICD_ICFGR.contains(daddr)) {
uint32_t ix = (daddr - GICD_ICFGR.start()) >> 2;
assert(ix < INT_BITS_MAX*2);
intConfig[ix] = data;
if (data & NN_CONFIG_MASK)
intConfig[ix] = 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");
return;
goto done;
}
switch(daddr) {
case GICD_CTLR:
enabled = data;
enabled = pkt->get<uint32_t>();
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 = (data & 0x200) && haveGem5Extensions;
gem5ExtensionsEnabled = (
(pkt->get<uint32_t>() & 0x200) && haveGem5Extensions);
DPRINTF(GIC, "gem5 extensions %s\n",
gem5ExtensionsEnabled ? "enabled" : "disabled");
break;
case GICD_SGIR:
softInt(ctx, data);
softInt(ctx, pkt->get<uint32_t>());
break;
default:
panic("Tried to write Gic distributor at offset %#x\n", daddr);
break;
}
done:
pkt->makeAtomicResponse();
return distPioDelay;
}
Tick
@ -517,36 +525,23 @@ Pl390::writeCpu(PacketPtr pkt)
assert(pkt->req->hasContextId());
const ContextID ctx = pkt->req->contextId();
const uint32_t data = pkt->get<uint32_t>();
IAR iar;
DPRINTF(GIC, "gic cpu write register cpu:%d %#x val: %#x\n",
ctx, daddr, data);
ctx, daddr, pkt->get<uint32_t>());
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] = data;
cpuEnabled[ctx] = pkt->get<uint32_t>();
break;
case GICC_PMR:
cpuPriority[ctx] = data;
cpuPriority[ctx] = pkt->get<uint32_t>();
break;
case GICC_BPR: {
auto bpr = data & 0x7;
if (bpr < GICC_BPR_MINIMUM)
bpr = GICC_BPR_MINIMUM;
cpuBpr[ctx] = bpr;
case GICC_BPR:
cpuBpr[ctx] = pkt->get<uint32_t>();
break;
}
case GICC_EOIR: {
const IAR iar = data;
case GICC_EOIR:
iar = pkt->get<uint32_t>();
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);
@ -574,12 +569,13 @@ Pl390::writeCpu(ContextID ctx, Addr daddr, uint32_t data)
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&
@ -670,17 +666,6 @@ 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)
{
@ -691,7 +676,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 = getCpuPriority(cpu);
uint8_t highest_pri = cpuPriority[cpu];
// Check SGIs
for (int swi = 0; swi < SGI_MAX; swi++) {
@ -732,8 +717,8 @@ Pl390::updateIntState(int hint)
(getIntPriority(cpu, int_nm) < highest_pri))
if ((!mp_sys) ||
(gem5ExtensionsEnabled
? (getCpuTarget(cpu, int_nm) == cpu)
: (getCpuTarget(cpu, int_nm) & (1 << cpu)))) {
? (cpuTarget[int_nm] == cpu)
: (cpuTarget[int_nm] & (1 << cpu)))) {
highest_pri = getIntPriority(cpu, int_nm);
highest_int = int_nm;
}
@ -748,8 +733,7 @@ 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 < getCpuPriority(cpu)) &&
if (enabled && cpuEnabled[cpu] && (highest_pri < cpuPriority[cpu]) &&
!(getActiveInt(cpu, intNumToWord(highest_int))
& (1 << intNumToBit(highest_int)))) {
@ -792,14 +776,13 @@ Pl390::updateRunPri()
void
Pl390::sendInt(uint32_t num)
{
uint8_t target = getCpuTarget(0, num);
DPRINTF(Interrupt, "Received Interrupt number %d, cpuTarget %#x: \n",
num, target);
if ((target & (target - 1)) && !gem5ExtensionsEnabled)
num, cpuTarget[num]);
if ((cpuTarget[num] & (cpuTarget[num] - 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(target, intNumToWord(num)) |= 1 << intNumToBit(num);
getPendingInt(cpuTarget[num], intNumToWord(num)) |= 1 << intNumToBit(num);
updateIntState(intNumToWord(num));
}
@ -897,6 +880,7 @@ 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
@ -955,6 +939,7 @@ 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, public BaseGicRegisters
class Pl390 : public BaseGic
{
protected:
// distributor memory addresses
@ -111,10 +111,6 @@ class Pl390 : public BaseGic, public BaseGicRegisters
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;
@ -176,11 +172,16 @@ class Pl390 : public BaseGic, public BaseGicRegisters
* 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}
intEnabled(0), pendingInt(0), activeInt(0),
intPriority {0}, cpuTarget {0}
{}
};
std::vector<BankedRegs*> bankedRegs;
@ -251,23 +252,12 @@ class Pl390 : public BaseGic, public BaseGicRegisters
*/
uint8_t cpuTarget[GLOBAL_INT_LINES];
uint8_t getCpuTarget(ContextID ctx, uint32_t ix) {
assert(ctx < sys->numRunningContexts());
uint8_t& getCpuTarget(ContextID ctx, uint32_t ix) {
assert(ix < INT_LINES_MAX);
if (ix < SGI_MAX + PPI_MAX) {
// "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;
return getBankedRegs(ctx).cpuTarget[ix];
} else {
return cpuTarget[ix - 32];
return cpuTarget[ix - (SGI_MAX + PPI_MAX)];
}
}
@ -280,7 +270,6 @@ class Pl390 : public BaseGic, public BaseGicRegisters
/** CPU priority */
uint8_t cpuPriority[CPU_MAX];
uint8_t getCpuPriority(unsigned cpu); // BPR-adjusted priority value
/** Binary point registers */
uint8_t cpuBpr[CPU_MAX];
@ -404,34 +393,21 @@ class Pl390 : public BaseGic, public BaseGicRegisters
* @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, 2017 ARM Limited
* Copyright (c) 2012, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -101,33 +101,14 @@ 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);
// 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));
DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
_state = DrainState::Running;
for (auto *obj : _allDrainable)
obj->dmDrainResume();
}
void
@ -173,17 +154,6 @@ 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
{
@ -219,8 +189,7 @@ Drainable::dmDrain()
void
Drainable::dmDrainResume()
{
panic_if(_drainState != DrainState::Drained &&
_drainState != DrainState::Resuming,
panic_if(_drainState != DrainState::Drained,
"Trying to resume an object that hasn't been drained\n");
_drainState = DrainState::Running;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, 2017 ARM Limited
* Copyright (c) 2012, 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@ -58,11 +58,7 @@ class Drainable;
* all objects have entered the Drained state.
*
* Before resuming simulation, the simulator calls resume() to
* 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.
* transfer the object to the Running state.
*
* \note Even though the state of an object (visible to the rest of
* the world through Drainable::getState()) could be used to determine
@ -72,8 +68,7 @@ class Drainable;
enum class DrainState {
Running, /** Running normally */
Draining, /** Draining buffers pending serialization/handover */
Drained, /** Buffers drained, ready for serialization/handover */
Resuming, /** Transient state while the simulator is resuming */
Drained /** Buffers drained, ready for serialization/handover */
};
#endif
@ -157,12 +152,6 @@ 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.
@ -272,7 +261,6 @@ class Drainable
switch (_drainState) {
case DrainState::Running:
case DrainState::Drained:
case DrainState::Resuming:
return;
case DrainState::Draining:
_drainState = DrainState::Drained;

View file

@ -52,6 +52,7 @@ 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]
@ -77,3 +78,4 @@ 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))