summaryrefslogtreecommitdiff
path: root/src/dev/arm
diff options
context:
space:
mode:
Diffstat (limited to 'src/dev/arm')
-rw-r--r--src/dev/arm/Gic.py12
-rw-r--r--src/dev/arm/SConscript4
-rw-r--r--src/dev/arm/base_gic.cc7
-rw-r--r--src/dev/arm/base_gic.hh8
-rw-r--r--src/dev/arm/gic_v3.cc275
-rw-r--r--src/dev/arm/gic_v3.hh142
-rw-r--r--src/dev/arm/gic_v3_cpu_interface.cc2362
-rw-r--r--src/dev/arm/gic_v3_cpu_interface.hh301
-rw-r--r--src/dev/arm/gic_v3_distributor.cc1132
-rw-r--r--src/dev/arm/gic_v3_distributor.hh218
-rw-r--r--src/dev/arm/gic_v3_redistributor.cc834
-rw-r--r--src/dev/arm/gic_v3_redistributor.hh183
12 files changed, 5478 insertions, 0 deletions
diff --git a/src/dev/arm/Gic.py b/src/dev/arm/Gic.py
index dddb7dfac..947036a80 100644
--- a/src/dev/arm/Gic.py
+++ b/src/dev/arm/Gic.py
@@ -160,3 +160,15 @@ class VGic(PioDevice):
node.appendPhandle(gic)
yield node
+
+class Gicv3(BaseGic):
+ type = 'Gicv3'
+ cxx_header = "dev/arm/gic_v3.hh"
+
+ dist_addr = Param.Addr(0x2c000000, "Address for distributor")
+ dist_pio_delay = Param.Latency('10ns', "Delay for PIO r/w to distributor")
+ redist_addr = Param.Addr(0x2c010000, "Address for redistributors")
+ redist_pio_delay = Param.Latency('10ns',
+ "Delay for PIO r/w to redistributors")
+ it_lines = Param.UInt32(1020,
+ "Number of interrupt lines supported (max = 1020)")
diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript
index b687ee8e1..c4aa52180 100644
--- a/src/dev/arm/SConscript
+++ b/src/dev/arm/SConscript
@@ -57,6 +57,10 @@ if env['TARGET_ISA'] == 'arm':
Source('generic_timer.cc')
Source('gic_v2.cc')
Source('gic_v2m.cc')
+ Source('gic_v3.cc')
+ Source('gic_v3_cpu_interface.cc')
+ Source('gic_v3_distributor.cc')
+ Source('gic_v3_redistributor.cc')
Source('pl011.cc')
Source('pl111.cc')
Source('hdlcd.cc')
diff --git a/src/dev/arm/base_gic.cc b/src/dev/arm/base_gic.cc
index 864bad38f..a23c12310 100644
--- a/src/dev/arm/base_gic.cc
+++ b/src/dev/arm/base_gic.cc
@@ -64,6 +64,13 @@ BaseGic::~BaseGic()
{
}
+void
+BaseGic::init()
+{
+ PioDevice::init();
+ getSystem()->setGIC(this);
+}
+
const BaseGic::Params *
BaseGic::params() const
{
diff --git a/src/dev/arm/base_gic.hh b/src/dev/arm/base_gic.hh
index f18539fe8..7c0cc0edc 100644
--- a/src/dev/arm/base_gic.hh
+++ b/src/dev/arm/base_gic.hh
@@ -46,6 +46,7 @@
#include <unordered_map>
+#include "arch/arm/system.hh"
#include "dev/io_device.hh"
class Platform;
@@ -67,6 +68,7 @@ class BaseGic : public PioDevice
BaseGic(const Params *p);
virtual ~BaseGic();
+ void init() override;
const Params * params() const;
@@ -99,6 +101,12 @@ class BaseGic : public PioDevice
*/
virtual void clearInt(uint32_t num) = 0;
+ ArmSystem *
+ getSystem() const
+ {
+ return (ArmSystem *) sys;
+ }
+
protected:
/** Platform this GIC belongs to. */
Platform *platform;
diff --git a/src/dev/arm/gic_v3.cc b/src/dev/arm/gic_v3.cc
new file mode 100644
index 000000000..738eb118b
--- /dev/null
+++ b/src/dev/arm/gic_v3.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#include "dev/arm/gic_v3.hh"
+
+#include "cpu/intr_control.hh"
+#include "debug/GIC.hh"
+#include "debug/Interrupt.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
+#include "dev/arm/gic_v3_distributor.hh"
+#include "dev/arm/gic_v3_redistributor.hh"
+#include "dev/platform.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+
+Gicv3::Gicv3(const Params * p)
+ : BaseGic(p)
+{
+}
+
+Gicv3::~Gicv3()
+{
+}
+
+void
+Gicv3::init()
+{
+ distRange = RangeSize(params()->dist_addr,
+ Gicv3Distributor::ADDR_RANGE_SIZE - 1);
+ redistRange = RangeSize(params()->redist_addr,
+ Gicv3Redistributor::ADDR_RANGE_SIZE * sys->numContexts() - 1);
+ addrRanges = {distRange, redistRange};
+ BaseGic::init();
+ distributor = new Gicv3Distributor(this, params()->it_lines);
+ redistributors.resize(sys->numContexts(), nullptr);
+ cpuInterfaces.resize(sys->numContexts(), nullptr);
+
+ for (int i = 0; i < sys->numContexts(); i++) {
+ redistributors[i] = new Gicv3Redistributor(this, i);
+ cpuInterfaces[i] = new Gicv3CPUInterface(this, i);
+ }
+
+ distributor->init();
+
+ for (int i = 0; i < sys->numContexts(); i++) {
+ redistributors[i]->init();
+ cpuInterfaces[i]->init();
+ }
+}
+
+void
+Gicv3::initState()
+{
+ distributor->initState();
+
+ for (int i = 0; i < sys->numContexts(); i++) {
+ redistributors[i]->initState();
+ cpuInterfaces[i]->initState();
+ }
+}
+
+Tick
+Gicv3::read(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr();
+ const size_t size = pkt->getSize();
+ const ContextID context_id = pkt->req->contextId();
+ bool is_secure_access = pkt->isSecure();
+ uint64_t resp = 0;
+ Tick delay = 0;
+
+ if (distRange.contains(addr)) {
+ const Addr daddr = addr - distRange.start();
+ panic_if(!distributor, "Distributor is null!");
+ resp = distributor->read(daddr, size, is_secure_access);
+ delay = params()->dist_pio_delay;
+ DPRINTF(GIC, "Gicv3::read(): (distributor) context_id %d register %#x "
+ "size %d is_secure_access %d (value %#x)\n",
+ context_id, daddr, size, is_secure_access, resp);
+ } else if (redistRange.contains(addr)) {
+ Addr daddr = addr - redistRange.start();
+ uint32_t redistributor_id =
+ daddr / Gicv3Redistributor::ADDR_RANGE_SIZE;
+ daddr = daddr % Gicv3Redistributor::ADDR_RANGE_SIZE;
+ panic_if(redistributor_id >= redistributors.size(),
+ "Invalid redistributor_id!");
+ panic_if(!redistributors[redistributor_id], "Redistributor is null!");
+ resp = redistributors[redistributor_id]->read(daddr, size,
+ is_secure_access);
+ delay = params()->redist_pio_delay;
+ DPRINTF(GIC, "Gicv3::read(): (redistributor %d) context_id %d "
+ "register %#x size %d is_secure_access %d (value %#x)\n",
+ redistributor_id, context_id, daddr, size, is_secure_access,
+ resp);
+ } else {
+ panic("Gicv3::read(): unknown address %#x\n", addr);
+ }
+
+ pkt->setUintX(resp, LittleEndianByteOrder);
+ pkt->makeAtomicResponse();
+ return delay;
+}
+
+Tick
+Gicv3::write(PacketPtr pkt)
+{
+ const size_t size = pkt->getSize();
+ uint64_t data = pkt->getUintX(LittleEndianByteOrder);
+ const Addr addr = pkt->getAddr();
+ const ContextID context_id = pkt->req->contextId();
+ bool is_secure_access = pkt->isSecure();
+ Tick delay = 0;
+
+ if (distRange.contains(addr)) {
+ const Addr daddr = addr - distRange.start();
+ panic_if(!distributor, "Distributor is null!");
+ DPRINTF(GIC, "Gicv3::write(): (distributor) context_id %d "
+ "register %#x size %d is_secure_access %d value %#x\n",
+ context_id, daddr, size, is_secure_access, data);
+ distributor->write(daddr, data, size, is_secure_access);
+ delay = params()->dist_pio_delay;
+ } else if (redistRange.contains(addr)) {
+ Addr daddr = addr - redistRange.start();
+ uint32_t redistributor_id =
+ daddr / Gicv3Redistributor::ADDR_RANGE_SIZE;
+ daddr = daddr % Gicv3Redistributor::ADDR_RANGE_SIZE;
+ panic_if(redistributor_id >= redistributors.size(),
+ "Invalid redistributor_id!");
+ panic_if(!redistributors[redistributor_id], "Redistributor is null!");
+ DPRINTF(GIC, "Gicv3::write(): (redistributor %d) context_id %d "
+ "register %#x size %d is_secure_access %d value %#x\n",
+ redistributor_id, context_id, daddr, size, is_secure_access,
+ data);
+ redistributors[redistributor_id]->write(daddr, data, size,
+ is_secure_access);
+ delay = params()->redist_pio_delay;
+ } else {
+ panic("Gicv3::write(): unknown address %#x\n", addr);
+ }
+
+ pkt->makeAtomicResponse();
+ return delay;
+}
+
+void
+Gicv3::sendInt(uint32_t int_id)
+{
+ panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+ panic_if(int_id >= Gicv3::INTID_SECURE, "Invalid SPI!");
+ DPRINTF(Interrupt, "Gicv3::sendInt(): received SPI %d\n", int_id);
+ distributor->sendInt(int_id);
+}
+
+void
+Gicv3::clearInt(uint32_t number)
+{
+ distributor->intDeasserted(number);
+}
+
+void
+Gicv3::sendPPInt(uint32_t int_id, uint32_t cpu)
+{
+ panic_if(cpu >= redistributors.size(), "Invalid cpuID sending PPI!");
+ panic_if(int_id < Gicv3::SGI_MAX, "Invalid PPI!");
+ panic_if(int_id >= Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid PPI!");
+ DPRINTF(Interrupt, "Gicv3::sendPPInt(): received PPI %d cpuTarget %#x\n",
+ int_id, cpu);
+ redistributors[cpu]->sendPPInt(int_id);
+}
+
+void
+Gicv3::clearPPInt(uint32_t num, uint32_t cpu)
+{
+}
+
+void
+Gicv3::postInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
+{
+ postDelayedInt(cpu, int_type);
+}
+
+void
+Gicv3::deassertInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
+{
+ platform->intrctrl->clear(cpu, int_type, 0);
+}
+
+void
+Gicv3::postDelayedInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
+{
+ platform->intrctrl->post(cpu, int_type, 0);
+}
+
+Gicv3Redistributor *
+Gicv3::getRedistributorByAffinity(uint32_t affinity)
+{
+ for (auto & redistributor : redistributors) {
+ if (redistributor->getAffinity() == affinity) {
+ return redistributor;
+ }
+ }
+
+ return nullptr;
+}
+
+void
+Gicv3::serialize(CheckpointOut & cp) const
+{
+ distributor->serializeSection(cp, "distributor");
+
+ for (uint32_t redistributor_id = 0;
+ redistributor_id < redistributors.size();
+ redistributor_id++)
+ redistributors[redistributor_id]->serializeSection(cp,
+ csprintf("redistributors.%i", redistributor_id));
+
+ for (uint32_t cpu_interface_id = 0;
+ cpu_interface_id < cpuInterfaces.size();
+ cpu_interface_id++)
+ cpuInterfaces[cpu_interface_id]->serializeSection(cp,
+ csprintf("cpuInterface.%i", cpu_interface_id));
+}
+
+void
+Gicv3::unserialize(CheckpointIn & cp)
+{
+ getSystem()->setGIC(this);
+
+ distributor->unserializeSection(cp, "distributor");
+
+ for (uint32_t redistributor_id = 0;
+ redistributor_id < redistributors.size();
+ redistributor_id++)
+ redistributors[redistributor_id]->unserializeSection(cp,
+ csprintf("redistributors.%i", redistributor_id));
+
+ for (uint32_t cpu_interface_id = 0;
+ cpu_interface_id < cpuInterfaces.size();
+ cpu_interface_id++)
+ cpuInterfaces[cpu_interface_id]->unserializeSection(cp,
+ csprintf("cpuInterface.%i", cpu_interface_id));
+}
+
+Gicv3 *
+Gicv3Params::create()
+{
+ return new Gicv3(this);
+}
diff --git a/src/dev/arm/gic_v3.hh b/src/dev/arm/gic_v3.hh
new file mode 100644
index 000000000..a24263969
--- /dev/null
+++ b/src/dev/arm/gic_v3.hh
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_H__
+#define __DEV_ARM_GICV3_H__
+
+#include "dev/arm/base_gic.hh"
+#include "params/Gicv3.hh"
+
+class Gicv3Distributor;
+class Gicv3Redistributor;
+class Gicv3CPUInterface;
+
+class Gicv3 : public BaseGic
+{
+ protected:
+
+ Gicv3Distributor * distributor;
+ std::vector<Gicv3Redistributor *> redistributors;
+ std::vector<Gicv3CPUInterface *> cpuInterfaces;
+ AddrRange distRange;
+ AddrRange redistRange;
+ AddrRangeList addrRanges;
+
+ public:
+
+ // Special interrupt IDs
+ static const int INTID_SECURE = 1020;
+ static const int INTID_NONSECURE = 1021;
+ static const int INTID_SPURIOUS = 1023;
+
+ // Number of Software Generated Interrupts
+ static const int SGI_MAX = 16;
+ // Number of Private Peripheral Interrupts
+ static const int PPI_MAX = 16;
+
+ typedef enum {
+ INT_INACTIVE,
+ INT_PENDING,
+ INT_ACTIVE,
+ INT_ACTIVE_PENDING,
+ } IntStatus;
+
+ typedef enum {
+ G0S,
+ G1S,
+ G1NS,
+ } GroupId;
+
+ typedef enum {
+ INT_LEVEL_SENSITIVE,
+ INT_EDGE_TRIGGERED,
+ } IntTriggerType;
+
+ typedef Gicv3Params Params;
+
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+
+ Gicv3(const Params * p);
+ ~Gicv3();
+ void init() override;
+ void initState() override;
+
+ AddrRangeList
+ getAddrRanges() const override
+ {
+ return addrRanges;
+ }
+
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+ void sendInt(uint32_t int_id) override;
+ void clearInt(uint32_t int_id) override;
+ void sendPPInt(uint32_t int_id, uint32_t cpu) override;
+ void clearPPInt(uint32_t int_id, uint32_t cpu) override;
+
+ void serialize(CheckpointOut & cp) const override;
+ void unserialize(CheckpointIn & cp) override;
+
+ Gicv3Distributor *
+ getDistributor() const
+ {
+ return distributor;
+ }
+
+ Gicv3CPUInterface *
+ getCPUInterface(int cpu_id) const
+ {
+ assert(cpu_id < cpuInterfaces.size() and cpuInterfaces[cpu_id]);
+ return cpuInterfaces[cpu_id];
+ }
+
+ Gicv3Redistributor *
+ getRedistributor(ContextID context_id) const
+ {
+ assert(context_id < redistributors.size() and
+ redistributors[context_id]);
+ return redistributors[context_id];
+ }
+
+ Gicv3Redistributor * getRedistributorByAffinity(uint32_t affinity);
+ void postInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+ void postDelayedInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+ void deassertInt(uint32_t cpu, ArmISA::InterruptTypes int_type);
+
+ protected:
+
+ void reset();
+};
+
+#endif //__DEV_ARM_GICV3_H__
diff --git a/src/dev/arm/gic_v3_cpu_interface.cc b/src/dev/arm/gic_v3_cpu_interface.cc
new file mode 100644
index 000000000..7245cc605
--- /dev/null
+++ b/src/dev/arm/gic_v3_cpu_interface.cc
@@ -0,0 +1,2362 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#include "dev/arm/gic_v3_cpu_interface.hh"
+
+#include "arch/arm/isa.hh"
+#include "debug/GIC.hh"
+#include "dev/arm/gic_v3.hh"
+#include "dev/arm/gic_v3_distributor.hh"
+#include "dev/arm/gic_v3_redistributor.hh"
+
+Gicv3CPUInterface::Gicv3CPUInterface(Gicv3 * gic, uint32_t cpu_id)
+ : BaseISADevice(),
+ gic(gic),
+ redistributor(nullptr),
+ distributor(nullptr),
+ cpuId(cpu_id)
+{
+}
+
+Gicv3CPUInterface::~Gicv3CPUInterface()
+{
+}
+
+void
+Gicv3CPUInterface::init()
+{
+ redistributor = gic->getRedistributor(cpuId);
+ distributor = gic->getDistributor();
+}
+
+void
+Gicv3CPUInterface::initState()
+{
+ reset();
+}
+
+void
+Gicv3CPUInterface::reset()
+{
+ hppi.prio = 0xff;
+}
+
+bool
+Gicv3CPUInterface::getHCREL2FMO()
+{
+ HCR hcr = isa->readMiscRegNoEffect(MISCREG_HCR_EL2);
+
+ if (hcr.tge && hcr.e2h) {
+ return false;
+ } else if (hcr.tge) {
+ return true;
+ } else {
+ return hcr.fmo;
+ }
+}
+
+bool
+Gicv3CPUInterface::getHCREL2IMO()
+{
+ HCR hcr = isa->readMiscRegNoEffect(MISCREG_HCR_EL2);
+
+ if (hcr.tge && hcr.e2h) {
+ return false;
+ } else if (hcr.tge) {
+ return true;
+ } else {
+ return hcr.imo;
+ }
+}
+
+ArmISA::MiscReg
+Gicv3CPUInterface::readMiscReg(int misc_reg)
+{
+ ArmISA::MiscReg value = isa->readMiscRegNoEffect(misc_reg);
+ bool hcr_fmo = getHCREL2FMO();
+ bool hcr_imo = getHCREL2IMO();
+
+ switch (misc_reg) {
+ case MISCREG_ICC_AP1R0:
+ case MISCREG_ICC_AP1R0_EL1: {
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return isa->readMiscRegNoEffect(MISCREG_ICV_AP1R0_EL1);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_AP1R1:
+ case MISCREG_ICC_AP1R1_EL1:
+
+ // only implemented if supporting 6 or more bits of priority
+ case MISCREG_ICC_AP1R2:
+ case MISCREG_ICC_AP1R2_EL1:
+
+ // only implemented if supporting 7 or more bits of priority
+ case MISCREG_ICC_AP1R3:
+ case MISCREG_ICC_AP1R3_EL1:
+ // only implemented if supporting 7 or more bits of priority
+ return 0;
+
+ case MISCREG_ICC_AP0R0:
+ case MISCREG_ICC_AP0R0_EL1: {
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return isa->readMiscRegNoEffect(MISCREG_ICV_AP0R0_EL1);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_AP0R1:
+ case MISCREG_ICC_AP0R1_EL1:
+
+ // only implemented if supporting 6 or more bits of priority
+ case MISCREG_ICC_AP0R2:
+ case MISCREG_ICC_AP0R2_EL1:
+
+ // only implemented if supporting 7 or more bits of priority
+ case MISCREG_ICC_AP0R3:
+ case MISCREG_ICC_AP0R3_EL1:
+ // only implemented if supporting 7 or more bits of priority
+ return 0;
+
+ case MISCREG_ICC_IGRPEN0:
+ case MISCREG_ICC_IGRPEN0_EL1: {
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return isa->readMiscRegNoEffect(MISCREG_ICV_IGRPEN0_EL1);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_IGRPEN1:
+ case MISCREG_ICC_IGRPEN1_EL1: {
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return isa->readMiscRegNoEffect(MISCREG_ICV_IGRPEN1_EL1);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_MGRPEN1:
+ case MISCREG_ICC_IGRPEN1_EL3: {
+ // EnableGrp1S and EnableGrp1NS are aliased with
+ // ICC_IGRPEN1_EL1_S.Enable and ICC_IGRPEN1_EL1_NS.Enable
+ bool enable_grp_1s =
+ isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S) &
+ ICC_IGRPEN1_EL1_ENABLE;
+ bool enable_grp_1ns =
+ isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS) &
+ ICC_IGRPEN1_EL1_ENABLE;
+ value = 0;
+
+ if (enable_grp_1s) {
+ value |= ICC_IGRPEN1_EL3_ENABLEGRP1S;
+ }
+
+ if (enable_grp_1ns) {
+ value |= ICC_IGRPEN1_EL3_ENABLEGRP1NS;
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_RPR:
+ case MISCREG_ICC_RPR_EL1: {
+ if ((currEL() == EL1) && !inSecureState() &&
+ (hcr_imo || hcr_fmo)) {
+ return readMiscReg(MISCREG_ICV_RPR_EL1);
+ }
+
+ uint8_t rprio = highestActivePriority();
+
+ if (haveEL(EL3) && !inSecureState() &&
+ (isa->readMiscRegNoEffect(MISCREG_SCR_EL3) & (1U << 2))) {
+ /* NS GIC access and Group 0 is inaccessible to NS */
+ if ((rprio & 0x80) == 0) {
+ /* NS should not see priorities in the Secure half of the
+ * range */
+ rprio = 0;
+ } else if (rprio != 0xff) {
+ /* Non-idle priority: show the Non-secure view of it */
+ rprio = (rprio << 1) & 0xff;
+ }
+ }
+
+ value = rprio;
+ break;
+ }
+
+ case MISCREG_ICV_RPR_EL1: {
+ value = virtualHighestActivePriority();
+ break;
+ }
+
+ case MISCREG_ICC_HPPIR0:
+ case MISCREG_ICC_HPPIR0_EL1: {
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return readMiscReg(MISCREG_ICV_HPPIR0_EL1);
+ }
+
+ value = getHPPIR0();
+ break;
+ }
+
+ case MISCREG_ICV_HPPIR0_EL1: {
+ value = Gicv3::INTID_SPURIOUS;
+ int lr_idx = getHPPVILR();
+
+ if (lr_idx >= 0) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+ Gicv3::GroupId group =
+ lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+
+ if (group == Gicv3::G0S) {
+ value = bits(lr, 31, 0);
+ }
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_HPPIR1:
+ case MISCREG_ICC_HPPIR1_EL1: {
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return readMiscReg(MISCREG_ICV_HPPIR1_EL1);
+ }
+
+ value = getHPPIR1();
+ break;
+ }
+
+ case MISCREG_ICV_HPPIR1_EL1: {
+ value = Gicv3::INTID_SPURIOUS;
+ int lr_idx = getHPPVILR();
+
+ if (lr_idx >= 0) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+ Gicv3::GroupId group =
+ lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+
+ if (group == Gicv3::G1NS) {
+ value = bits(lr, 31, 0);
+ }
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_BPR0:
+ case MISCREG_ICC_BPR0_EL1:
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return readMiscReg(MISCREG_ICV_BPR0_EL1);
+ }
+
+ M5_FALLTHROUGH;
+
+ case MISCREG_ICC_BPR1:
+ case MISCREG_ICC_BPR1_EL1:
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return readMiscReg(MISCREG_ICV_BPR1_EL1);
+ }
+
+ {
+ Gicv3::GroupId group =
+ misc_reg == MISCREG_ICC_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1S;
+
+ if (group == Gicv3::G1S && !inSecureState()) {
+ group = Gicv3::G1NS;
+ }
+
+ if ((group == Gicv3::G1S) &&
+ !isEL3OrMon() &&
+ (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S)
+ & ICC_CTLR_EL1_CBPR)) {
+ group = Gicv3::G0S;
+ }
+
+ bool sat_inc = false;
+
+ if ((group == Gicv3::G1NS) &&
+ (currEL() < EL3) &&
+ (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS)
+ & ICC_CTLR_EL1_CBPR)) {
+ // Reads return BPR0 + 1 saturated to 7, WI
+ group = Gicv3::G0S;
+ sat_inc = true;
+ }
+
+ uint8_t bpr;
+
+ if (group == Gicv3::G0S) {
+ bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1);
+ } else {
+ bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR1_EL1);
+ }
+
+ if (sat_inc) {
+ bpr++;
+
+ if (bpr > 7) {
+ bpr = 7;
+ }
+ }
+
+ value = bpr;
+ break;
+ }
+
+ case MISCREG_ICV_BPR0_EL1:
+ case MISCREG_ICV_BPR1_EL1: {
+ Gicv3::GroupId group =
+ misc_reg == MISCREG_ICV_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1NS;
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+ bool sat_inc = false;
+
+ if (group == Gicv3::G1NS && (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) {
+ // reads return bpr0 + 1 saturated to 7, writes ignored
+ group = Gicv3::G0S;
+ sat_inc = true;
+ }
+
+ uint8_t vbpr;
+
+ if (group == Gicv3::G0S) {
+ vbpr = bits(ich_vmcr_el2, 23, 21);
+ } else {
+ vbpr = bits(ich_vmcr_el2, 20, 18);
+ }
+
+ if (sat_inc) {
+ vbpr++;
+
+ if (vbpr > 7) {
+ vbpr = 7;
+ }
+ }
+
+ value = vbpr;
+ break;
+ }
+
+ case MISCREG_ICC_PMR:
+ case MISCREG_ICC_PMR_EL1: // Priority Mask Register
+ if ((currEL() == EL1) && !inSecureState() &&
+ (hcr_imo || hcr_fmo)) {
+ return isa->readMiscRegNoEffect(MISCREG_ICV_PMR_EL1);
+ }
+
+ if (haveEL(EL3) && !inSecureState() &&
+ (isa->readMiscRegNoEffect(MISCREG_SCR_EL3) & (1U << 2))) {
+ /* NS GIC access and Group 0 is inaccessible to NS */
+ if ((value & 0x80) == 0) {
+ /* NS should not see priorities in the Secure half of the
+ * range */
+ value = 0;
+ } else if (value != 0xff) {
+ /* Non-idle priority: show the Non-secure view of it */
+ value = (value << 1) & 0xff;
+ }
+ }
+
+ break;
+
+ case MISCREG_ICC_IAR0:
+ case MISCREG_ICC_IAR0_EL1: { // Interrupt Acknowledge Register 0
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return readMiscReg(MISCREG_ICV_IAR0_EL1);
+ }
+
+ uint32_t int_id;
+
+ if (hppiCanPreempt()) {
+ int_id = getHPPIR0();
+
+ // avoid activation for special interrupts
+ if (int_id < Gicv3::INTID_SECURE) {
+ activateIRQ(int_id, hppi.group);
+ }
+ } else {
+ int_id = Gicv3::INTID_SPURIOUS;
+ }
+
+ value = int_id;
+ break;
+ }
+
+ case MISCREG_ICV_IAR0_EL1: {
+ int lr_idx = getHPPVILR();
+ uint32_t int_id = Gicv3::INTID_SPURIOUS;
+
+ if (lr_idx >= 0) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+ if (!(lr & ICH_LR_EL2_GROUP) && hppviCanPreempt(lr_idx)) {
+ int_id = value = bits(lr, 31, 0);
+
+ if (int_id < Gicv3::INTID_SECURE ||
+ int_id > Gicv3::INTID_SPURIOUS) {
+ virtualActivateIRQ(lr_idx);
+ } else {
+ // Bogus... Pseudocode says:
+ // - Move from pending to invalid...
+ // - Return de bogus id...
+ lr &= ~ICH_LR_EL2_STATE_PENDING_BIT;
+ isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx,
+ lr);
+ }
+ }
+ }
+
+ value = int_id;
+ virtualUpdate();
+ break;
+ }
+
+ case MISCREG_ICC_IAR1:
+ case MISCREG_ICC_IAR1_EL1: { // Interrupt Acknowledge Register 1
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return readMiscReg(MISCREG_ICV_IAR1_EL1);
+ }
+
+ uint32_t int_id;
+
+ if (hppiCanPreempt()) {
+ int_id = getHPPIR1();
+
+ // avoid activation for special interrupts
+ if (int_id < Gicv3::INTID_SECURE) {
+ activateIRQ(int_id, hppi.group);
+ }
+ } else {
+ int_id = Gicv3::INTID_SPURIOUS;
+ }
+
+ value = int_id;
+ break;
+ }
+
+ case MISCREG_ICV_IAR1_EL1: {
+ int lr_idx = getHPPVILR();
+ uint32_t int_id = Gicv3::INTID_SPURIOUS;
+
+ if (lr_idx >= 0) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+ if (lr & ICH_LR_EL2_GROUP && hppviCanPreempt(lr_idx)) {
+ int_id = value = bits(lr, 31, 0);
+
+ if (int_id < Gicv3::INTID_SECURE ||
+ int_id > Gicv3::INTID_SPURIOUS) {
+ virtualActivateIRQ(lr_idx);
+ } else {
+ // Bogus... Pseudocode says:
+ // - Move from pending to invalid...
+ // - Return de bogus id...
+ lr &= ~ICH_LR_EL2_STATE_PENDING_BIT;
+ isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx,
+ lr);
+ }
+ }
+ }
+
+ value = int_id;
+ virtualUpdate();
+ break;
+ }
+
+ case MISCREG_ICC_SRE:
+ case MISCREG_ICC_SRE_EL1: { // System Register Enable Register
+ bool dfb;
+ bool dib;
+
+ if (haveEL(EL3) && !distributor->DS) {
+ // DIB is RO alias of ICC_SRE_EL3.DIB
+ // DFB is RO alias of ICC_SRE_EL3.DFB
+ ArmISA::MiscReg icc_sre_el3 =
+ isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL3);
+ dfb = icc_sre_el3 & ICC_SRE_EL3_DFB;
+ dib = icc_sre_el3 & ICC_SRE_EL3_DIB;
+ } else if (haveEL(EL3) && distributor->DS) {
+ // DIB is RW alias of ICC_SRE_EL3.DIB
+ // DFB is RW alias of ICC_SRE_EL3.DFB
+ ArmISA::MiscReg icc_sre_el3 =
+ isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL3);
+ dfb = icc_sre_el3 & ICC_SRE_EL3_DFB;
+ dib = icc_sre_el3 & ICC_SRE_EL3_DIB;
+ } else if ((!haveEL(EL3) || distributor->DS) and haveEL(EL2)) {
+ // DIB is RO alias of ICC_SRE_EL2.DIB
+ // DFB is RO alias of ICC_SRE_EL2.DFB
+ ArmISA::MiscReg icc_sre_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL2);
+ dfb = icc_sre_el2 & ICC_SRE_EL2_DFB;
+ dib = icc_sre_el2 & ICC_SRE_EL2_DIB;
+ } else {
+ dfb = value & ICC_SRE_EL1_DFB;
+ dib = value & ICC_SRE_EL1_DIB;
+ }
+
+ value = ICC_SRE_EL1_SRE;
+
+ if (dfb) {
+ value |= ICC_SRE_EL1_DFB;
+ }
+
+ if (dib) {
+ value |= ICC_SRE_EL1_DIB;
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_HSRE:
+ case MISCREG_ICC_SRE_EL2: // System Register Enable Register
+ /*
+ * Enable [3] == 1
+ * (Secure EL1 accesses to Secure ICC_SRE_EL1 do not trap to EL2,
+ * RAO/WI)
+ * DIB [2] == 1 (IRQ bypass not supported, RAO/WI)
+ * DFB [1] == 1 (FIQ bypass not supported, RAO/WI)
+ * SRE [0] == 1 (Only system register interface supported, RAO/WI)
+ */
+ value = ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_DIB | ICC_SRE_EL2_DFB |
+ ICC_SRE_EL2_SRE;
+ break;
+
+ case MISCREG_ICC_MSRE:
+ case MISCREG_ICC_SRE_EL3: // System Register Enable Register
+ /*
+ * Enable [3] == 1
+ * (Secure EL1 accesses to Secure ICC_SRE_EL1 do not trap to EL3,
+ * RAO/WI)
+ * DIB [2] == 1 (IRQ bypass not supported, RAO/WI)
+ * DFB [1] == 1 (FIQ bypass not supported, RAO/WI)
+ * SRE [0] == 1 (Only system register interface supported, RAO/WI)
+ */
+ value = ICC_SRE_EL3_ENABLE | ICC_SRE_EL3_DIB | ICC_SRE_EL3_DFB |
+ ICC_SRE_EL3_SRE;
+ break;
+
+ case MISCREG_ICC_CTLR:
+ case MISCREG_ICC_CTLR_EL1: { // Control Register
+ if ((currEL() == EL1) && !inSecureState() &&
+ (hcr_imo || hcr_fmo)) {
+ return readMiscReg(MISCREG_ICV_CTLR_EL1);
+ }
+
+ // Add value for RO bits
+ // IDbits [13:11], 001 = 24 bits | 000 = 16 bits
+ // PRIbits [10:8], number of priority bits implemented, minus one
+ value |= ICC_CTLR_EL1_RSS | ICC_CTLR_EL1_A3V |
+ (1 << 11) | ((PRIORITY_BITS - 1) << 8);
+ break;
+ }
+
+ case MISCREG_ICV_CTLR_EL1: {
+ value = ICC_CTLR_EL1_A3V | (1 << ICC_CTLR_EL1_IDBITS_SHIFT) |
+ (7 << ICC_CTLR_EL1_PRIBITS_SHIFT);
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+ if (ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM) {
+ value |= ICC_CTLR_EL1_EOIMODE;
+ }
+
+ if (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) {
+ value |= ICC_CTLR_EL1_CBPR;
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_MCTLR:
+ case MISCREG_ICC_CTLR_EL3: {
+ // Add value for RO bits
+ // RSS [18]
+ // A3V [15]
+ // IDbits [13:11], 001 = 24 bits | 000 = 16 bits
+ // PRIbits [10:8], number of priority bits implemented, minus one
+ value |= ICC_CTLR_EL3_RSS | ICC_CTLR_EL3_A3V | (0 << 11) |
+ ((PRIORITY_BITS - 1) << 8);
+ // Aliased bits...
+ ArmISA::MiscReg icc_ctlr_el1_ns =
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
+ ArmISA::MiscReg icc_ctlr_el1_s =
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
+
+ if (icc_ctlr_el1_ns & ICC_CTLR_EL1_EOIMODE) {
+ value |= ICC_CTLR_EL3_EOIMODE_EL1NS;
+ }
+
+ if (icc_ctlr_el1_ns & ICC_CTLR_EL1_CBPR) {
+ value |= ICC_CTLR_EL3_CBPR_EL1NS;
+ }
+
+ if (icc_ctlr_el1_s & ICC_CTLR_EL1_EOIMODE) {
+ value |= ICC_CTLR_EL3_EOIMODE_EL1S;
+ }
+
+ if (icc_ctlr_el1_s & ICC_CTLR_EL1_CBPR) {
+ value |= ICC_CTLR_EL3_CBPR_EL1S;
+ }
+
+ break;
+ }
+
+ case MISCREG_ICH_HCR:
+ case MISCREG_ICH_HCR_EL2:
+ break;
+
+ case MISCREG_ICH_AP0R0:
+ case MISCREG_ICH_AP0R0_EL2:
+ break;
+
+ case MISCREG_ICH_AP1R0:
+ case MISCREG_ICH_AP1R0_EL2:
+ break;
+
+ case MISCREG_ICH_MISR:
+ case MISCREG_ICH_MISR_EL2: {
+ value = 0;
+ // Scan list registers and fill in the U, NP and EOI bits
+ eoiMaintenanceInterruptStatus((uint32_t *) &value);
+ ArmISA::MiscReg ich_hcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+ if (ich_hcr_el2 &
+ (ICH_HCR_EL2_LRENPIE | ICH_HCR_EL2_EOICOUNT_MASK)) {
+ value |= ICH_MISR_EL2_LRENP;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0EIE) &&
+ (ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) {
+ value |= ICH_MISR_EL2_VGRP0E;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0DIE) &&
+ !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ value |= ICH_MISR_EL2_VGRP0D;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1EIE) &&
+ (ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ value |= ICH_MISR_EL2_VGRP1E;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1DIE) &&
+ !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ value |= ICH_MISR_EL2_VGRP1D;
+ }
+
+ break;
+ }
+
+ case MISCREG_ICH_VTR:
+ case MISCREG_ICH_VTR_EL2:
+ /*
+ * PRIbits [31:29]
+ * PREbits [28:26]
+ * IDbits [25:23]
+ * SEIS [22] == 0 (SEI Support)
+ * A3V [21] == 1
+ * (Non-zero values supported for Affinity 3 in SGI genearion)
+ * nV4 [20] == 0
+ * (Support for direct injection of virtual interrupts)
+ * TDS [19] == 0 (Implementation supports ICH_HCR_EL2.TDIR)
+ * ListRegs [4:0]
+ */
+ value = (16 - 1) << 0 |
+ (5 - 1) << 26 |
+ (5 - 1) << 29;
+ value =
+ ((VIRTUAL_NUM_LIST_REGS - 1) << ICH_VTR_EL2_LISTREGS_SHIFT) |
+ // ICH_VTR_EL2_TDS |
+ // ICH_VTR_EL2_NV4 |
+ ICH_VTR_EL2_A3V |
+ (1 << ICH_VTR_EL2_IDBITS_SHIFT) |
+ ((VIRTUAL_PREEMPTION_BITS - 1) << ICH_VTR_EL2_PREBITS_SHIFT) |
+ ((VIRTUAL_PRIORITY_BITS - 1) << ICH_VTR_EL2_PRIBITS_SHIFT);
+ break;
+
+ case MISCREG_ICH_EISR:
+ case MISCREG_ICH_EISR_EL2:
+ value = eoiMaintenanceInterruptStatus(nullptr);
+ break;
+
+ case MISCREG_ICH_ELRSR:
+ case MISCREG_ICH_ELRSR_EL2:
+ value = 0;
+
+ for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+ if ((lr & ICH_LR_EL2_STATE_MASK) == 0 &&
+ ((lr & ICH_LR_EL2_HW) != 0 ||
+ (lr & ICH_LR_EL2_EOI) == 0)) {
+ value |= (1 << lr_idx);
+ }
+ }
+
+ break;
+
+ case MISCREG_ICH_LRC0 ... MISCREG_ICH_LRC15:
+ // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 high half part)
+ value = value >> 32;
+ break;
+
+ case MISCREG_ICH_LR0 ... MISCREG_ICH_LR15:
+ // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 low half part)
+ value = value & 0xffffffff;
+ break;
+
+ case MISCREG_ICH_LR0_EL2 ... MISCREG_ICH_LR15_EL2:
+ break;
+
+ case MISCREG_ICH_VMCR:
+ case MISCREG_ICH_VMCR_EL2:
+ break;
+
+ default:
+ panic("Gicv3CPUInterface::readMiscReg(): "
+ "unknown register %d (%s)",
+ misc_reg, miscRegName[misc_reg]);
+ }
+
+ DPRINTF(GIC, "Gicv3CPUInterface::readMiscReg(): "
+ "register %s value %#x\n", miscRegName[misc_reg], value);
+ return value;
+}
+
+void
+Gicv3CPUInterface::setMiscReg(int misc_reg, ArmISA::MiscReg val)
+{
+ bool do_virtual_update = false;
+ DPRINTF(GIC, "Gicv3CPUInterface::setMiscReg(): "
+ "register %s value %#x\n", miscRegName[misc_reg], val);
+ bool hcr_fmo = getHCREL2FMO();
+ bool hcr_imo = getHCREL2IMO();
+
+ switch (misc_reg) {
+ case MISCREG_ICC_AP1R0:
+ case MISCREG_ICC_AP1R0_EL1:
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return isa->setMiscRegNoEffect(MISCREG_ICV_AP1R0_EL1, val);
+ }
+
+ break;
+
+ case MISCREG_ICC_AP1R1:
+ case MISCREG_ICC_AP1R1_EL1:
+
+ // only implemented if supporting 6 or more bits of priority
+ case MISCREG_ICC_AP1R2:
+ case MISCREG_ICC_AP1R2_EL1:
+
+ // only implemented if supporting 7 or more bits of priority
+ case MISCREG_ICC_AP1R3:
+ case MISCREG_ICC_AP1R3_EL1:
+ // only implemented if supporting 7 or more bits of priority
+ break;
+
+ case MISCREG_ICC_AP0R0:
+ case MISCREG_ICC_AP0R0_EL1:
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return isa->setMiscRegNoEffect(MISCREG_ICV_AP0R0_EL1, val);
+ }
+
+ break;
+
+ case MISCREG_ICC_AP0R1:
+ case MISCREG_ICC_AP0R1_EL1:
+
+ // only implemented if supporting 6 or more bits of priority
+ case MISCREG_ICC_AP0R2:
+ case MISCREG_ICC_AP0R2_EL1:
+
+ // only implemented if supporting 7 or more bits of priority
+ case MISCREG_ICC_AP0R3:
+ case MISCREG_ICC_AP0R3_EL1:
+ // only implemented if supporting 7 or more bits of priority
+ break;
+
+ case MISCREG_ICC_EOIR0:
+ case MISCREG_ICC_EOIR0_EL1: { // End Of Interrupt Register 0
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return setMiscReg(MISCREG_ICV_EOIR0_EL1, val);
+ }
+
+ int int_id = val & 0xffffff;
+
+ // avoid activation for special interrupts
+ if (int_id >= Gicv3::INTID_SECURE) {
+ return;
+ }
+
+ Gicv3::GroupId group = Gicv3::G0S;
+
+ if (highestActiveGroup() != group) {
+ return;
+ }
+
+ dropPriority(group);
+
+ if (!isEOISplitMode()) {
+ deactivateIRQ(int_id, group);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICV_EOIR0_EL1: {
+ int int_id = val & 0xffffff;
+
+ // avoid deactivation for special interrupts
+ if (int_id >= Gicv3::INTID_SECURE &&
+ int_id <= Gicv3::INTID_SPURIOUS) {
+ return;
+ }
+
+ uint8_t drop_prio = virtualDropPriority();
+
+ if (drop_prio == 0xff) {
+ return;
+ }
+
+ int lr_idx = virtualFindActive(int_id);
+
+ if (lr_idx < 0) {
+ // No LR found matching
+ virtualIncrementEOICount();
+ } else {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+ Gicv3::GroupId lr_group =
+ lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+ uint8_t lr_group_prio = bits(lr, 55, 48) & 0xf8;
+
+ if (lr_group == Gicv3::G0S && lr_group_prio == drop_prio) {
+ //JAIRO if (!virtualIsEOISplitMode())
+ {
+ virtualDeactivateIRQ(lr_idx);
+ }
+ }
+ }
+
+ virtualUpdate();
+ break;
+ }
+
+ case MISCREG_ICC_EOIR1:
+ case MISCREG_ICC_EOIR1_EL1: { // End Of Interrupt Register 1
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return setMiscReg(MISCREG_ICV_EOIR1_EL1, val);
+ }
+
+ int int_id = val & 0xffffff;
+
+ // avoid deactivation for special interrupts
+ if (int_id >= Gicv3::INTID_SECURE) {
+ return;
+ }
+
+ Gicv3::GroupId group =
+ inSecureState() ? Gicv3::G1S : Gicv3::G1NS;
+
+ if (highestActiveGroup() == Gicv3::G0S) {
+ return;
+ }
+
+ if (distributor->DS == 0) {
+ if (highestActiveGroup() == Gicv3::G1S && !inSecureState()) {
+ return;
+ } else if (highestActiveGroup() == Gicv3::G1NS &&
+ !(!inSecureState() or (currEL() == EL3))) {
+ return;
+ }
+ }
+
+ dropPriority(group);
+
+ if (!isEOISplitMode()) {
+ deactivateIRQ(int_id, group);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICV_EOIR1_EL1: {
+ int int_id = val & 0xffffff;
+
+ // avoid deactivation for special interrupts
+ if (int_id >= Gicv3::INTID_SECURE &&
+ int_id <= Gicv3::INTID_SPURIOUS) {
+ return;
+ }
+
+ uint8_t drop_prio = virtualDropPriority();
+
+ if (drop_prio == 0xff) {
+ return;
+ }
+
+ int lr_idx = virtualFindActive(int_id);
+
+ if (lr_idx < 0) {
+ // No LR found matching
+ virtualIncrementEOICount();
+ } else {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+ Gicv3::GroupId lr_group =
+ lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+ uint8_t lr_group_prio = bits(lr, 55, 48) & 0xf8;
+
+ if (lr_group == Gicv3::G1NS && lr_group_prio == drop_prio) {
+ if (!virtualIsEOISplitMode()) {
+ virtualDeactivateIRQ(lr_idx);
+ }
+ }
+ }
+
+ virtualUpdate();
+ break;
+ }
+
+ case MISCREG_ICC_DIR:
+ case MISCREG_ICC_DIR_EL1: { // Deactivate Interrupt Register
+ if ((currEL() == EL1) && !inSecureState() &&
+ (hcr_imo || hcr_fmo)) {
+ return setMiscReg(MISCREG_ICV_DIR_EL1, val);
+ }
+
+ int int_id = val & 0xffffff;
+
+ // avoid deactivation for special interrupts
+ if (int_id >= Gicv3::INTID_SECURE) {
+ return;
+ }
+
+ if (!isEOISplitMode()) {
+ return;
+ }
+
+ /*
+ * Check whether we're allowed to deactivate.
+ * These checks are correspond to the spec's pseudocode.
+ */
+ Gicv3::GroupId group =
+ int_id >= 32 ? distributor->getIntGroup(int_id) :
+ redistributor->getIntGroup(int_id);
+ bool irq_is_grp0 = group == Gicv3::G0S;
+ bool single_sec_state = distributor->DS;
+ bool irq_is_secure = !single_sec_state && (group != Gicv3::G1NS);
+ SCR scr_el3 = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
+ bool route_fiq_to_el3 = scr_el3.fiq;
+ bool route_irq_to_el3 = scr_el3.irq;
+ bool route_fiq_to_el2 = hcr_fmo;
+ bool route_irq_to_el2 = hcr_imo;
+
+ switch (currEL()) {
+ case EL3:
+ break;
+
+ case EL2:
+ if (single_sec_state && irq_is_grp0 && !route_fiq_to_el3) {
+ break;
+ }
+
+ if (!irq_is_secure && !irq_is_grp0 && !route_irq_to_el3) {
+ break;
+ }
+
+ return;
+
+ case EL1:
+ if (!isSecureBelowEL3()) {
+ if (single_sec_state && irq_is_grp0 &&
+ !route_fiq_to_el3 && !route_fiq_to_el2) {
+ break;
+ }
+
+ if (!irq_is_secure && !irq_is_grp0 &&
+ !route_irq_to_el3 && !route_irq_to_el2) {
+ break;
+ }
+ } else {
+ if (irq_is_grp0 && !route_fiq_to_el3) {
+ break;
+ }
+
+ if (!irq_is_grp0 &&
+ (!irq_is_secure || !single_sec_state) &&
+ !route_irq_to_el3) {
+ break;
+ }
+ }
+
+ return;
+
+ default:
+ break;
+ }
+
+ deactivateIRQ(int_id, group);
+ break;
+ }
+
+ case MISCREG_ICV_DIR_EL1: {
+ int int_id = val & 0xffffff;
+
+ // avoid deactivation for special interrupts
+ if (int_id >= Gicv3::INTID_SECURE &&
+ int_id <= Gicv3::INTID_SPURIOUS) {
+ return;
+ }
+
+ if (!virtualIsEOISplitMode()) {
+ return;
+ }
+
+ int lr_idx = virtualFindActive(int_id);
+
+ if (lr_idx < 0) {
+ // No LR found matching
+ virtualIncrementEOICount();
+ } else {
+ virtualDeactivateIRQ(lr_idx);
+ }
+
+ virtualUpdate();
+ break;
+ }
+
+ case MISCREG_ICC_BPR0:
+ case MISCREG_ICC_BPR0_EL1: // Binary Point Register 0
+ case MISCREG_ICC_BPR1:
+ case MISCREG_ICC_BPR1_EL1: { // Binary Point Register 1
+ if ((currEL() == EL1) && !inSecureState()) {
+ if (misc_reg == MISCREG_ICC_BPR0_EL1 && hcr_fmo) {
+ return setMiscReg(MISCREG_ICV_BPR0_EL1, val);
+ } else if (misc_reg == MISCREG_ICC_BPR1_EL1 && hcr_imo) {
+ return setMiscReg(MISCREG_ICV_BPR1_EL1, val);
+ }
+ }
+
+ Gicv3::GroupId group =
+ misc_reg == MISCREG_ICC_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1S;
+
+ if (group == Gicv3::G1S && !inSecureState()) {
+ group = Gicv3::G1NS;
+ }
+
+ if ((group == Gicv3::G1S) &&
+ !isEL3OrMon() &&
+ (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S) &
+ ICC_CTLR_EL1_CBPR)) {
+ group = Gicv3::G0S;
+ }
+
+ if ((group == Gicv3::G1NS) &&
+ (currEL() < EL3) &&
+ (isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS) &
+ ICC_CTLR_EL1_CBPR)) {
+ // Reads return BPR0 + 1 saturated to 7, WI
+ return;
+ }
+
+ uint8_t min_val = (group == Gicv3::G1NS) ?
+ GIC_MIN_BPR_NS : GIC_MIN_BPR;
+ val &= 0x7;
+
+ if (val < min_val) {
+ val = min_val;
+ }
+
+ break;
+ }
+
+ case MISCREG_ICV_BPR0_EL1:
+ case MISCREG_ICV_BPR1_EL1: {
+ Gicv3::GroupId group =
+ misc_reg == MISCREG_ICV_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1NS;
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+ if (group == Gicv3::G1NS && (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) {
+ // reads return bpr0 + 1 saturated to 7, writes ignored
+ return;
+ }
+
+ uint8_t min_VPBR = 7 - VIRTUAL_PREEMPTION_BITS;
+
+ if (group != Gicv3::G0S) {
+ min_VPBR++;
+ }
+
+ if (val < min_VPBR) {
+ val = min_VPBR;
+ }
+
+ if (group == Gicv3::G0S) {
+ ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+ ICH_VMCR_EL2_VBPR0_SHIFT + 2, ICH_VMCR_EL2_VBPR0_SHIFT,
+ val);
+ } else {
+ ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+ ICH_VMCR_EL2_VBPR1_SHIFT + 2, ICH_VMCR_EL2_VBPR1_SHIFT,
+ val);
+ }
+
+ isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+ do_virtual_update = true;
+ break;
+ }
+
+ case MISCREG_ICC_CTLR:
+ case MISCREG_ICC_CTLR_EL1: { // Control Register
+ if ((currEL() == EL1) && !inSecureState() &&
+ (hcr_imo || hcr_fmo)) {
+ return setMiscReg(MISCREG_ICV_CTLR_EL1, val);
+ }
+
+ /*
+ * RSS is RO.
+ * A3V is RO.
+ * SEIS is RO.
+ * IDbits is RO.
+ * PRIbits is RO.
+ * If EL3 is implemented and GICD_CTLR.DS == 0, then PMHE is RO.
+ * So, only CBPR[0] and EOIMODE[1] are RW.
+ * If EL3 is implemented and GICD_CTLR.DS == 0, then CBPR is RO.
+ */
+ uint64_t mask;
+
+ if (haveEL(EL3) and distributor->DS == 0) {
+ mask = ICC_CTLR_EL1_EOIMODE;
+ } else if (haveEL(EL3) and distributor->DS == 1) {
+ mask = ICC_CTLR_EL1_PMHE | ICC_CTLR_EL1_CBPR |
+ ICC_CTLR_EL1_EOIMODE;
+ } else {
+ mask = ICC_CTLR_EL1_CBPR | ICC_CTLR_EL1_EOIMODE;
+ }
+
+ ArmISA::MiscReg old_val =
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1);
+ old_val &= ~mask;
+ val = old_val | (val & mask);
+ break;
+ }
+
+ case MISCREG_ICV_CTLR_EL1: {
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+ ich_vmcr_el2 = insertBits(ich_vmcr_el2, ICH_VMCR_EL2_VCBPR_SHIFT,
+ val & ICC_CTLR_EL1_CBPR ? 1 : 0);
+ ich_vmcr_el2 = insertBits(ich_vmcr_el2, ICH_VMCR_EL2_VEOIM_SHIFT,
+ val & ICC_CTLR_EL1_EOIMODE ? 1 : 0);
+ isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+ do_virtual_update = true;
+ break;
+ }
+
+ case MISCREG_ICC_MCTLR:
+ case MISCREG_ICC_CTLR_EL3: {
+ ArmISA::MiscReg icc_ctlr_el1_s =
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
+ ArmISA::MiscReg icc_ctlr_el1_ns =
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
+
+ // ICC_CTLR_EL1(NS).EOImode is an alias of
+ // ICC_CTLR_EL3.EOImode_EL1NS
+ if (val & ICC_CTLR_EL3_EOIMODE_EL1NS) {
+ icc_ctlr_el1_ns |= ICC_CTLR_EL1_EOIMODE;
+ } else {
+ icc_ctlr_el1_ns &= ~ICC_CTLR_EL1_EOIMODE;
+ }
+
+ // ICC_CTLR_EL1(NS).CBPR is an alias of ICC_CTLR_EL3.CBPR_EL1NS
+ if (val & ICC_CTLR_EL3_CBPR_EL1NS) {
+ icc_ctlr_el1_ns |= ICC_CTLR_EL1_CBPR;
+ } else {
+ icc_ctlr_el1_ns &= ~ICC_CTLR_EL1_CBPR;
+ }
+
+ // ICC_CTLR_EL1(S).EOImode is an alias of ICC_CTLR_EL3.EOImode_EL1S
+ if (val & ICC_CTLR_EL3_EOIMODE_EL1S) {
+ icc_ctlr_el1_s |= ICC_CTLR_EL1_EOIMODE;
+ } else {
+ icc_ctlr_el1_s &= ~ICC_CTLR_EL1_EOIMODE;
+ }
+
+ // ICC_CTLR_EL1(S).CBPR is an alias of ICC_CTLR_EL3.CBPR_EL1S
+ if (val & ICC_CTLR_EL3_CBPR_EL1S) {
+ icc_ctlr_el1_s |= ICC_CTLR_EL1_CBPR;
+ } else {
+ icc_ctlr_el1_s &= ~ICC_CTLR_EL1_CBPR;
+ }
+
+ isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S, icc_ctlr_el1_s);
+ isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS, icc_ctlr_el1_ns);
+ // Only ICC_CTLR_EL3_EOIMODE_EL3 is writable
+ ArmISA::MiscReg old_icc_ctlr_el3 =
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3);
+ old_icc_ctlr_el3 &= ~(ICC_CTLR_EL3_EOIMODE_EL3 | ICC_CTLR_EL3_RM);
+ val = old_icc_ctlr_el3 |
+ (val & (ICC_CTLR_EL3_EOIMODE_EL3 | ICC_CTLR_EL3_RM));
+ break;
+ }
+
+ case MISCREG_ICC_PMR:
+ case MISCREG_ICC_PMR_EL1: { // Priority Mask Register
+ if ((currEL() == EL1) && !inSecureState() &&
+ (hcr_imo || hcr_fmo)) {
+ return isa->setMiscRegNoEffect(MISCREG_ICV_PMR_EL1, val);
+ }
+
+ val &= 0xff;
+ SCR scr_el3 = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
+
+ if (haveEL(EL3) && !inSecureState() && (scr_el3.fiq)) {
+ /*
+ * NS access and Group 0 is inaccessible to NS: return the
+ * NS view of the current priority
+ */
+ ArmISA::MiscReg old_icc_pmr_el1 =
+ isa->readMiscRegNoEffect(MISCREG_ICC_PMR_EL1);
+
+ if (!(old_icc_pmr_el1 & 0x80)) {
+ /* Current PMR in the secure range, don't allow NS to
+ * change it */
+ return;
+ }
+
+ val = (val >> 1) | 0x80;
+ }
+
+ val &= ~0U << (8 - PRIORITY_BITS);
+ break;
+ }
+
+ case MISCREG_ICC_IGRPEN0:
+ case MISCREG_ICC_IGRPEN0_EL1: { // Interrupt Group 0 Enable Register
+ if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
+ return setMiscReg(MISCREG_ICV_IGRPEN0_EL1, val);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICV_IGRPEN0_EL1: {
+ bool enable = val & 0x1;
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+ ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+ ICH_VMCR_EL2_VENG0_SHIFT, enable);
+ isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+ virtualUpdate();
+ return;
+ }
+
+ case MISCREG_ICC_IGRPEN1:
+ case MISCREG_ICC_IGRPEN1_EL1: { // Interrupt Group 1 Enable Register
+ if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
+ return setMiscReg(MISCREG_ICV_IGRPEN1_EL1, val);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICV_IGRPEN1_EL1: {
+ bool enable = val & 0x1;
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+ ich_vmcr_el2 = insertBits(ich_vmcr_el2,
+ ICH_VMCR_EL2_VENG1_SHIFT, enable);
+ isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
+ virtualUpdate();
+ return;
+ }
+
+ case MISCREG_ICC_MGRPEN1:
+ case MISCREG_ICC_IGRPEN1_EL3: {
+ // EnableGrp1S and EnableGrp1NS are aliased with
+ // ICC_IGRPEN1_EL1_S.Enable and ICC_IGRPEN1_EL1_NS.Enable
+ bool enable_grp_1s = val & ICC_IGRPEN1_EL3_ENABLEGRP1S;
+ bool enable_grp_1ns = val & ICC_IGRPEN1_EL3_ENABLEGRP1NS;
+ isa->setMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S, enable_grp_1s);
+ isa->setMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS, enable_grp_1ns);
+ return;
+ }
+
+ // Software Generated Interrupt Group 0 Register
+ case MISCREG_ICC_SGI0R:
+ case MISCREG_ICC_SGI0R_EL1:
+
+ // Software Generated Interrupt Group 1 Register
+ case MISCREG_ICC_SGI1R:
+ case MISCREG_ICC_SGI1R_EL1:
+
+ // Alias Software Generated Interrupt Group 1 Register
+ case MISCREG_ICC_ASGI1R:
+ case MISCREG_ICC_ASGI1R_EL1: {
+ bool ns = !inSecureState();
+ Gicv3::GroupId group;
+
+ if (misc_reg == MISCREG_ICC_SGI1R_EL1) {
+ group = ns ? Gicv3::G1NS : Gicv3::G1S;
+ } else if (misc_reg == MISCREG_ICC_ASGI1R_EL1) {
+ group = ns ? Gicv3::G1S : Gicv3::G1NS;
+ } else {
+ group = Gicv3::G0S;
+ }
+
+ if (distributor->DS && group == Gicv3::G1S) {
+ group = Gicv3::G0S;
+ }
+
+ uint8_t aff3 = bits(val, 55, 48);
+ uint8_t aff2 = bits(val, 39, 32);
+ uint8_t aff1 = bits(val, 23, 16);;
+ uint16_t target_list = bits(val, 15, 0);
+ uint32_t int_id = bits(val, 27, 24);
+ bool irm = bits(val, 40, 40);
+ uint8_t rs = bits(val, 47, 44);
+
+ for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+ Gicv3Redistributor * redistributor_i =
+ gic->getRedistributor(i);
+ uint32_t affinity_i = redistributor_i->getAffinity();
+
+ if (irm) {
+ // Interrupts routed to all PEs in the system,
+ // excluding "self"
+ if (affinity_i == redistributor->getAffinity()) {
+ continue;
+ }
+ } else {
+ // Interrupts routed to the PEs specified by
+ // Aff3.Aff2.Aff1.<target list>
+ if ((affinity_i >> 8) !=
+ ((aff3 << 16) | (aff2 << 8) | (aff1 << 0))) {
+ continue;
+ }
+
+ uint8_t aff0_i = bits(affinity_i, 7, 0);
+
+ if (!(aff0_i >= rs * 16 && aff0_i < (rs + 1) * 16 &&
+ ((0x1 << (aff0_i - rs * 16)) & target_list))) {
+ continue;
+ }
+ }
+
+ redistributor_i->sendSGI(int_id, group, ns);
+ }
+
+ break;
+ }
+
+ case MISCREG_ICC_SRE:
+ case MISCREG_ICC_SRE_EL1: { // System Register Enable Register EL1
+ if (!(val & ICC_SRE_EL1_SRE)) {
+ warn("Gicv3CPUInterface::setMiscReg(): "
+ "ICC_SRE_EL*.SRE is RAO/WI, legacy not supported!\n");
+ }
+
+ bool dfb = val & ICC_SRE_EL1_DFB;
+ bool dib = val & ICC_SRE_EL1_DIB;
+
+ if (haveEL(EL3) && !distributor->DS) {
+ // DIB is RO alias of ICC_SRE_EL3.DIB
+ // DFB is RO alias of ICC_SRE_EL3.DFB
+ } else if (haveEL(EL3) && distributor->DS) {
+ // DIB is RW alias of ICC_SRE_EL3.DIB
+ // DFB is RW alias of ICC_SRE_EL3.DFB
+ ArmISA::MiscReg icc_sre_el3 =
+ isa->readMiscRegNoEffect(MISCREG_ICC_SRE_EL3);
+ icc_sre_el3 = insertBits(icc_sre_el3, ICC_SRE_EL3_DFB, dfb);
+ icc_sre_el3 = insertBits(icc_sre_el3, ICC_SRE_EL3_DIB, dib);
+ isa->setMiscRegNoEffect(MISCREG_ICC_SRE_EL3, icc_sre_el3);
+ } else if ((!haveEL(EL3) || distributor->DS) and haveEL(EL2)) {
+ // DIB is RO alias of ICC_SRE_EL2.DIB
+ // DFB is RO alias of ICC_SRE_EL2.DFB
+ } else {
+ isa->setMiscRegNoEffect(misc_reg, val);
+ }
+
+ return;
+ }
+
+ case MISCREG_ICC_HSRE:
+ case MISCREG_ICC_SRE_EL2: // System Register Enable Register EL2
+ case MISCREG_ICC_MSRE:
+ case MISCREG_ICC_SRE_EL3: // System Register Enable Register EL3
+ if (!(val & (1 << 0))) {
+ warn("Gicv3CPUInterface::setMiscReg(): "
+ "ICC_SRE_EL*.SRE is RAO/WI, legacy not supported!\n");
+ }
+
+ // All bits are RAO/WI
+ break;
+
+ case MISCREG_ICH_HCR:
+ case MISCREG_ICH_HCR_EL2:
+ val &= ICH_HCR_EL2_EN | ICH_HCR_EL2_UIE | ICH_HCR_EL2_LRENPIE |
+ ICH_HCR_EL2_NPIE | ICH_HCR_EL2_VGRP0EIE |
+ ICH_HCR_EL2_VGRP0DIE | ICH_HCR_EL2_VGRP1EIE |
+ ICH_HCR_EL2_VGRP1DIE | ICH_HCR_EL2_TC | ICH_HCR_EL2_TALL0 |
+ ICH_HCR_EL2_TALL1 | ICH_HCR_EL2_TDIR |
+ ICH_HCR_EL2_EOICOUNT_MASK;
+ do_virtual_update = true;
+ break;
+
+ case MISCREG_ICH_LRC0 ... MISCREG_ICH_LRC15:
+ // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 high half part)
+ {
+ // Enforce RES0 bits in priority field, 5 of 8 bits used
+ val = insertBits(val, ICH_LRC_PRIORITY_SHIFT + 2,
+ ICH_LRC_PRIORITY_SHIFT, 0);
+ ArmISA::MiscReg old_val = isa->readMiscRegNoEffect(misc_reg);
+ val = (old_val & 0xffffffff) | (val << 32);
+ do_virtual_update = true;
+ break;
+ }
+
+ case MISCREG_ICH_LR0 ... MISCREG_ICH_LR15: {
+ // AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 low half part)
+ ArmISA::MiscReg old_val = isa->readMiscRegNoEffect(misc_reg);
+ val = (old_val & 0xffffffff00000000) | (val & 0xffffffff);
+ do_virtual_update = true;
+ break;
+ }
+
+ case MISCREG_ICH_LR0_EL2 ... MISCREG_ICH_LR15_EL2: { // AArch64
+ // Enforce RES0 bits in priority field, 5 of 8 bits used
+ val = insertBits(val, ICH_LR_EL2_PRIORITY_SHIFT + 2,
+ ICH_LR_EL2_PRIORITY_SHIFT, 0);
+ do_virtual_update = true;
+ break;
+ }
+
+ case MISCREG_ICH_VMCR:
+ case MISCREG_ICH_VMCR_EL2: {
+ val &= ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1 |
+ ICH_VMCR_EL2_VCBPR | ICH_VMCR_EL2_VEOIM |
+ ICH_VMCR_EL2_VBPR1_MASK | ICH_VMCR_EL2_VBPR0_MASK |
+ ICH_VMCR_EL2_VPMR_MASK;
+ val |= ICH_VMCR_EL2_VFIQEN; // RES1
+ // Check VBPRs against minimun allowed value
+ uint8_t vbpr0 = bits(val, 23, 21);
+ uint8_t vbpr1 = bits(val, 20, 18);
+ uint8_t min_vpr0 = 7 - VIRTUAL_PREEMPTION_BITS;
+ uint8_t min_vpr1 = min_vpr0 + 1;
+ vbpr0 = vbpr0 < min_vpr0 ? min_vpr0 : vbpr0;
+ vbpr1 = vbpr1 < min_vpr1 ? min_vpr1 : vbpr1;
+ val = insertBits(val, ICH_VMCR_EL2_VBPR0_SHIFT + 2,
+ ICH_VMCR_EL2_VBPR0_SHIFT, vbpr0);
+ val = insertBits(val, ICH_VMCR_EL2_VBPR1_SHIFT + 2,
+ ICH_VMCR_EL2_VBPR1_SHIFT, vbpr1);
+ break;
+ }
+
+ case MISCREG_ICH_AP0R0 ... MISCREG_ICH_AP0R3:
+ case MISCREG_ICH_AP0R0_EL2 ... MISCREG_ICH_AP0R3_EL2:
+ case MISCREG_ICH_AP1R0 ... MISCREG_ICH_AP1R3:
+ case MISCREG_ICH_AP1R0_EL2 ... MISCREG_ICH_AP1R3_EL2:
+ break;
+
+ default:
+ panic("Gicv3CPUInterface::setMiscReg(): "
+ "unknown register %d (%s)",
+ misc_reg, miscRegName[misc_reg]);
+ }
+
+ isa->setMiscRegNoEffect(misc_reg, val);
+
+ if (do_virtual_update) {
+ virtualUpdate();
+ }
+}
+
+int
+Gicv3CPUInterface::virtualFindActive(uint32_t int_id)
+{
+ for (uint32_t lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+ uint32_t lr_intid = bits(lr, 31, 0);
+
+ if ((lr & ICH_LR_EL2_STATE_ACTIVE_BIT) && lr_intid == int_id) {
+ return lr_idx;
+ }
+ }
+
+ return -1;
+}
+
+uint32_t
+Gicv3CPUInterface::getHPPIR0()
+{
+ if (hppi.prio == 0xff) {
+ return Gicv3::INTID_SPURIOUS;
+ }
+
+ bool irq_is_secure = !distributor->DS && hppi.group != Gicv3::G1NS;
+
+ if ((hppi.group != Gicv3::G0S) && isEL3OrMon()) {
+ /* Indicate to EL3 that there's a Group 1 interrupt for the
+ * other state pending.
+ */
+ return irq_is_secure ? Gicv3::INTID_SECURE : Gicv3::INTID_NONSECURE;
+ }
+
+ if ((hppi.group != Gicv3::G0S)) { // && !isEL3OrMon())
+ return Gicv3::INTID_SPURIOUS;
+ }
+
+ if (irq_is_secure && !inSecureState()) {
+ // Secure interrupts not visible in Non-secure
+ return Gicv3::INTID_SPURIOUS;
+ }
+
+ return hppi.intid;
+}
+
+uint32_t
+Gicv3CPUInterface::getHPPIR1()
+{
+ if (hppi.prio == 0xff) {
+ return Gicv3::INTID_SPURIOUS;
+ }
+
+ //if ((currEL() == EL3) && ICC_CTLR_EL3_RM)
+ if ((currEL() == EL3) &&
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3) & ICC_CTLR_EL3_RM) {
+ if (hppi.group == Gicv3::G0S) {
+ return Gicv3::INTID_SECURE;
+ } else if (hppi.group == Gicv3::G1NS) {
+ return Gicv3::INTID_NONSECURE;
+ }
+ }
+
+ if (hppi.group == Gicv3::G0S) {
+ return Gicv3::INTID_SPURIOUS;
+ }
+
+ bool irq_is_secure = (distributor->DS == 0) && (hppi.group != Gicv3::G1NS);
+
+ if (irq_is_secure) {
+ if (!inSecureState()) {
+ // Secure interrupts not visible in Non-secure
+ return Gicv3::INTID_SPURIOUS;
+ }
+ } else if (!isEL3OrMon() && inSecureState()) {
+ // Group 1 non-secure interrupts not visible in Secure EL1
+ return Gicv3::INTID_SPURIOUS;
+ }
+
+ return hppi.intid;
+}
+
+void
+Gicv3CPUInterface::dropPriority(Gicv3::GroupId group)
+{
+ int apr_misc_reg;
+ ArmISA::MiscReg apr;
+ apr_misc_reg = group == Gicv3::G0S ?
+ MISCREG_ICC_AP0R0_EL1 : MISCREG_ICC_AP1R0_EL1;
+ apr = isa->readMiscRegNoEffect(apr_misc_reg);
+
+ if (apr) {
+ /* Clear the lowest set bit */
+ apr &= apr - 1;
+ isa->setMiscRegNoEffect(apr_misc_reg, apr);
+ }
+
+ update();
+}
+
+uint8_t
+Gicv3CPUInterface::virtualDropPriority()
+{
+ /* Drop the priority of the currently active virtual interrupt
+ * (favouring group 0 if there is a set active bit at
+ * the same priority for both group 0 and group 1).
+ * Return the priority value for the bit we just cleared,
+ * or 0xff if no bits were set in the AP registers at all.
+ * Note that though the ich_apr[] are uint64_t only the low
+ * 32 bits are actually relevant.
+ */
+ int apr_max = 1 << (VIRTUAL_PREEMPTION_BITS - 5);
+
+ for (int i = 0; i < apr_max; i++) {
+ ArmISA::MiscReg vapr0 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i);
+ ArmISA::MiscReg vapr1 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i);
+
+ if (!vapr0 && !vapr1) {
+ continue;
+ }
+
+ int vapr0_count = ctz32(vapr0);
+ int vapr1_count = ctz32(vapr1);
+
+ if (vapr0_count <= vapr1_count) {
+ /* Clear the lowest set bit */
+ vapr0 &= vapr0 - 1;
+ isa->setMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i, vapr0);
+ return (vapr0_count + i * 32) << (GIC_MIN_VBPR + 1);
+ } else {
+ /* Clear the lowest set bit */
+ vapr1 &= vapr1 - 1;
+ isa->setMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i, vapr1);
+ return (vapr1_count + i * 32) << (GIC_MIN_VBPR + 1);
+ }
+ }
+
+ return 0xff;
+}
+
+void
+Gicv3CPUInterface::activateIRQ(uint32_t int_id, Gicv3::GroupId group)
+{
+ // Update active priority registers.
+ uint32_t prio = hppi.prio & 0xf8;
+ int apr_bit = prio >> (8 - PRIORITY_BITS);
+ int reg_bit = apr_bit % 32;
+ int apr_idx = group == Gicv3::G0S ?
+ MISCREG_ICC_AP0R0_EL1 : MISCREG_ICC_AP1R0_EL1;
+ ArmISA::MiscReg apr = isa->readMiscRegNoEffect(apr_idx);
+ apr |= (1 << reg_bit);
+ isa->setMiscRegNoEffect(apr_idx, apr);
+
+ // Move interrupt state from pending to active.
+ if (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
+ // SGI or PPI, redistributor
+ redistributor->activateIRQ(int_id);
+ redistributor->updateAndInformCPUInterface();
+ } else if (int_id < Gicv3::INTID_SECURE) {
+ // SPI, distributor
+ distributor->activateIRQ(int_id);
+ distributor->updateAndInformCPUInterfaces();
+ }
+}
+
+void
+Gicv3CPUInterface::virtualActivateIRQ(uint32_t lr_idx)
+{
+ // Update active priority registers.
+ ArmISA::MiscReg lr = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
+ lr_idx);
+ Gicv3::GroupId group = lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+ uint8_t prio = bits(lr, 55, 48) & 0xf8;
+ int apr_bit = prio >> (8 - VIRTUAL_PREEMPTION_BITS);
+ int reg_no = apr_bit / 32;
+ int reg_bit = apr_bit % 32;
+ int apr_idx = group == Gicv3::G0S ?
+ MISCREG_ICH_AP0R0_EL2 + reg_no : MISCREG_ICH_AP1R0_EL2 + reg_no;
+ ArmISA::MiscReg apr = isa->readMiscRegNoEffect(apr_idx);
+ apr |= (1 << reg_bit);
+ isa->setMiscRegNoEffect(apr_idx, apr);
+ // Move interrupt state from pending to active.
+ lr &= ~ICH_LR_EL2_STATE_PENDING_BIT;
+ lr |= ICH_LR_EL2_STATE_ACTIVE_BIT;
+ isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx, lr);
+}
+
+void
+Gicv3CPUInterface::deactivateIRQ(uint32_t int_id, Gicv3::GroupId group)
+{
+ if (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
+ // SGI or PPI, redistributor
+ redistributor->deactivateIRQ(int_id);
+ redistributor->updateAndInformCPUInterface();
+ } else if (int_id < Gicv3::INTID_SECURE) {
+ // SPI, distributor
+ distributor->deactivateIRQ(int_id);
+ distributor->updateAndInformCPUInterfaces();
+ } else {
+ return;
+ }
+}
+
+void
+Gicv3CPUInterface::virtualDeactivateIRQ(int lr_idx)
+{
+ ArmISA::MiscReg lr = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
+ lr_idx);
+
+ if (lr & ICH_LR_EL2_HW) {
+ // Deactivate the associated physical interrupt
+ int pintid = bits(lr, 41, 32);
+
+ if (pintid < Gicv3::INTID_SECURE) {
+ Gicv3::GroupId group =
+ pintid >= 32 ? distributor->getIntGroup(pintid) :
+ redistributor->getIntGroup(pintid);
+ deactivateIRQ(pintid, group);
+ }
+ }
+
+ // Remove the active bit
+ lr &= ~ICH_LR_EL2_STATE_ACTIVE_BIT;
+ isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx, lr);
+}
+
+/*
+ * Return a mask word which clears the subpriority bits from
+ * a priority value for an interrupt in the specified group.
+ * This depends on the BPR value. For CBPR0 (S or NS):
+ * a BPR of 0 means the group priority bits are [7:1];
+ * a BPR of 1 means they are [7:2], and so on down to
+ * ...
+ * a BPR of 7 meaning no group priority bits at all.
+ * For CBPR1 NS:
+ * a BPR of 0 is impossible (the minimum value is 1)
+ * a BPR of 1 means the group priority bits are [7:1];
+ * a BPR of 2 means they are [7:2], and so on down to
+ * ...
+ * a BPR of 7 meaning the group priority is [7].
+ *
+ * Which BPR to use depends on the group of the interrupt and
+ * the current ICC_CTLR.CBPR settings.
+ *
+ * This corresponds to the GroupBits() pseudocode from 4.8.2.
+ */
+uint32_t
+Gicv3CPUInterface::groupPriorityMask(Gicv3::GroupId group)
+{
+ if ((group == Gicv3::G1S &&
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S)
+ & ICC_CTLR_EL1_CBPR) ||
+ (group == Gicv3::G1NS &&
+ isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS)
+ & ICC_CTLR_EL1_CBPR)) {
+ group = Gicv3::G0S;
+ }
+
+ int bpr;
+
+ if (group == Gicv3::G0S) {
+ bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1) & 0x7;
+ } else {
+ bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR1_EL1) & 0x7;
+ }
+
+ if (group == Gicv3::G1NS) {
+ assert(bpr > 0);
+ bpr--;
+ }
+
+ return ~0U << (bpr + 1);
+}
+
+uint32_t
+Gicv3CPUInterface::virtualGroupPriorityMask(Gicv3::GroupId group)
+{
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+ if (group == Gicv3::G1NS && (ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR)) {
+ group = Gicv3::G0S;
+ }
+
+ int bpr;
+
+ if (group == Gicv3::G0S) {
+ bpr = bits(ich_vmcr_el2, 23, 21);
+ } else {
+ bpr = bits(ich_vmcr_el2, 20, 18);
+ }
+
+ if (group == Gicv3::G1NS) {
+ assert(bpr > 0);
+ bpr--;
+ }
+
+ return ~0U << (bpr + 1);
+}
+
+bool
+Gicv3CPUInterface::isEOISplitMode()
+{
+ if (isEL3OrMon()) {
+ return isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3) &
+ ICC_CTLR_EL3_EOIMODE_EL3;
+ } else {
+ return isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1) &
+ ICC_CTLR_EL1_EOIMODE;
+ }
+}
+
+bool
+Gicv3CPUInterface::virtualIsEOISplitMode()
+{
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+ return ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM;
+}
+
+int
+Gicv3CPUInterface::highestActiveGroup()
+{
+ int g0_ctz = ctz32(isa->readMiscRegNoEffect(MISCREG_ICC_AP0R0_EL1));
+ int gq_ctz = ctz32(isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_S));
+ int g1nz_ctz = ctz32(isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_NS));
+
+ if (g1nz_ctz < g0_ctz && g1nz_ctz < gq_ctz) {
+ return Gicv3::G1NS;
+ }
+
+ if (gq_ctz < g0_ctz) {
+ return Gicv3::G1S;
+ }
+
+ if (g0_ctz < 32) {
+ return Gicv3::G0S;
+ }
+
+ return -1;
+}
+
+void
+Gicv3CPUInterface::update()
+{
+ bool signal_IRQ = false;
+ bool signal_FIQ = false;
+
+ if (hppi.group == Gicv3::G1S && !haveEL(EL3)) {
+ /*
+ * Secure enabled GIC sending a G1S IRQ to a secure disabled
+ * CPU -> send G0 IRQ
+ */
+ hppi.group = Gicv3::G0S;
+ }
+
+ if (hppiCanPreempt()) {
+ ArmISA::InterruptTypes int_type = intSignalType(hppi.group);
+ DPRINTF(GIC, "Gicv3CPUInterface::update(): "
+ "posting int as %d!\n", int_type);
+ int_type == ArmISA::INT_IRQ ? signal_IRQ = true : signal_FIQ = true;
+ }
+
+ if (signal_IRQ) {
+ gic->postInt(cpuId, ArmISA::INT_IRQ);
+ } else {
+ gic->deassertInt(cpuId, ArmISA::INT_IRQ);
+ }
+
+ if (signal_FIQ) {
+ gic->postInt(cpuId, ArmISA::INT_FIQ);
+ } else {
+ gic->deassertInt(cpuId, ArmISA::INT_FIQ);
+ }
+}
+
+void
+Gicv3CPUInterface::virtualUpdate()
+{
+ bool signal_IRQ = false;
+ bool signal_FIQ = false;
+ int lr_idx = getHPPVILR();
+
+ if (lr_idx >= 0) {
+ ArmISA::MiscReg ich_lr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+ if (hppviCanPreempt(lr_idx)) {
+ if (ich_lr_el2 & ICH_LR_EL2_GROUP) {
+ signal_IRQ = true;
+ } else {
+ signal_FIQ = true;
+ }
+ }
+ }
+
+ ArmISA::MiscReg ich_hcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+
+ if (ich_hcr_el2 & ICH_HCR_EL2_EN) {
+ if (maintenanceInterruptStatus()) {
+ redistributor->sendPPInt(25);
+ }
+ }
+
+ if (signal_IRQ) {
+ DPRINTF(GIC, "Gicv3CPUInterface::virtualUpdate(): "
+ "posting int as %d!\n", ArmISA::INT_VIRT_IRQ);
+ gic->postInt(cpuId, ArmISA::INT_VIRT_IRQ);
+ } else {
+ gic->deassertInt(cpuId, ArmISA::INT_VIRT_IRQ);
+ }
+
+ if (signal_FIQ) {
+ DPRINTF(GIC, "Gicv3CPUInterface::virtualUpdate(): "
+ "posting int as %d!\n", ArmISA::INT_VIRT_FIQ);
+ gic->postInt(cpuId, ArmISA::INT_VIRT_FIQ);
+ } else {
+ gic->deassertInt(cpuId, ArmISA::INT_VIRT_FIQ);
+ }
+}
+
+// Returns the intex of the LR with the HPPI
+int
+Gicv3CPUInterface::getHPPVILR()
+{
+ int idx = -1;
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+ if (!(ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) {
+ // VG0 and VG1 disabled...
+ return idx;
+ }
+
+ uint8_t highest_prio = 0xff;
+
+ for (int i = 0; i < 16; i++) {
+ ArmISA::MiscReg ich_lri_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + i);
+ uint8_t state = bits(ich_lri_el2, 63, 62);
+
+ if (state != Gicv3::INT_PENDING) {
+ continue;
+ }
+
+ if (ich_lri_el2 & ICH_LR_EL2_GROUP) {
+ // VG1
+ if (!(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ continue;
+ }
+ } else {
+ // VG0
+ if (!(ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) {
+ continue;
+ }
+ }
+
+ uint8_t prio = bits(ich_lri_el2, 55, 48);
+
+ if (prio < highest_prio) {
+ highest_prio = prio;
+ idx = i;
+ }
+ }
+
+ return idx;
+}
+
+bool
+Gicv3CPUInterface::hppviCanPreempt(int lr_idx)
+{
+ ArmISA::MiscReg lr = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
+ lr_idx);
+
+ if (!(isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2) & ICH_HCR_EL2_EN)) {
+ // virtual interface is disabled
+ return false;
+ }
+
+ uint8_t prio = bits(lr, 55, 48);
+ uint8_t vpmr =
+ bits(isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2), 31, 24);
+
+ if (prio >= vpmr) {
+ // prioriry masked
+ return false;
+ }
+
+ uint8_t rprio = virtualHighestActivePriority();
+
+ if (rprio == 0xff) {
+ return true;
+ }
+
+ Gicv3::GroupId group = lr & ICH_LR_EL2_GROUP ? Gicv3::G1NS : Gicv3::G0S;
+ uint32_t prio_mask = virtualGroupPriorityMask(group);
+
+ if ((prio & prio_mask) < (rprio & prio_mask)) {
+ return true;
+ }
+
+ return false;
+}
+
+uint8_t
+Gicv3CPUInterface::virtualHighestActivePriority()
+{
+ uint8_t num_aprs = 1 << (VIRTUAL_PRIORITY_BITS - 5);
+
+ for (int i = 0; i < num_aprs; i++) {
+ ArmISA::MiscReg vapr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i) |
+ isa->readMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i);
+
+ if (!vapr) {
+ continue;
+ }
+
+ return (i * 32 + ctz32(vapr)) << (GIC_MIN_VBPR + 1);
+ }
+
+ // no active interrups, return idle priority
+ return 0xff;
+}
+
+void
+Gicv3CPUInterface::virtualIncrementEOICount()
+{
+ // Increment the EOICOUNT field in ICH_HCR_EL2
+ ArmISA::MiscReg ich_hcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+ uint32_t EOI_cout = bits(ich_hcr_el2, 31, 27);
+ EOI_cout++;
+ ich_hcr_el2 = insertBits(ich_hcr_el2, 31, 27, EOI_cout);
+ isa->setMiscRegNoEffect(MISCREG_ICH_HCR_EL2, ich_hcr_el2);
+}
+
+/*
+ * Should we signal the interrupt as IRQ or FIQ?
+ * see spec section 4.6.2
+ */
+ArmISA::InterruptTypes
+Gicv3CPUInterface::intSignalType(Gicv3::GroupId group)
+{
+ bool is_fiq = false;
+
+ switch (group) {
+ case Gicv3::G0S:
+ is_fiq = true;
+ break;
+
+ case Gicv3::G1S:
+ is_fiq = (distributor->DS == 0) &&
+ (!inSecureState() || ((currEL() == EL3) && isAA64()));
+ break;
+
+ case Gicv3::G1NS:
+ is_fiq = (distributor->DS == 0) && inSecureState();
+ break;
+
+ default:
+ panic("Gicv3CPUInterface::intSignalType(): invalid group!");
+ }
+
+ if (is_fiq) {
+ return ArmISA::INT_FIQ;
+ } else {
+ return ArmISA::INT_IRQ;
+ }
+}
+
+bool
+Gicv3CPUInterface::hppiCanPreempt()
+{
+ if (hppi.prio == 0xff) {
+ // there is no pending interrupt
+ return false;
+ }
+
+ if (!groupEnabled(hppi.group)) {
+ // group disabled at CPU interface
+ return false;
+ }
+
+ if (hppi.prio >= isa->readMiscRegNoEffect(MISCREG_ICC_PMR_EL1)) {
+ // priority masked
+ return false;
+ }
+
+ uint8_t rprio = highestActivePriority();
+
+ if (rprio == 0xff) {
+ return true;
+ }
+
+ uint32_t prio_mask = groupPriorityMask(hppi.group);
+
+ if ((hppi.prio & prio_mask) < (rprio & prio_mask)) {
+ return true;
+ }
+
+ return false;
+}
+
+uint8_t
+Gicv3CPUInterface::highestActivePriority()
+{
+ uint32_t apr = isa->readMiscRegNoEffect(MISCREG_ICC_AP0R0_EL1) |
+ isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_NS) |
+ isa->readMiscRegNoEffect(MISCREG_ICC_AP1R0_EL1_S);
+
+ if (apr) {
+ return ctz32(apr) << (GIC_MIN_BPR + 1);
+ }
+
+ // no active interrups, return idle priority
+ return 0xff;
+}
+
+bool
+Gicv3CPUInterface::groupEnabled(Gicv3::GroupId group)
+{
+ switch (group) {
+ case Gicv3::G0S:
+ return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN0_EL1) &
+ ICC_IGRPEN0_EL1_ENABLE;
+
+ case Gicv3::G1S:
+ //if (distributor->DS)
+ //{
+ // return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS) &
+ // ICC_IGRPEN1_EL1_ENABLE;
+ //}
+ //else
+ //{
+ return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S) &
+ ICC_IGRPEN1_EL1_ENABLE;
+
+ //}
+
+ case Gicv3::G1NS:
+ return isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS) &
+ ICC_IGRPEN1_EL1_ENABLE;
+
+ default:
+ panic("Gicv3CPUInterface::groupEnable(): invalid group!\n");
+ }
+}
+
+bool
+Gicv3CPUInterface::inSecureState()
+{
+ if (!gic->getSystem()->haveSecurity()) {
+ return false;
+ }
+
+ CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+ SCR scr = isa->readMiscRegNoEffect(MISCREG_SCR);
+ return ArmISA::inSecureState(scr, cpsr);
+}
+
+int
+Gicv3CPUInterface::currEL()
+{
+ CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+ bool is_64 = opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
+
+ if (is_64) {
+ return (ExceptionLevel)(uint8_t) cpsr.el;
+ } else {
+ switch (cpsr.mode) {
+ case MODE_USER:
+ return 0;
+
+ case MODE_HYP:
+ return 2;
+
+ case MODE_MON:
+ return 3;
+
+ default:
+ return 1;
+ }
+ }
+}
+
+bool
+Gicv3CPUInterface::haveEL(ExceptionLevel el)
+{
+ switch (el) {
+ case EL0:
+ case EL1:
+ return true;
+
+ case EL2:
+ return gic->getSystem()->haveVirtualization();
+
+ case EL3:
+ return gic->getSystem()->haveSecurity();
+
+ default:
+ warn("Unimplemented Exception Level\n");
+ return false;
+ }
+}
+
+bool
+Gicv3CPUInterface::isSecureBelowEL3()
+{
+ SCR scr = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
+ return haveEL(EL3) && scr.ns == 0;
+}
+
+bool
+Gicv3CPUInterface::isAA64()
+{
+ CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+ return opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
+}
+
+bool
+Gicv3CPUInterface::isEL3OrMon()
+{
+ if (haveEL(EL3)) {
+ CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
+ bool is_64 = opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
+
+ if (is_64 && (cpsr.el == EL3)) {
+ return true;
+ } else if (!is_64 && (cpsr.mode == MODE_MON)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint32_t
+Gicv3CPUInterface::eoiMaintenanceInterruptStatus(uint32_t * misr)
+{
+ /* Return a set of bits indicating the EOI maintenance interrupt status
+ * for each list register. The EOI maintenance interrupt status is
+ * 1 if LR.State == 0 && LR.HW == 0 && LR.EOI == 1
+ * (see the GICv3 spec for the ICH_EISR_EL2 register).
+ * If misr is not NULL then we should also collect the information
+ * about the MISR.EOI, MISR.NP and MISR.U bits.
+ */
+ uint32_t value = 0;
+ int valid_count = 0;
+ bool seen_pending = false;
+
+ for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
+ ArmISA::MiscReg lr =
+ isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
+
+ if ((lr & (ICH_LR_EL2_STATE_MASK | ICH_LR_EL2_HW | ICH_LR_EL2_EOI)) ==
+ ICH_LR_EL2_EOI) {
+ value |= (1 << lr_idx);
+ }
+
+ if ((lr & ICH_LR_EL2_STATE_MASK)) {
+ valid_count++;
+ }
+
+ if (bits(lr, ICH_LR_EL2_STATE_SHIFT + ICH_LR_EL2_STATE_LENGTH,
+ ICH_LR_EL2_STATE_SHIFT) == ICH_LR_EL2_STATE_PENDING) {
+ seen_pending = true;
+ }
+ }
+
+ if (misr) {
+ ArmISA::MiscReg ich_hcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+
+ if (valid_count < 2 && (ich_hcr_el2 & ICH_HCR_EL2_UIE)) {
+ *misr |= ICH_MISR_EL2_U;
+ }
+
+ if (!seen_pending && (ich_hcr_el2 & ICH_HCR_EL2_NPIE)) {
+ *misr |= ICH_MISR_EL2_NP;
+ }
+
+ if (value) {
+ *misr |= ICH_MISR_EL2_EOI;
+ }
+ }
+
+ return value;
+}
+
+uint32_t
+Gicv3CPUInterface::maintenanceInterruptStatus()
+{
+ /* Return a set of bits indicating the maintenance interrupt status
+ * (as seen in the ICH_MISR_EL2 register).
+ */
+ uint32_t value = 0;
+ /* Scan list registers and fill in the U, NP and EOI bits */
+ eoiMaintenanceInterruptStatus(&value);
+ ArmISA::MiscReg ich_hcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
+ ArmISA::MiscReg ich_vmcr_el2 =
+ isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
+
+ if (ich_hcr_el2 & (ICH_HCR_EL2_LRENPIE | ICH_HCR_EL2_EOICOUNT_MASK)) {
+ value |= ICH_MISR_EL2_LRENP;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0EIE) &&
+ (ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) {
+ value |= ICH_MISR_EL2_VGRP0E;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP0DIE) &&
+ !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ value |= ICH_MISR_EL2_VGRP0D;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1EIE) &&
+ (ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ value |= ICH_MISR_EL2_VGRP1E;
+ }
+
+ if ((ich_hcr_el2 & ICH_HCR_EL2_VGRP1DIE) &&
+ !(ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) {
+ value |= ICH_MISR_EL2_VGRP1D;
+ }
+
+ return value;
+}
+
+void
+Gicv3CPUInterface::serialize(CheckpointOut & cp) const
+{
+ SERIALIZE_SCALAR(hppi.intid);
+ SERIALIZE_SCALAR(hppi.prio);
+ SERIALIZE_ENUM(hppi.group);
+}
+
+void
+Gicv3CPUInterface::unserialize(CheckpointIn & cp)
+{
+ UNSERIALIZE_SCALAR(hppi.intid);
+ UNSERIALIZE_SCALAR(hppi.prio);
+ UNSERIALIZE_ENUM(hppi.group);
+}
diff --git a/src/dev/arm/gic_v3_cpu_interface.hh b/src/dev/arm/gic_v3_cpu_interface.hh
new file mode 100644
index 000000000..63d3b5327
--- /dev/null
+++ b/src/dev/arm/gic_v3_cpu_interface.hh
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_CPU_INTERFACE_H__
+#define __DEV_ARM_GICV3_CPU_INTERFACE_H__
+
+#include "arch/arm/isa_device.hh"
+#include "dev/arm/gic_v3.hh"
+
+class Gicv3Redistributor;
+class Gicv3Distributor;
+
+class Gicv3CPUInterface : public ArmISA::BaseISADevice, public Serializable
+{
+ private:
+
+ friend class Gicv3Redistributor;
+ friend class Gicv3Distributor;
+
+ protected:
+
+ Gicv3 * gic;
+ Gicv3Redistributor * redistributor;
+ Gicv3Distributor * distributor;
+ uint32_t cpuId;
+
+ static const uint32_t ICC_SRE_EL1_SRE = 1 << 0;
+ static const uint32_t ICC_SRE_EL1_DFB = 1 << 1;
+ static const uint32_t ICC_SRE_EL1_DIB = 1 << 2;
+
+ static const uint32_t ICC_SRE_EL2_SRE = 1 << 0;
+ static const uint32_t ICC_SRE_EL2_DFB = 1 << 1;
+ static const uint32_t ICC_SRE_EL2_DIB = 1 << 2;
+ static const uint32_t ICC_SRE_EL2_ENABLE = 1 << 3;
+
+ static const uint32_t ICC_SRE_EL3_SRE = 1 << 0;
+ static const uint32_t ICC_SRE_EL3_DFB = 1 << 1;
+ static const uint32_t ICC_SRE_EL3_DIB = 1 << 2;
+ static const uint32_t ICC_SRE_EL3_ENABLE = 1 << 3;
+
+ static const uint32_t ICC_CTLR_EL3_CBPR_EL1S = 1 << 0;
+ static const uint32_t ICC_CTLR_EL3_CBPR_EL1NS = 1 << 1;
+ static const uint32_t ICC_CTLR_EL3_EOIMODE_EL3 = 1 << 2;
+ static const uint32_t ICC_CTLR_EL3_EOIMODE_EL1S = 1 << 3;
+ static const uint32_t ICC_CTLR_EL3_EOIMODE_EL1NS = 1 << 4;
+ static const uint32_t ICC_CTLR_EL3_RM = 1 << 5;
+ static const uint32_t ICC_CTLR_EL3_PMHE = 1 << 6;
+ static const uint32_t ICC_CTLR_EL3_PRIBITS_SHIFT = 8;
+ static const uint32_t ICC_CTLR_EL3_IDBITS_SHIFT = 11;
+ static const uint32_t ICC_CTLR_EL3_SEIS = 1 << 14;
+ static const uint32_t ICC_CTLR_EL3_A3V = 1 << 15;
+ static const uint32_t ICC_CTLR_EL3_nDS = 1 << 17;
+ static const uint32_t ICC_CTLR_EL3_RSS = 1 << 18;
+
+ static const uint32_t ICC_CTLR_EL1_CBPR = 1 << 0;
+ static const uint32_t ICC_CTLR_EL1_EOIMODE = 1 << 1;
+ static const uint32_t ICC_CTLR_EL1_PMHE = 1 << 6;
+ static const uint32_t ICC_CTLR_EL1_SEIS = 1 << 14;
+ static const uint32_t ICC_CTLR_EL1_A3V = 1 << 15;
+ static const uint32_t ICC_CTLR_EL1_RSS = 1 << 18;
+ static const uint32_t ICC_CTLR_EL1_PRIBITS_SHIFT = 8;
+ static const uint32_t ICC_CTLR_EL1_PRIBITS_MASK =
+ 7U << ICC_CTLR_EL1_PRIBITS_SHIFT;
+ static const uint32_t ICC_CTLR_EL1_IDBITS_SHIFT = 11;
+
+ static const uint32_t ICC_IGRPEN0_EL1_ENABLE = 1 << 0;
+ static const uint32_t ICC_IGRPEN1_EL1_ENABLE = 1 << 0;
+
+ static const uint32_t ICC_IGRPEN1_EL3_ENABLEGRP1NS = 1 << 0;
+ static const uint32_t ICC_IGRPEN1_EL3_ENABLEGRP1S = 1 << 1;
+
+ static const uint8_t PRIORITY_BITS = 5;
+
+ /* Minimum BPR for Secure, or when security not enabled */
+ static const uint8_t GIC_MIN_BPR = 2;
+ /* Minimum BPR for Nonsecure when security is enabled */
+ static const uint8_t GIC_MIN_BPR_NS = GIC_MIN_BPR + 1;
+
+ static const uint8_t VIRTUAL_PRIORITY_BITS = 5;
+ static const uint8_t VIRTUAL_PREEMPTION_BITS = 5;
+ static const uint8_t VIRTUAL_NUM_LIST_REGS = 16;
+
+ static const uint8_t GIC_MIN_VBPR = 7 - VIRTUAL_PREEMPTION_BITS;
+
+ typedef struct {
+ uint32_t intid;
+ uint8_t prio;
+ Gicv3::GroupId group;
+ } hppi_t;
+
+ hppi_t hppi;
+
+ // GIC CPU interface memory mapped control registers (legacy)
+ enum {
+ GICC_CTLR = 0x0000,
+ GICC_PMR = 0x0004,
+ GICC_BPR = 0x0008,
+ GICC_IAR = 0x000C,
+ GICC_EOIR = 0x0010,
+ GICC_RPR = 0x0014,
+ GICC_HPPI = 0x0018,
+ GICC_ABPR = 0x001C,
+ GICC_AIAR = 0x0020,
+ GICC_AEOIR = 0x0024,
+ GICC_AHPPIR = 0x0028,
+ GICC_STATUSR = 0x002C,
+ GICC_IIDR = 0x00FC,
+ };
+
+ static const AddrRange GICC_APR;
+ static const AddrRange GICC_NSAPR;
+
+ // GIC CPU virtual interface memory mapped control registers (legacy)
+ enum {
+ GICH_HCR = 0x0000,
+ GICH_VTR = 0x0004,
+ GICH_VMCR = 0x0008,
+ GICH_MISR = 0x0010,
+ GICH_EISR = 0x0020,
+ GICH_ELRSR = 0x0030,
+ };
+
+ static const AddrRange GICH_APR;
+ static const AddrRange GICH_LR;
+
+ static const uint32_t ICH_HCR_EL2_EN = 1 << 0;
+ static const uint32_t ICH_HCR_EL2_UIE = 1 << 1;
+ static const uint32_t ICH_HCR_EL2_LRENPIE = 1 << 2;
+ static const uint32_t ICH_HCR_EL2_NPIE = 1 << 3;
+ static const uint32_t ICH_HCR_EL2_VGRP0EIE = 1 << 4;
+ static const uint32_t ICH_HCR_EL2_VGRP0DIE = 1 << 5;
+ static const uint32_t ICH_HCR_EL2_VGRP1EIE = 1 << 6;
+ static const uint32_t ICH_HCR_EL2_VGRP1DIE = 1 << 7;
+ static const uint32_t ICH_HCR_EL2_TC = 1 << 10;
+ static const uint32_t ICH_HCR_EL2_TALL0 = 1 << 11;
+ static const uint32_t ICH_HCR_EL2_TALL1 = 1 << 12;
+ static const uint32_t ICH_HCR_EL2_TSEI = 1 << 13;
+ static const uint32_t ICH_HCR_EL2_TDIR = 1 << 14;
+ static const uint32_t ICH_HCR_EL2_EOICOUNT_MASK = 0x1fU << 27;
+
+ static const uint64_t ICH_LR_EL2_VINTID_SHIFT = 0;
+ static const uint64_t ICH_LR_EL2_VINTID_LENGTH = 32;
+ static const uint64_t ICH_LR_EL2_VINTID_MASK =
+ (0xffffffffULL << ICH_LR_EL2_VINTID_SHIFT);
+ static const uint64_t ICH_LR_EL2_PINTID_SHIFT = 32;
+ static const uint64_t ICH_LR_EL2_PINTID_LENGTH = 10;
+ static const uint64_t ICH_LR_EL2_PINTID_MASK =
+ (0x3ffULL << ICH_LR_EL2_PINTID_SHIFT);
+ /* Note that EOI shares with the top bit of the pINTID field */
+ static const uint64_t ICH_LR_EL2_EOI = (1ULL << 41);
+ static const uint64_t ICH_LR_EL2_PRIORITY_SHIFT = 48;
+ static const uint64_t ICH_LR_EL2_PRIORITY_LENGTH = 8;
+ static const uint64_t ICH_LR_EL2_PRIORITY_MASK =
+ (0xffULL << ICH_LR_EL2_PRIORITY_SHIFT);
+ static const uint64_t ICH_LR_EL2_GROUP = (1ULL << 60);
+ static const uint64_t ICH_LR_EL2_HW = (1ULL << 61);
+ static const uint64_t ICH_LR_EL2_STATE_SHIFT = 62;
+ static const uint64_t ICH_LR_EL2_STATE_LENGTH = 2;
+ static const uint64_t ICH_LR_EL2_STATE_MASK =
+ (3ULL << ICH_LR_EL2_STATE_SHIFT);
+ /* values for the state field: */
+ static const uint64_t ICH_LR_EL2_STATE_INVALID = 0;
+ static const uint64_t ICH_LR_EL2_STATE_PENDING = 1;
+ static const uint64_t ICH_LR_EL2_STATE_ACTIVE = 2;
+ static const uint64_t ICH_LR_EL2_STATE_ACTIVE_PENDING = 3;
+ static const uint64_t ICH_LR_EL2_STATE_PENDING_BIT =
+ (1ULL << ICH_LR_EL2_STATE_SHIFT);
+ static const uint64_t ICH_LR_EL2_STATE_ACTIVE_BIT =
+ (2ULL << ICH_LR_EL2_STATE_SHIFT);
+
+ static const uint64_t ICH_LRC_PRIORITY_SHIFT =
+ ICH_LR_EL2_PRIORITY_SHIFT - 32;
+ static const uint64_t ICH_LRC_PRIORITY_LENGTH =
+ ICH_LR_EL2_PRIORITY_LENGTH;
+
+ static const uint32_t ICH_MISR_EL2_EOI = (1 << 0);
+ static const uint32_t ICH_MISR_EL2_U = (1 << 1);
+ static const uint32_t ICH_MISR_EL2_LRENP = (1 << 2);
+ static const uint32_t ICH_MISR_EL2_NP = (1 << 3);
+ static const uint32_t ICH_MISR_EL2_VGRP0E = (1 << 4);
+ static const uint32_t ICH_MISR_EL2_VGRP0D = (1 << 5);
+ static const uint32_t ICH_MISR_EL2_VGRP1E = (1 << 6);
+ static const uint32_t ICH_MISR_EL2_VGRP1D = (1 << 7);
+
+ static const uint32_t ICH_VMCR_EL2_VENG0_SHIFT = 0;
+ static const uint32_t ICH_VMCR_EL2_VENG0 =
+ (1 << ICH_VMCR_EL2_VENG0_SHIFT);
+ static const uint32_t ICH_VMCR_EL2_VENG1_SHIFT = 1;
+ static const uint32_t ICH_VMCR_EL2_VENG1 =
+ (1 << ICH_VMCR_EL2_VENG1_SHIFT);
+ static const uint32_t ICH_VMCR_EL2_VACKCTL = (1 << 2);
+ static const uint32_t ICH_VMCR_EL2_VFIQEN = (1 << 3);
+ static const uint32_t ICH_VMCR_EL2_VCBPR_SHIFT = 4;
+ static const uint32_t ICH_VMCR_EL2_VCBPR =
+ (1 << ICH_VMCR_EL2_VCBPR_SHIFT);
+ static const uint32_t ICH_VMCR_EL2_VEOIM_SHIFT = 9;
+ static const uint32_t ICH_VMCR_EL2_VEOIM =
+ (1 << ICH_VMCR_EL2_VEOIM_SHIFT);
+ static const uint32_t ICH_VMCR_EL2_VBPR1_SHIFT = 18;
+ static const uint32_t ICH_VMCR_EL2_VBPR1_LENGTH = 3;
+ static const uint32_t ICH_VMCR_EL2_VBPR1_MASK =
+ (0x7U << ICH_VMCR_EL2_VBPR1_SHIFT);
+ static const uint32_t ICH_VMCR_EL2_VBPR0_SHIFT = 21;
+ static const uint32_t ICH_VMCR_EL2_VBPR0_LENGTH = 3;
+ static const uint32_t ICH_VMCR_EL2_VBPR0_MASK =
+ (0x7U << ICH_VMCR_EL2_VBPR0_SHIFT);
+ static const uint32_t ICH_VMCR_EL2_VPMR_SHIFT = 24;
+ static const uint32_t ICH_VMCR_EL2_VPMR_LENGTH = 8;
+ static const uint32_t ICH_VMCR_EL2_VPMR_MASK =
+ (0xffU << ICH_VMCR_EL2_VPMR_SHIFT);
+
+ static const uint32_t ICH_VTR_EL2_LISTREGS_SHIFT = 0;
+ static const uint32_t ICH_VTR_EL2_TDS = 1 << 19;
+ static const uint32_t ICH_VTR_EL2_NV4 = 1 << 20;
+ static const uint32_t ICH_VTR_EL2_A3V = 1 << 21;
+ static const uint32_t ICH_VTR_EL2_SEIS = 1 << 22;
+ static const uint32_t ICH_VTR_EL2_IDBITS_SHIFT = 23;
+ static const uint32_t ICH_VTR_EL2_PREBITS_SHIFT = 26;
+ static const uint32_t ICH_VTR_EL2_PRIBITS_SHIFT = 29;
+
+ public:
+
+ Gicv3CPUInterface(Gicv3 * gic, uint32_t cpu_id);
+ ~Gicv3CPUInterface();
+ void init();
+ void initState();
+
+ ArmISA::MiscReg readMiscReg(int misc_reg) override;
+ void setMiscReg(int misc_reg, ArmISA::MiscReg val) override;
+ void update();
+ void virtualUpdate();
+
+ void serialize(CheckpointOut & cp) const override;
+ void unserialize(CheckpointIn & cp) override;
+
+ protected:
+
+ void reset();
+ bool hppiCanPreempt();
+ bool hppviCanPreempt(int lrIdx);
+ bool groupEnabled(Gicv3::GroupId group);
+ uint8_t highestActivePriority();
+ uint8_t virtualHighestActivePriority();
+ bool inSecureState();
+ int currEL();
+ bool haveEL(ArmISA::ExceptionLevel el);
+ void activateIRQ(uint32_t intid, Gicv3::GroupId group);
+ void virtualActivateIRQ(uint32_t lrIdx);
+ void deactivateIRQ(uint32_t intid, Gicv3::GroupId group);
+ void virtualDeactivateIRQ(int lrIdx);
+ uint32_t groupPriorityMask(Gicv3::GroupId group);
+ uint32_t virtualGroupPriorityMask(Gicv3::GroupId group);
+ void dropPriority(Gicv3::GroupId group);
+ uint8_t virtualDropPriority();
+ ArmISA::InterruptTypes intSignalType(Gicv3::GroupId group);
+ bool isEOISplitMode();
+ bool virtualIsEOISplitMode();
+ bool isSecureBelowEL3();
+ bool inSecureState2();
+ uint32_t eoiMaintenanceInterruptStatus(uint32_t * misr);
+ uint32_t maintenanceInterruptStatus();
+ int highestActiveGroup();
+ bool getHCREL2FMO();
+ bool getHCREL2IMO();
+ uint32_t getHPPIR1();
+ uint32_t getHPPIR0();
+ int getHPPVILR();
+ int virtualFindActive(uint32_t intid);
+ void virtualIncrementEOICount();
+ bool isEL3OrMon();
+ bool isAA64();
+};
+
+#endif //__DEV_ARM_GICV3_CPU_INTERFACE_H__
diff --git a/src/dev/arm/gic_v3_distributor.cc b/src/dev/arm/gic_v3_distributor.cc
new file mode 100644
index 000000000..00f29a74c
--- /dev/null
+++ b/src/dev/arm/gic_v3_distributor.cc
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+#include "dev/arm/gic_v3_distributor.hh"
+
+#include <algorithm>
+
+#include "debug/GIC.hh"
+#include "dev/arm/gic_v3.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
+#include "dev/arm/gic_v3_redistributor.hh"
+
+const AddrRange Gicv3Distributor::GICD_IGROUPR(0x0080, 0x00ff);
+const AddrRange Gicv3Distributor::GICD_ISENABLER(0x0100, 0x017f);
+const AddrRange Gicv3Distributor::GICD_ICENABLER(0x0180, 0x01ff);
+const AddrRange Gicv3Distributor::GICD_ISPENDR(0x0200, 0x027f);
+const AddrRange Gicv3Distributor::GICD_ICPENDR(0x0280, 0x02ff);
+const AddrRange Gicv3Distributor::GICD_ISACTIVER(0x0300, 0x037f);
+const AddrRange Gicv3Distributor::GICD_ICACTIVER(0x0380, 0x03ff);
+const AddrRange Gicv3Distributor::GICD_IPRIORITYR(0x0400, 0x07ff);
+const AddrRange Gicv3Distributor::GICD_ITARGETSR(0x0800, 0x08ff);
+const AddrRange Gicv3Distributor::GICD_ICFGR(0x0c00, 0x0cff);
+const AddrRange Gicv3Distributor::GICD_IGRPMODR(0x0d00, 0x0d7f);
+const AddrRange Gicv3Distributor::GICD_NSACR(0x0e00, 0x0eff);
+const AddrRange Gicv3Distributor::GICD_CPENDSGIR(0x0f10, 0x0f1f);
+const AddrRange Gicv3Distributor::GICD_SPENDSGIR(0x0f20, 0x0f2f);
+const AddrRange Gicv3Distributor::GICD_IROUTER(0x6000, 0x7fe0);
+
+Gicv3Distributor::Gicv3Distributor(Gicv3 * gic, uint32_t it_lines)
+ : gic(gic),
+ itLines(it_lines),
+ irqGroup(it_lines),
+ irqEnabled(it_lines),
+ irqPending(it_lines),
+ irqActive(it_lines),
+ irqPriority(it_lines),
+ irqConfig(it_lines),
+ irqGrpmod(it_lines),
+ irqNsacr(it_lines),
+ irqAffinityRouting(it_lines)
+{
+ panic_if(it_lines > Gicv3::INTID_SECURE, "Invalid value for it_lines!");
+}
+
+Gicv3Distributor::~Gicv3Distributor()
+{
+}
+
+void
+Gicv3Distributor::init()
+{
+}
+
+void
+Gicv3Distributor::initState()
+{
+ reset();
+}
+
+void
+Gicv3Distributor::reset()
+{
+ std::fill(irqGroup.begin(), irqGroup.end(), 0);
+ // Imp. defined reset value
+ std::fill(irqEnabled.begin(), irqEnabled.end(), false);
+ std::fill(irqPending.begin(), irqPending.end(), false);
+ std::fill(irqActive.begin(), irqActive.end(), false);
+ // Imp. defined reset value
+ std::fill(irqPriority.begin(), irqPriority.end(), 0xAAAAAAAA);
+ std::fill(irqConfig.begin(), irqConfig.end(),
+ Gicv3::INT_LEVEL_SENSITIVE); // Imp. defined reset value
+ std::fill(irqGrpmod.begin(), irqGrpmod.end(), 0);
+ std::fill(irqNsacr.begin(), irqNsacr.end(), 0);
+ /*
+ * For our implementation affinity routing is always enabled,
+ * no GICv2 legacy
+ */
+ ARE = true;
+
+ if (gic->getSystem()->haveSecurity()) {
+ DS = false;
+ } else {
+ DS = true;
+ }
+
+ EnableGrp0 = 0;
+ EnableGrp1NS = 0;
+ EnableGrp1S = 0;
+}
+
+uint64_t
+Gicv3Distributor::read(Addr addr, size_t size, bool is_secure_access)
+{
+ if (GICD_IGROUPR.contains(addr)) { // Interrupt Group Registers
+ uint64_t val = 0x0;
+
+ if (!DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return 0;
+ }
+
+ int first_intid = (addr - GICD_IGROUPR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ val |= irqGroup[int_id] << i;
+ }
+
+ return val;
+ // Interrupt Set-Enable Registers
+ } else if (GICD_ISENABLER.contains(addr)) {
+ uint64_t val = 0x0;
+ int first_intid = (addr - GICD_ISENABLER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ val |= irqEnabled[int_id] << i;
+ }
+
+ return val;
+ } else if (GICD_ICENABLER.contains(addr)) {
+ // Interrupt Clear-Enable Registers
+ uint64_t val = 0x0;
+ int first_intid = (addr - GICD_ICENABLER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ val |= (irqEnabled[int_id] << i);
+ }
+
+ return val;
+ } else if (GICD_ISPENDR.contains(addr)) {
+ uint64_t val = 0x0;
+ int first_intid = (addr - GICD_ISPENDR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] == 0) {
+ // Group 0 or Secure Group 1 interrupts are RAZ/WI
+ continue;
+ }
+ }
+
+ val |= (irqPending[int_id] << i);
+ }
+
+ return val;
+ } else if (GICD_ICPENDR.contains(addr)) {
+ uint64_t val = 0x0;
+ int first_intid = (addr - GICD_ICPENDR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] < 2) {
+ // Group 0 or Secure Group 1 interrupts are RAZ/WI
+ continue;
+ }
+ }
+
+ val |= (irqPending[int_id] << i);
+ }
+
+ return val;
+ // Interrupt Set-Active Registers
+ } else if (GICD_ISACTIVER.contains(addr)) {
+ int first_intid = (addr - GICD_ISACTIVER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ uint64_t val = 0x0;
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ // Group 0 or Secure Group 1 interrupts are RAZ/WI
+ if (irqNsacr[int_id] < 2) {
+ continue;
+ }
+ }
+
+ val |= (irqActive[int_id] << i);
+ }
+
+ return val;
+ // Interrupt Clear-Active Registers
+ } else if (GICD_ICACTIVER.contains(addr)) {
+ int first_intid = (addr - GICD_ICACTIVER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ uint64_t val = 0x0;
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] < 2) {
+ continue;
+ }
+ }
+
+ val |= (irqActive[int_id] << i);
+ }
+
+ return val;
+ // Interrupt Priority Registers
+ } else if (GICD_IPRIORITYR.contains(addr)) {
+ uint64_t val = 0x0;
+ int first_intid = addr - GICD_IPRIORITYR.start();
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ for (int i = 0, int_id = first_intid; i < size && int_id < itLines;
+ i++, int_id++) {
+ uint8_t prio = irqPriority[int_id];
+
+ if (!DS && !is_secure_access) {
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ continue;
+ } else {
+ // NS view
+ prio = (prio << 1) & 0xff;
+ }
+ }
+
+ val |= prio << (i * 8);
+ }
+
+ return val;
+ } else if (GICD_ITARGETSR.contains(addr)) {
+ // Interrupt Processor Targets Registers
+ // ARE always on, RAZ/WI
+ warn("Gicv3Distributor::read(): "
+ "GICD_ITARGETSR is RAZ/WI, legacy not supported!\n");
+ return 0;
+ // Interrupt Configuration Registers
+ } else if (GICD_ICFGR.contains(addr)) {
+ int first_intid = (addr - GICD_ICFGR.start()) * 4;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ uint64_t val = 0x0;
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i = i + 2, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ if (irqConfig[int_id] == Gicv3::INT_EDGE_TRIGGERED) {
+ val |= (0x2 << i);
+ }
+ }
+
+ return val;
+ } else if (GICD_IGRPMODR.contains(addr)) {
+ // Interrupt Group Modifier Registers
+ if (DS) {
+ // RAZ/WI if security disabled
+ return 0;
+ } else {
+ if (!is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return 0;
+ } else {
+ int first_intid = (addr - GICD_IGRPMODR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ uint64_t val = 0x0;
+
+ for (int i = 0, int_id = first_intid;
+ i < 8 * size && int_id < itLines; i++, int_id++) {
+ val |= irqGrpmod[int_id] << i;
+ }
+
+ return val;
+ }
+ }
+
+ // Non-secure Access Control Registers
+ } else if (GICD_NSACR.contains(addr)) {
+ // 2 bits per interrupt
+ int first_intid = (addr - GICD_NSACR.start()) * 4;
+
+ if (isNotSPI(first_intid)) {
+ return 0;
+ }
+
+ if (DS || (!DS && !is_secure_access)) {
+ return 0;
+ }
+
+ uint64_t val = 0x0;
+
+ for (int i = 0, int_id = first_intid;
+ i < 8 * size && int_id < itLines; i = i + 2, int_id++) {
+ val |= irqNsacr[int_id] << i;
+ }
+
+ return val;
+ } else if (GICD_CPENDSGIR.contains(addr)) { // SGI Clear-Pending Registers
+ // ARE always on, RAZ/WI
+ warn("Gicv3Distributor::read(): "
+ "GICD_CPENDSGIR is RAZ/WI, legacy not supported!\n");
+ return 0x0;
+ } else if (GICD_SPENDSGIR.contains(addr)) { // SGI Set-Pending Registers
+ // ARE always on, RAZ/WI
+ warn("Gicv3Distributor::read(): "
+ "GICD_SPENDSGIR is RAZ/WI, legacy not supported!\n");
+ return 0x0;
+ } else if (GICD_IROUTER.contains(addr)) { // Interrupt Routing Registers
+ // 64 bit registers. 2 or 1 access.
+ int int_id = (addr - GICD_IROUTER.start()) / 8;
+
+ if (isNotSPI(int_id)) {
+ return 0;
+ }
+
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] < 3) {
+ return 0;
+ }
+ }
+
+ if (size == 4) {
+ if (addr & 7) { // high half of 64 bit register
+ return irqAffinityRouting[int_id] >> 32;
+ } else { // high low of 64 bit register
+ return irqAffinityRouting[int_id] & 0xFFFFFFFF;
+ }
+ } else {
+ return irqAffinityRouting[int_id];
+ }
+ }
+
+ switch (addr) {
+ case GICD_CTLR: // Control Register
+ if (!DS) {
+ if (is_secure_access) {
+ // E1NWF [7] RAZ/WI
+ // DS [6] - Disable Security
+ // ARE_NS [5] RAO/WI
+ // ARE_S [4] RAO/WI
+ // EnableGrp1S [2]
+ // EnableGrp1NS [1]
+ // EnableGrp0 [0]
+ return (EnableGrp0 << 0) |
+ (EnableGrp1NS << 1) |
+ (EnableGrp1S << 2) |
+ (1 << 4) |
+ (1 << 5) |
+ (DS << 6);
+ } else {
+ // ARE_NS [4] RAO/WI;
+ // EnableGrp1A [1] is a read-write alias of the Secure
+ // GICD_CTLR.EnableGrp1NS
+ // EnableGrp1 [0] RES0
+ return (1 << 4) | (EnableGrp1NS << 1);
+ }
+ } else {
+ return (DS << 6) | (ARE << 4) |
+ (EnableGrp1NS << 1) | (EnableGrp0 << 0);
+ }
+
+ case GICD_TYPER: // Interrupt Controller Type Register
+ /*
+ * RSS [26] == 1
+ * (The implementation does supports targeted SGIs with affinity
+ * level 0 values of 0 - 255)
+ * No1N [25] == 1
+ * (1 of N SPI interrupts are not supported)
+ * A3V [24] == 1
+ * (Supports nonzero values of Affinity level 3)
+ * IDbits [23:19] == 0xf
+ * (The number of interrupt identifier bits supported, minus one)
+ * DVIS [18] == 0
+ * (The implementation does not support Direct Virtual LPI
+ * injection)
+ * LPIS [17] == 0
+ * (The implementation does not support LPIs)
+ * MBIS [16] == 0
+ * (The implementation does not support message-based interrupts
+ * by writing to Distributor registers)
+ * SecurityExtn [10] == X
+ * (The GIC implementation supports two Security states)
+ * CPUNumber [7:5] == 0
+ * (since for us ARE is always 1 [(ARE = 0) == Gicv2 legacy])
+ * ITLinesNumber [4:0] == N
+ * (MaxSPIIntId = 32 (N + 1) - 1)
+ */
+ {
+ int max_spi_int_id = itLines - 1;
+ int it_lines_number = ceil((max_spi_int_id + 1) / 32.0) - 1;
+ return (1 << 26) | (1 << 25) | (1 << 24) | (0xf << 19) |
+ (gic->getSystem()->haveSecurity() << 10) |
+ (it_lines_number << 0);
+ }
+
+ case GICD_IIDR: // Implementer Identification Register
+ //return 0x43b; // ARM JEP106 code (r0p0 GIC-500)
+ return 0;
+
+ case GICD_STATUSR: // Error Reporting Status Register
+ // Optional register, RAZ/WI
+ return 0x0;
+
+ case GICD_PIDR0: { // Peripheral ID0 Register
+ uint8_t part_0 = 0x92; // Part number, bits[7:0]
+ return part_0;
+ }
+
+ case GICD_PIDR1: { // Peripheral ID1 Register
+ uint8_t des_0 = 0xB; // JEP106 identification code, bits[3:0]
+ uint8_t part_1 = 0x4; // Part number, bits[11:8]
+ return (des_0 << 4) | (part_1 << 0);
+ }
+
+ case GICD_PIDR2: { // Peripheral ID2 Register
+ uint8_t arch_rev = 0x3; // 0x3 GICv3
+ uint8_t jdec = 0x1; // JEP code
+ uint8_t des_1 = 0x3; // JEP106 identification code, bits[6:4]
+ return (arch_rev << 4) | (jdec << 3) | (des_1 << 0);
+ }
+
+ case GICD_PIDR3: // Peripheral ID3 Register
+ return 0x0; // Implementation defined
+
+ case GICD_PIDR4: { // Peripheral ID4 Register
+ uint8_t size = 0x4; // 64 KB software visible page
+ uint8_t des_2 = 0x4; // ARM implementation
+ return (size << 4) | (des_2 << 0);
+ }
+
+ case GICD_PIDR5: // Peripheral ID5 Register
+ case GICD_PIDR6: // Peripheral ID6 Register
+ case GICD_PIDR7: // Peripheral ID7 Register
+ return 0; // RES0
+
+ default:
+ panic("Gicv3Distributor::read(): invalid offset %#x\n", addr);
+ break;
+ }
+}
+
+void
+Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
+ bool is_secure_access)
+{
+ if (GICD_IGROUPR.contains(addr)) { // Interrupt Group Registers
+ if (!DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return;
+ }
+
+ int first_intid = (addr - GICD_IGROUPR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ irqGroup[int_id] = data & (1 << i) ? 1 : 0;
+ DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d group %d\n",
+ int_id, irqGroup[int_id]);
+ }
+
+ return;
+ // Interrupt Set-Enable Registers
+ } else if (GICD_ISENABLER.contains(addr)) {
+ int first_intid = (addr - GICD_ISENABLER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ bool enable = data & (1 << i) ? 1 : 0;
+
+ if (enable) {
+ if (!irqEnabled[int_id]) {
+ DPRINTF(GIC, "Gicv3Distributor::write(): "
+ "int_id %d enabled\n", int_id);
+ }
+
+ irqEnabled[int_id] = true;
+ }
+ }
+
+ return;
+ } else if (GICD_ICENABLER.contains(addr)) {
+ // Interrupt Clear-Enable Registers
+ int first_intid = (addr - GICD_ICENABLER.start()) * 8;
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ bool disable = data & (1 << i) ? 1 : 0;
+
+ if (disable) {
+ if (irqEnabled[int_id]) {
+ DPRINTF(GIC, "Gicv3Distributor::write(): "
+ "int_id %d disabled\n", int_id);
+ }
+
+ irqEnabled[int_id] = false;
+ }
+ }
+
+ return;
+ } else if (GICD_ISPENDR.contains(addr)) {
+ int first_intid = (addr - GICD_ISPENDR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] == 0) {
+ // Group 0 or Secure Group 1 interrupts are RAZ/WI
+ continue;
+ }
+ }
+
+ bool pending = data & (1 << i) ? 1 : 0;
+
+ if (pending) {
+ DPRINTF(GIC, "Gicv3Distributor::write() (GICD_ISPENDR): "
+ "int_id %d (SPI) pending bit set\n", int_id);
+ irqPending[int_id] = true;
+ }
+ }
+
+ updateAndInformCPUInterfaces();
+ return;
+ } else if (GICD_ICPENDR.contains(addr)) {
+ int first_intid = (addr - GICD_ICPENDR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] < 2) {
+ // Group 0 or Secure Group 1 interrupts are RAZ/WI
+ continue;
+ }
+ }
+
+ bool clear = data & (1 << i) ? 1 : 0;
+
+ if (clear) {
+ irqPending[int_id] = false;
+ }
+ }
+
+ updateAndInformCPUInterfaces();
+ return;
+ // Interrupt Set-Active Registers
+ } else if (GICD_ISACTIVER.contains(addr)) {
+ int first_intid = (addr - GICD_ISACTIVER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ bool active = data & (1 << i) ? 1 : 0;
+
+ if (active) {
+ irqActive[int_id] = 1;
+ }
+ }
+
+ return;
+ } else if (GICD_ICACTIVER.contains(addr)) {
+ // Interrupt Clear-Active Registers
+ int first_intid = (addr - GICD_ICACTIVER.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i++, int_id++) {
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ continue;
+ }
+
+ bool clear = data & (1 << i) ? 1 : 0;
+
+ if (clear) {
+ if (irqActive[int_id]) {
+ DPRINTF(GIC, "Gicv3Distributor::write(): "
+ "int_id %d active cleared\n", int_id);
+ }
+
+ irqActive[int_id] = false;
+ }
+ }
+
+ return;
+ // Interrupt Priority Registers
+ } else if (GICD_IPRIORITYR.contains(addr)) {
+ int first_intid = addr - GICD_IPRIORITYR.start();
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < size && int_id < itLines;
+ i++, int_id++) {
+ uint8_t prio = bits(data, (i + 1) * 8 - 1, (i * 8));
+
+ if (!DS && !is_secure_access) {
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ // RAZ/WI for non-secure accesses to secure interrupts
+ continue;
+ } else {
+ prio = 0x80 | (prio >> 1);
+ }
+ }
+
+ irqPriority[int_id] = prio;
+ DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d priority %d\n",
+ int_id, irqPriority[int_id]);
+ }
+
+ return;
+ } else if (GICD_ITARGETSR.contains(addr)) {
+ // Interrupt Processor Targets Registers
+ // ARE always on, RAZ/WI
+ warn("Gicv3Distributor::write(): "
+ "GICD_ITARGETSR is RAZ/WI, legacy not supported!\n");
+ return;
+ // Interrupt Configuration Registers
+ } else if (GICD_ICFGR.contains(addr)) {
+ /* Here only the odd bits are used; even bits are RES0 */
+ int first_intid = (addr - GICD_ICFGR.start()) * 4;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
+ i = i + 2, int_id++) {
+ irqConfig[int_id] = data & (0x2 << i) ?
+ Gicv3::INT_EDGE_TRIGGERED :
+ Gicv3::INT_LEVEL_SENSITIVE;
+ DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d config %d\n",
+ int_id, irqConfig[int_id]);
+ }
+
+ return;
+ } else if (GICD_IGRPMODR.contains(addr)) {
+ // Interrupt Group Modifier Registers
+ if (DS) {
+ return;
+ } else {
+ if (!is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return;
+ } else {
+ int first_intid = (addr - GICD_IGRPMODR.start()) * 8;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid;
+ i < 8 * size && int_id < itLines; i++, int_id++) {
+ irqGrpmod[int_id] = data & (0x1 << i);
+ }
+
+ return ;
+ }
+ }
+
+ // Non-secure Access Control Registers
+ } else if (GICD_NSACR.contains(addr)) {
+ // 2 bits per interrupt
+ int first_intid = (addr - GICD_NSACR.start()) * 4;
+
+ if (isNotSPI(first_intid)) {
+ return;
+ }
+
+ if (DS || (!DS && !is_secure_access)) {
+ return;
+ }
+
+ for (int i = 0, int_id = first_intid;
+ i < 8 * size && int_id < itLines; i = i + 2, int_id++) {
+ irqNsacr[int_id] = (data >> (2 * int_id)) & 0x3;
+ }
+
+ return;
+ } else if (GICD_IROUTER.contains(addr)) { // Interrupt Routing Registers
+ // 64 bit registers. 2 accesses.
+ int int_id = (addr - GICD_IROUTER.start()) / 8;
+
+ if (isNotSPI(int_id)) {
+ return;
+ }
+
+ if (nsAccessToSecInt(int_id, is_secure_access))
+ {
+ if (irqNsacr[int_id] < 3) {
+ // Group 0 or Secure Group 1 interrupts are RAZ/WI
+ return;
+ }
+ }
+
+ if (size == 4) {
+ if (addr & 7) { // high half of 64 bit register
+ irqAffinityRouting[int_id] =
+ (irqAffinityRouting[int_id] & 0xffffffff) | (data << 32);
+ } else { // low half of 64 bit register
+ irqAffinityRouting[int_id] =
+ (irqAffinityRouting[int_id] & 0xffffffff00000000) |
+ (data & 0xffffffff);
+ }
+ } else {
+ irqAffinityRouting[int_id] = data;
+ }
+
+ DPRINTF(GIC, "Gicv3Distributor::write(): "
+ "int_id %d GICD_IROUTER %#llx\n",
+ int_id, irqAffinityRouting[int_id]);
+ return;
+ }
+
+ switch (addr) {
+ case GICD_CTLR: // Control Register
+ if (DS) {
+ /*
+ * E1NWF [7]
+ * 1 of N wakeup functionality not supported, RAZ/WI
+ * DS [6] - RAO/WI
+ * ARE [4]
+ * affinity routing always on, no GICv2 legacy, RAO/WI
+ * EnableGrp1 [1]
+ * EnableGrp0 [0]
+ */
+ if ((data & (1 << 4)) == 0) {
+ warn("Gicv3Distributor::write(): "
+ "setting ARE to 0 is not supported!\n");
+ }
+
+ EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1NS;
+ EnableGrp0 = data & GICD_CTLR_ENABLEGRP0;
+ DPRINTF(GIC, "Gicv3Distributor::write(): (DS 1)"
+ "EnableGrp1NS %d EnableGrp0 %d\n",
+ EnableGrp1NS, EnableGrp0);
+ } else {
+ if (is_secure_access) {
+ /*
+ * E1NWF [7]
+ * 1 of N wakeup functionality not supported, RAZ/WI
+ * DS [6]
+ * ARE_NS [5]
+ * affinity routing always on, no GICv2 legacy, RAO/WI
+ * ARE_S [4]
+ * affinity routing always on, no GICv2 legacy, RAO/WI
+ * EnableGrp1S [2]
+ * EnableGrp1NS [1]
+ * EnableGrp0 [0]
+ */
+ if ((data & (1 << 5)) == 0) {
+ warn("Gicv3Distributor::write(): "
+ "setting ARE_NS to 0 is not supported!\n");
+ }
+
+ if ((data & (1 << 4)) == 0) {
+ warn("Gicv3Distributor::write(): "
+ "setting ARE_S to 0 is not supported!\n");
+ }
+
+ DS = data & GICD_CTLR_DS;
+ EnableGrp1S = data & GICD_CTLR_ENABLEGRP1S;
+ EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1NS;
+ EnableGrp0 = data & GICD_CTLR_ENABLEGRP0;
+ DPRINTF(GIC, "Gicv3Distributor::write(): (DS 0 secure)"
+ "DS %d "
+ "EnableGrp1S %d EnableGrp1NS %d EnableGrp0 %d\n",
+ DS, EnableGrp1S, EnableGrp1NS, EnableGrp0);
+
+ if (data & GICD_CTLR_DS) {
+ EnableGrp1S = 0;
+ }
+ } else {
+ /*
+ * ARE_NS [4] RAO/WI;
+ * EnableGrp1A [1] is a read-write alias of the Secure
+ * GICD_CTLR.EnableGrp1NS
+ * EnableGrp1 [0] RES0
+ */
+ if ((data & (1 << 4)) == 0) {
+ warn("Gicv3Distributor::write(): "
+ "setting ARE_NS to 0 is not supported!\n");
+ }
+
+ EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1A;
+ DPRINTF(GIC, "Gicv3Distributor::write(): (DS 0 non-secure)"
+ "EnableGrp1NS %d\n", EnableGrp1NS);
+ }
+ }
+
+ break;
+
+ default:
+ panic("Gicv3Distributor::write(): invalid offset %#x\n", addr);
+ break;
+ }
+}
+
+void
+Gicv3Distributor::sendInt(uint32_t int_id)
+{
+ panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+ panic_if(int_id > itLines, "Invalid SPI!");
+ irqPending[int_id] = true;
+ DPRINTF(GIC, "Gicv3Distributor::sendInt(): "
+ "int_id %d (SPI) pending bit set\n", int_id);
+ updateAndInformCPUInterfaces();
+}
+
+void
+Gicv3Distributor::intDeasserted(uint32_t int_id)
+{
+ panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+ panic_if(int_id > itLines, "Invalid SPI!");
+ irqPending[int_id] = false;
+ updateAndInformCPUInterfaces();
+}
+
+void
+Gicv3Distributor::updateAndInformCPUInterfaces()
+{
+ update();
+
+ for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+ gic->getCPUInterface(i)->update();
+ }
+}
+
+void
+Gicv3Distributor::fullUpdate()
+{
+ for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+ Gicv3CPUInterface * cpu_interface_i = gic->getCPUInterface(i);
+ cpu_interface_i->hppi.prio = 0xff;
+ }
+
+ update();
+
+ for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+ Gicv3Redistributor * redistributor_i = gic->getRedistributor(i);
+ redistributor_i->update();
+ }
+}
+
+void
+Gicv3Distributor::update()
+{
+ std::vector<bool> new_hppi(gic->getSystem()->numContexts(), false);
+
+ // Find the highest priority pending SPI
+ for (int int_id = Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id < itLines;
+ int_id++) {
+ Gicv3::GroupId int_group = getIntGroup(int_id);
+ bool group_enabled = groupEnabled(int_group);
+
+ if (irqPending[int_id] && irqEnabled[int_id] &&
+ !irqActive[int_id] && group_enabled) {
+ IROUTER affinity_routing = irqAffinityRouting[int_id];
+ Gicv3Redistributor * target_redistributor = nullptr;
+
+ if (affinity_routing.IRM) {
+ // Interrupts routed to any PE defined as a participating node
+ for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+ Gicv3Redistributor * redistributor_i =
+ gic->getRedistributor(i);
+
+ if (redistributor_i->
+ canBeSelectedFor1toNInterrupt(int_group)) {
+ target_redistributor = redistributor_i;
+ break;
+ }
+ }
+ } else {
+ uint32_t affinity = (affinity_routing.Aff3 << 24) |
+ (affinity_routing.Aff3 << 16) |
+ (affinity_routing.Aff1 << 8) |
+ (affinity_routing.Aff0 << 0);
+ target_redistributor =
+ gic->getRedistributorByAffinity(affinity);
+ }
+
+ if (!target_redistributor) {
+ // Interrrupts targeting not present cpus must remain pending
+ return;
+ }
+
+ Gicv3CPUInterface * target_cpu_interface =
+ target_redistributor->getCPUInterface();
+ uint32_t target_cpu = target_redistributor->cpuId;
+
+ if ((irqPriority[int_id] < target_cpu_interface->hppi.prio) ||
+ /*
+ * Multiple pending ints with same priority.
+ * Implementation choice which one to signal.
+ * Our implementation selects the one with the lower id.
+ */
+ (irqPriority[int_id] == target_cpu_interface->hppi.prio &&
+ int_id < target_cpu_interface->hppi.intid)) {
+ target_cpu_interface->hppi.intid = int_id;
+ target_cpu_interface->hppi.prio = irqPriority[int_id];
+ target_cpu_interface->hppi.group = int_group;
+ new_hppi[target_cpu] = true;
+ }
+ }
+ }
+
+ for (int i = 0; i < gic->getSystem()->numContexts(); i++) {
+ Gicv3Redistributor * redistributor_i = gic->getRedistributor(i);
+ Gicv3CPUInterface * cpu_interface_i =
+ redistributor_i->getCPUInterface();
+
+ if (!new_hppi[i] && cpu_interface_i->hppi.prio != 0xff &&
+ cpu_interface_i->hppi.intid >=
+ (Gicv3::SGI_MAX + Gicv3::PPI_MAX) &&
+ cpu_interface_i->hppi.intid < Gicv3::INTID_SECURE) {
+ fullUpdate();
+ }
+ }
+}
+
+Gicv3::IntStatus
+Gicv3Distributor::intStatus(uint32_t int_id)
+{
+ panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+ panic_if(int_id > itLines, "Invalid SPI!");
+
+ if (irqPending[int_id]) {
+ if (irqActive[int_id]) {
+ return Gicv3::INT_ACTIVE_PENDING;
+ }
+
+ return Gicv3::INT_PENDING;
+ } else if (irqActive[int_id]) {
+ return Gicv3::INT_ACTIVE;
+ } else {
+ return Gicv3::INT_INACTIVE;
+ }
+}
+
+Gicv3::GroupId
+Gicv3Distributor::getIntGroup(int int_id)
+{
+ panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
+ panic_if(int_id > itLines, "Invalid SPI!");
+
+ if (DS) {
+ if (irqGroup[int_id] == 1) {
+ return Gicv3::G1NS;
+ } else {
+ return Gicv3::G0S;
+ }
+ } else {
+ if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 0) {
+ return Gicv3::G0S;
+ } else if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 1) {
+ return Gicv3::G1NS;
+ } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 0) {
+ return Gicv3::G1S;
+ } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 1) {
+ return Gicv3::G1NS;
+ }
+ }
+
+ M5_UNREACHABLE;
+}
+
+void
+Gicv3Distributor::activateIRQ(uint32_t int_id)
+{
+ irqPending[int_id] = false;
+ irqActive[int_id] = true;
+}
+
+void
+Gicv3Distributor::deactivateIRQ(uint32_t int_id)
+{
+ irqActive[int_id] = false;
+}
+
+void
+Gicv3Distributor::serialize(CheckpointOut & cp) const
+{
+ SERIALIZE_SCALAR(ARE);
+ SERIALIZE_SCALAR(DS);
+ SERIALIZE_SCALAR(EnableGrp1S);
+ SERIALIZE_SCALAR(EnableGrp1NS);
+ SERIALIZE_SCALAR(EnableGrp0);
+ SERIALIZE_CONTAINER(irqGroup);
+ SERIALIZE_CONTAINER(irqEnabled);
+ SERIALIZE_CONTAINER(irqPending);
+ SERIALIZE_CONTAINER(irqActive);
+ SERIALIZE_CONTAINER(irqPriority);
+ SERIALIZE_CONTAINER(irqConfig);
+ SERIALIZE_CONTAINER(irqGrpmod);
+ SERIALIZE_CONTAINER(irqNsacr);
+ SERIALIZE_CONTAINER(irqAffinityRouting);
+}
+
+void
+Gicv3Distributor::unserialize(CheckpointIn & cp)
+{
+ UNSERIALIZE_SCALAR(ARE);
+ UNSERIALIZE_SCALAR(DS);
+ UNSERIALIZE_SCALAR(EnableGrp1S);
+ UNSERIALIZE_SCALAR(EnableGrp1NS);
+ UNSERIALIZE_SCALAR(EnableGrp0);
+ UNSERIALIZE_CONTAINER(irqGroup);
+ UNSERIALIZE_CONTAINER(irqEnabled);
+ UNSERIALIZE_CONTAINER(irqPending);
+ UNSERIALIZE_CONTAINER(irqActive);
+ UNSERIALIZE_CONTAINER(irqPriority);
+ UNSERIALIZE_CONTAINER(irqConfig);
+ UNSERIALIZE_CONTAINER(irqGrpmod);
+ UNSERIALIZE_CONTAINER(irqNsacr);
+ UNSERIALIZE_CONTAINER(irqAffinityRouting);
+}
diff --git a/src/dev/arm/gic_v3_distributor.hh b/src/dev/arm/gic_v3_distributor.hh
new file mode 100644
index 000000000..334eceee5
--- /dev/null
+++ b/src/dev/arm/gic_v3_distributor.hh
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_DISTRIBUTOR_H__
+#define __DEV_ARM_GICV3_DISTRIBUTOR_H__
+
+#include "base/addr_range.hh"
+#include "dev/arm/gic_v3.hh"
+#include "sim/serialize.hh"
+
+class Gicv3Distributor : public Serializable
+{
+ private:
+
+ friend class Gicv3Redistributor;
+ friend class Gicv3CPUInterface;
+
+ protected:
+
+ Gicv3 * gic;
+ const uint32_t itLines;
+
+ enum {
+ // Control Register
+ GICD_CTLR = 0x0000,
+ // Interrupt Controller Type Register
+ GICD_TYPER = 0x0004,
+ // Implementer Identification Register
+ GICD_IIDR = 0x0008,
+ // Error Reporting Status Register
+ GICD_STATUSR = 0x0010,
+ // Peripheral ID0 Register
+ GICD_PIDR0 = 0xffe0,
+ // Peripheral ID1 Register
+ GICD_PIDR1 = 0xffe4,
+ // Peripheral ID2 Register
+ GICD_PIDR2 = 0xffe8,
+ // Peripheral ID3 Register
+ GICD_PIDR3 = 0xffec,
+ // Peripheral ID4 Register
+ GICD_PIDR4 = 0xffd0,
+ // Peripheral ID5 Register
+ GICD_PIDR5 = 0xffd4,
+ // Peripheral ID6 Register
+ GICD_PIDR6 = 0xffd8,
+ // Peripheral ID7 Register
+ GICD_PIDR7 = 0xffdc,
+ };
+
+ // Interrupt Group Registers
+ static const AddrRange GICD_IGROUPR;
+ // Interrupt Set-Enable Registers
+ static const AddrRange GICD_ISENABLER;
+ // Interrupt Clear-Enable Registers
+ static const AddrRange GICD_ICENABLER;
+ // Interrupt Set-Pending Registers
+ static const AddrRange GICD_ISPENDR;
+ // Interrupt Clear-Pending Registers
+ static const AddrRange GICD_ICPENDR;
+ // Interrupt Set-Active Registers
+ static const AddrRange GICD_ISACTIVER;
+ // Interrupt Clear-Active Registers
+ static const AddrRange GICD_ICACTIVER;
+ // Interrupt Priority Registers
+ static const AddrRange GICD_IPRIORITYR;
+ // Interrupt Processor Targets Registers
+ static const AddrRange GICD_ITARGETSR; // GICv2 legacy
+ // Interrupt Configuration Registers
+ static const AddrRange GICD_ICFGR;
+ // Interrupt Group Modifier Registers
+ static const AddrRange GICD_IGRPMODR;
+ // Non-secure Access Control Registers
+ static const AddrRange GICD_NSACR;
+ // SGI Clear-Pending Registers
+ static const AddrRange GICD_CPENDSGIR; // GICv2 legacy
+ // SGI Set-Pending Registers
+ static const AddrRange GICD_SPENDSGIR; // GICv2 legacy
+ // Interrupt Routing Registers
+ static const AddrRange GICD_IROUTER;
+
+ BitUnion64(IROUTER)
+ Bitfield<63, 40> res0_1;
+ Bitfield<39, 32> Aff3;
+ Bitfield<31> IRM;
+ Bitfield<30, 24> res0_2;
+ Bitfield<23, 16> Aff2;
+ Bitfield<15, 8> Aff1;
+ Bitfield<7, 0> Aff0;
+ EndBitUnion(IROUTER)
+
+ static const uint32_t GICD_CTLR_ENABLEGRP0 = 1 << 0;
+ static const uint32_t GICD_CTLR_ENABLEGRP1NS = 1 << 1;
+ static const uint32_t GICD_CTLR_ENABLEGRP1S = 1 << 2;
+ static const uint32_t GICD_CTLR_ENABLEGRP1 = 1 << 0;
+ static const uint32_t GICD_CTLR_ENABLEGRP1A = 1 << 1;
+ static const uint32_t GICD_CTLR_DS = 1 << 6;
+
+ bool ARE;
+ bool DS;
+ bool EnableGrp1S;
+ bool EnableGrp1NS;
+ bool EnableGrp0;
+ std::vector <uint8_t> irqGroup;
+ std::vector <bool> irqEnabled;
+ std::vector <bool> irqPending;
+ std::vector <bool> irqActive;
+ std::vector <uint8_t> irqPriority;
+ std::vector <Gicv3::IntTriggerType> irqConfig;
+ std::vector <uint8_t> irqGrpmod;
+ std::vector <uint8_t> irqNsacr;
+ std::vector <IROUTER> irqAffinityRouting;
+
+ public:
+
+ static const uint32_t ADDR_RANGE_SIZE = 0x10000;
+
+ Gicv3Distributor(Gicv3 * gic, uint32_t it_lines);
+ ~Gicv3Distributor();
+ void init();
+ void initState();
+
+ uint64_t read(Addr addr, size_t size, bool is_secure_access);
+ void write(Addr addr, uint64_t data, size_t size,
+ bool is_secure_access);
+ void serialize(CheckpointOut & cp) const override;
+ void unserialize(CheckpointIn & cp) override;
+
+ bool
+ groupEnabled(Gicv3::GroupId group)
+ {
+ if (DS == 0) {
+ switch (group) {
+ case Gicv3::G0S:
+ return EnableGrp0;
+
+ case Gicv3::G1S:
+ return EnableGrp1S;
+
+ case Gicv3::G1NS:
+ return EnableGrp1NS;
+
+ default:
+ panic("Gicv3Distributor::groupEnabled(): "
+ "invalid group!\n");
+ }
+ } else {
+ switch (group) {
+ case Gicv3::G0S:
+ return EnableGrp0;
+
+ case Gicv3::G1S:
+ case Gicv3::G1NS:
+ return EnableGrp1NS;
+
+ default:
+ panic("Gicv3Distributor::groupEnabled(): "
+ "invalid group!\n");
+ }
+ }
+ }
+
+ void sendInt(uint32_t int_id);
+ void intDeasserted(uint32_t int_id);
+ Gicv3::IntStatus intStatus(uint32_t int_id);
+ void updateAndInformCPUInterfaces();
+ void update();
+ void fullUpdate();
+ void activateIRQ(uint32_t int_id);
+ void deactivateIRQ(uint32_t int_id);
+
+ inline bool isNotSPI(uint8_t int_id)
+ {
+ if (int_id < (Gicv3::SGI_MAX + Gicv3::PPI_MAX) || int_id >= itLines) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline bool nsAccessToSecInt(uint8_t int_id, bool is_secure_access)
+ {
+ return !DS && !is_secure_access && getIntGroup(int_id) != Gicv3::G1NS;
+ }
+
+ protected:
+
+ void reset();
+ Gicv3::GroupId getIntGroup(int int_id);
+};
+
+#endif //__DEV_ARM_GICV3_DISTRIBUTOR_H__
diff --git a/src/dev/arm/gic_v3_redistributor.cc b/src/dev/arm/gic_v3_redistributor.cc
new file mode 100644
index 000000000..ec1e388c7
--- /dev/null
+++ b/src/dev/arm/gic_v3_redistributor.cc
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#include "dev/arm/gic_v3_redistributor.hh"
+
+#include "arch/arm/utility.hh"
+#include "debug/GIC.hh"
+#include "dev/arm/gic_v3_cpu_interface.hh"
+#include "dev/arm/gic_v3_distributor.hh"
+
+const AddrRange Gicv3Redistributor::GICR_IPRIORITYR(SGI_base + 0x0400,
+ SGI_base + 0x041f);
+
+Gicv3Redistributor::Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id)
+ : gic(gic),
+ distributor(nullptr),
+ cpuInterface(nullptr),
+ cpuId(cpu_id),
+ irqGroup(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqEnabled(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqPending(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqActive(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqPriority(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqConfig(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqGrpmod(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ irqNsacr(Gicv3::SGI_MAX + Gicv3::PPI_MAX)
+{
+}
+
+Gicv3Redistributor::~Gicv3Redistributor()
+{
+}
+
+void
+Gicv3Redistributor::init()
+{
+ distributor = gic->getDistributor();
+ cpuInterface = gic->getCPUInterface(cpuId);
+}
+
+void
+Gicv3Redistributor::initState()
+{
+ reset();
+}
+
+void
+Gicv3Redistributor::reset()
+{
+ peInLowPowerState = true;
+ std::fill(irqGroup.begin(), irqGroup.end(), 0);
+ std::fill(irqEnabled.begin(), irqEnabled.end(), false);
+ std::fill(irqPending.begin(), irqPending.end(), false);
+ std::fill(irqActive.begin(), irqActive.end(), false);
+ std::fill(irqPriority.begin(), irqPriority.end(), 0);
+
+ // SGIs have edge-triggered behavior
+ for (uint32_t int_id = 0; int_id < Gicv3::SGI_MAX; int_id++) {
+ irqConfig[int_id] = Gicv3::INT_EDGE_TRIGGERED;
+ }
+
+ std::fill(irqGrpmod.begin(), irqGrpmod.end(), 0);
+ std::fill(irqNsacr.begin(), irqNsacr.end(), 0);
+ DPG1S = false;
+ DPG1NS = false;
+ DPG0 = false;
+}
+
+uint64_t
+Gicv3Redistributor::read(Addr addr, size_t size, bool is_secure_access)
+{
+ if (GICR_IPRIORITYR.contains(addr)) { // Interrupt Priority Registers
+ uint64_t value = 0;
+ int first_intid = addr - GICR_IPRIORITYR.start();
+
+ for (int i = 0, int_id = first_intid; i < size; i++, int_id++) {
+ uint8_t prio = irqPriority[int_id];
+
+ if (!distributor->DS && !is_secure_access) {
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ continue;
+ } else {
+ // NS view
+ prio = (prio << 1) & 0xff;
+ }
+ }
+
+ value |= prio << (i * 8);
+ }
+
+ return value;
+ }
+
+ switch (addr) {
+ case GICR_CTLR: { // Control Register
+ uint64_t value = 0;
+
+ if (DPG1S) {
+ value |= GICR_CTLR_DPG1S;
+ }
+
+ if (DPG1NS) {
+ value |= GICR_CTLR_DPG1NS;
+ }
+
+ if (DPG0) {
+ value |= GICR_CTLR_DPG0;
+ }
+
+ return value;
+ }
+
+ case GICR_IIDR: // Implementer Identification Register
+ //return 0x43b; // r0p0 GIC-500
+ return 0;
+
+ case GICR_TYPER: { // Type Register
+ /*
+ * Affinity_Value [63:32] == X
+ * (The identity of the PE associated with this Redistributor)
+ * CommonLPIAff [25:24] == 01
+ * (All Redistributors with the same Aff3 value must share an
+ * LPI Configuration table)
+ * Processor_Number [23:8] == X
+ * (A unique identifier for the PE)
+ * DPGS [5] == 1
+ * (GICR_CTLR.DPG* bits are supported)
+ * Last [4] == X
+ * (This Redistributor is the highest-numbered Redistributor in
+ * a series of contiguous Redistributor pages)
+ * DirectLPI [3] == 0
+ * (direct injection of LPIs not supported)
+ * VLPIS [1] == 0
+ * (virtual LPIs not supported)
+ * PLPIS [0] == 0
+ * (physical LPIs not supported)
+ */
+ uint64_t affinity = getAffinity();
+ int last = cpuId == (gic->getSystem()->numContexts() - 1);
+ return (affinity << 32) | (1 << 24) | (cpuId << 8) |
+ (1 << 5) | (last << 4);
+ }
+
+ case GICR_WAKER: // Wake Register
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return 0;
+ }
+
+ if (peInLowPowerState) {
+ return GICR_WAKER_ChildrenAsleep | GICR_WAKER_ProcessorSleep;
+ } else {
+ return 0;
+ }
+
+ case GICR_PIDR0: { // Peripheral ID0 Register
+ uint8_t part_0 = 0x92; // Part number, bits[7:0]
+ return part_0;
+ }
+
+ case GICR_PIDR1: { // Peripheral ID1 Register
+ uint8_t des_0 = 0xB; // JEP106 identification code, bits[3:0]
+ uint8_t part_1 = 0x4; // Part number, bits[11:8]
+ return (des_0 << 4) | (part_1 << 0);
+ }
+
+ case GICR_PIDR2: { // Peripheral ID2 Register
+ uint8_t arch_rev = 0x3; // 0x3 GICv3
+ uint8_t jedec = 0x1; // JEP code
+ uint8_t des_1 = 0x3; // JEP106 identification code, bits[6:4]
+ return (arch_rev << 4) | (jedec << 3) | (des_1 << 0);
+ }
+
+ case GICR_PIDR3: // Peripheral ID3 Register
+ return 0x0; // Implementation defined
+
+ case GICR_PIDR4: { // Peripheral ID4 Register
+ uint8_t size = 0x4; // 64 KB software visible page
+ uint8_t des_2 = 0x4; // ARM implementation
+ return (size << 4) | (des_2 << 0);
+ }
+
+ case GICR_PIDR5: // Peripheral ID5 Register
+ case GICR_PIDR6: // Peripheral ID6 Register
+ case GICR_PIDR7: // Peripheral ID7 Register
+ return 0; // RES0
+
+ case GICR_IGROUPR0: { // Interrupt Group Register 0
+ uint64_t value = 0;
+
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return 0;
+ }
+
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ value |= (irqGroup[int_id] << int_id);
+ }
+
+ return value;
+ }
+
+ case GICR_ISENABLER0: // Interrupt Set-Enable Register 0
+ case GICR_ICENABLER0: { // Interrupt Clear-Enable Register 0
+ uint64_t value = 0;
+
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ if (irqEnabled[int_id]) {
+ value |= (1 << int_id);
+ }
+ }
+
+ return value;
+ }
+
+ case GICR_ISPENDR0: // Interrupt Set-Pending Register 0
+ case GICR_ICPENDR0: { // Interrupt Clear-Pending Register 0
+ uint64_t value = 0;
+
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ value |= (irqPending[int_id] << int_id);
+ }
+
+ return value;
+ }
+
+ case GICR_ISACTIVER0: // Interrupt Set-Active Register 0
+ case GICR_ICACTIVER0: { // Interrupt Clear-Active Register 0
+ uint64_t value = 0;
+
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ value |= irqActive[int_id] << int_id;
+ }
+
+ return value;
+ }
+
+ case GICR_ICFGR0: // SGI Configuration Register
+ case GICR_ICFGR1: { // PPI Configuration Register
+ uint64_t value = 0;
+ uint32_t first_int_id = addr == GICR_ICFGR0 ? 0 : Gicv3::SGI_MAX;
+
+ for (int i = 0, int_id = first_int_id; i < 32;
+ i = i + 2, int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ if (irqConfig[int_id] == Gicv3::INT_EDGE_TRIGGERED) {
+ value |= (0x2) << i;
+ }
+ }
+
+ return value;
+ }
+
+ case GICR_IGRPMODR0: { // Interrupt Group Modifier Register 0
+ uint64_t value = 0;
+
+ if (distributor->DS) {
+ value = 0;
+ } else {
+ if (!is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ value = 0;
+ } else {
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ value |= irqGrpmod[int_id] << int_id;
+ }
+ }
+ }
+
+ return value;
+ }
+
+ case GICR_NSACR: { // Non-secure Access Control Register
+ uint64_t value = 0;
+
+ if (distributor->DS) {
+ // RAZ/WI
+ value = 0;
+ } else {
+ if (!is_secure_access) {
+ // RAZ/WI
+ value = 0;
+ } else {
+ for (int i = 0, int_id = 0; i < 8 * size;
+ i = i + 2, int_id++) {
+ value |= irqNsacr[int_id] << i;
+ }
+ }
+ }
+
+ return value;
+ }
+
+ default:
+ panic("Gicv3Redistributor::read(): invalid offset %#x\n", addr);
+ break;
+ }
+}
+
+void
+Gicv3Redistributor::write(Addr addr, uint64_t data, size_t size,
+ bool is_secure_access)
+{
+ if (GICR_IPRIORITYR.contains(addr)) { // Interrupt Priority Registers
+ int first_intid = addr - GICR_IPRIORITYR.start();
+
+ for (int i = 0, int_id = first_intid; i < size; i++, int_id++) {
+ uint8_t prio = bits(data, (i + 1) * 8 - 1, (i * 8));
+
+ if (!distributor->DS && !is_secure_access) {
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ continue;
+ } else {
+ // NS view
+ prio = 0x80 | (prio >> 1);
+ }
+ }
+
+ irqPriority[int_id] = prio;
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d priority %d\n", int_id, irqPriority[int_id]);
+ }
+
+ return;
+ }
+
+ switch (addr) {
+ case GICR_CTLR: {
+ // GICR_TYPER.LPIS is 0 so Enable_LPIs is RES0
+ DPG1S = data & GICR_CTLR_DPG1S;
+ DPG1NS = data & GICR_CTLR_DPG1NS;
+ DPG0 = data & GICR_CTLR_DPG0;
+ break;
+ }
+
+ case GICR_WAKER: // Wake Register
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return;
+ }
+
+ if (not peInLowPowerState and
+ (data & GICR_WAKER_ProcessorSleep)) {
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "PE entering in low power state\n");
+ } else if (peInLowPowerState and
+ not(data & GICR_WAKER_ProcessorSleep)) {
+ DPRINTF(GIC, "Gicv3Redistributor::write(): powering up PE\n");
+ }
+
+ peInLowPowerState = data & GICR_WAKER_ProcessorSleep;
+ break;
+
+ case GICR_IGROUPR0: // Interrupt Group Register 0
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ return;
+ }
+
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ irqGroup[int_id] = data & (1 << int_id) ? 1 : 0;
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d group %d\n", int_id, irqGroup[int_id]);
+ }
+
+ break;
+
+ case GICR_ISENABLER0: // Interrupt Set-Enable Register 0
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ bool enable = data & (1 << int_id) ? 1 : 0;
+
+ if (enable) {
+ irqEnabled[int_id] = true;
+ }
+
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d enable %i\n", int_id, irqEnabled[int_id]);
+ }
+
+ break;
+
+ case GICR_ICENABLER0: // Interrupt Clear-Enable Register 0
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ bool disable = data & (1 << int_id) ? 1 : 0;
+
+ if (disable) {
+ irqEnabled[int_id] = false;
+ }
+
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d enable %i\n", int_id, irqEnabled[int_id]);
+ }
+
+ break;
+
+ case GICR_ISPENDR0: // Interrupt Set-Pending Register 0
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ bool pending = data & (1 << int_id) ? 1 : 0;
+
+ if (pending) {
+ DPRINTF(GIC, "Gicv3Redistributor::write() "
+ "(GICR_ISPENDR0): int_id %d (PPI) "
+ "pending bit set\n", int_id);
+ irqPending[int_id] = true;
+ }
+ }
+
+ updateAndInformCPUInterface();
+ break;
+
+ case GICR_ICPENDR0:// Interrupt Clear-Pending Register 0
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ bool clear = data & (1 << int_id) ? 1 : 0;
+
+ if (clear) {
+ irqPending[int_id] = false;
+ }
+ }
+
+ break;
+
+ case GICR_ISACTIVER0: // Interrupt Set-Active Register 0
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ bool activate = data & (1 << int_id) ? 1 : 0;
+
+ if (activate) {
+ if (!irqActive[int_id]) {
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d active set\n", int_id);
+ }
+
+ irqActive[int_id] = true;
+ }
+ }
+
+ break;
+
+ case GICR_ICACTIVER0: // Interrupt Clear-Active Register 0
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ bool clear = data & (1 << int_id) ? 1 : 0;
+
+ if (clear) {
+ if (irqActive[int_id]) {
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d active cleared\n", int_id);
+ }
+
+ irqActive[int_id] = false;
+ }
+ }
+
+ break;
+
+ case GICR_ICFGR1: { // PPI Configuration Register
+ int first_intid = Gicv3::SGI_MAX;
+
+ for (int i = 0, int_id = first_intid; i < 8 * size;
+ i = i + 2, int_id++) {
+ if (!distributor->DS && !is_secure_access) {
+ // RAZ/WI for non-secure accesses for secure interrupts
+ if (getIntGroup(int_id) != Gicv3::G1NS) {
+ continue;
+ }
+ }
+
+ irqConfig[int_id] = data & (0x2 << i)
+ ? Gicv3::INT_EDGE_TRIGGERED :
+ Gicv3::INT_LEVEL_SENSITIVE;
+ DPRINTF(GIC, "Gicv3Redistributor::write(): "
+ "int_id %d (PPI) config %d\n",
+ int_id, irqConfig[int_id]);
+ }
+
+ break;
+ }
+
+ case GICR_IGRPMODR0: { // Interrupt Group Modifier Register 0
+ if (distributor->DS) {
+ // RAZ/WI if secutiry disabled
+ } else {
+ for (int int_id = 0; int_id < 8 * size; int_id++) {
+ if (!is_secure_access) {
+ // RAZ/WI for non-secure accesses
+ continue;
+ }
+
+ irqGrpmod[int_id] = data & (1 << int_id);
+ }
+ }
+
+ break;
+ }
+
+ case GICR_NSACR: { // Non-secure Access Control Register
+ if (distributor->DS) {
+ // RAZ/WI
+ } else {
+ if (!is_secure_access) {
+ // RAZ/WI
+ } else {
+ for (int i = 0, int_id = 0; i < 8 * size;
+ i = i + 2, int_id++) {
+ irqNsacr[int_id] = (data >> i) & 0x3;
+ }
+ }
+ }
+
+ break;
+ }
+
+ default:
+ panic("Gicv3Redistributor::write(): invalid offset %#x\n", addr);
+ break;
+ }
+}
+
+void
+Gicv3Redistributor::sendPPInt(uint32_t int_id)
+{
+ assert((int_id >= Gicv3::SGI_MAX) &&
+ (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX));
+ irqPending[int_id] = true;
+ DPRINTF(GIC, "Gicv3Redistributor::sendPPInt(): "
+ "int_id %d (PPI) pending bit set\n", int_id);
+ updateAndInformCPUInterface();
+}
+
+void
+Gicv3Redistributor::sendSGI(uint32_t int_id, Gicv3::GroupId group, bool ns)
+{
+ assert(int_id < Gicv3::SGI_MAX);
+ Gicv3::GroupId int_group = getIntGroup(int_id);
+
+ // asked for secure group 1
+ // configured as group 0
+ // send group 0
+ if (int_group == Gicv3::G0S && group == Gicv3::G1S) {
+ group = Gicv3::G0S;
+ }
+
+ if (group == Gicv3::G0S and int_group != Gicv3::G0S) {
+ return;
+ }
+
+ if (ns && distributor->DS == 0) {
+ int nsaccess = irqNsacr[int_id];
+
+ if ((int_group == Gicv3::G0S && nsaccess < 1) ||
+ (int_group == Gicv3::G1S && nsaccess < 2)) {
+ return;
+ }
+ }
+
+ irqPending[int_id] = true;
+ DPRINTF(GIC, "Gicv3ReDistributor::sendSGI(): "
+ "int_id %d (SGI) pending bit set\n", int_id);
+ updateAndInformCPUInterface();
+}
+
+Gicv3::IntStatus
+Gicv3Redistributor::intStatus(uint32_t int_id)
+{
+ assert(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX);
+
+ if (irqPending[int_id]) {
+ if (irqActive[int_id]) {
+ return Gicv3::INT_ACTIVE_PENDING;
+ }
+
+ return Gicv3::INT_PENDING;
+ } else if (irqActive[int_id]) {
+ return Gicv3::INT_ACTIVE;
+ } else {
+ return Gicv3::INT_INACTIVE;
+ }
+}
+
+/*
+ * Recalculate the highest priority pending interrupt after a
+ * change to redistributor state.
+ */
+void
+Gicv3Redistributor::update()
+{
+ bool new_hppi = false;
+
+ for (int int_id = 0; int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id++) {
+ Gicv3::GroupId int_group = getIntGroup(int_id);
+ bool group_enabled = distributor->groupEnabled(int_group);
+
+ if (irqPending[int_id] && irqEnabled[int_id] &&
+ !irqActive[int_id] && group_enabled) {
+ if ((irqPriority[int_id] < cpuInterface->hppi.prio) ||
+ /*
+ * Multiple pending ints with same priority.
+ * Implementation choice which one to signal.
+ * Our implementation selects the one with the lower id.
+ */
+ (irqPriority[int_id] == cpuInterface->hppi.prio &&
+ int_id < cpuInterface->hppi.intid)) {
+ cpuInterface->hppi.intid = int_id;
+ cpuInterface->hppi.prio = irqPriority[int_id];
+ cpuInterface->hppi.group = int_group;
+ new_hppi = true;
+ }
+ }
+ }
+
+ if (!new_hppi && cpuInterface->hppi.prio != 0xff &&
+ cpuInterface->hppi.intid < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
+ distributor->fullUpdate();
+ }
+}
+
+void
+Gicv3Redistributor::updateAndInformCPUInterface()
+{
+ update();
+ cpuInterface->update();
+}
+
+Gicv3::GroupId
+Gicv3Redistributor::getIntGroup(int int_id)
+{
+ assert(int_id < (Gicv3::SGI_MAX + Gicv3::PPI_MAX));
+
+ if (distributor->DS) {
+ if (irqGroup[int_id] == 0) {
+ return Gicv3::G0S;
+ } else {
+ return Gicv3::G1NS;
+ }
+ } else {
+ if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 0) {
+ return Gicv3::G0S;
+ } else if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 1) {
+ return Gicv3::G1NS;
+ } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 0) {
+ return Gicv3::G1S;
+ } else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 1) {
+ return Gicv3::G1NS;
+ }
+ }
+
+ M5_UNREACHABLE;
+}
+
+void
+Gicv3Redistributor::activateIRQ(uint32_t int_id)
+{
+ irqPending[int_id] = false;
+ irqActive[int_id] = true;
+}
+
+void
+Gicv3Redistributor::deactivateIRQ(uint32_t int_id)
+{
+ irqActive[int_id] = false;
+}
+
+uint32_t
+Gicv3Redistributor::getAffinity()
+{
+ ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
+ uint64_t mpidr = getMPIDR(gic->getSystem(), tc);
+ /*
+ * Aff3 = MPIDR[39:32]
+ * (Note getMPIDR() returns uint32_t so Aff3 is always 0...)
+ * Aff2 = MPIDR[23:16]
+ * Aff1 = MPIDR[15:8]
+ * Aff0 = MPIDR[7:0]
+ * affinity = Aff3.Aff2.Aff1.Aff0
+ */
+ uint64_t affinity = ((mpidr & 0xff00000000) >> 8) | (mpidr & (0xffffff));
+ return affinity;
+}
+
+bool
+Gicv3Redistributor::canBeSelectedFor1toNInterrupt(Gicv3::GroupId group)
+{
+ if (peInLowPowerState) {
+ return false;
+ }
+
+ if (!distributor->groupEnabled(group)) {
+ return false;
+ }
+
+ if ((group == Gicv3::G1S) && DPG1S) {
+ return false;
+ }
+
+ if ((group == Gicv3::G1NS) && DPG1NS) {
+ return false;
+ }
+
+ if ((group == Gicv3::G0S) && DPG0) {
+ return false;
+ }
+
+ return true;
+}
+
+void
+Gicv3Redistributor::serialize(CheckpointOut & cp) const
+{
+ SERIALIZE_SCALAR(peInLowPowerState);
+ SERIALIZE_CONTAINER(irqGroup);
+ SERIALIZE_CONTAINER(irqEnabled);
+ SERIALIZE_CONTAINER(irqPending);
+ SERIALIZE_CONTAINER(irqActive);
+ SERIALIZE_CONTAINER(irqPriority);
+ SERIALIZE_CONTAINER(irqConfig);
+ SERIALIZE_CONTAINER(irqGrpmod);
+ SERIALIZE_CONTAINER(irqNsacr);
+ SERIALIZE_SCALAR(DPG1S);
+ SERIALIZE_SCALAR(DPG1NS);
+ SERIALIZE_SCALAR(DPG0);
+}
+
+void
+Gicv3Redistributor::unserialize(CheckpointIn & cp)
+{
+ UNSERIALIZE_SCALAR(peInLowPowerState);
+ UNSERIALIZE_CONTAINER(irqGroup);
+ UNSERIALIZE_CONTAINER(irqEnabled);
+ UNSERIALIZE_CONTAINER(irqPending);
+ UNSERIALIZE_CONTAINER(irqActive);
+ UNSERIALIZE_CONTAINER(irqPriority);
+ UNSERIALIZE_CONTAINER(irqConfig);
+ UNSERIALIZE_CONTAINER(irqGrpmod);
+ UNSERIALIZE_CONTAINER(irqNsacr);
+ UNSERIALIZE_SCALAR(DPG1S);
+ UNSERIALIZE_SCALAR(DPG1NS);
+ UNSERIALIZE_SCALAR(DPG0);
+}
diff --git a/src/dev/arm/gic_v3_redistributor.hh b/src/dev/arm/gic_v3_redistributor.hh
new file mode 100644
index 000000000..11553974a
--- /dev/null
+++ b/src/dev/arm/gic_v3_redistributor.hh
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2018 Metempsy Technology Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Jairo Balart
+ */
+
+#ifndef __DEV_ARM_GICV3_REDISTRIBUTOR_H__
+#define __DEV_ARM_GICV3_REDISTRIBUTOR_H__
+
+#include "base/addr_range.hh"
+#include "dev/arm/gic_v3.hh"
+#include "sim/serialize.hh"
+
+class Gicv3Distributor;
+class Gicv3CPUInterface;
+
+class Gicv3Redistributor : public Serializable
+{
+ private:
+
+ friend class Gicv3CPUInterface;
+ friend class Gicv3Distributor;
+
+ protected:
+
+ Gicv3 * gic;
+ Gicv3Distributor * distributor;
+ Gicv3CPUInterface * cpuInterface;
+ uint32_t cpuId;
+
+ /*
+ * GICv3 defines 2 contiguous 64KB frames for each redistributor.
+ * Order of frames must be RD_base, SGI_base.
+ */
+ static const uint32_t RD_base = 0x0;
+ static const uint32_t SGI_base = 0x10000;
+
+ enum {
+ // Control Register
+ GICR_CTLR = RD_base + 0x0000,
+ // Implementer Identification Register
+ GICR_IIDR = RD_base + 0x0004,
+ // Type Register
+ GICR_TYPER = RD_base + 0x0008,
+ // Wake Register
+ GICR_WAKER = RD_base + 0x0014,
+ // Peripheral ID0 Register
+ GICR_PIDR0 = RD_base + 0xffe0,
+ // Peripheral ID1 Register
+ GICR_PIDR1 = RD_base + 0xffe4,
+ // Peripheral ID2 Register
+ GICR_PIDR2 = RD_base + 0xffe8,
+ // Peripheral ID3 Register
+ GICR_PIDR3 = RD_base + 0xffec,
+ // Peripheral ID4 Register
+ GICR_PIDR4 = RD_base + 0xffd0,
+ // Peripheral ID5 Register
+ GICR_PIDR5 = RD_base + 0xffd4,
+ // Peripheral ID6 Register
+ GICR_PIDR6 = RD_base + 0xffd8,
+ // Peripheral ID7 Register
+ GICR_PIDR7 = RD_base + 0xffdc,
+ };
+
+ static const uint32_t GICR_WAKER_ProcessorSleep = 1 << 1;
+ static const uint32_t GICR_WAKER_ChildrenAsleep = 1 << 2;
+
+ bool peInLowPowerState;
+
+ enum {
+ // Interrupt Group Register 0
+ GICR_IGROUPR0 = SGI_base + 0x0080,
+ // Interrupt Set-Enable Register 0
+ GICR_ISENABLER0 = SGI_base + 0x0100,
+ // Interrupt Clear-Enable Register 0
+ GICR_ICENABLER0 = SGI_base + 0x0180,
+ // Interrupt Set-Pending Register 0
+ GICR_ISPENDR0 = SGI_base + 0x0200,
+ // Interrupt Clear-Pending Register 0
+ GICR_ICPENDR0 = SGI_base + 0x0280,
+ // Interrupt Set-Active Register 0
+ GICR_ISACTIVER0 = SGI_base + 0x0300,
+ // Interrupt Clear-Active Register 0
+ GICR_ICACTIVER0 = SGI_base + 0x0380,
+ // SGI Configuration Register
+ GICR_ICFGR0 = SGI_base + 0x0c00,
+ // PPI Configuration Register
+ GICR_ICFGR1 = SGI_base + 0x0c04,
+ // Interrupt Group Modifier Register 0
+ GICR_IGRPMODR0 = SGI_base + 0x0d00,
+ // Non-secure Access Control Register
+ GICR_NSACR = SGI_base + 0x0e00,
+ };
+
+ // Interrupt Priority Registers
+ static const AddrRange GICR_IPRIORITYR;
+
+ std::vector <uint8_t> irqGroup;
+ std::vector <bool> irqEnabled;
+ std::vector <bool> irqPending;
+ std::vector <bool> irqActive;
+ std::vector <uint8_t> irqPriority;
+ std::vector <Gicv3::IntTriggerType> irqConfig;
+ std::vector <uint8_t> irqGrpmod;
+ std::vector <uint8_t> irqNsacr;
+
+ bool DPG1S;
+ bool DPG1NS;
+ bool DPG0;
+
+ static const uint32_t GICR_CTLR_DPG0 = 1 << 24;
+ static const uint32_t GICR_CTLR_DPG1NS = 1 << 25;
+ static const uint32_t GICR_CTLR_DPG1S = 1 << 26;
+
+ public:
+
+ /*
+ * GICv3 defines only 2 64K consecutive frames for the redistributor
+ * (RD_base and SGI_base) but we are using 2 extra 64K stride frames
+ * to match GICv4 that defines 4 64K consecutive frames for them.
+ * Note this must match with DTB/DTS GIC node definition and boot
+ * loader code.
+ */
+ static const uint32_t ADDR_RANGE_SIZE = 0x40000;
+
+ Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id);
+ ~Gicv3Redistributor();
+ void init();
+ void initState();
+
+ uint64_t read(Addr addr, size_t size, bool is_secure_access);
+ void write(Addr addr, uint64_t data, size_t size,
+ bool is_secure_access);
+ void sendPPInt(uint32_t int_id);
+ void sendSGI(uint32_t int_id, Gicv3::GroupId group, bool ns);
+ void serialize(CheckpointOut & cp) const override;
+ void unserialize(CheckpointIn & cp) override;
+ uint32_t getAffinity();
+
+ Gicv3CPUInterface *
+ getCPUInterface() const
+ {
+ return cpuInterface;
+ }
+
+ bool canBeSelectedFor1toNInterrupt(Gicv3::GroupId group);
+
+ protected:
+
+ void reset();
+ void update();
+ void updateAndInformCPUInterface();
+ Gicv3::IntStatus intStatus(uint32_t int_id);
+ Gicv3::GroupId getIntGroup(int int_id);
+ void activateIRQ(uint32_t int_id);
+ void deactivateIRQ(uint32_t int_id);
+};
+
+#endif //__DEV_ARM_GICV3_REDISTRIBUTOR_H__