Files
gem5/src/arch/arm/faults.cc
Gabe Black 4fe56ff720 arch-arm,cpu: Replace rename modes with split reg/elem register files.
This simplifies the O3 CPU, and removes special cases around how vector
registers are handled. Now ARM is responsible for maintaining its
different register personalities internally.

Also, this re-establishes the invariant that registers are indexed as
complete, opaque entities with no internal structure, at least as far as
the CPU is concerned.

To make sure the KVM CPU sees the correct state, we need to sync over
the vector registers if we're in 32 bit mode when moving state to or
from gem5's ThreadContext.

Change-Id: I36416d609310ae0bc50c18809f5d9e19bfbb4d37
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/49147
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
2021-10-28 06:00:07 +00:00

1874 lines
54 KiB
C++

/*
* Copyright (c) 2010, 2012-2014, 2016-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) 2003-2005 The Regents of The University of Michigan
* Copyright (c) 2007-2008 The Florida State University
* 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 "arch/arm/faults.hh"
#include "arch/arm/insts/static_inst.hh"
#include "arch/arm/interrupts.hh"
#include "arch/arm/isa.hh"
#include "arch/arm/self_debug.hh"
#include "arch/arm/system.hh"
#include "arch/arm/utility.hh"
#include "base/compiler.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/thread_context.hh"
#include "debug/Faults.hh"
#include "sim/full_system.hh"
namespace gem5
{
namespace ArmISA
{
const uint32_t HighVecs = 0xFFFF0000;
uint8_t ArmFault::shortDescFaultSources[] = {
0x01, // AlignmentFault
0x04, // InstructionCacheMaintenance
0xff, // SynchExtAbtOnTranslTableWalkL0 (INVALID)
0x0c, // SynchExtAbtOnTranslTableWalkL1
0x0e, // SynchExtAbtOnTranslTableWalkL2
0xff, // SynchExtAbtOnTranslTableWalkL3 (INVALID)
0xff, // SynchPtyErrOnTranslTableWalkL0 (INVALID)
0x1c, // SynchPtyErrOnTranslTableWalkL1
0x1e, // SynchPtyErrOnTranslTableWalkL2
0xff, // SynchPtyErrOnTranslTableWalkL3 (INVALID)
0xff, // TranslationL0 (INVALID)
0x05, // TranslationL1
0x07, // TranslationL2
0xff, // TranslationL3 (INVALID)
0xff, // AccessFlagL0 (INVALID)
0x03, // AccessFlagL1
0x06, // AccessFlagL2
0xff, // AccessFlagL3 (INVALID)
0xff, // DomainL0 (INVALID)
0x09, // DomainL1
0x0b, // DomainL2
0xff, // DomainL3 (INVALID)
0xff, // PermissionL0 (INVALID)
0x0d, // PermissionL1
0x0f, // PermissionL2
0xff, // PermissionL3 (INVALID)
0x02, // DebugEvent
0x08, // SynchronousExternalAbort
0x10, // TLBConflictAbort
0x19, // SynchPtyErrOnMemoryAccess
0x16, // AsynchronousExternalAbort
0x18, // AsynchPtyErrOnMemoryAccess
0xff, // AddressSizeL0 (INVALID)
0xff, // AddressSizeL1 (INVALID)
0xff, // AddressSizeL2 (INVALID)
0xff, // AddressSizeL3 (INVALID)
0x40, // PrefetchTLBMiss
0x80 // PrefetchUncacheable
};
static_assert(sizeof(ArmFault::shortDescFaultSources) ==
ArmFault::NumFaultSources,
"Invalid size of ArmFault::shortDescFaultSources[]");
uint8_t ArmFault::longDescFaultSources[] = {
0x21, // AlignmentFault
0xff, // InstructionCacheMaintenance (INVALID)
0xff, // SynchExtAbtOnTranslTableWalkL0 (INVALID)
0x15, // SynchExtAbtOnTranslTableWalkL1
0x16, // SynchExtAbtOnTranslTableWalkL2
0x17, // SynchExtAbtOnTranslTableWalkL3
0xff, // SynchPtyErrOnTranslTableWalkL0 (INVALID)
0x1d, // SynchPtyErrOnTranslTableWalkL1
0x1e, // SynchPtyErrOnTranslTableWalkL2
0x1f, // SynchPtyErrOnTranslTableWalkL3
0xff, // TranslationL0 (INVALID)
0x05, // TranslationL1
0x06, // TranslationL2
0x07, // TranslationL3
0xff, // AccessFlagL0 (INVALID)
0x09, // AccessFlagL1
0x0a, // AccessFlagL2
0x0b, // AccessFlagL3
0xff, // DomainL0 (INVALID)
0x3d, // DomainL1
0x3e, // DomainL2
0xff, // DomainL3 (RESERVED)
0xff, // PermissionL0 (INVALID)
0x0d, // PermissionL1
0x0e, // PermissionL2
0x0f, // PermissionL3
0x22, // DebugEvent
0x10, // SynchronousExternalAbort
0x30, // TLBConflictAbort
0x18, // SynchPtyErrOnMemoryAccess
0x11, // AsynchronousExternalAbort
0x19, // AsynchPtyErrOnMemoryAccess
0xff, // AddressSizeL0 (INVALID)
0xff, // AddressSizeL1 (INVALID)
0xff, // AddressSizeL2 (INVALID)
0xff, // AddressSizeL3 (INVALID)
0x40, // PrefetchTLBMiss
0x80 // PrefetchUncacheable
};
static_assert(sizeof(ArmFault::longDescFaultSources) ==
ArmFault::NumFaultSources,
"Invalid size of ArmFault::longDescFaultSources[]");
uint8_t ArmFault::aarch64FaultSources[] = {
0x21, // AlignmentFault
0xff, // InstructionCacheMaintenance (INVALID)
0x14, // SynchExtAbtOnTranslTableWalkL0
0x15, // SynchExtAbtOnTranslTableWalkL1
0x16, // SynchExtAbtOnTranslTableWalkL2
0x17, // SynchExtAbtOnTranslTableWalkL3
0x1c, // SynchPtyErrOnTranslTableWalkL0
0x1d, // SynchPtyErrOnTranslTableWalkL1
0x1e, // SynchPtyErrOnTranslTableWalkL2
0x1f, // SynchPtyErrOnTranslTableWalkL3
0x04, // TranslationL0
0x05, // TranslationL1
0x06, // TranslationL2
0x07, // TranslationL3
0x08, // AccessFlagL0
0x09, // AccessFlagL1
0x0a, // AccessFlagL2
0x0b, // AccessFlagL3
// @todo: Section & Page Domain Fault in AArch64?
0xff, // DomainL0 (INVALID)
0xff, // DomainL1 (INVALID)
0xff, // DomainL2 (INVALID)
0xff, // DomainL3 (INVALID)
0x0c, // PermissionL0
0x0d, // PermissionL1
0x0e, // PermissionL2
0x0f, // PermissionL3
0x22, // DebugEvent
0x10, // SynchronousExternalAbort
0x30, // TLBConflictAbort
0x18, // SynchPtyErrOnMemoryAccess
0xff, // AsynchronousExternalAbort (INVALID)
0xff, // AsynchPtyErrOnMemoryAccess (INVALID)
0x00, // AddressSizeL0
0x01, // AddressSizeL1
0x02, // AddressSizeL2
0x03, // AddressSizeL3
0x40, // PrefetchTLBMiss
0x80 // PrefetchUncacheable
};
static_assert(sizeof(ArmFault::aarch64FaultSources) ==
ArmFault::NumFaultSources,
"Invalid size of ArmFault::aarch64FaultSources[]");
// Fields: name, offset, cur{ELT,ELH}Offset, lowerEL{64,32}Offset, next mode,
// {ARM, Thumb, ARM_ELR, Thumb_ELR} PC offset, hyp trap,
// {A, F} disable, class, stat
template<> ArmFault::FaultVals ArmFaultVals<Reset>::vals(
// Some dummy values (the reset vector has an IMPLEMENTATION DEFINED
// location in AArch64)
"Reset", 0x000, 0x000, 0x000, 0x000, 0x000, MODE_SVC,
0, 0, 0, 0, false, true, true, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<UndefinedInstruction>::vals(
"Undefined Instruction", 0x004, 0x000, 0x200, 0x400, 0x600, MODE_UNDEFINED,
4, 2, 0, 0, true, false, false, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<SupervisorCall>::vals(
"Supervisor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
4, 2, 4, 2, true, false, false, EC_SVC_TO_HYP
);
template<> ArmFault::FaultVals ArmFaultVals<SecureMonitorCall>::vals(
"Secure Monitor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_MON,
4, 4, 4, 4, false, true, true, EC_SMC_TO_HYP
);
template<> ArmFault::FaultVals ArmFaultVals<HypervisorCall>::vals(
"Hypervisor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_HYP,
4, 4, 4, 4, true, false, false, EC_HVC
);
template<> ArmFault::FaultVals ArmFaultVals<PrefetchAbort>::vals(
"Prefetch Abort", 0x00C, 0x000, 0x200, 0x400, 0x600, MODE_ABORT,
4, 4, 0, 0, true, true, false, EC_PREFETCH_ABORT_TO_HYP
);
template<> ArmFault::FaultVals ArmFaultVals<DataAbort>::vals(
"Data Abort", 0x010, 0x000, 0x200, 0x400, 0x600, MODE_ABORT,
8, 8, 0, 0, true, true, false, EC_DATA_ABORT_TO_HYP
);
template<> ArmFault::FaultVals ArmFaultVals<VirtualDataAbort>::vals(
"Virtual Data Abort", 0x010, 0x000, 0x200, 0x400, 0x600, MODE_ABORT,
8, 8, 0, 0, true, true, false, EC_INVALID
);
template<> ArmFault::FaultVals ArmFaultVals<HypervisorTrap>::vals(
// @todo: double check these values
"Hypervisor Trap", 0x014, 0x000, 0x200, 0x400, 0x600, MODE_HYP,
0, 0, 0, 0, false, false, false, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<SecureMonitorTrap>::vals(
"Secure Monitor Trap", 0x004, 0x000, 0x200, 0x400, 0x600, MODE_MON,
4, 2, 0, 0, false, false, false, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<Interrupt>::vals(
"IRQ", 0x018, 0x080, 0x280, 0x480, 0x680, MODE_IRQ,
4, 4, 0, 0, false, true, false, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<VirtualInterrupt>::vals(
"Virtual IRQ", 0x018, 0x080, 0x280, 0x480, 0x680, MODE_IRQ,
4, 4, 0, 0, false, true, false, EC_INVALID
);
template<> ArmFault::FaultVals ArmFaultVals<FastInterrupt>::vals(
"FIQ", 0x01C, 0x100, 0x300, 0x500, 0x700, MODE_FIQ,
4, 4, 0, 0, false, true, true, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<VirtualFastInterrupt>::vals(
"Virtual FIQ", 0x01C, 0x100, 0x300, 0x500, 0x700, MODE_FIQ,
4, 4, 0, 0, false, true, true, EC_INVALID
);
template<> ArmFault::FaultVals ArmFaultVals<IllegalInstSetStateFault>::vals(
"Illegal Inst Set State Fault", 0x004, 0x000, 0x200, 0x400, 0x600, MODE_UNDEFINED,
4, 2, 0, 0, true, false, false, EC_ILLEGAL_INST
);
template<> ArmFault::FaultVals ArmFaultVals<SupervisorTrap>::vals(
// Some dummy values (SupervisorTrap is AArch64-only)
"Supervisor Trap", 0x014, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, false, false, false, EC_UNKNOWN
);
template<> ArmFault::FaultVals ArmFaultVals<PCAlignmentFault>::vals(
// Some dummy values (PCAlignmentFault is AArch64-only)
"PC Alignment Fault", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, true, false, false, EC_PC_ALIGNMENT
);
template<> ArmFault::FaultVals ArmFaultVals<SPAlignmentFault>::vals(
// Some dummy values (SPAlignmentFault is AArch64-only)
"SP Alignment Fault", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, true, false, false, EC_STACK_PTR_ALIGNMENT
);
template<> ArmFault::FaultVals ArmFaultVals<SystemError>::vals(
// Some dummy values (SError is AArch64-only)
"SError", 0x000, 0x180, 0x380, 0x580, 0x780, MODE_SVC,
0, 0, 0, 0, false, true, true, EC_SERROR
);
template<> ArmFault::FaultVals ArmFaultVals<SoftwareBreakpoint>::vals(
// Some dummy values (SoftwareBreakpoint is AArch64-only)
"Software Breakpoint", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, true, false, false, EC_SOFTWARE_BREAKPOINT
);
template<> ArmFault::FaultVals ArmFaultVals<HardwareBreakpoint>::vals(
"Hardware Breakpoint", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, true, false, false, EC_HW_BREAKPOINT
);
template<> ArmFault::FaultVals ArmFaultVals<Watchpoint>::vals(
"Watchpoint", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, true, false, false, EC_WATCHPOINT
);
template<> ArmFault::FaultVals ArmFaultVals<SoftwareStepFault>::vals(
"SoftwareStep", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC,
0, 0, 0, 0, true, false, false, EC_SOFTWARE_STEP
);
template<> ArmFault::FaultVals ArmFaultVals<ArmSev>::vals(
// Some dummy values
"ArmSev Flush", 0x000, 0x000, 0x000, 0x000, 0x000, MODE_SVC,
0, 0, 0, 0, false, true, true, EC_UNKNOWN
);
Addr
ArmFault::getVector(ThreadContext *tc)
{
Addr base;
// Check for invalid modes
CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR);
assert(ArmSystem::haveEL(tc, EL3) || cpsr.mode != MODE_MON);
assert(ArmSystem::haveEL(tc, EL2) || cpsr.mode != MODE_HYP);
switch (cpsr.mode)
{
case MODE_MON:
base = tc->readMiscReg(MISCREG_MVBAR);
break;
case MODE_HYP:
base = tc->readMiscReg(MISCREG_HVBAR);
break;
default:
SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR);
if (sctlr.v) {
base = HighVecs;
} else {
base = ArmSystem::haveEL(tc, EL3) ?
tc->readMiscReg(MISCREG_VBAR) : 0;
}
break;
}
return base + offset(tc);
}
Addr
ArmFault::getVector64(ThreadContext *tc)
{
Addr vbar;
switch (toEL) {
case EL3:
assert(ArmSystem::haveEL(tc, EL3));
vbar = tc->readMiscReg(MISCREG_VBAR_EL3);
break;
case EL2:
assert(ArmSystem::haveEL(tc, EL2));
vbar = tc->readMiscReg(MISCREG_VBAR_EL2);
break;
case EL1:
vbar = tc->readMiscReg(MISCREG_VBAR_EL1);
break;
default:
panic("Invalid target exception level");
break;
}
return vbar + offset64(tc);
}
MiscRegIndex
ArmFault::getSyndromeReg64() const
{
switch (toEL) {
case EL1:
return MISCREG_ESR_EL1;
case EL2:
return MISCREG_ESR_EL2;
case EL3:
return MISCREG_ESR_EL3;
default:
panic("Invalid exception level");
break;
}
}
MiscRegIndex
ArmFault::getFaultAddrReg64() const
{
switch (toEL) {
case EL1:
return MISCREG_FAR_EL1;
case EL2:
return MISCREG_FAR_EL2;
case EL3:
return MISCREG_FAR_EL3;
default:
panic("Invalid exception level");
break;
}
}
void
ArmFault::setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg)
{
uint32_t value;
uint32_t exc_class = (uint32_t) ec(tc);
uint32_t issVal = iss();
assert(!from64 || ArmSystem::highestELIs64(tc));
value = exc_class << 26;
// HSR.IL not valid for Prefetch Aborts (0x20, 0x21) and Data Aborts (0x24,
// 0x25) for which the ISS information is not valid (ARMv7).
// @todo: ARMv8 revises AArch32 functionality: when HSR.IL is not
// valid it is treated as RES1.
if (to64) {
value |= 1 << 25;
} else if ((bits(exc_class, 5, 3) != 4) ||
(bits(exc_class, 2) && bits(issVal, 24))) {
if (!machInst.thumb || machInst.bigThumb)
value |= 1 << 25;
}
// Condition code valid for EC[5:4] nonzero
if (!from64 && ((bits(exc_class, 5, 4) == 0) &&
(bits(exc_class, 3, 0) != 0))) {
if (!machInst.thumb) {
uint32_t cond;
ConditionCode condCode = (ConditionCode) (uint32_t) machInst.condCode;
// If its on unconditional instruction report with a cond code of
// 0xE, ie the unconditional code
cond = (condCode == COND_UC) ? COND_AL : condCode;
value |= cond << 20;
value |= 1 << 24;
}
value |= bits(issVal, 19, 0);
} else {
value |= issVal;
}
tc->setMiscReg(syndrome_reg, value);
}
void
ArmFault::update(ThreadContext *tc)
{
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
// Determine source exception level and mode
fromMode = (OperatingMode) (uint8_t) cpsr.mode;
fromEL = opModeToEL(fromMode);
if (opModeIs64(fromMode))
from64 = true;
// Determine target exception level (aarch64) or target execution
// mode (aarch32).
if (ArmSystem::haveEL(tc, EL3) && routeToMonitor(tc)) {
toMode = MODE_MON;
toEL = EL3;
} else if (ArmSystem::haveEL(tc, EL2) && routeToHyp(tc)) {
toMode = MODE_HYP;
toEL = EL2;
hypRouted = true;
} else {
toMode = nextMode();
toEL = opModeToEL(toMode);
}
if (fromEL > toEL)
toEL = fromEL;
// Check for Set Priviledge Access Never, if PAN is supported
AA64MMFR1 mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
if (mmfr1.pan) {
if (toEL == EL1) {
const SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR_EL1);
span = !sctlr.span;
}
const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
if (toEL == EL2 && hcr.e2h && hcr.tge) {
const SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR_EL2);
span = !sctlr.span;
}
}
to64 = ELIs64(tc, toEL);
// The fault specific informations have been updated; it is
// now possible to use them inside the fault.
faultUpdated = true;
}
void
ArmFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
// Update fault state informations, like the starting mode (aarch32)
// or EL (aarch64) and the ending mode or EL.
// From the update function we are also evaluating if the fault must
// be handled in AArch64 mode (to64).
update(tc);
if (from64 != to64) {
// Switching modes, sync up versions of the vector register file.
if (from64) {
syncVecRegsToElems(tc);
} else {
syncVecElemsToRegs(tc);
}
}
if (to64) {
// Invoke exception handler in AArch64 state
invoke64(tc, inst);
} else {
// Invoke exception handler in AArch32 state
invoke32(tc, inst);
}
}
void
ArmFault::invoke32(ThreadContext *tc, const StaticInstPtr &inst)
{
if (vectorCatch(tc, inst))
return;
// ARMv7 (ARM ARM issue C B1.9)
bool have_security = ArmSystem::haveEL(tc, EL3);
FaultBase::invoke(tc);
if (!FullSystem)
return;
countStat()++;
SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR);
SCR scr = tc->readMiscReg(MISCREG_SCR);
CPSR saved_cpsr = tc->readMiscReg(MISCREG_CPSR);
saved_cpsr.nz = tc->readCCReg(CCREG_NZ);
saved_cpsr.c = tc->readCCReg(CCREG_C);
saved_cpsr.v = tc->readCCReg(CCREG_V);
saved_cpsr.ge = tc->readCCReg(CCREG_GE);
[[maybe_unused]] Addr cur_pc = tc->pcState().pc();
ITSTATE it = tc->pcState().itstate();
saved_cpsr.it2 = it.top6;
saved_cpsr.it1 = it.bottom2;
// if we have a valid instruction then use it to annotate this fault with
// extra information. This is used to generate the correct fault syndrome
// information
[[maybe_unused]] ArmStaticInst *arm_inst = instrAnnotate(inst);
// Ensure Secure state if initially in Monitor mode
if (have_security && saved_cpsr.mode == MODE_MON) {
SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR);
if (scr.ns) {
scr.ns = 0;
tc->setMiscRegNoEffect(MISCREG_SCR, scr);
}
}
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
cpsr.mode = toMode;
// some bits are set differently if we have been routed to hyp mode
if (cpsr.mode == MODE_HYP) {
SCTLR hsctlr = tc->readMiscReg(MISCREG_HSCTLR);
cpsr.t = hsctlr.te;
cpsr.e = hsctlr.ee;
if (!scr.ea) {cpsr.a = 1;}
if (!scr.fiq) {cpsr.f = 1;}
if (!scr.irq) {cpsr.i = 1;}
} else if (cpsr.mode == MODE_MON) {
// Special case handling when entering monitor mode
cpsr.t = sctlr.te;
cpsr.e = sctlr.ee;
cpsr.a = 1;
cpsr.f = 1;
cpsr.i = 1;
} else {
cpsr.t = sctlr.te;
cpsr.e = sctlr.ee;
// The *Disable functions are virtual and different per fault
cpsr.a = cpsr.a | abortDisable(tc);
cpsr.f = cpsr.f | fiqDisable(tc);
cpsr.i = 1;
}
cpsr.it1 = cpsr.it2 = 0;
cpsr.j = 0;
cpsr.pan = span ? 1 : saved_cpsr.pan;
tc->setMiscReg(MISCREG_CPSR, cpsr);
// Make sure mailbox sets to one always
tc->setMiscReg(MISCREG_SEV_MAILBOX, 1);
// Clear the exclusive monitor
tc->setMiscReg(MISCREG_LOCKFLAG, 0);
if (cpsr.mode == MODE_HYP) {
tc->setMiscReg(MISCREG_ELR_HYP, cur_pc +
(saved_cpsr.t ? thumbPcOffset(true) : armPcOffset(true)));
} else {
tc->setIntReg(INTREG_LR, cur_pc +
(saved_cpsr.t ? thumbPcOffset(false) : armPcOffset(false)));
}
switch (cpsr.mode) {
case MODE_FIQ:
tc->setMiscReg(MISCREG_SPSR_FIQ, saved_cpsr);
break;
case MODE_IRQ:
tc->setMiscReg(MISCREG_SPSR_IRQ, saved_cpsr);
break;
case MODE_SVC:
tc->setMiscReg(MISCREG_SPSR_SVC, saved_cpsr);
break;
case MODE_MON:
assert(have_security);
tc->setMiscReg(MISCREG_SPSR_MON, saved_cpsr);
break;
case MODE_ABORT:
tc->setMiscReg(MISCREG_SPSR_ABT, saved_cpsr);
break;
case MODE_UNDEFINED:
tc->setMiscReg(MISCREG_SPSR_UND, saved_cpsr);
if (ec(tc) != EC_UNKNOWN)
setSyndrome(tc, MISCREG_HSR);
break;
case MODE_HYP:
assert(ArmSystem::haveEL(tc, EL2));
tc->setMiscReg(MISCREG_SPSR_HYP, saved_cpsr);
setSyndrome(tc, MISCREG_HSR);
break;
default:
panic("unknown Mode\n");
}
Addr new_pc = getVector(tc);
DPRINTF(Faults, "Invoking Fault:%s cpsr:%#x PC:%#x lr:%#x newVec: %#x "
"%s\n", name(), cpsr, cur_pc, tc->readIntReg(INTREG_LR),
new_pc, arm_inst ? csprintf("inst: %#x", arm_inst->encoding()) :
std::string());
PCState pc(new_pc);
pc.thumb(cpsr.t);
pc.nextThumb(pc.thumb());
pc.jazelle(cpsr.j);
pc.nextJazelle(pc.jazelle());
pc.aarch64(!cpsr.width);
pc.nextAArch64(!cpsr.width);
pc.illegalExec(false);
tc->pcState(pc);
}
void
ArmFault::invoke64(ThreadContext *tc, const StaticInstPtr &inst)
{
// Determine actual misc. register indices for ELR_ELx and SPSR_ELx
MiscRegIndex elr_idx, spsr_idx;
switch (toEL) {
case EL1:
elr_idx = MISCREG_ELR_EL1;
spsr_idx = MISCREG_SPSR_EL1;
break;
case EL2:
assert(ArmSystem::haveEL(tc, EL2));
elr_idx = MISCREG_ELR_EL2;
spsr_idx = MISCREG_SPSR_EL2;
break;
case EL3:
assert(ArmSystem::haveEL(tc, EL3));
elr_idx = MISCREG_ELR_EL3;
spsr_idx = MISCREG_SPSR_EL3;
break;
default:
panic("Invalid target exception level");
break;
}
// Save process state into SPSR_ELx
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
CPSR spsr = cpsr;
spsr.nz = tc->readCCReg(CCREG_NZ);
spsr.c = tc->readCCReg(CCREG_C);
spsr.v = tc->readCCReg(CCREG_V);
spsr.ss = isResetSPSR() ? 0: cpsr.ss;
if (from64) {
// Force some bitfields to 0
spsr.q = 0;
spsr.it1 = 0;
spsr.j = 0;
spsr.ge = 0;
spsr.it2 = 0;
spsr.t = 0;
} else {
spsr.ge = tc->readCCReg(CCREG_GE);
ITSTATE it = tc->pcState().itstate();
spsr.it2 = it.top6;
spsr.it1 = it.bottom2;
spsr.uao = 0;
}
tc->setMiscReg(spsr_idx, spsr);
// Save preferred return address into ELR_ELx
Addr curr_pc = tc->pcState().pc();
Addr ret_addr = curr_pc;
if (from64)
ret_addr += armPcElrOffset();
else
ret_addr += spsr.t ? thumbPcElrOffset() : armPcElrOffset();
tc->setMiscReg(elr_idx, ret_addr);
Addr vec_address = getVector64(tc);
// Update process state
OperatingMode64 mode = 0;
mode.spX = 1;
mode.el = toEL;
mode.width = 0;
cpsr.mode = mode;
cpsr.daif = 0xf;
cpsr.il = 0;
cpsr.ss = 0;
cpsr.pan = span ? 1 : spsr.pan;
cpsr.uao = 0;
tc->setMiscReg(MISCREG_CPSR, cpsr);
// If we have a valid instruction then use it to annotate this fault with
// extra information. This is used to generate the correct fault syndrome
// information
[[maybe_unused]] ArmStaticInst *arm_inst = instrAnnotate(inst);
// Set PC to start of exception handler
Addr new_pc = purifyTaggedAddr(vec_address, tc, toEL, true);
DPRINTF(Faults, "Invoking Fault (AArch64 target EL):%s cpsr:%#x PC:%#x "
"elr:%#x newVec: %#x %s\n", name(), cpsr, curr_pc, ret_addr,
new_pc, arm_inst ? csprintf("inst: %#x", arm_inst->encoding()) :
std::string());
PCState pc(new_pc);
pc.aarch64(!cpsr.width);
pc.nextAArch64(!cpsr.width);
pc.illegalExec(false);
pc.stepped(false);
tc->pcState(pc);
// Save exception syndrome
if ((nextMode() != MODE_IRQ) && (nextMode() != MODE_FIQ))
setSyndrome(tc, getSyndromeReg64());
}
bool
ArmFault::vectorCatch(ThreadContext *tc, const StaticInstPtr &inst)
{
SelfDebug *sd = ArmISA::ISA::getSelfDebug(tc);
VectorCatch* vc = sd->getVectorCatch(tc);
if (!vc->isVCMatch()) {
Fault fault = sd->testVectorCatch(tc, 0x0, this);
if (fault != NoFault)
fault->invoke(tc, inst);
return true;
}
return false;
}
ArmStaticInst *
ArmFault::instrAnnotate(const StaticInstPtr &inst)
{
if (inst) {
auto arm_inst = static_cast<ArmStaticInst *>(inst.get());
arm_inst->annotateFault(this);
return arm_inst;
} else {
return nullptr;
}
}
Addr
Reset::getVector(ThreadContext *tc)
{
Addr base;
// Check for invalid modes
[[maybe_unused]] CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR);
assert(ArmSystem::haveEL(tc, EL3) || cpsr.mode != MODE_MON);
assert(ArmSystem::haveEL(tc, EL2) || cpsr.mode != MODE_HYP);
// RVBAR is aliased (implemented as) MVBAR in gem5, since the two
// are mutually exclusive; there is no need to check here for
// which register to use since they hold the same value
base = tc->readMiscReg(MISCREG_MVBAR);
return base + offset(tc);
}
void
Reset::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
if (FullSystem) {
tc->getCpuPtr()->clearInterrupts(tc->threadId());
tc->clearArchRegs();
}
if (!ArmSystem::highestELIs64(tc)) {
ArmFault::invoke(tc, inst);
tc->setMiscReg(MISCREG_VMPIDR,
getMPIDR(dynamic_cast<ArmSystem*>(tc->getSystemPtr()), tc));
// Unless we have SMC code to get us there, boot in HYP!
if (ArmSystem::haveEL(tc, EL2) &&
!ArmSystem::haveEL(tc, EL3)) {
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
cpsr.mode = MODE_HYP;
tc->setMiscReg(MISCREG_CPSR, cpsr);
}
} else {
// Advance the PC to the IMPLEMENTATION DEFINED reset value
PCState pc = ArmSystem::resetAddr(tc);
pc.aarch64(true);
pc.nextAArch64(true);
tc->pcState(pc);
}
}
void
UndefinedInstruction::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
if (FullSystem) {
ArmFault::invoke(tc, inst);
return;
}
// If the mnemonic isn't defined this has to be an unknown instruction.
assert(unknown || mnemonic != NULL);
auto arm_inst = static_cast<ArmStaticInst *>(inst.get());
if (disabled) {
panic("Attempted to execute disabled instruction "
"'%s' (inst 0x%08x)", mnemonic, arm_inst->encoding());
} else if (unknown) {
panic("Attempted to execute unknown instruction (inst 0x%08x)",
arm_inst->encoding());
} else {
panic("Attempted to execute unimplemented instruction "
"'%s' (inst 0x%08x)", mnemonic, arm_inst->encoding());
}
}
bool
UndefinedInstruction::routeToHyp(ThreadContext *tc) const
{
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
return fromEL == EL2 ||
(EL2Enabled(tc) && (fromEL == EL0) && hcr.tge);
}
uint32_t
UndefinedInstruction::iss() const
{
// If UndefinedInstruction is routed to hypervisor, iss field is 0.
if (hypRouted) {
return 0;
}
if (overrideEc == EC_INVALID)
return issRaw;
uint32_t new_iss = 0;
uint32_t op0, op1, op2, CRn, CRm, Rt, dir;
dir = bits(machInst, 21, 21);
op0 = bits(machInst, 20, 19);
op1 = bits(machInst, 18, 16);
CRn = bits(machInst, 15, 12);
CRm = bits(machInst, 11, 8);
op2 = bits(machInst, 7, 5);
Rt = bits(machInst, 4, 0);
new_iss = op0 << 20 | op2 << 17 | op1 << 14 | CRn << 10 |
Rt << 5 | CRm << 1 | dir;
return new_iss;
}
void
SupervisorCall::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
if (FullSystem) {
ArmFault::invoke(tc, inst);
return;
}
// As of now, there isn't a 32 bit thumb version of this instruction.
assert(!machInst.bigThumb);
tc->getSystemPtr()->workload->syscall(tc);
// Advance the PC since that won't happen automatically.
PCState pc = tc->pcState();
assert(inst);
inst->advancePC(pc);
tc->pcState(pc);
}
bool
SupervisorCall::routeToHyp(ThreadContext *tc) const
{
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL == EL0 && hcr.tge);
}
ExceptionClass
SupervisorCall::ec(ThreadContext *tc) const
{
return (overrideEc != EC_INVALID) ? overrideEc :
(from64 ? EC_SVC_64 : vals.ec);
}
uint32_t
SupervisorCall::iss() const
{
// Even if we have a 24 bit imm from an arm32 instruction then we only use
// the bottom 16 bits for the ISS value (it doesn't hurt for AArch64 SVC).
return issRaw & 0xFFFF;
}
uint32_t
SecureMonitorCall::iss() const
{
if (from64)
return bits(machInst, 20, 5);
return 0;
}
ExceptionClass
UndefinedInstruction::ec(ThreadContext *tc) const
{
// If UndefinedInstruction is routed to hypervisor,
// HSR.EC field is 0.
if (hypRouted)
return EC_UNKNOWN;
else
return (overrideEc != EC_INVALID) ? overrideEc : vals.ec;
}
HypervisorCall::HypervisorCall(ExtMachInst _machInst, uint32_t _imm) :
ArmFaultVals<HypervisorCall>(_machInst, _imm)
{
bStep = true;
}
bool
HypervisorCall::routeToMonitor(ThreadContext *tc) const
{
return from64 && fromEL == EL3;
}
bool
HypervisorCall::routeToHyp(ThreadContext *tc) const
{
return !from64 || fromEL != EL3;
}
ExceptionClass
HypervisorCall::ec(ThreadContext *tc) const
{
return from64 ? EC_HVC_64 : vals.ec;
}
ExceptionClass
HypervisorTrap::ec(ThreadContext *tc) const
{
return (overrideEc != EC_INVALID) ? overrideEc : vals.ec;
}
template<class T>
FaultOffset
ArmFaultVals<T>::offset(ThreadContext *tc)
{
bool isHypTrap = false;
// Normally we just use the exception vector from the table at the top if
// this file, however if this exception has caused a transition to hype
// mode, and its an exception type that would only do this if it has been
// trapped then we use the hyp trap vector instead of the normal vector
if (vals.hypTrappable) {
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
if (cpsr.mode == MODE_HYP) {
CPSR spsr = tc->readMiscReg(MISCREG_SPSR_HYP);
isHypTrap = spsr.mode != MODE_HYP;
}
}
return isHypTrap ? 0x14 : vals.offset;
}
template<class T>
FaultOffset
ArmFaultVals<T>::offset64(ThreadContext *tc)
{
if (toEL == fromEL) {
if (opModeIsT(fromMode))
return vals.currELTOffset;
return vals.currELHOffset;
} else {
bool lower_32 = false;
if (toEL == EL3) {
if (EL2Enabled(tc))
lower_32 = ELIs32(tc, EL2);
else
lower_32 = ELIs32(tc, EL1);
} else if (ELIsInHost(tc, fromEL) && fromEL == EL0 && toEL == EL2) {
lower_32 = ELIs32(tc, EL0);
} else {
lower_32 = ELIs32(tc, static_cast<ExceptionLevel>(toEL - 1));
}
if (lower_32)
return vals.lowerEL32Offset;
return vals.lowerEL64Offset;
}
}
// void
// SupervisorCall::setSyndrome64(ThreadContext *tc, MiscRegIndex esr_idx)
// {
// ESR esr = 0;
// esr.ec = machInst.aarch64 ? SvcAArch64 : SvcAArch32;
// esr.il = !machInst.thumb;
// if (machInst.aarch64)
// esr.imm16 = bits(machInst.instBits, 20, 5);
// else if (machInst.thumb)
// esr.imm16 = bits(machInst.instBits, 7, 0);
// else
// esr.imm16 = bits(machInst.instBits, 15, 0);
// tc->setMiscReg(esr_idx, esr);
// }
void
SecureMonitorCall::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
if (FullSystem) {
ArmFault::invoke(tc, inst);
return;
}
}
ExceptionClass
SecureMonitorCall::ec(ThreadContext *tc) const
{
return (from64 ? EC_SMC_64 : vals.ec);
}
bool
SupervisorTrap::routeToHyp(ThreadContext *tc) const
{
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
return EL2Enabled(tc) && currEL(tc) <= EL1 && hcr.tge;
}
uint32_t
SupervisorTrap::iss() const
{
// If SupervisorTrap is routed to hypervisor, iss field is 0.
if (hypRouted) {
return 0;
}
return issRaw;
}
ExceptionClass
SupervisorTrap::ec(ThreadContext *tc) const
{
if (hypRouted)
return EC_UNKNOWN;
else
return (overrideEc != EC_INVALID) ? overrideEc : vals.ec;
}
ExceptionClass
SecureMonitorTrap::ec(ThreadContext *tc) const
{
return (overrideEc != EC_INVALID) ? overrideEc :
(from64 ? EC_SMC_64 : vals.ec);
}
template<class T>
void
AbortFault<T>::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
if (tranMethod == ArmFault::UnknownTran) {
tranMethod = longDescFormatInUse(tc) ? ArmFault::LpaeTran
: ArmFault::VmsaTran;
if ((tranMethod == ArmFault::VmsaTran) && this->routeToMonitor(tc)) {
// See ARM ARM B3-1416
bool override_LPAE = false;
TTBCR ttbcr_s = tc->readMiscReg(MISCREG_TTBCR_S);
[[maybe_unused]] TTBCR ttbcr_ns =
tc->readMiscReg(MISCREG_TTBCR_NS);
if (ttbcr_s.eae) {
override_LPAE = true;
} else {
// Unimplemented code option, not seen in testing. May need
// extension according to the manual exceprt above.
DPRINTF(Faults, "Warning: Incomplete translation method "
"override detected.\n");
}
if (override_LPAE)
tranMethod = ArmFault::LpaeTran;
}
}
if (source == ArmFault::AsynchronousExternalAbort) {
tc->getCpuPtr()->clearInterrupt(tc->threadId(), INT_ABT, 0);
}
// Get effective fault source encoding
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
// source must be determined BEFORE invoking generic routines which will
// try to set hsr etc. and are based upon source!
ArmFaultVals<T>::invoke(tc, inst);
if (!this->to64) { // AArch32
FSR fsr = getFsr(tc);
if (cpsr.mode == MODE_HYP) {
tc->setMiscReg(T::HFarIndex, faultAddr);
} else if (stage2) {
tc->setMiscReg(MISCREG_HPFAR, (faultAddr >> 8) & ~0xf);
tc->setMiscReg(T::HFarIndex, OVAddr);
} else if (debugType > ArmFault::NODEBUG) {
DBGDS32 Rext = tc->readMiscReg(MISCREG_DBGDSCRext);
tc->setMiscReg(T::FarIndex, faultAddr);
if (debugType == ArmFault::BRKPOINT){
Rext.moe = 0x1;
} else if (debugType == ArmFault::VECTORCATCH){
Rext.moe = 0x5;
} else if (debugType > ArmFault::VECTORCATCH) {
Rext.moe = 0xa;
fsr.cm = (debugType == ArmFault::WPOINT_CM)? 1 : 0;
}
tc->setMiscReg(T::FsrIndex, fsr);
tc->setMiscReg(MISCREG_DBGDSCRext, Rext);
} else {
tc->setMiscReg(T::FsrIndex, fsr);
tc->setMiscReg(T::FarIndex, faultAddr);
}
DPRINTF(Faults, "Abort Fault source=%#x fsr=%#x faultAddr=%#x "\
"tranMethod=%#x\n", source, fsr, faultAddr, tranMethod);
} else { // AArch64
// Set the FAR register. Nothing else to do if we are in AArch64 state
// because the syndrome register has already been set inside invoke64()
if (stage2) {
// stage 2 fault, set HPFAR_EL2 to the faulting IPA
// and FAR_EL2 to the Original VA
tc->setMiscReg(AbortFault<T>::getFaultAddrReg64(), OVAddr);
tc->setMiscReg(MISCREG_HPFAR_EL2, bits(faultAddr, 47, 12) << 4);
DPRINTF(Faults, "Abort Fault (Stage 2) VA: 0x%x IPA: 0x%x\n",
OVAddr, faultAddr);
} else {
tc->setMiscReg(AbortFault<T>::getFaultAddrReg64(), faultAddr);
}
}
}
template<class T>
void
AbortFault<T>::setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg)
{
srcEncoded = getFaultStatusCode(tc);
if (srcEncoded == ArmFault::FaultSourceInvalid) {
panic("Invalid fault source\n");
}
ArmFault::setSyndrome(tc, syndrome_reg);
}
template<class T>
uint8_t
AbortFault<T>::getFaultStatusCode(ThreadContext *tc) const
{
panic_if(!this->faultUpdated,
"Trying to use un-updated ArmFault internal variables\n");
uint8_t fsc = 0;
if (!this->to64) {
// AArch32
assert(tranMethod != ArmFault::UnknownTran);
if (tranMethod == ArmFault::LpaeTran) {
fsc = ArmFault::longDescFaultSources[source];
} else {
fsc = ArmFault::shortDescFaultSources[source];
}
} else {
// AArch64
fsc = ArmFault::aarch64FaultSources[source];
}
return fsc;
}
template<class T>
FSR
AbortFault<T>::getFsr(ThreadContext *tc) const
{
FSR fsr = 0;
auto fsc = getFaultStatusCode(tc);
// AArch32
assert(tranMethod != ArmFault::UnknownTran);
if (tranMethod == ArmFault::LpaeTran) {
fsr.status = fsc;
fsr.lpae = 1;
} else {
fsr.fsLow = bits(fsc, 3, 0);
fsr.fsHigh = bits(fsc, 4);
fsr.domain = static_cast<uint8_t>(domain);
}
fsr.wnr = (write ? 1 : 0);
fsr.ext = 0;
return fsr;
}
template<class T>
bool
AbortFault<T>::abortDisable(ThreadContext *tc)
{
if (ArmSystem::haveEL(tc, EL3)) {
SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return (!scr.ns || scr.aw);
}
return true;
}
template<class T>
void
AbortFault<T>::annotate(ArmFault::AnnotationIDs id, uint64_t val)
{
switch (id)
{
case ArmFault::S1PTW:
s1ptw = val;
break;
case ArmFault::OVA:
OVAddr = val;
break;
// Just ignore unknown ID's
default:
break;
}
}
template<class T>
uint32_t
AbortFault<T>::iss() const
{
uint32_t val;
val = srcEncoded & 0x3F;
val |= write << 6;
val |= s1ptw << 7;
return (val);
}
template<class T>
bool
AbortFault<T>::isMMUFault() const
{
// NOTE: Not relying on LL information being aligned to lowest bits here
return
(source == ArmFault::AlignmentFault) ||
((source >= ArmFault::TranslationLL) &&
(source < ArmFault::TranslationLL + 4)) ||
((source >= ArmFault::AccessFlagLL) &&
(source < ArmFault::AccessFlagLL + 4)) ||
((source >= ArmFault::DomainLL) &&
(source < ArmFault::DomainLL + 4)) ||
((source >= ArmFault::PermissionLL) &&
(source < ArmFault::PermissionLL + 4));
}
template<class T>
bool
AbortFault<T>::getFaultVAddr(Addr &va) const
{
va = (stage2 ? OVAddr : faultAddr);
return true;
}
ExceptionClass
PrefetchAbort::ec(ThreadContext *tc) const
{
if (to64) {
// AArch64
if (toEL == fromEL)
return EC_PREFETCH_ABORT_CURR_EL;
else
return EC_PREFETCH_ABORT_LOWER_EL;
} else {
// AArch32
// Abort faults have different EC codes depending on whether
// the fault originated within HYP mode, or not. So override
// the method and add the extra adjustment of the EC value.
ExceptionClass ec = ArmFaultVals<PrefetchAbort>::vals.ec;
CPSR spsr = tc->readMiscReg(MISCREG_SPSR_HYP);
if (spsr.mode == MODE_HYP) {
ec = ((ExceptionClass) (((uint32_t) ec) + 1));
}
return ec;
}
}
bool
PrefetchAbort::routeToMonitor(ThreadContext *tc) const
{
SCR scr = 0;
if (from64)
scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3);
else
scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return scr.ea && !isMMUFault();
}
bool
PrefetchAbort::routeToHyp(ThreadContext *tc) const
{
bool toHyp;
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
HDCR hdcr = tc->readMiscRegNoEffect(MISCREG_HDCR);
toHyp = fromEL == EL2;
toHyp |= ArmSystem::haveEL(tc, EL2) && !isSecure(tc) &&
currEL(tc) <= EL1 && (hcr.tge || stage2 ||
(source == DebugEvent && hdcr.tde));
return toHyp;
}
ExceptionClass
DataAbort::ec(ThreadContext *tc) const
{
if (to64) {
// AArch64
if (source == ArmFault::AsynchronousExternalAbort) {
panic("Asynchronous External Abort should be handled with "
"SystemErrors (SErrors)!");
}
if (toEL == fromEL)
return EC_DATA_ABORT_CURR_EL;
else
return EC_DATA_ABORT_LOWER_EL;
} else {
// AArch32
// Abort faults have different EC codes depending on whether
// the fault originated within HYP mode, or not. So override
// the method and add the extra adjustment of the EC value.
ExceptionClass ec = ArmFaultVals<DataAbort>::vals.ec;
CPSR spsr = tc->readMiscReg(MISCREG_SPSR_HYP);
if (spsr.mode == MODE_HYP) {
ec = ((ExceptionClass) (((uint32_t) ec) + 1));
}
return ec;
}
}
bool
DataAbort::routeToMonitor(ThreadContext *tc) const
{
SCR scr = 0;
if (from64)
scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3);
else
scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return scr.ea && !isMMUFault();
}
bool
DataAbort::routeToHyp(ThreadContext *tc) const
{
bool toHyp;
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
HDCR hdcr = tc->readMiscRegNoEffect(MISCREG_HDCR);
bool amo = hcr.amo;
if (hcr.tge == 1)
amo = (!HaveVirtHostExt(tc) || hcr.e2h == 0);
// if in Hyp mode then stay in Hyp mode
toHyp = fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1
&& (hcr.tge || stage2 ||
((source == AsynchronousExternalAbort) && amo) ||
((fromEL == EL0) && hcr.tge &&
((source == AlignmentFault) ||
(source == SynchronousExternalAbort))) ||
((source == DebugEvent) && (hdcr.tde || hcr.tge))));
return toHyp;
}
uint32_t
DataAbort::iss() const
{
uint32_t val;
// Add on the data abort specific fields to the generic abort ISS value
val = AbortFault<DataAbort>::iss();
val |= cm << 8;
// ISS is valid if not caused by a stage 1 page table walk, and when taken
// to AArch64 only when directed to EL2
if (!s1ptw && stage2 && (!to64 || toEL == EL2)) {
val |= isv << 24;
if (isv) {
val |= sas << 22;
val |= sse << 21;
val |= srt << 16;
// AArch64 only. These assignments are safe on AArch32 as well
// because these vars are initialized to false
val |= sf << 15;
val |= ar << 14;
}
}
return (val);
}
void
DataAbort::annotate(AnnotationIDs id, uint64_t val)
{
AbortFault<DataAbort>::annotate(id, val);
switch (id)
{
case SAS:
isv = true;
sas = val;
break;
case SSE:
isv = true;
sse = val;
break;
case SRT:
isv = true;
srt = val;
break;
case SF:
isv = true;
sf = val;
break;
case AR:
isv = true;
ar = val;
break;
case CM:
cm = val;
break;
case OFA:
faultAddr = val;
break;
// Just ignore unknown ID's
default:
break;
}
}
void
VirtualDataAbort::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
AbortFault<VirtualDataAbort>::invoke(tc, inst);
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
hcr.va = 0;
tc->setMiscRegNoEffect(MISCREG_HCR, hcr);
}
bool
Interrupt::routeToMonitor(ThreadContext *tc) const
{
assert(ArmSystem::haveEL(tc, EL3));
SCR scr = 0;
if (from64)
scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3);
else
scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return scr.irq;
}
bool
Interrupt::routeToHyp(ThreadContext *tc) const
{
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || hcr.imo));
}
bool
Interrupt::abortDisable(ThreadContext *tc)
{
if (ArmSystem::haveEL(tc, EL3)) {
SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return (!scr.ns || scr.aw);
}
return true;
}
VirtualInterrupt::VirtualInterrupt()
{}
bool
FastInterrupt::routeToMonitor(ThreadContext *tc) const
{
assert(ArmSystem::haveEL(tc, EL3));
SCR scr = 0;
if (from64)
scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3);
else
scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return scr.fiq;
}
bool
FastInterrupt::routeToHyp(ThreadContext *tc) const
{
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || hcr.fmo));
}
bool
FastInterrupt::abortDisable(ThreadContext *tc)
{
if (ArmSystem::haveEL(tc, EL3)) {
SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return (!scr.ns || scr.aw);
}
return true;
}
bool
FastInterrupt::fiqDisable(ThreadContext *tc)
{
if (ArmSystem::haveEL(tc, EL2)) {
return true;
} else if (ArmSystem::haveEL(tc, EL3)) {
SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR);
return (!scr.ns || scr.fw);
}
return true;
}
VirtualFastInterrupt::VirtualFastInterrupt()
{}
void
PCAlignmentFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
ArmFaultVals<PCAlignmentFault>::invoke(tc, inst);
assert(from64);
// Set the FAR
tc->setMiscReg(getFaultAddrReg64(), faultPC);
}
bool
PCAlignmentFault::routeToHyp(ThreadContext *tc) const
{
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
return fromEL == EL2 || (EL2Enabled(tc) && fromEL <= EL1 && hcr.tge);
}
SPAlignmentFault::SPAlignmentFault()
{}
bool
SPAlignmentFault::routeToHyp(ThreadContext *tc) const
{
assert(from64);
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
return EL2Enabled(tc) && currEL(tc) <= EL1 && hcr.tge == 1;
}
SystemError::SystemError()
{}
void
SystemError::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
tc->getCpuPtr()->clearInterrupt(tc->threadId(), INT_ABT, 0);
ArmFault::invoke(tc, inst);
}
bool
SystemError::routeToMonitor(ThreadContext *tc) const
{
assert(ArmSystem::haveEL(tc, EL3));
assert(from64);
SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3);
return scr.ea || fromEL == EL3;
}
bool
SystemError::routeToHyp(ThreadContext *tc) const
{
assert(from64);
HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || hcr.amo));
}
SoftwareBreakpoint::SoftwareBreakpoint(ExtMachInst _mach_inst, uint32_t _iss)
: ArmFaultVals<SoftwareBreakpoint>(_mach_inst, _iss)
{}
bool
SoftwareBreakpoint::routeToHyp(ThreadContext *tc) const
{
const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
const HDCR mdcr = tc->readMiscRegNoEffect(MISCREG_MDCR_EL2);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || mdcr.tde));
}
ExceptionClass
SoftwareBreakpoint::ec(ThreadContext *tc) const
{
return from64 ? EC_SOFTWARE_BREAKPOINT_64 : vals.ec;
}
HardwareBreakpoint::HardwareBreakpoint(Addr _vaddr, uint32_t _iss)
: ArmFaultVals<HardwareBreakpoint>(0x0, _iss), vAddr(_vaddr)
{}
bool
HardwareBreakpoint::routeToHyp(ThreadContext *tc) const
{
const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
const HDCR mdcr = tc->readMiscRegNoEffect(MISCREG_MDCR_EL2);
return EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || mdcr.tde);
}
ExceptionClass
HardwareBreakpoint::ec(ThreadContext *tc) const
{
// AArch64
if (toEL == fromEL)
return EC_HW_BREAKPOINT_CURR_EL;
else
return EC_HW_BREAKPOINT_LOWER_EL;
}
void
HardwareBreakpoint::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
ArmFaultVals<HardwareBreakpoint>::invoke(tc, inst);
MiscRegIndex elr_idx;
switch (toEL) {
case EL1:
elr_idx = MISCREG_ELR_EL1;
break;
case EL2:
assert(ArmSystem::haveEL(tc, EL2));
elr_idx = MISCREG_ELR_EL2;
break;
case EL3:
assert(ArmSystem::haveEL(tc, EL3));
elr_idx = MISCREG_ELR_EL3;
break;
default:
panic("Invalid target exception level");
break;
}
tc->setMiscReg(elr_idx, vAddr);
}
Watchpoint::Watchpoint(ExtMachInst _mach_inst, Addr _vaddr,
bool _write, bool _cm)
: ArmFaultVals<Watchpoint>(_mach_inst), vAddr(_vaddr),
write(_write), cm(_cm)
{}
uint32_t
Watchpoint::iss() const
{
uint32_t iss = 0x0022;
// NV
// if (toEL == EL2)
// iss |= 0x02000;
if (cm)
iss |= 0x00100;
if (write)
iss |= 0x00040;
return iss;
}
void
Watchpoint::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
ArmFaultVals<Watchpoint>::invoke(tc, inst);
// Set the FAR
tc->setMiscReg(getFaultAddrReg64(), vAddr);
}
bool
Watchpoint::routeToHyp(ThreadContext *tc) const
{
const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
const HDCR mdcr = tc->readMiscRegNoEffect(MISCREG_MDCR_EL2);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || mdcr.tde));
}
void
Watchpoint::annotate(AnnotationIDs id, uint64_t val)
{
ArmFaultVals<Watchpoint>::annotate(id, val);
switch (id)
{
case OFA:
vAddr = val;
break;
// Just ignore unknown ID's
default:
break;
}
}
ExceptionClass
Watchpoint::ec(ThreadContext *tc) const
{
// AArch64
if (toEL == fromEL)
return EC_WATCHPOINT_CURR_EL;
else
return EC_WATCHPOINT_LOWER_EL;
}
SoftwareStepFault::SoftwareStepFault(ExtMachInst _mach_inst, bool is_ldx,
bool _stepped)
: ArmFaultVals<SoftwareStepFault>(_mach_inst), isldx(is_ldx),
stepped(_stepped)
{
bStep = true;
}
bool
SoftwareStepFault::routeToHyp(ThreadContext *tc) const
{
const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
const HDCR mdcr = tc->readMiscRegNoEffect(MISCREG_MDCR_EL2);
return fromEL == EL2 ||
(EL2Enabled(tc) && fromEL <= EL1 && (hcr.tge || mdcr.tde));
}
ExceptionClass
SoftwareStepFault::ec(ThreadContext *tc) const
{
// AArch64
if (toEL == fromEL)
return EC_SOFTWARE_STEP_CURR_EL;
else
return EC_SOFTWARE_STEP_LOWER_EL;
}
uint32_t
SoftwareStepFault::iss() const
{
uint32_t iss= 0x0022;
if (stepped) {
iss |= 0x1000000;
}
if (isldx) {
iss |= 0x40;
}
return iss;
}
void
ArmSev::invoke(ThreadContext *tc, const StaticInstPtr &inst) {
DPRINTF(Faults, "Invoking ArmSev Fault\n");
if (!FullSystem)
return;
// Set sev_mailbox to 1, clear the pending interrupt from remote
// SEV execution and let pipeline continue as pcState is still
// valid.
tc->setMiscReg(MISCREG_SEV_MAILBOX, 1);
tc->getCpuPtr()->clearInterrupt(tc->threadId(), INT_SEV, 0);
}
// Instantiate all the templates to make the linker happy
template class ArmFaultVals<Reset>;
template class ArmFaultVals<UndefinedInstruction>;
template class ArmFaultVals<SupervisorCall>;
template class ArmFaultVals<SecureMonitorCall>;
template class ArmFaultVals<HypervisorCall>;
template class ArmFaultVals<PrefetchAbort>;
template class ArmFaultVals<DataAbort>;
template class ArmFaultVals<VirtualDataAbort>;
template class ArmFaultVals<HypervisorTrap>;
template class ArmFaultVals<Interrupt>;
template class ArmFaultVals<VirtualInterrupt>;
template class ArmFaultVals<FastInterrupt>;
template class ArmFaultVals<VirtualFastInterrupt>;
template class ArmFaultVals<SupervisorTrap>;
template class ArmFaultVals<SecureMonitorTrap>;
template class ArmFaultVals<PCAlignmentFault>;
template class ArmFaultVals<SPAlignmentFault>;
template class ArmFaultVals<SystemError>;
template class ArmFaultVals<SoftwareBreakpoint>;
template class ArmFaultVals<HardwareBreakpoint>;
template class ArmFaultVals<Watchpoint>;
template class ArmFaultVals<SoftwareStepFault>;
template class ArmFaultVals<ArmSev>;
template class AbortFault<PrefetchAbort>;
template class AbortFault<DataAbort>;
template class AbortFault<VirtualDataAbort>;
IllegalInstSetStateFault::IllegalInstSetStateFault()
{}
bool
IllegalInstSetStateFault::routeToHyp(ThreadContext *tc) const
{
const HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2);
return EL2Enabled(tc) && fromEL == EL0 && hcr.tge;
}
bool
getFaultVAddr(Fault fault, Addr &va)
{
auto arm_fault = dynamic_cast<ArmFault *>(fault.get());
if (arm_fault) {
return arm_fault->getFaultVAddr(va);
} else {
auto pgt_fault = dynamic_cast<GenericPageTableFault *>(fault.get());
if (pgt_fault) {
va = pgt_fault->getFaultVAddr();
return true;
}
auto align_fault = dynamic_cast<GenericAlignmentFault *>(fault.get());
if (align_fault) {
va = align_fault->getFaultVAddr();
return true;
}
// Return false since it's not an address triggered exception
return false;
}
}
} // namespace ArmISA
} // namespace gem5