The create() method on Params structs usually instantiate SimObjects using a constructor which takes the Params struct as a parameter somehow. There has been a lot of needless variation in how that was done, making it annoying to pass Params down to base classes. Some of the different forms were: const Params & Params & Params * const Params * Params const* This change goes through and fixes up every constructor and every create() method to use the const Params & form. We use a reference because the Params struct should never be null. We use const because neither the create method nor the consuming object should modify the record of the parameters as they came in from the config. That would make consuming them not idempotent, and make it impossible to tell what the actual simulation configuration was since it would change from any user visible form (config script, config.ini, dot pdf output). Change-Id: I77453cba52fdcfd5f4eec92dfb0bddb5a9945f31 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/35938 Reviewed-by: Gabe Black <gabeblack@google.com> Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br> Maintainer: Gabe Black <gabeblack@google.com> Tested-by: kokoro <noreply+kokoro@google.com>
2626 lines
78 KiB
C++
2626 lines
78 KiB
C++
/*
|
|
* Copyright (c) 2019 ARM Limited
|
|
* All rights reserved
|
|
*
|
|
* The license below extends only to copyright in the software and shall
|
|
* not be construed as granting a license to any other intellectual
|
|
* property including but not limited to intellectual property relating
|
|
* to a hardware implementation of the functionality of the software
|
|
* licensed hereunder. You may use the software subject to the license
|
|
* terms below provided that you ensure that this notice is replicated
|
|
* unmodified and in its entirety in all distributions of the software,
|
|
* modified or unmodified, in source code or in binary form.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "dev/arm/gic_v3_cpu_interface.hh"
|
|
|
|
#include "arch/arm/faults.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"
|
|
|
|
using namespace ArmISA;
|
|
|
|
const uint8_t Gicv3CPUInterface::GIC_MIN_BPR;
|
|
const uint8_t Gicv3CPUInterface::GIC_MIN_BPR_NS;
|
|
|
|
Gicv3CPUInterface::Gicv3CPUInterface(Gicv3 * gic, uint32_t cpu_id)
|
|
: BaseISADevice(),
|
|
gic(gic),
|
|
redistributor(nullptr),
|
|
distributor(nullptr),
|
|
cpuId(cpu_id)
|
|
{
|
|
hppi.prio = 0xff;
|
|
hppi.intid = Gicv3::INTID_SPURIOUS;
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::init()
|
|
{
|
|
redistributor = gic->getRedistributor(cpuId);
|
|
distributor = gic->getDistributor();
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::resetHppi(uint32_t intid)
|
|
{
|
|
if (intid == hppi.intid)
|
|
hppi.prio = 0xff;
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::setThreadContext(ThreadContext *tc)
|
|
{
|
|
maintenanceInterrupt = gic->params().maint_int->get(tc);
|
|
fatal_if(maintenanceInterrupt->num() >= redistributor->irqPending.size(),
|
|
"Invalid maintenance interrupt number\n");
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::getHCREL2FMO() const
|
|
{
|
|
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() const
|
|
{
|
|
HCR hcr = isa->readMiscRegNoEffect(MISCREG_HCR_EL2);
|
|
|
|
if (hcr.tge && hcr.e2h) {
|
|
return false;
|
|
} else if (hcr.tge) {
|
|
return true;
|
|
} else {
|
|
return hcr.imo;
|
|
}
|
|
}
|
|
|
|
RegVal
|
|
Gicv3CPUInterface::readMiscReg(int misc_reg)
|
|
{
|
|
RegVal value = isa->readMiscRegNoEffect(misc_reg);
|
|
bool hcr_fmo = getHCREL2FMO();
|
|
bool hcr_imo = getHCREL2IMO();
|
|
|
|
switch (misc_reg) {
|
|
// Active Priorities Group 1 Registers
|
|
case MISCREG_ICC_AP1R0:
|
|
case MISCREG_ICC_AP1R0_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return isa->readMiscRegNoEffect(MISCREG_ICV_AP1R0_EL1);
|
|
}
|
|
|
|
return readBankedMiscReg(MISCREG_ICC_AP1R0_EL1);
|
|
}
|
|
|
|
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;
|
|
|
|
// Active Priorities Group 0 Registers
|
|
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;
|
|
|
|
// Interrupt Group 0 Enable register EL1
|
|
case MISCREG_ICC_IGRPEN0:
|
|
case MISCREG_ICC_IGRPEN0_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
|
|
return readMiscReg(MISCREG_ICV_IGRPEN0_EL1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MISCREG_ICV_IGRPEN0_EL1: {
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
value = ich_vmcr_el2.VENG0;
|
|
break;
|
|
}
|
|
|
|
// Interrupt Group 1 Enable register EL1
|
|
case MISCREG_ICC_IGRPEN1:
|
|
case MISCREG_ICC_IGRPEN1_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return readMiscReg(MISCREG_ICV_IGRPEN1_EL1);
|
|
}
|
|
|
|
value = readBankedMiscReg(MISCREG_ICC_IGRPEN1_EL1);
|
|
break;
|
|
}
|
|
|
|
case MISCREG_ICV_IGRPEN1_EL1: {
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
value = ich_vmcr_el2.VENG1;
|
|
break;
|
|
}
|
|
|
|
// Interrupt Group 1 Enable register EL3
|
|
case MISCREG_ICC_MGRPEN1:
|
|
case MISCREG_ICC_IGRPEN1_EL3: {
|
|
ICC_IGRPEN1_EL3 igrp_el3 = 0;
|
|
igrp_el3.EnableGrp1S = ((ICC_IGRPEN1_EL1)isa->readMiscRegNoEffect(
|
|
MISCREG_ICC_IGRPEN1_EL1_S)).Enable;
|
|
|
|
igrp_el3.EnableGrp1NS = ((ICC_IGRPEN1_EL1)isa->readMiscRegNoEffect(
|
|
MISCREG_ICC_IGRPEN1_EL1_NS)).Enable;
|
|
|
|
value = igrp_el3;
|
|
break;
|
|
}
|
|
|
|
// Running Priority Register
|
|
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))) {
|
|
// Spec section 4.8.1
|
|
// For Non-secure access to ICC_RPR_EL1 when SCR_EL3.FIQ == 1
|
|
if ((rprio & 0x80) == 0) {
|
|
// If the current priority mask value is in the range of
|
|
// 0x00-0x7F a read access returns the value 0x0
|
|
rprio = 0;
|
|
} else if (rprio != 0xff) {
|
|
// If the current priority mask value is in the range of
|
|
// 0x80-0xFF a read access returns the Non-secure read of
|
|
// the current value
|
|
rprio = (rprio << 1) & 0xff;
|
|
}
|
|
}
|
|
|
|
value = rprio;
|
|
break;
|
|
}
|
|
|
|
// Virtual Running Priority Register
|
|
case MISCREG_ICV_RPR_EL1: {
|
|
value = virtualHighestActivePriority();
|
|
break;
|
|
}
|
|
|
|
// Highest Priority Pending Interrupt Register 0
|
|
case MISCREG_ICC_HPPIR0:
|
|
case MISCREG_ICC_HPPIR0_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
|
|
return readMiscReg(MISCREG_ICV_HPPIR0_EL1);
|
|
}
|
|
|
|
value = getHPPIR0();
|
|
break;
|
|
}
|
|
|
|
// Virtual Highest Priority Pending Interrupt Register 0
|
|
case MISCREG_ICV_HPPIR0_EL1: {
|
|
value = Gicv3::INTID_SPURIOUS;
|
|
int lr_idx = getHPPVILR();
|
|
|
|
if (lr_idx >= 0) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
Gicv3::GroupId group =
|
|
ich_lr_el2.Group ? Gicv3::G1NS : Gicv3::G0S;
|
|
|
|
if (group == Gicv3::G0S) {
|
|
value = ich_lr_el2.vINTID;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Highest Priority Pending Interrupt Register 1
|
|
case MISCREG_ICC_HPPIR1:
|
|
case MISCREG_ICC_HPPIR1_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return readMiscReg(MISCREG_ICV_HPPIR1_EL1);
|
|
}
|
|
|
|
value = getHPPIR1();
|
|
break;
|
|
}
|
|
|
|
// Virtual Highest Priority Pending Interrupt Register 1
|
|
case MISCREG_ICV_HPPIR1_EL1: {
|
|
value = Gicv3::INTID_SPURIOUS;
|
|
int lr_idx = getHPPVILR();
|
|
|
|
if (lr_idx >= 0) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
Gicv3::GroupId group =
|
|
ich_lr_el2.Group ? Gicv3::G1NS : Gicv3::G0S;
|
|
|
|
if (group == Gicv3::G1NS) {
|
|
value = ich_lr_el2.vINTID;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Binary Point Register 0
|
|
case MISCREG_ICC_BPR0:
|
|
case MISCREG_ICC_BPR0_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
|
|
return readMiscReg(MISCREG_ICV_BPR0_EL1);
|
|
}
|
|
|
|
value = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1);
|
|
break;
|
|
}
|
|
|
|
// Binary Point Register 1
|
|
case MISCREG_ICC_BPR1:
|
|
case MISCREG_ICC_BPR1_EL1: {
|
|
value = bpr1(isSecureBelowEL3() ? Gicv3::G1S : Gicv3::G1NS);
|
|
break;
|
|
}
|
|
|
|
// Virtual Binary Point Register 0
|
|
case MISCREG_ICV_BPR0_EL1: {
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
value = ich_vmcr_el2.VBPR0;
|
|
break;
|
|
}
|
|
|
|
// Virtual Binary Point Register 1
|
|
case MISCREG_ICV_BPR1_EL1: {
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
if (ich_vmcr_el2.VCBPR) {
|
|
// bpr0 + 1 saturated to 7, WI
|
|
value = ich_vmcr_el2.VBPR0 + 1;
|
|
value = value < 7 ? value : 7;
|
|
} else {
|
|
value = ich_vmcr_el2.VBPR1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Interrupt Priority Mask Register
|
|
case MISCREG_ICC_PMR:
|
|
case MISCREG_ICC_PMR_EL1:
|
|
if ((currEL() == EL1) && !inSecureState() && (hcr_imo || hcr_fmo)) {
|
|
return readMiscReg(MISCREG_ICV_PMR_EL1);
|
|
}
|
|
|
|
if (haveEL(EL3) && !inSecureState() &&
|
|
(isa->readMiscRegNoEffect(MISCREG_SCR_EL3) & (1U << 2))) {
|
|
// Spec section 4.8.1
|
|
// For Non-secure access to ICC_PMR_EL1 when SCR_EL3.FIQ == 1:
|
|
if ((value & 0x80) == 0) {
|
|
// If the current priority mask value is in the range of
|
|
// 0x00-0x7F a read access returns the value 0x00.
|
|
value = 0;
|
|
} else if (value != 0xff) {
|
|
// If the current priority mask value is in the range of
|
|
// 0x80-0xFF a read access returns the Non-secure read of the
|
|
// current value.
|
|
value = (value << 1) & 0xff;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case MISCREG_ICV_PMR_EL1: { // Priority Mask Register
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
value = ich_vmcr_el2.VPMR;
|
|
break;
|
|
}
|
|
|
|
// Interrupt Acknowledge Register 0
|
|
case MISCREG_ICC_IAR0:
|
|
case MISCREG_ICC_IAR0_EL1: {
|
|
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 ||
|
|
int_id >= Gicv3Redistributor::SMALLEST_LPI_ID) {
|
|
activateIRQ(int_id, hppi.group);
|
|
}
|
|
} else {
|
|
int_id = Gicv3::INTID_SPURIOUS;
|
|
}
|
|
|
|
value = int_id;
|
|
break;
|
|
}
|
|
|
|
// Virtual Interrupt Acknowledge Register 0
|
|
case MISCREG_ICV_IAR0_EL1: {
|
|
int lr_idx = getHPPVILR();
|
|
uint32_t int_id = Gicv3::INTID_SPURIOUS;
|
|
|
|
if (lr_idx >= 0) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if (!ich_lr_el2.Group && hppviCanPreempt(lr_idx)) {
|
|
int_id = ich_lr_el2.vINTID;
|
|
|
|
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...
|
|
ich_lr_el2.State = ICH_LR_EL2_STATE_INVALID;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx,
|
|
ich_lr_el2);
|
|
}
|
|
}
|
|
}
|
|
|
|
value = int_id;
|
|
virtualUpdate();
|
|
break;
|
|
}
|
|
|
|
// Interrupt Acknowledge Register 1
|
|
case MISCREG_ICC_IAR1:
|
|
case MISCREG_ICC_IAR1_EL1: {
|
|
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 ||
|
|
int_id >= Gicv3Redistributor::SMALLEST_LPI_ID) {
|
|
activateIRQ(int_id, hppi.group);
|
|
}
|
|
} else {
|
|
int_id = Gicv3::INTID_SPURIOUS;
|
|
}
|
|
|
|
value = int_id;
|
|
break;
|
|
}
|
|
|
|
// Virtual Interrupt Acknowledge Register 1
|
|
case MISCREG_ICV_IAR1_EL1: {
|
|
int lr_idx = getHPPVILR();
|
|
uint32_t int_id = Gicv3::INTID_SPURIOUS;
|
|
|
|
if (lr_idx >= 0) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if (ich_lr_el2.Group && hppviCanPreempt(lr_idx)) {
|
|
int_id = ich_lr_el2.vINTID;
|
|
|
|
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...
|
|
ich_lr_el2.State = ICH_LR_EL2_STATE_INVALID;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx,
|
|
ich_lr_el2);
|
|
}
|
|
}
|
|
}
|
|
|
|
value = int_id;
|
|
virtualUpdate();
|
|
break;
|
|
}
|
|
|
|
// System Register Enable Register EL1
|
|
case MISCREG_ICC_SRE:
|
|
case MISCREG_ICC_SRE_EL1: {
|
|
/*
|
|
* 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)
|
|
*/
|
|
ICC_SRE_EL1 icc_sre_el1 = 0;
|
|
icc_sre_el1.SRE = 1;
|
|
icc_sre_el1.DIB = 1;
|
|
icc_sre_el1.DFB = 1;
|
|
value = icc_sre_el1;
|
|
break;
|
|
}
|
|
|
|
// System Register Enable Register EL2
|
|
case MISCREG_ICC_HSRE:
|
|
case MISCREG_ICC_SRE_EL2: {
|
|
/*
|
|
* Enable [3] == 1
|
|
* (EL1 accesses to 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)
|
|
*/
|
|
ICC_SRE_EL2 icc_sre_el2 = 0;
|
|
icc_sre_el2.SRE = 1;
|
|
icc_sre_el2.DIB = 1;
|
|
icc_sre_el2.DFB = 1;
|
|
icc_sre_el2.Enable = 1;
|
|
value = icc_sre_el2;
|
|
break;
|
|
}
|
|
|
|
// System Register Enable Register EL3
|
|
case MISCREG_ICC_MSRE:
|
|
case MISCREG_ICC_SRE_EL3: {
|
|
/*
|
|
* Enable [3] == 1
|
|
* (EL1 accesses to ICC_SRE_EL1 do not trap to EL3.
|
|
* EL2 accesses to ICC_SRE_EL1 and ICC_SRE_EL2 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)
|
|
*/
|
|
ICC_SRE_EL3 icc_sre_el3 = 0;
|
|
icc_sre_el3.SRE = 1;
|
|
icc_sre_el3.DIB = 1;
|
|
icc_sre_el3.DFB = 1;
|
|
icc_sre_el3.Enable = 1;
|
|
value = icc_sre_el3;
|
|
break;
|
|
}
|
|
|
|
// Control Register
|
|
case MISCREG_ICC_CTLR:
|
|
case MISCREG_ICC_CTLR_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && (hcr_imo || hcr_fmo)) {
|
|
return readMiscReg(MISCREG_ICV_CTLR_EL1);
|
|
}
|
|
|
|
value = readBankedMiscReg(MISCREG_ICC_CTLR_EL1);
|
|
// Enforce value for RO bits
|
|
// ExtRange [19], INTIDs in the range 1024..8191 not supported
|
|
// RSS [18], SGIs with affinity level 0 values of 0-255 are supported
|
|
// A3V [15], supports non-zero values of the Aff3 field in SGI
|
|
// generation System registers
|
|
// SEIS [14], does not support generation of SEIs (deprecated)
|
|
// IDbits [13:11], 001 = 24 bits | 000 = 16 bits
|
|
// PRIbits [10:8], number of priority bits implemented, minus one
|
|
ICC_CTLR_EL1 icc_ctlr_el1 = value;
|
|
icc_ctlr_el1.ExtRange = 0;
|
|
icc_ctlr_el1.RSS = 1;
|
|
icc_ctlr_el1.A3V = 1;
|
|
icc_ctlr_el1.SEIS = 0;
|
|
icc_ctlr_el1.IDbits = 1;
|
|
icc_ctlr_el1.PRIbits = PRIORITY_BITS - 1;
|
|
value = icc_ctlr_el1;
|
|
break;
|
|
}
|
|
|
|
// Virtual Control Register
|
|
case MISCREG_ICV_CTLR_EL1: {
|
|
ICV_CTLR_EL1 icv_ctlr_el1 = value;
|
|
icv_ctlr_el1.RSS = 0;
|
|
icv_ctlr_el1.A3V = 1;
|
|
icv_ctlr_el1.SEIS = 0;
|
|
icv_ctlr_el1.IDbits = 1;
|
|
icv_ctlr_el1.PRIbits = 7;
|
|
value = icv_ctlr_el1;
|
|
break;
|
|
}
|
|
|
|
// Control Register
|
|
case MISCREG_ICC_MCTLR:
|
|
case MISCREG_ICC_CTLR_EL3: {
|
|
// Enforce value for RO bits
|
|
// ExtRange [19], INTIDs in the range 1024..8191 not supported
|
|
// RSS [18], SGIs with affinity level 0 values of 0-255 are supported
|
|
// nDS [17], supports disabling of security
|
|
// A3V [15], supports non-zero values of the Aff3 field in SGI
|
|
// generation System registers
|
|
// SEIS [14], does not support generation of SEIs (deprecated)
|
|
// IDbits [13:11], 001 = 24 bits | 000 = 16 bits
|
|
// PRIbits [10:8], number of priority bits implemented, minus one
|
|
ICC_CTLR_EL3 icc_ctlr_el3 = value;
|
|
icc_ctlr_el3.ExtRange = 0;
|
|
icc_ctlr_el3.RSS = 1;
|
|
icc_ctlr_el3.nDS = 0;
|
|
icc_ctlr_el3.A3V = 1;
|
|
icc_ctlr_el3.SEIS = 0;
|
|
icc_ctlr_el3.IDbits = 0;
|
|
icc_ctlr_el3.PRIbits = PRIORITY_BITS - 1;
|
|
value = icc_ctlr_el3;
|
|
break;
|
|
}
|
|
|
|
// Hyp Control Register
|
|
case MISCREG_ICH_HCR:
|
|
case MISCREG_ICH_HCR_EL2:
|
|
break;
|
|
|
|
// Hyp Active Priorities Group 0 Registers
|
|
case MISCREG_ICH_AP0R0:
|
|
case MISCREG_ICH_AP0R0_EL2:
|
|
break;
|
|
|
|
// only implemented if supporting 6 or more bits of priority
|
|
case MISCREG_ICH_AP0R1:
|
|
case MISCREG_ICH_AP0R1_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP0R2:
|
|
case MISCREG_ICH_AP0R2_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP0R3:
|
|
case MISCREG_ICH_AP0R3_EL2:
|
|
// Unimplemented registers are RAZ/WI
|
|
return 0;
|
|
|
|
// Hyp Active Priorities Group 1 Registers
|
|
case MISCREG_ICH_AP1R0:
|
|
case MISCREG_ICH_AP1R0_EL2:
|
|
break;
|
|
|
|
// only implemented if supporting 6 or more bits of priority
|
|
case MISCREG_ICH_AP1R1:
|
|
case MISCREG_ICH_AP1R1_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP1R2:
|
|
case MISCREG_ICH_AP1R2_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP1R3:
|
|
case MISCREG_ICH_AP1R3_EL2:
|
|
// Unimplemented registers are RAZ/WI
|
|
return 0;
|
|
|
|
// Maintenance Interrupt State Register
|
|
case MISCREG_ICH_MISR:
|
|
case MISCREG_ICH_MISR_EL2:
|
|
value = maintenanceInterruptStatus();
|
|
break;
|
|
|
|
// VGIC Type Register
|
|
case MISCREG_ICH_VTR:
|
|
case MISCREG_ICH_VTR_EL2: {
|
|
ICH_VTR_EL2 ich_vtr_el2 = value;
|
|
|
|
ich_vtr_el2.ListRegs = VIRTUAL_NUM_LIST_REGS - 1;
|
|
ich_vtr_el2.A3V = 1;
|
|
ich_vtr_el2.IDbits = 1;
|
|
ich_vtr_el2.PREbits = VIRTUAL_PREEMPTION_BITS - 1;
|
|
ich_vtr_el2.PRIbits = VIRTUAL_PRIORITY_BITS - 1;
|
|
|
|
value = ich_vtr_el2;
|
|
break;
|
|
}
|
|
|
|
// End of Interrupt Status Register
|
|
case MISCREG_ICH_EISR:
|
|
case MISCREG_ICH_EISR_EL2:
|
|
value = eoiMaintenanceInterruptStatus();
|
|
break;
|
|
|
|
// Empty List Register Status Register
|
|
case MISCREG_ICH_ELRSR:
|
|
case MISCREG_ICH_ELRSR_EL2:
|
|
value = 0;
|
|
|
|
for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if ((ich_lr_el2.State == ICH_LR_EL2_STATE_INVALID) &&
|
|
(ich_lr_el2.HW || !ich_lr_el2.EOI)) {
|
|
value |= (1 << lr_idx);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
// List Registers
|
|
case MISCREG_ICH_LRC0 ... MISCREG_ICH_LRC15:
|
|
// AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 high half part)
|
|
value = value >> 32;
|
|
break;
|
|
|
|
// List Registers
|
|
case MISCREG_ICH_LR0 ... MISCREG_ICH_LR15:
|
|
// AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 low half part)
|
|
value = value & 0xffffffff;
|
|
break;
|
|
|
|
// List Registers
|
|
case MISCREG_ICH_LR0_EL2 ... MISCREG_ICH_LR15_EL2:
|
|
break;
|
|
|
|
// Virtual Machine Control Register
|
|
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, RegVal 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) {
|
|
// Active Priorities Group 1 Registers
|
|
case MISCREG_ICC_AP1R0:
|
|
case MISCREG_ICC_AP1R0_EL1:
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return isa->setMiscRegNoEffect(MISCREG_ICV_AP1R0_EL1, val);
|
|
}
|
|
|
|
setBankedMiscReg(MISCREG_ICC_AP1R0_EL1, val);
|
|
return;
|
|
|
|
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;
|
|
|
|
// Active Priorities Group 0 Registers
|
|
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;
|
|
|
|
// End Of Interrupt Register 0
|
|
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 &&
|
|
int_id <= Gicv3::INTID_SPURIOUS) {
|
|
return;
|
|
}
|
|
|
|
Gicv3::GroupId group = Gicv3::G0S;
|
|
|
|
if (highestActiveGroup() != group) {
|
|
return;
|
|
}
|
|
|
|
dropPriority(group);
|
|
|
|
if (!isEOISplitMode()) {
|
|
deactivateIRQ(int_id, group);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Virtual End Of Interrupt Register 0
|
|
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 {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
Gicv3::GroupId lr_group =
|
|
ich_lr_el2.Group ? Gicv3::G1NS : Gicv3::G0S;
|
|
uint8_t lr_group_prio = ich_lr_el2.Priority & 0xf8;
|
|
|
|
if (lr_group == Gicv3::G0S && lr_group_prio == drop_prio) {
|
|
//if (!virtualIsEOISplitMode())
|
|
{
|
|
virtualDeactivateIRQ(lr_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtualUpdate();
|
|
break;
|
|
}
|
|
|
|
// End Of Interrupt Register 1
|
|
case MISCREG_ICC_EOIR1:
|
|
case MISCREG_ICC_EOIR1_EL1: {
|
|
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 &&
|
|
int_id <= Gicv3::INTID_SPURIOUS) {
|
|
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;
|
|
}
|
|
|
|
// Virtual End Of Interrupt Register 1
|
|
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 matching LR found
|
|
virtualIncrementEOICount();
|
|
} else {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
Gicv3::GroupId lr_group =
|
|
ich_lr_el2.Group ? Gicv3::G1NS : Gicv3::G0S;
|
|
uint8_t lr_group_prio = ich_lr_el2.Priority & 0xf8;
|
|
|
|
if (lr_group == Gicv3::G1NS && lr_group_prio == drop_prio) {
|
|
if (!virtualIsEOISplitMode()) {
|
|
virtualDeactivateIRQ(lr_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtualUpdate();
|
|
break;
|
|
}
|
|
|
|
// Deactivate Interrupt Register
|
|
case MISCREG_ICC_DIR:
|
|
case MISCREG_ICC_DIR_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() &&
|
|
(hcr_imo || hcr_fmo)) {
|
|
return setMiscReg(MISCREG_ICV_DIR_EL1, val);
|
|
}
|
|
|
|
int int_id = val & 0xffffff;
|
|
|
|
// The following checks are as per spec pseudocode
|
|
// aarch64/support/ICC_DIR_EL1
|
|
|
|
// Check for spurious ID
|
|
if (int_id >= Gicv3::INTID_SECURE) {
|
|
return;
|
|
}
|
|
|
|
// EOI mode is not set, so don't deactivate
|
|
if (!isEOISplitMode()) {
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Deactivate Virtual Interrupt Register
|
|
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 matching LR found
|
|
virtualIncrementEOICount();
|
|
} else {
|
|
virtualDeactivateIRQ(lr_idx);
|
|
}
|
|
|
|
virtualUpdate();
|
|
break;
|
|
}
|
|
|
|
// Binary Point Register 0
|
|
case MISCREG_ICC_BPR0:
|
|
case MISCREG_ICC_BPR0_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
|
|
return setMiscReg(MISCREG_ICV_BPR0_EL1, val);
|
|
}
|
|
break;
|
|
}
|
|
// Binary Point Register 1
|
|
case MISCREG_ICC_BPR1:
|
|
case MISCREG_ICC_BPR1_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return setMiscReg(MISCREG_ICV_BPR1_EL1, val);
|
|
}
|
|
|
|
val &= 0x7;
|
|
|
|
if (isSecureBelowEL3()) {
|
|
// group == Gicv3::G1S
|
|
ICC_CTLR_EL1 icc_ctlr_el1_s =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
|
|
|
|
val = val > GIC_MIN_BPR ? val : GIC_MIN_BPR;
|
|
if (haveEL(EL3) && !isEL3OrMon() && icc_ctlr_el1_s.CBPR) {
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_BPR0_EL1, val);
|
|
} else {
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_BPR1_EL1_S, val);
|
|
}
|
|
return;
|
|
} else {
|
|
// group == Gicv3::G1NS
|
|
ICC_CTLR_EL1 icc_ctlr_el1_ns =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
|
|
|
|
val = val > GIC_MIN_BPR_NS ? val : GIC_MIN_BPR_NS;
|
|
if (haveEL(EL3) && !isEL3OrMon() && icc_ctlr_el1_ns.CBPR) {
|
|
// Non secure writes from EL1 and EL2 are ignored
|
|
} else {
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_BPR1_EL1_NS, val);
|
|
}
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Virtual Binary Point Register 0
|
|
case MISCREG_ICV_BPR0_EL1:
|
|
// Virtual Binary Point Register 1
|
|
case MISCREG_ICV_BPR1_EL1: {
|
|
Gicv3::GroupId group =
|
|
misc_reg == MISCREG_ICV_BPR0_EL1 ? Gicv3::G0S : Gicv3::G1NS;
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
if ((group == Gicv3::G1NS) && ich_vmcr_el2.VCBPR) {
|
|
// BPR0 + 1 saturated to 7, WI
|
|
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.VBPR0 = val;
|
|
} else {
|
|
ich_vmcr_el2.VBPR1 = val;
|
|
}
|
|
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
|
|
do_virtual_update = true;
|
|
break;
|
|
}
|
|
|
|
// Control Register EL1
|
|
case MISCREG_ICC_CTLR:
|
|
case MISCREG_ICC_CTLR_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && (hcr_imo || hcr_fmo)) {
|
|
return setMiscReg(MISCREG_ICV_CTLR_EL1, val);
|
|
}
|
|
|
|
/*
|
|
* ExtRange is RO.
|
|
* RSS is RO.
|
|
* A3V is RO.
|
|
* SEIS is RO.
|
|
* IDbits is RO.
|
|
* PRIbits is RO.
|
|
*/
|
|
ICC_CTLR_EL1 requested_icc_ctlr_el1 = val;
|
|
ICC_CTLR_EL1 icc_ctlr_el1 =
|
|
readBankedMiscReg(MISCREG_ICC_CTLR_EL1);
|
|
|
|
ICC_CTLR_EL3 icc_ctlr_el3 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3);
|
|
|
|
// The following could be refactored but it is following
|
|
// spec description section 9.2.6 point by point.
|
|
|
|
// PMHE
|
|
if (haveEL(EL3)) {
|
|
// PMHE is alias of ICC_CTLR_EL3.PMHE
|
|
|
|
if (distributor->DS == 0) {
|
|
// PMHE is RO
|
|
} else if (distributor->DS == 1) {
|
|
// PMHE is RW
|
|
icc_ctlr_el1.PMHE = requested_icc_ctlr_el1.PMHE;
|
|
icc_ctlr_el3.PMHE = icc_ctlr_el1.PMHE;
|
|
}
|
|
} else {
|
|
// PMHE is RW (by implementation choice)
|
|
icc_ctlr_el1.PMHE = requested_icc_ctlr_el1.PMHE;
|
|
}
|
|
|
|
// EOImode
|
|
icc_ctlr_el1.EOImode = requested_icc_ctlr_el1.EOImode;
|
|
|
|
if (inSecureState()) {
|
|
// EOIMode is alias of ICC_CTLR_EL3.EOImode_EL1S
|
|
icc_ctlr_el3.EOImode_EL1S = icc_ctlr_el1.EOImode;
|
|
} else {
|
|
// EOIMode is alias of ICC_CTLR_EL3.EOImode_EL1NS
|
|
icc_ctlr_el3.EOImode_EL1NS = icc_ctlr_el1.EOImode;
|
|
}
|
|
|
|
// CBPR
|
|
if (haveEL(EL3)) {
|
|
// CBPR is alias of ICC_CTLR_EL3.CBPR_EL1{S,NS}
|
|
|
|
if (distributor->DS == 0) {
|
|
// CBPR is RO
|
|
} else {
|
|
// CBPR is RW
|
|
icc_ctlr_el1.CBPR = requested_icc_ctlr_el1.CBPR;
|
|
|
|
if (inSecureState()) {
|
|
icc_ctlr_el3.CBPR_EL1S = icc_ctlr_el1.CBPR;
|
|
} else {
|
|
icc_ctlr_el3.CBPR_EL1NS = icc_ctlr_el1.CBPR;
|
|
}
|
|
}
|
|
} else {
|
|
// CBPR is RW
|
|
icc_ctlr_el1.CBPR = requested_icc_ctlr_el1.CBPR;
|
|
}
|
|
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL3, icc_ctlr_el3);
|
|
|
|
setBankedMiscReg(MISCREG_ICC_CTLR_EL1, icc_ctlr_el1);
|
|
return;
|
|
}
|
|
|
|
// Virtual Control Register
|
|
case MISCREG_ICV_CTLR_EL1: {
|
|
ICV_CTLR_EL1 requested_icv_ctlr_el1 = val;
|
|
ICV_CTLR_EL1 icv_ctlr_el1 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICV_CTLR_EL1);
|
|
icv_ctlr_el1.EOImode = requested_icv_ctlr_el1.EOImode;
|
|
icv_ctlr_el1.CBPR = requested_icv_ctlr_el1.CBPR;
|
|
val = icv_ctlr_el1;
|
|
|
|
// Aliases
|
|
// ICV_CTLR_EL1.CBPR aliases ICH_VMCR_EL2.VCBPR.
|
|
// ICV_CTLR_EL1.EOImode aliases ICH_VMCR_EL2.VEOIM.
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
ich_vmcr_el2.VCBPR = icv_ctlr_el1.CBPR;
|
|
ich_vmcr_el2.VEOIM = icv_ctlr_el1.EOImode;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
|
|
break;
|
|
}
|
|
|
|
// Control Register EL3
|
|
case MISCREG_ICC_MCTLR:
|
|
case MISCREG_ICC_CTLR_EL3: {
|
|
/*
|
|
* ExtRange is RO.
|
|
* RSS is RO.
|
|
* nDS is RO.
|
|
* A3V is RO.
|
|
* SEIS is RO.
|
|
* IDbits is RO.
|
|
* PRIbits is RO.
|
|
* PMHE is RAO/WI, priority-based routing is always used.
|
|
*/
|
|
ICC_CTLR_EL3 requested_icc_ctlr_el3 = val;
|
|
|
|
// Aliases
|
|
if (haveEL(EL3))
|
|
{
|
|
ICC_CTLR_EL1 icc_ctlr_el1_s =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
|
|
ICC_CTLR_EL1 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
|
|
icc_ctlr_el1_ns.EOImode = requested_icc_ctlr_el3.EOImode_EL1NS;
|
|
// ICC_CTLR_EL1(S).EOImode is an alias of
|
|
// ICC_CTLR_EL3.EOImode_EL1S
|
|
icc_ctlr_el1_s.EOImode = requested_icc_ctlr_el3.EOImode_EL1S;
|
|
// ICC_CTLR_EL1(NS).CBPR is an alias of ICC_CTLR_EL3.CBPR_EL1NS
|
|
icc_ctlr_el1_ns.CBPR = requested_icc_ctlr_el3.CBPR_EL1NS;
|
|
// ICC_CTLR_EL1(S).CBPR is an alias of ICC_CTLR_EL3.CBPR_EL1S
|
|
icc_ctlr_el1_s.CBPR = requested_icc_ctlr_el3.CBPR_EL1S;
|
|
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S, icc_ctlr_el1_s);
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS,
|
|
icc_ctlr_el1_ns);
|
|
}
|
|
|
|
ICC_CTLR_EL3 icc_ctlr_el3 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3);
|
|
|
|
icc_ctlr_el3.RM = requested_icc_ctlr_el3.RM;
|
|
icc_ctlr_el3.EOImode_EL1NS = requested_icc_ctlr_el3.EOImode_EL1NS;
|
|
icc_ctlr_el3.EOImode_EL1S = requested_icc_ctlr_el3.EOImode_EL1S;
|
|
icc_ctlr_el3.EOImode_EL3 = requested_icc_ctlr_el3.EOImode_EL3;
|
|
icc_ctlr_el3.CBPR_EL1NS = requested_icc_ctlr_el3.CBPR_EL1NS;
|
|
icc_ctlr_el3.CBPR_EL1S = requested_icc_ctlr_el3.CBPR_EL1S;
|
|
|
|
val = icc_ctlr_el3;
|
|
break;
|
|
}
|
|
|
|
// Priority Mask Register
|
|
case MISCREG_ICC_PMR:
|
|
case MISCREG_ICC_PMR_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && (hcr_imo || hcr_fmo)) {
|
|
return setMiscReg(MISCREG_ICV_PMR_EL1, val);
|
|
}
|
|
|
|
val &= 0xff;
|
|
SCR scr_el3 = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
|
|
|
|
if (haveEL(EL3) && !inSecureState() && (scr_el3.fiq)) {
|
|
// Spec section 4.8.1
|
|
// For Non-secure access to ICC_PMR_EL1 SCR_EL3.FIQ == 1:
|
|
RegVal old_icc_pmr_el1 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_PMR_EL1);
|
|
|
|
if (!(old_icc_pmr_el1 & 0x80)) {
|
|
// If the current priority mask value is in the range of
|
|
// 0x00-0x7F then WI
|
|
return;
|
|
}
|
|
|
|
// If the current priority mask value is in the range of
|
|
// 0x80-0xFF then a write access to ICC_PMR_EL1 succeeds,
|
|
// based on the Non-secure read of the priority mask value
|
|
// written to the register.
|
|
|
|
val = (val >> 1) | 0x80;
|
|
}
|
|
|
|
val &= ~0U << (8 - PRIORITY_BITS);
|
|
break;
|
|
}
|
|
|
|
case MISCREG_ICV_PMR_EL1: { // Priority Mask Register
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
ich_vmcr_el2.VPMR = val & 0xff;
|
|
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
|
|
virtualUpdate();
|
|
return;
|
|
}
|
|
|
|
// Interrupt Group 0 Enable Register EL1
|
|
case MISCREG_ICC_IGRPEN0:
|
|
case MISCREG_ICC_IGRPEN0_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_fmo) {
|
|
return setMiscReg(MISCREG_ICV_IGRPEN0_EL1, val);
|
|
}
|
|
|
|
isa->setMiscRegNoEffect(MISCREG_ICC_IGRPEN0_EL1, val);
|
|
updateDistributor();
|
|
return;
|
|
}
|
|
|
|
// Virtual Interrupt Group 0 Enable register
|
|
case MISCREG_ICV_IGRPEN0_EL1: {
|
|
bool enable = val & 0x1;
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
ich_vmcr_el2.VENG0 = enable;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
|
|
virtualUpdate();
|
|
return;
|
|
}
|
|
|
|
// Interrupt Group 1 Enable register EL1
|
|
case MISCREG_ICC_IGRPEN1:
|
|
case MISCREG_ICC_IGRPEN1_EL1: {
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return setMiscReg(MISCREG_ICV_IGRPEN1_EL1, val);
|
|
}
|
|
|
|
setBankedMiscReg(MISCREG_ICC_IGRPEN1_EL1, val);
|
|
updateDistributor();
|
|
return;
|
|
}
|
|
|
|
// Virtual Interrupt Group 1 Enable register
|
|
case MISCREG_ICV_IGRPEN1_EL1: {
|
|
bool enable = val & 0x1;
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
ich_vmcr_el2.VENG1 = enable;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_VMCR_EL2, ich_vmcr_el2);
|
|
virtualUpdate();
|
|
return;
|
|
}
|
|
|
|
// Interrupt Group 1 Enable register
|
|
case MISCREG_ICC_MGRPEN1:
|
|
case MISCREG_ICC_IGRPEN1_EL3: {
|
|
ICC_IGRPEN1_EL3 icc_igrpen1_el3 = val;
|
|
|
|
isa->setMiscRegNoEffect(
|
|
MISCREG_ICC_IGRPEN1_EL1_S, icc_igrpen1_el3.EnableGrp1S);
|
|
isa->setMiscRegNoEffect(
|
|
MISCREG_ICC_IGRPEN1_EL1_NS, icc_igrpen1_el3.EnableGrp1NS);
|
|
updateDistributor();
|
|
return;
|
|
}
|
|
|
|
// Software Generated Interrupt Group 0 Register
|
|
case MISCREG_ICC_SGI0R:
|
|
case MISCREG_ICC_SGI0R_EL1:
|
|
generateSGI(val, Gicv3::G0S);
|
|
break;
|
|
|
|
// Software Generated Interrupt Group 1 Register
|
|
case MISCREG_ICC_SGI1R:
|
|
case MISCREG_ICC_SGI1R_EL1: {
|
|
Gicv3::GroupId group = inSecureState() ? Gicv3::G1S : Gicv3::G1NS;
|
|
|
|
generateSGI(val, group);
|
|
break;
|
|
}
|
|
|
|
// Alias Software Generated Interrupt Group 1 Register
|
|
case MISCREG_ICC_ASGI1R:
|
|
case MISCREG_ICC_ASGI1R_EL1: {
|
|
Gicv3::GroupId group = inSecureState() ? Gicv3::G1NS : Gicv3::G1S;
|
|
|
|
generateSGI(val, group);
|
|
break;
|
|
}
|
|
|
|
// System Register Enable Register EL1
|
|
case MISCREG_ICC_SRE:
|
|
case MISCREG_ICC_SRE_EL1:
|
|
// System Register Enable Register EL2
|
|
case MISCREG_ICC_HSRE:
|
|
case MISCREG_ICC_SRE_EL2:
|
|
// System Register Enable Register EL3
|
|
case MISCREG_ICC_MSRE:
|
|
case MISCREG_ICC_SRE_EL3:
|
|
// All bits are RAO/WI
|
|
return;
|
|
|
|
// Hyp Control Register
|
|
case MISCREG_ICH_HCR:
|
|
case MISCREG_ICH_HCR_EL2: {
|
|
ICH_HCR_EL2 requested_ich_hcr_el2 = val;
|
|
ICH_HCR_EL2 ich_hcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
|
|
|
|
if (requested_ich_hcr_el2.EOIcount >= ich_hcr_el2.EOIcount)
|
|
{
|
|
// EOIcount - Permitted behaviors are:
|
|
// - Increment EOIcount.
|
|
// - Leave EOIcount unchanged.
|
|
ich_hcr_el2.EOIcount = requested_ich_hcr_el2.EOIcount;
|
|
}
|
|
|
|
ich_hcr_el2.TDIR = requested_ich_hcr_el2.TDIR;
|
|
ich_hcr_el2.TSEI = requested_ich_hcr_el2.TSEI;
|
|
ich_hcr_el2.TALL1 = requested_ich_hcr_el2.TALL1;;
|
|
ich_hcr_el2.TALL0 = requested_ich_hcr_el2.TALL0;;
|
|
ich_hcr_el2.TC = requested_ich_hcr_el2.TC;
|
|
ich_hcr_el2.VGrp1DIE = requested_ich_hcr_el2.VGrp1DIE;
|
|
ich_hcr_el2.VGrp1EIE = requested_ich_hcr_el2.VGrp1EIE;
|
|
ich_hcr_el2.VGrp0DIE = requested_ich_hcr_el2.VGrp0DIE;
|
|
ich_hcr_el2.VGrp0EIE = requested_ich_hcr_el2.VGrp0EIE;
|
|
ich_hcr_el2.NPIE = requested_ich_hcr_el2.NPIE;
|
|
ich_hcr_el2.LRENPIE = requested_ich_hcr_el2.LRENPIE;
|
|
ich_hcr_el2.UIE = requested_ich_hcr_el2.UIE;
|
|
ich_hcr_el2.En = requested_ich_hcr_el2.En;
|
|
val = ich_hcr_el2;
|
|
do_virtual_update = true;
|
|
break;
|
|
}
|
|
|
|
// List Registers
|
|
case MISCREG_ICH_LRC0 ... MISCREG_ICH_LRC15: {
|
|
// AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 high half part)
|
|
ICH_LRC requested_ich_lrc = val;
|
|
ICH_LRC ich_lrc = isa->readMiscRegNoEffect(misc_reg);
|
|
|
|
ich_lrc.State = requested_ich_lrc.State;
|
|
ich_lrc.HW = requested_ich_lrc.HW;
|
|
ich_lrc.Group = requested_ich_lrc.Group;
|
|
|
|
// Priority, bits [23:16]
|
|
// At least five bits must be implemented.
|
|
// Unimplemented bits are RES0 and start from bit[16] up to bit[18].
|
|
// We implement 5 bits.
|
|
ich_lrc.Priority = (requested_ich_lrc.Priority & 0xf8) |
|
|
(ich_lrc.Priority & 0x07);
|
|
|
|
// pINTID, bits [12:0]
|
|
// When ICH_LR<n>.HW is 0 this field has the following meaning:
|
|
// - Bits[12:10] : RES0.
|
|
// - Bit[9] : EOI.
|
|
// - Bits[8:0] : RES0.
|
|
// When ICH_LR<n>.HW is 1:
|
|
// - This field is only required to implement enough bits to hold a
|
|
// valid value for the implemented INTID size. Any unused higher
|
|
// order bits are RES0.
|
|
if (requested_ich_lrc.HW == 0) {
|
|
ich_lrc.EOI = requested_ich_lrc.EOI;
|
|
} else {
|
|
ich_lrc.pINTID = requested_ich_lrc.pINTID;
|
|
}
|
|
|
|
val = ich_lrc;
|
|
do_virtual_update = true;
|
|
break;
|
|
}
|
|
|
|
// List Registers
|
|
case MISCREG_ICH_LR0 ... MISCREG_ICH_LR15: {
|
|
// AArch32 (maps to AArch64 MISCREG_ICH_LR<n>_EL2 low half part)
|
|
RegVal old_val = isa->readMiscRegNoEffect(misc_reg);
|
|
val = (old_val & 0xffffffff00000000) | (val & 0xffffffff);
|
|
do_virtual_update = true;
|
|
break;
|
|
}
|
|
|
|
// List Registers
|
|
case MISCREG_ICH_LR0_EL2 ... MISCREG_ICH_LR15_EL2: { // AArch64
|
|
ICH_LR_EL2 requested_ich_lr_el2 = val;
|
|
ICH_LR_EL2 ich_lr_el2 = isa->readMiscRegNoEffect(misc_reg);
|
|
|
|
ich_lr_el2.State = requested_ich_lr_el2.State;
|
|
ich_lr_el2.HW = requested_ich_lr_el2.HW;
|
|
ich_lr_el2.Group = requested_ich_lr_el2.Group;
|
|
|
|
// Priority, bits [55:48]
|
|
// At least five bits must be implemented.
|
|
// Unimplemented bits are RES0 and start from bit[48] up to bit[50].
|
|
// We implement 5 bits.
|
|
ich_lr_el2.Priority = (requested_ich_lr_el2.Priority & 0xf8) |
|
|
(ich_lr_el2.Priority & 0x07);
|
|
|
|
// pINTID, bits [44:32]
|
|
// When ICH_LR<n>_EL2.HW is 0 this field has the following meaning:
|
|
// - Bits[44:42] : RES0.
|
|
// - Bit[41] : EOI.
|
|
// - Bits[40:32] : RES0.
|
|
// When ICH_LR<n>_EL2.HW is 1:
|
|
// - This field is only required to implement enough bits to hold a
|
|
// valid value for the implemented INTID size. Any unused higher
|
|
// order bits are RES0.
|
|
if (requested_ich_lr_el2.HW == 0) {
|
|
ich_lr_el2.EOI = requested_ich_lr_el2.EOI;
|
|
} else {
|
|
ich_lr_el2.pINTID = requested_ich_lr_el2.pINTID;
|
|
}
|
|
|
|
// vINTID, bits [31:0]
|
|
// It is IMPLEMENTATION DEFINED how many bits are implemented,
|
|
// though at least 16 bits must be implemented.
|
|
// Unimplemented bits are RES0.
|
|
ich_lr_el2.vINTID = requested_ich_lr_el2.vINTID;
|
|
|
|
val = ich_lr_el2;
|
|
do_virtual_update = true;
|
|
break;
|
|
}
|
|
|
|
// Virtual Machine Control Register
|
|
case MISCREG_ICH_VMCR:
|
|
case MISCREG_ICH_VMCR_EL2: {
|
|
ICH_VMCR_EL2 requested_ich_vmcr_el2 = val;
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
ich_vmcr_el2.VPMR = requested_ich_vmcr_el2.VPMR;
|
|
uint8_t min_vpr0 = 7 - VIRTUAL_PREEMPTION_BITS;
|
|
|
|
if (requested_ich_vmcr_el2.VBPR0 < min_vpr0) {
|
|
ich_vmcr_el2.VBPR0 = min_vpr0;
|
|
} else {
|
|
ich_vmcr_el2.VBPR0 = requested_ich_vmcr_el2.VBPR0;
|
|
}
|
|
|
|
uint8_t min_vpr1 = min_vpr0 + 1;
|
|
|
|
if (requested_ich_vmcr_el2.VBPR1 < min_vpr1) {
|
|
ich_vmcr_el2.VBPR1 = min_vpr1;
|
|
} else {
|
|
ich_vmcr_el2.VBPR1 = requested_ich_vmcr_el2.VBPR1;
|
|
}
|
|
|
|
ich_vmcr_el2.VEOIM = requested_ich_vmcr_el2.VEOIM;
|
|
ich_vmcr_el2.VCBPR = requested_ich_vmcr_el2.VCBPR;
|
|
ich_vmcr_el2.VENG1 = requested_ich_vmcr_el2.VENG1;
|
|
ich_vmcr_el2.VENG0 = requested_ich_vmcr_el2.VENG0;
|
|
val = ich_vmcr_el2;
|
|
break;
|
|
}
|
|
|
|
// Hyp Active Priorities Group 0 Registers
|
|
case MISCREG_ICH_AP0R0:
|
|
case MISCREG_ICH_AP0R0_EL2:
|
|
break;
|
|
|
|
// only implemented if supporting 6 or more bits of priority
|
|
case MISCREG_ICH_AP0R1:
|
|
case MISCREG_ICH_AP0R1_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP0R2:
|
|
case MISCREG_ICH_AP0R2_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP0R3:
|
|
case MISCREG_ICH_AP0R3_EL2:
|
|
// Unimplemented registers are RAZ/WI
|
|
return;
|
|
|
|
// Hyp Active Priorities Group 1 Registers
|
|
case MISCREG_ICH_AP1R0:
|
|
case MISCREG_ICH_AP1R0_EL2:
|
|
break;
|
|
|
|
// only implemented if supporting 6 or more bits of priority
|
|
case MISCREG_ICH_AP1R1:
|
|
case MISCREG_ICH_AP1R1_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP1R2:
|
|
case MISCREG_ICH_AP1R2_EL2:
|
|
// only implemented if supporting 7 or more bits of priority
|
|
case MISCREG_ICH_AP1R3:
|
|
case MISCREG_ICH_AP1R3_EL2:
|
|
// Unimplemented registers are RAZ/WI
|
|
return;
|
|
|
|
default:
|
|
panic("Gicv3CPUInterface::setMiscReg(): unknown register %d (%s)",
|
|
misc_reg, miscRegName[misc_reg]);
|
|
}
|
|
|
|
isa->setMiscRegNoEffect(misc_reg, val);
|
|
|
|
if (do_virtual_update) {
|
|
virtualUpdate();
|
|
}
|
|
}
|
|
|
|
RegVal
|
|
Gicv3CPUInterface::readBankedMiscReg(MiscRegIndex misc_reg) const
|
|
{
|
|
return isa->readMiscRegNoEffect(
|
|
isa->snsBankedIndex64(misc_reg, !isSecureBelowEL3()));
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::setBankedMiscReg(MiscRegIndex misc_reg, RegVal val) const
|
|
{
|
|
isa->setMiscRegNoEffect(
|
|
isa->snsBankedIndex64(misc_reg, !isSecureBelowEL3()), val);
|
|
}
|
|
|
|
int
|
|
Gicv3CPUInterface::virtualFindActive(uint32_t int_id) const
|
|
{
|
|
for (uint32_t lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if (((ich_lr_el2.State == ICH_LR_EL2_STATE_ACTIVE) ||
|
|
(ich_lr_el2.State == ICH_LR_EL2_STATE_ACTIVE_PENDING)) &&
|
|
(ich_lr_el2.vINTID == int_id)) {
|
|
return lr_idx;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
uint32_t
|
|
Gicv3CPUInterface::getHPPIR0() const
|
|
{
|
|
if (hppi.prio == 0xff || !groupEnabled(hppi.group)) {
|
|
return Gicv3::INTID_SPURIOUS;
|
|
}
|
|
|
|
bool irq_is_secure = !distributor->DS && hppi.group != Gicv3::G1NS;
|
|
|
|
if ((hppi.group != Gicv3::G0S) && isEL3OrMon()) {
|
|
// 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() const
|
|
{
|
|
if (hppi.prio == 0xff || !groupEnabled(hppi.group)) {
|
|
return Gicv3::INTID_SPURIOUS;
|
|
}
|
|
|
|
ICC_CTLR_EL3 icc_ctlr_el3 = isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3);
|
|
if ((currEL() == 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 = 0;
|
|
|
|
switch (group) {
|
|
case Gicv3::G0S:
|
|
apr_misc_reg = MISCREG_ICC_AP0R0_EL1;
|
|
break;
|
|
case Gicv3::G1S:
|
|
apr_misc_reg = MISCREG_ICC_AP1R0_EL1_S;
|
|
break;
|
|
case Gicv3::G1NS:
|
|
apr_misc_reg = MISCREG_ICC_AP1R0_EL1_NS;
|
|
break;
|
|
default:
|
|
panic("Invalid Gicv3::GroupId");
|
|
}
|
|
|
|
RegVal apr = isa->readMiscRegNoEffect(apr_misc_reg);
|
|
|
|
if (apr) {
|
|
apr &= apr - 1;
|
|
isa->setMiscRegNoEffect(apr_misc_reg, apr);
|
|
}
|
|
|
|
update();
|
|
}
|
|
|
|
uint8_t
|
|
Gicv3CPUInterface::virtualDropPriority()
|
|
{
|
|
int apr_max = 1 << (VIRTUAL_PREEMPTION_BITS - 5);
|
|
|
|
for (int i = 0; i < apr_max; i++) {
|
|
RegVal vapr0 = isa->readMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i);
|
|
RegVal 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) {
|
|
vapr0 &= vapr0 - 1;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_AP0R0_EL2 + i, vapr0);
|
|
return (vapr0_count + i * 32) << (GIC_MIN_VBPR + 1);
|
|
} else {
|
|
vapr1 &= vapr1 - 1;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_AP1R0_EL2 + i, vapr1);
|
|
return (vapr1_count + i * 32) << (GIC_MIN_VBPR + 1);
|
|
}
|
|
}
|
|
|
|
return 0xff;
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::generateSGI(RegVal val, Gicv3::GroupId group)
|
|
{
|
|
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);
|
|
|
|
bool ns = !inSecureState();
|
|
|
|
for (int i = 0; i < gic->getSystem()->threads.size(); 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);
|
|
}
|
|
}
|
|
|
|
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 = 0;
|
|
switch (group) {
|
|
case Gicv3::G0S:
|
|
apr_idx = MISCREG_ICC_AP0R0_EL1;
|
|
break;
|
|
case Gicv3::G1S:
|
|
apr_idx = MISCREG_ICC_AP1R0_EL1_S;
|
|
break;
|
|
case Gicv3::G1NS:
|
|
apr_idx = MISCREG_ICC_AP1R0_EL1_NS;
|
|
break;
|
|
default:
|
|
panic("Invalid Gicv3::GroupId");
|
|
}
|
|
|
|
RegVal 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);
|
|
} else if (int_id < Gicv3::INTID_SECURE) {
|
|
// SPI, distributor
|
|
distributor->activateIRQ(int_id);
|
|
} else if (int_id >= Gicv3Redistributor::SMALLEST_LPI_ID) {
|
|
// LPI, Redistributor
|
|
redistributor->setClrLPI(int_id, false);
|
|
}
|
|
|
|
// By setting the priority to 0xff we are effectively
|
|
// making the int_id not pending anymore at the cpu
|
|
// interface.
|
|
resetHppi(int_id);
|
|
updateDistributor();
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::virtualActivateIRQ(uint32_t lr_idx)
|
|
{
|
|
// Update active priority registers.
|
|
ICH_LR_EL2 ich_lr_el = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
|
|
lr_idx);
|
|
Gicv3::GroupId group = ich_lr_el.Group ? Gicv3::G1NS : Gicv3::G0S;
|
|
uint8_t prio = ich_lr_el.Priority & 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;
|
|
RegVal apr = isa->readMiscRegNoEffect(apr_idx);
|
|
apr |= (1 << reg_bit);
|
|
isa->setMiscRegNoEffect(apr_idx, apr);
|
|
// Move interrupt state from pending to active.
|
|
ich_lr_el.State = ICH_LR_EL2_STATE_ACTIVE;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx, ich_lr_el);
|
|
}
|
|
|
|
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);
|
|
} else if (int_id < Gicv3::INTID_SECURE) {
|
|
// SPI, distributor
|
|
distributor->deactivateIRQ(int_id);
|
|
}
|
|
|
|
updateDistributor();
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::virtualDeactivateIRQ(int lr_idx)
|
|
{
|
|
ICH_LR_EL2 ich_lr_el2 = isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 +
|
|
lr_idx);
|
|
|
|
if (ich_lr_el2.HW) {
|
|
// Deactivate the associated physical interrupt
|
|
if (ich_lr_el2.pINTID < Gicv3::INTID_SECURE) {
|
|
Gicv3::GroupId group = ich_lr_el2.pINTID >= 32 ?
|
|
distributor->getIntGroup(ich_lr_el2.pINTID) :
|
|
redistributor->getIntGroup(ich_lr_el2.pINTID);
|
|
deactivateIRQ(ich_lr_el2.pINTID, group);
|
|
}
|
|
}
|
|
|
|
// Remove the active bit
|
|
ich_lr_el2.State = ich_lr_el2.State & ~ICH_LR_EL2_STATE_ACTIVE;
|
|
isa->setMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx, ich_lr_el2);
|
|
}
|
|
|
|
/*
|
|
* Returns the priority group field for the current BPR value for the group.
|
|
* GroupBits() Pseudocode from spec.
|
|
*/
|
|
uint32_t
|
|
Gicv3CPUInterface::groupPriorityMask(Gicv3::GroupId group)
|
|
{
|
|
ICC_CTLR_EL1 icc_ctlr_el1_s =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
|
|
ICC_CTLR_EL1 icc_ctlr_el1_ns =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
|
|
|
|
if ((group == Gicv3::G1S && icc_ctlr_el1_s.CBPR) ||
|
|
(group == Gicv3::G1NS && icc_ctlr_el1_ns.CBPR)) {
|
|
group = Gicv3::G0S;
|
|
}
|
|
|
|
int bpr;
|
|
|
|
if (group == Gicv3::G0S) {
|
|
bpr = readMiscReg(MISCREG_ICC_BPR0_EL1) & 0x7;
|
|
} else if (group == Gicv3::G1S) {
|
|
bpr = bpr1(Gicv3::G1S) & 0x7;
|
|
} else {
|
|
bpr = bpr1(Gicv3::G1NS) & 0x7;
|
|
}
|
|
|
|
if (group == Gicv3::G1NS) {
|
|
assert(bpr > 0);
|
|
bpr--;
|
|
}
|
|
|
|
return ~0U << (bpr + 1);
|
|
}
|
|
|
|
uint32_t
|
|
Gicv3CPUInterface::virtualGroupPriorityMask(Gicv3::GroupId group) const
|
|
{
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
if ((group == Gicv3::G1NS) && ich_vmcr_el2.VCBPR) {
|
|
group = Gicv3::G0S;
|
|
}
|
|
|
|
int bpr;
|
|
|
|
if (group == Gicv3::G0S) {
|
|
bpr = ich_vmcr_el2.VBPR0;
|
|
} else {
|
|
bpr = ich_vmcr_el2.VBPR1;
|
|
}
|
|
|
|
if (group == Gicv3::G1NS) {
|
|
assert(bpr > 0);
|
|
bpr--;
|
|
}
|
|
|
|
return ~0U << (bpr + 1);
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::isEOISplitMode() const
|
|
{
|
|
if (isEL3OrMon()) {
|
|
ICC_CTLR_EL3 icc_ctlr_el3 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL3);
|
|
return icc_ctlr_el3.EOImode_EL3;
|
|
} else {
|
|
ICC_CTLR_EL1 icc_ctlr_el1 = 0;
|
|
if (inSecureState())
|
|
icc_ctlr_el1 = isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
|
|
else
|
|
icc_ctlr_el1 = isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
|
|
return icc_ctlr_el1.EOImode;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::virtualIsEOISplitMode() const
|
|
{
|
|
ICH_VMCR_EL2 ich_vmcr_el2 = isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
return ich_vmcr_el2.VEOIM;
|
|
}
|
|
|
|
int
|
|
Gicv3CPUInterface::highestActiveGroup() const
|
|
{
|
|
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::updateDistributor()
|
|
{
|
|
distributor->update();
|
|
}
|
|
|
|
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()) {
|
|
InterruptTypes int_type = intSignalType(hppi.group);
|
|
DPRINTF(GIC, "Gicv3CPUInterface::update(): "
|
|
"posting int as %d!\n", int_type);
|
|
int_type == INT_IRQ ? signal_IRQ = true : signal_FIQ = true;
|
|
}
|
|
|
|
if (signal_IRQ) {
|
|
gic->postInt(cpuId, INT_IRQ);
|
|
} else {
|
|
gic->deassertInt(cpuId, INT_IRQ);
|
|
}
|
|
|
|
if (signal_FIQ) {
|
|
gic->postInt(cpuId, INT_FIQ);
|
|
} else {
|
|
gic->deassertInt(cpuId, INT_FIQ);
|
|
}
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::virtualUpdate()
|
|
{
|
|
bool signal_IRQ = false;
|
|
bool signal_FIQ = false;
|
|
int lr_idx = getHPPVILR();
|
|
|
|
if (lr_idx >= 0) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if (hppviCanPreempt(lr_idx)) {
|
|
if (ich_lr_el2.Group) {
|
|
signal_IRQ = true;
|
|
} else {
|
|
signal_FIQ = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
ICH_HCR_EL2 ich_hcr_el2 = isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
|
|
|
|
const bool maint_pending = redistributor->irqPending[
|
|
maintenanceInterrupt->num()];
|
|
|
|
if (ich_hcr_el2.En && !maint_pending && maintenanceInterruptStatus()) {
|
|
maintenanceInterrupt->raise();
|
|
} else if (maint_pending) {
|
|
maintenanceInterrupt->clear();
|
|
}
|
|
|
|
if (signal_IRQ) {
|
|
DPRINTF(GIC, "Gicv3CPUInterface::virtualUpdate(): "
|
|
"posting int as %d!\n", INT_VIRT_IRQ);
|
|
gic->postInt(cpuId, INT_VIRT_IRQ);
|
|
} else {
|
|
gic->deassertInt(cpuId, INT_VIRT_IRQ);
|
|
}
|
|
|
|
if (signal_FIQ) {
|
|
DPRINTF(GIC, "Gicv3CPUInterface::virtualUpdate(): "
|
|
"posting int as %d!\n", INT_VIRT_FIQ);
|
|
gic->postInt(cpuId, INT_VIRT_FIQ);
|
|
} else {
|
|
gic->deassertInt(cpuId, INT_VIRT_FIQ);
|
|
}
|
|
}
|
|
|
|
// Returns the index of the LR with the HPPI
|
|
int
|
|
Gicv3CPUInterface::getHPPVILR() const
|
|
{
|
|
int idx = -1;
|
|
ICH_VMCR_EL2 ich_vmcr_el2 = isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
if (!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++) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + i);
|
|
|
|
if (ich_lr_el2.State != Gicv3::INT_PENDING) {
|
|
continue;
|
|
}
|
|
|
|
if (ich_lr_el2.Group) {
|
|
// VG1
|
|
if (!ich_vmcr_el2.VENG1) {
|
|
continue;
|
|
}
|
|
} else {
|
|
// VG0
|
|
if (!ich_vmcr_el2.VENG0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
uint8_t prio = ich_lr_el2.Priority;
|
|
|
|
if (prio < highest_prio) {
|
|
highest_prio = prio;
|
|
idx = i;
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::hppviCanPreempt(int lr_idx) const
|
|
{
|
|
ICH_HCR_EL2 ich_hcr_el2 = isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
|
|
if (!ich_hcr_el2.En) {
|
|
// virtual interface is disabled
|
|
return false;
|
|
}
|
|
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
uint8_t prio = ich_lr_el2.Priority;
|
|
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 = 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() const
|
|
{
|
|
uint8_t num_aprs = 1 << (VIRTUAL_PRIORITY_BITS - 5);
|
|
|
|
for (int i = 0; i < num_aprs; i++) {
|
|
RegVal 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
|
|
RegVal 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);
|
|
}
|
|
|
|
// spec section 4.6.2
|
|
InterruptTypes
|
|
Gicv3CPUInterface::intSignalType(Gicv3::GroupId group) const
|
|
{
|
|
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 INT_FIQ;
|
|
} else {
|
|
return 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() const
|
|
{
|
|
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) const
|
|
{
|
|
switch (group) {
|
|
case Gicv3::G0S: {
|
|
ICC_IGRPEN0_EL1 icc_igrpen0_el1 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN0_EL1);
|
|
return icc_igrpen0_el1.Enable && distributor->EnableGrp0;
|
|
}
|
|
|
|
case Gicv3::G1S: {
|
|
ICC_IGRPEN1_EL1 icc_igrpen1_el1_s =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_S);
|
|
return icc_igrpen1_el1_s.Enable && distributor->EnableGrp1S;
|
|
}
|
|
|
|
case Gicv3::G1NS: {
|
|
ICC_IGRPEN1_EL1 icc_igrpen1_el1_ns =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_IGRPEN1_EL1_NS);
|
|
return icc_igrpen1_el1_ns.Enable && distributor->EnableGrp1NS;
|
|
}
|
|
|
|
default:
|
|
panic("Gicv3CPUInterface::groupEnable(): invalid group!\n");
|
|
}
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::inSecureState() const
|
|
{
|
|
if (!gic->getSystem()->haveSecurity()) {
|
|
return false;
|
|
}
|
|
|
|
CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
|
|
SCR scr = isa->readMiscRegNoEffect(MISCREG_SCR);
|
|
return ::inSecureState(scr, cpsr);
|
|
}
|
|
|
|
int
|
|
Gicv3CPUInterface::currEL() const
|
|
{
|
|
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) const
|
|
{
|
|
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() const
|
|
{
|
|
SCR scr = isa->readMiscRegNoEffect(MISCREG_SCR_EL3);
|
|
return haveEL(EL3) && scr.ns == 0;
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::isAA64() const
|
|
{
|
|
CPSR cpsr = isa->readMiscRegNoEffect(MISCREG_CPSR);
|
|
return opModeIs64((OperatingMode)(uint8_t) cpsr.mode);
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::isEL3OrMon() const
|
|
{
|
|
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;
|
|
}
|
|
|
|
// Computes ICH_EISR_EL2
|
|
uint64_t
|
|
Gicv3CPUInterface::eoiMaintenanceInterruptStatus() const
|
|
{
|
|
// ICH_EISR_EL2
|
|
// Bits [63:16] - RES0
|
|
// Status<n>, bit [n], for n = 0 to 15
|
|
// EOI maintenance interrupt status bit for List register <n>:
|
|
// 0 if List register <n>, ICH_LR<n>_EL2, does not have an EOI
|
|
// maintenance interrupt.
|
|
// 1 if List register <n>, ICH_LR<n>_EL2, has an EOI maintenance
|
|
// interrupt that has not been handled.
|
|
//
|
|
// For any ICH_LR<n>_EL2, the corresponding status bit is set to 1 if all
|
|
// of the following are true:
|
|
// - ICH_LR<n>_EL2.State is 0b00 (ICH_LR_EL2_STATE_INVALID).
|
|
// - ICH_LR<n>_EL2.HW is 0.
|
|
// - ICH_LR<n>_EL2.EOI (bit [41]) is 1.
|
|
|
|
uint64_t value = 0;
|
|
|
|
for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if ((ich_lr_el2.State == ICH_LR_EL2_STATE_INVALID) &&
|
|
!ich_lr_el2.HW && ich_lr_el2.EOI) {
|
|
value |= (1 << lr_idx);
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
Gicv3CPUInterface::ICH_MISR_EL2
|
|
Gicv3CPUInterface::maintenanceInterruptStatus() const
|
|
{
|
|
// Comments are copied from SPEC section 9.4.7 (ID012119)
|
|
ICH_MISR_EL2 ich_misr_el2 = 0;
|
|
ICH_HCR_EL2 ich_hcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_HCR_EL2);
|
|
ICH_VMCR_EL2 ich_vmcr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_VMCR_EL2);
|
|
|
|
// End Of Interrupt. [bit 0]
|
|
// This maintenance interrupt is asserted when at least one bit in
|
|
// ICH_EISR_EL2 is 1.
|
|
|
|
if (eoiMaintenanceInterruptStatus()) {
|
|
ich_misr_el2.EOI = 1;
|
|
}
|
|
|
|
// Underflow. [bit 1]
|
|
// This maintenance interrupt is asserted when ICH_HCR_EL2.UIE==1 and
|
|
// zero or one of the List register entries are marked as a valid
|
|
// interrupt, that is, if the corresponding ICH_LR<n>_EL2.State bits
|
|
// do not equal 0x0.
|
|
uint32_t num_valid_interrupts = 0;
|
|
uint32_t num_pending_interrupts = 0;
|
|
|
|
for (int lr_idx = 0; lr_idx < VIRTUAL_NUM_LIST_REGS; lr_idx++) {
|
|
ICH_LR_EL2 ich_lr_el2 =
|
|
isa->readMiscRegNoEffect(MISCREG_ICH_LR0_EL2 + lr_idx);
|
|
|
|
if (ich_lr_el2.State != ICH_LR_EL2_STATE_INVALID) {
|
|
num_valid_interrupts++;
|
|
}
|
|
|
|
if (ich_lr_el2.State == ICH_LR_EL2_STATE_PENDING) {
|
|
num_pending_interrupts++;
|
|
}
|
|
}
|
|
|
|
if (ich_hcr_el2.UIE && (num_valid_interrupts < 2)) {
|
|
ich_misr_el2.U = 1;
|
|
}
|
|
|
|
// List Register Entry Not Present. [bit 2]
|
|
// This maintenance interrupt is asserted when ICH_HCR_EL2.LRENPIE==1
|
|
// and ICH_HCR_EL2.EOIcount is non-zero.
|
|
if (ich_hcr_el2.LRENPIE && ich_hcr_el2.EOIcount) {
|
|
ich_misr_el2.LRENP = 1;
|
|
}
|
|
|
|
// No Pending. [bit 3]
|
|
// This maintenance interrupt is asserted when ICH_HCR_EL2.NPIE==1 and
|
|
// no List register is in pending state.
|
|
if (ich_hcr_el2.NPIE && (num_pending_interrupts == 0)) {
|
|
ich_misr_el2.NP = 1;
|
|
}
|
|
|
|
// vPE Group 0 Enabled. [bit 4]
|
|
// This maintenance interrupt is asserted when
|
|
// ICH_HCR_EL2.VGrp0EIE==1 and ICH_VMCR_EL2.VENG0==1.
|
|
if (ich_hcr_el2.VGrp0EIE && ich_vmcr_el2.VENG0) {
|
|
ich_misr_el2.VGrp0E = 1;
|
|
}
|
|
|
|
// vPE Group 0 Disabled. [bit 5]
|
|
// This maintenance interrupt is asserted when
|
|
// ICH_HCR_EL2.VGrp0DIE==1 and ICH_VMCR_EL2.VENG0==0.
|
|
if (ich_hcr_el2.VGrp0DIE && !ich_vmcr_el2.VENG0) {
|
|
ich_misr_el2.VGrp0D = 1;
|
|
}
|
|
|
|
// vPE Group 1 Enabled. [bit 6]
|
|
// This maintenance interrupt is asserted when
|
|
// ICH_HCR_EL2.VGrp1EIE==1 and ICH_VMCR_EL2.VENG1==is 1.
|
|
if (ich_hcr_el2.VGrp1EIE && ich_vmcr_el2.VENG1) {
|
|
ich_misr_el2.VGrp1E = 1;
|
|
}
|
|
|
|
// vPE Group 1 Disabled. [bit 7]
|
|
// This maintenance interrupt is asserted when
|
|
// ICH_HCR_EL2.VGrp1DIE==1 and ICH_VMCR_EL2.VENG1==is 0.
|
|
if (ich_hcr_el2.VGrp1DIE && !ich_vmcr_el2.VENG1) {
|
|
ich_misr_el2.VGrp1D = 1;
|
|
}
|
|
|
|
return ich_misr_el2;
|
|
}
|
|
|
|
RegVal
|
|
Gicv3CPUInterface::bpr1(Gicv3::GroupId group)
|
|
{
|
|
bool hcr_imo = getHCREL2IMO();
|
|
if ((currEL() == EL1) && !inSecureState() && hcr_imo) {
|
|
return readMiscReg(MISCREG_ICV_BPR1_EL1);
|
|
}
|
|
|
|
RegVal bpr = 0;
|
|
|
|
if (group == Gicv3::G1S) {
|
|
ICC_CTLR_EL1 icc_ctlr_el1_s =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_S);
|
|
|
|
if (!isEL3OrMon() && icc_ctlr_el1_s.CBPR) {
|
|
bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1);
|
|
} else {
|
|
bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR1_EL1_S);
|
|
bpr = bpr > GIC_MIN_BPR ? bpr : GIC_MIN_BPR;
|
|
}
|
|
} else if (group == Gicv3::G1NS) {
|
|
ICC_CTLR_EL1 icc_ctlr_el1_ns =
|
|
isa->readMiscRegNoEffect(MISCREG_ICC_CTLR_EL1_NS);
|
|
|
|
// Check if EL3 is implemented and this is a non secure accesses at
|
|
// EL1 and EL2
|
|
if (haveEL(EL3) && !isEL3OrMon() && icc_ctlr_el1_ns.CBPR) {
|
|
// Reads return BPR0 + 1 saturated to 7, WI
|
|
bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR0_EL1) + 1;
|
|
bpr = bpr < 7 ? bpr : 7;
|
|
} else {
|
|
bpr = isa->readMiscRegNoEffect(MISCREG_ICC_BPR1_EL1_NS);
|
|
bpr = bpr > GIC_MIN_BPR_NS ? bpr : GIC_MIN_BPR_NS;
|
|
}
|
|
} else {
|
|
panic("Should be used with G1S and G1NS only\n");
|
|
}
|
|
|
|
return bpr;
|
|
}
|
|
|
|
bool
|
|
Gicv3CPUInterface::havePendingInterrupts() const
|
|
{
|
|
return gic->haveAsserted(cpuId) || hppi.prio != 0xff;
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::clearPendingInterrupts()
|
|
{
|
|
gic->deassertAll(cpuId);
|
|
resetHppi(hppi.intid);
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::assertWakeRequest()
|
|
{
|
|
auto *tc = gic->getSystem()->threads[cpuId];
|
|
if (ArmSystem::callSetWakeRequest(tc)) {
|
|
Reset().invoke(tc);
|
|
tc->activate();
|
|
}
|
|
}
|
|
|
|
void
|
|
Gicv3CPUInterface::deassertWakeRequest()
|
|
{
|
|
auto *tc = gic->getSystem()->threads[cpuId];
|
|
ArmSystem::callClearWakeRequest(tc);
|
|
}
|
|
|
|
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);
|
|
}
|