X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Farch%2Farm%2Ffaults.cc;h=0a39dbba63eb76ad64ce8382f63224c3e43ecc2d;hb=68127ca3da543db0c2f3d131d2b3f3525a35ec50;hp=b79c0917410b5029eb8317073f38fcba3537fb5b;hpb=7a7c4c5fca83a8d47c7e71c9c080a882ebe204a9;p=gem5.git diff --git a/src/arch/arm/faults.cc b/src/arch/arm/faults.cc index b79c09174..0a39dbba6 100644 --- a/src/arch/arm/faults.cc +++ b/src/arch/arm/faults.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2010, 2012-2014, 2016 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. @@ -26,490 +38,1418 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Authors: Gabe Black - * Stephen Hines + * Authors: Ali Saidi + * Gabe Black + * Giacomo Gabrielli + * Thomas Grocutt */ #include "arch/arm/faults.hh" -#include "cpu/thread_context.hh" -#include "cpu/base.hh" +#include "arch/arm/system.hh" +#include "arch/arm/utility.hh" +#include "arch/arm/insts/static_inst.hh" +#include "base/compiler.hh" #include "base/trace.hh" -#if !FULL_SYSTEM -#include "sim/process.hh" -#include "mem/page_table.hh" -#endif +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "debug/Faults.hh" +#include "sim/full_system.hh" namespace ArmISA { -FaultName MachineCheckFault::_name = "Machine Check"; -FaultVect MachineCheckFault::_vect = 0x0401; -FaultStat MachineCheckFault::_count; +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 + 0xff, // DebugEvent (INVALID) + 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::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, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Undefined Instruction", 0x004, 0x000, 0x200, 0x400, 0x600, MODE_UNDEFINED, + 4, 2, 0, 0, true, false, false, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Supervisor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_SVC, + 4, 2, 4, 2, true, false, false, EC_SVC_TO_HYP, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Secure Monitor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_MON, + 4, 4, 4, 4, false, true, true, EC_SMC_TO_HYP, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Hypervisor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_HYP, + 4, 4, 4, 4, true, false, false, EC_HVC, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Prefetch Abort", 0x00C, 0x000, 0x200, 0x400, 0x600, MODE_ABORT, + 4, 4, 0, 0, true, true, false, EC_PREFETCH_ABORT_TO_HYP, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Data Abort", 0x010, 0x000, 0x200, 0x400, 0x600, MODE_ABORT, + 8, 8, 0, 0, true, true, false, EC_DATA_ABORT_TO_HYP, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Virtual Data Abort", 0x010, 0x000, 0x200, 0x400, 0x600, MODE_ABORT, + 8, 8, 0, 0, true, true, false, EC_INVALID, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + // @todo: double check these values + "Hypervisor Trap", 0x014, 0x000, 0x200, 0x400, 0x600, MODE_HYP, + 0, 0, 0, 0, false, false, false, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "IRQ", 0x018, 0x080, 0x280, 0x480, 0x680, MODE_IRQ, + 4, 4, 0, 0, false, true, false, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Virtual IRQ", 0x018, 0x080, 0x280, 0x480, 0x680, MODE_IRQ, + 4, 4, 0, 0, false, true, false, EC_INVALID, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "FIQ", 0x01C, 0x100, 0x300, 0x500, 0x700, MODE_FIQ, + 4, 4, 0, 0, false, true, true, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + "Virtual FIQ", 0x01C, 0x100, 0x300, 0x500, 0x700, MODE_FIQ, + 4, 4, 0, 0, false, true, true, EC_INVALID, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::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, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + // Some dummy values (SecureMonitorTrap is AArch64-only) + "Secure Monitor Trap", 0x014, 0x000, 0x200, 0x400, 0x600, MODE_MON, + 0, 0, 0, 0, false, false, false, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::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, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::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, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::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, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + // Some dummy values + "Pipe Flush", 0x000, 0x000, 0x000, 0x000, 0x000, MODE_SVC, + 0, 0, 0, 0, false, true, true, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + // Some dummy values + "ArmSev Flush", 0x000, 0x000, 0x000, 0x000, 0x000, MODE_SVC, + 0, 0, 0, 0, false, true, true, EC_UNKNOWN, FaultStat() +}; +template<> ArmFault::FaultVals ArmFaultVals::vals = { + // Some dummy values (SPAlignmentFault is AArch64-only) + "Illegal Inst Set State Fault", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC, + 0, 0, 0, 0, true, false, false, EC_ILLEGAL_INST, FaultStat() +}; + +Addr +ArmFault::getVector(ThreadContext *tc) +{ + Addr base; -FaultName AlignmentFault::_name = "Alignment"; -FaultVect AlignmentFault::_vect = 0x0301; -FaultStat AlignmentFault::_count; + // ARM ARM issue C B1.8.1 + bool haveSecurity = ArmSystem::haveSecurity(tc); -FaultName ResetFault::_name = "Reset Fault"; -#if FULL_SYSTEM -FaultVect ResetFault::_vect = 0xBFC00000; -#else -FaultVect ResetFault::_vect = 0x001; -#endif -FaultStat ResetFault::_count; + // panic if SCTLR.VE because I have no idea what to do with vectored + // interrupts + SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR); + assert(!sctlr.ve); + // Check for invalid modes + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); + assert(haveSecurity || cpsr.mode != MODE_MON); + assert(ArmSystem::haveVirtualization(tc) || cpsr.mode != MODE_HYP); -FaultName AddressErrorFault::_name = "Address Error"; -FaultVect AddressErrorFault::_vect = 0x0180; -FaultStat AddressErrorFault::_count; + switch (cpsr.mode) + { + case MODE_MON: + base = tc->readMiscReg(MISCREG_MVBAR); + break; + case MODE_HYP: + base = tc->readMiscReg(MISCREG_HVBAR); + break; + default: + if (sctlr.v) { + base = HighVecs; + } else { + base = haveSecurity ? tc->readMiscReg(MISCREG_VBAR) : 0; + } + break; + } + return base + offset(tc); +} -FaultName StoreAddressErrorFault::_name = "Store Address Error"; -FaultVect StoreAddressErrorFault::_vect = 0x0180; -FaultStat StoreAddressErrorFault::_count; +Addr +ArmFault::getVector64(ThreadContext *tc) +{ + Addr vbar; + switch (toEL) { + case EL3: + assert(ArmSystem::haveSecurity(tc)); + vbar = tc->readMiscReg(MISCREG_VBAR_EL3); + break; + case EL2: + assert(ArmSystem::haveVirtualization(tc)); + 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(); +} +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; + } +} -FaultName SystemCallFault::_name = "Syscall"; -FaultVect SystemCallFault::_vect = 0x0180; -FaultStat SystemCallFault::_count; +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; + } +} -FaultName CoprocessorUnusableFault::_name = "Coprocessor Unusable Fault"; -FaultVect CoprocessorUnusableFault::_vect = 0x180; -FaultStat CoprocessorUnusableFault::_count; +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); +} -FaultName ReservedInstructionFault::_name = "Reserved Instruction Fault"; -FaultVect ReservedInstructionFault::_vect = 0x0180; -FaultStat ReservedInstructionFault::_count; +void +ArmFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) +{ + CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); + + if (ArmSystem::highestELIs64(tc)) { // ARMv8 + // Determine source exception level and mode + fromMode = (OperatingMode) (uint8_t) cpsr.mode; + fromEL = opModeToEL(fromMode); + if (opModeIs64(fromMode)) + from64 = true; + + // Determine target exception level + if (ArmSystem::haveSecurity(tc) && routeToMonitor(tc)) + toEL = EL3; + else if (ArmSystem::haveVirtualization(tc) && routeToHyp(tc)) + toEL = EL2; + else + toEL = opModeToEL(nextMode()); + if (fromEL > toEL) + toEL = fromEL; + + if (toEL == ArmSystem::highestEL(tc) || ELIs64(tc, toEL)) { + // Invoke exception handler in AArch64 state + to64 = true; + invoke64(tc, inst); + return; + } + } -FaultName ThreadFault::_name = "Thread Fault"; -FaultVect ThreadFault::_vect = 0x00F1; -FaultStat ThreadFault::_count; + // ARMv7 (ARM ARM issue C B1.9) + + bool have_security = ArmSystem::haveSecurity(tc); + bool have_virtualization = ArmSystem::haveVirtualization(tc); + + 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); + + Addr curPc M5_VAR_USED = 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 + if (inst) { + ArmStaticInst *armInst = reinterpret_cast(inst.get()); + armInst->annotateFault(this); + } + if (have_security && routeToMonitor(tc)) + cpsr.mode = MODE_MON; + else if (have_virtualization && routeToHyp(tc)) + cpsr.mode = MODE_HYP; + else + cpsr.mode = nextMode(); + + // 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); + } + } -FaultName ArithmeticFault::_name = "Arithmetic Overflow Exception"; -FaultVect ArithmeticFault::_vect = 0x180; -FaultStat ArithmeticFault::_count; + // 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; + 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, curPc + + (saved_cpsr.t ? thumbPcOffset(true) : armPcOffset(true))); + } else { + tc->setIntReg(INTREG_LR, curPc + + (saved_cpsr.t ? thumbPcOffset(false) : armPcOffset(false))); + } -FaultName UnimplementedOpcodeFault::_name = "opdec"; -FaultVect UnimplementedOpcodeFault::_vect = 0x0481; -FaultStat UnimplementedOpcodeFault::_count; + 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(have_virtualization); + tc->setMiscReg(MISCREG_SPSR_HYP, saved_cpsr); + setSyndrome(tc, MISCREG_HSR); + break; + default: + panic("unknown Mode\n"); + } -FaultName InterruptFault::_name = "interrupt"; -FaultVect InterruptFault::_vect = 0x0180; -FaultStat InterruptFault::_count; + Addr newPc = getVector(tc); + DPRINTF(Faults, "Invoking Fault:%s cpsr:%#x PC:%#x lr:%#x newVec: %#x\n", + name(), cpsr, curPc, tc->readIntReg(INTREG_LR), newPc); + PCState pc(newPc); + pc.thumb(cpsr.t); + pc.nextThumb(pc.thumb()); + pc.jazelle(cpsr.j); + pc.nextJazelle(pc.jazelle()); + pc.aarch64(!cpsr.width); + pc.nextAArch64(!cpsr.width); + tc->pcState(pc); +} -FaultName TrapFault::_name = "Trap"; -FaultVect TrapFault::_vect = 0x0180; -FaultStat TrapFault::_count; +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::haveVirtualization(tc)); + elr_idx = MISCREG_ELR_EL2; + spsr_idx = MISCREG_SPSR_EL2; + break; + case EL3: + assert(ArmSystem::haveSecurity(tc)); + elr_idx = MISCREG_ELR_EL3; + spsr_idx = MISCREG_SPSR_EL3; + break; + default: + panic("Invalid target exception level"); + break; + } -FaultName BreakpointFault::_name = "Breakpoint"; -FaultVect BreakpointFault::_vect = 0x0180; -FaultStat BreakpointFault::_count; + // 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); + if (from64) { + // Force some bitfields to 0 + spsr.q = 0; + spsr.it1 = 0; + spsr.j = 0; + spsr.res0_23_22 = 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; + // Force some bitfields to 0 + spsr.res0_23_22 = 0; + spsr.ss = 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); + + // 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; + tc->setMiscReg(MISCREG_CPSR, cpsr); + + // Set PC to start of exception handler + Addr new_pc = purifyTaggedAddr(getVector64(tc), tc, toEL); + DPRINTF(Faults, "Invoking Fault (AArch64 target EL):%s cpsr:%#x PC:%#x " + "elr:%#x newVec: %#x\n", name(), cpsr, curr_pc, ret_addr, new_pc); + PCState pc(new_pc); + pc.aarch64(!cpsr.width); + pc.nextAArch64(!cpsr.width); + tc->pcState(pc); + + // 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 + if (inst) + reinterpret_cast(inst.get())->annotateFault(this); + // Save exception syndrome + if ((nextMode() != MODE_IRQ) && (nextMode() != MODE_FIQ)) + setSyndrome(tc, getSyndromeReg64()); +} +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(tc->getSystemPtr()), tc)); + + // Unless we have SMC code to get us there, boot in HYP! + if (ArmSystem::haveVirtualization(tc) && + !ArmSystem::haveSecurity(tc)) { + 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::resetAddr64(tc); + pc.aarch64(true); + pc.nextAArch64(true); + tc->pcState(pc); + } +} -FaultName ItbInvalidFault::_name = "Invalid TLB Entry Exception (I-Fetch/LW)"; -FaultVect ItbInvalidFault::_vect = 0x0180; -FaultStat ItbInvalidFault::_count; +void +UndefinedInstruction::invoke(ThreadContext *tc, const StaticInstPtr &inst) +{ + if (FullSystem) { + ArmFault::invoke(tc, inst); + return; + } -FaultName ItbPageFault::_name = "itbmiss"; -FaultVect ItbPageFault::_vect = 0x0181; -FaultStat ItbPageFault::_count; + // If the mnemonic isn't defined this has to be an unknown instruction. + assert(unknown || mnemonic != NULL); + if (disabled) { + panic("Attempted to execute disabled instruction " + "'%s' (inst 0x%08x)", mnemonic, machInst); + } else if (unknown) { + panic("Attempted to execute unknown instruction (inst 0x%08x)", + machInst); + } else { + panic("Attempted to execute unimplemented instruction " + "'%s' (inst 0x%08x)", mnemonic, machInst); + } +} -FaultName ItbMissFault::_name = "itbmiss"; -FaultVect ItbMissFault::_vect = 0x0181; -FaultStat ItbMissFault::_count; +bool +UndefinedInstruction::routeToHyp(ThreadContext *tc) const +{ + bool toHyp; -FaultName ItbAcvFault::_name = "iaccvio"; -FaultVect ItbAcvFault::_vect = 0x0081; -FaultStat ItbAcvFault::_count; + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); -FaultName ItbRefillFault::_name = "TLB Refill Exception (I-Fetch/LW)"; -FaultVect ItbRefillFault::_vect = 0x0180; -FaultStat ItbRefillFault::_count; + // if in Hyp mode then stay in Hyp mode + toHyp = scr.ns && (cpsr.mode == MODE_HYP); + // if HCR.TGE is set to 1, take to Hyp mode through Hyp Trap vector + toHyp |= !inSecureState(scr, cpsr) && hcr.tge && (cpsr.mode == MODE_USER); + return toHyp; +} -FaultName NDtbMissFault::_name = "dtb_miss_single"; -FaultVect NDtbMissFault::_vect = 0x0201; -FaultStat NDtbMissFault::_count; +uint32_t +UndefinedInstruction::iss() const +{ + if (overrideEc == EC_INVALID) + return issRaw; -FaultName PDtbMissFault::_name = "dtb_miss_double"; -FaultVect PDtbMissFault::_vect = 0x0281; -FaultStat PDtbMissFault::_count; + uint32_t new_iss = 0; + uint32_t op0, op1, op2, CRn, CRm, Rt, dir; -FaultName DtbPageFault::_name = "dfault"; -FaultVect DtbPageFault::_vect = 0x0381; -FaultStat DtbPageFault::_count; + 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); -FaultName DtbAcvFault::_name = "dfault"; -FaultVect DtbAcvFault::_vect = 0x0381; -FaultStat DtbAcvFault::_count; + new_iss = op0 << 20 | op2 << 17 | op1 << 14 | CRn << 10 | + Rt << 5 | CRm << 1 | dir; -FaultName DtbInvalidFault::_name = "Invalid TLB Entry Exception (Store)"; -FaultVect DtbInvalidFault::_vect = 0x0180; -FaultStat DtbInvalidFault::_count; + return new_iss; +} -FaultName DtbRefillFault::_name = "TLB Refill Exception (Store)"; -FaultVect DtbRefillFault::_vect = 0x0180; -FaultStat DtbRefillFault::_count; +void +SupervisorCall::invoke(ThreadContext *tc, const StaticInstPtr &inst) +{ + if (FullSystem) { + ArmFault::invoke(tc, inst); + return; + } -FaultName TLBModifiedFault::_name = "TLB Modified Exception"; -FaultVect TLBModifiedFault::_vect = 0x0180; -FaultStat TLBModifiedFault::_count; + // As of now, there isn't a 32 bit thumb version of this instruction. + assert(!machInst.bigThumb); + uint32_t callNum; + CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); + OperatingMode mode = (OperatingMode)(uint8_t)cpsr.mode; + if (opModeIs64(mode)) + callNum = tc->readIntReg(INTREG_X8); + else + callNum = tc->readIntReg(INTREG_R7); + tc->syscall(callNum); + + // Advance the PC since that won't happen automatically. + PCState pc = tc->pcState(); + assert(inst); + inst->advancePC(pc); + tc->pcState(pc); +} -FaultName FloatEnableFault::_name = "float_enable_fault"; -FaultVect FloatEnableFault::_vect = 0x0581; -FaultStat FloatEnableFault::_count; +bool +SupervisorCall::routeToHyp(ThreadContext *tc) const +{ + bool toHyp; -FaultName IntegerOverflowFault::_name = "Integer Overflow Fault"; -FaultVect IntegerOverflowFault::_vect = 0x0501; -FaultStat IntegerOverflowFault::_count; + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); -FaultName DspStateDisabledFault::_name = "DSP Disabled Fault"; -FaultVect DspStateDisabledFault::_vect = 0x001a; -FaultStat DspStateDisabledFault::_count; + // if in Hyp mode then stay in Hyp mode + toHyp = scr.ns && (cpsr.mode == MODE_HYP); + // if HCR.TGE is set to 1, take to Hyp mode through Hyp Trap vector + toHyp |= !inSecureState(scr, cpsr) && hcr.tge && (cpsr.mode == MODE_USER); + return toHyp; +} -#if FULL_SYSTEM -void ArmFault::setHandlerPC(Addr HandlerBase, ThreadContext *tc) +ExceptionClass +SupervisorCall::ec(ThreadContext *tc) const { - tc->setPC(HandlerBase); - tc->setNextPC(HandlerBase+sizeof(MachInst)); - tc->setNextNPC(HandlerBase+2*sizeof(MachInst)); + return (overrideEc != EC_INVALID) ? overrideEc : + (from64 ? EC_SVC_64 : vals.ec); } -void ArmFault::setExceptionState(ThreadContext *tc,uint8_t ExcCode) +uint32_t +SupervisorCall::iss() const { - // modify SRS Ctl - Save CSS, put ESS into CSS - MiscReg stat = tc->readMiscReg(ArmISA::Status); - if(bits(stat,Status_EXL) != 1 && bits(stat,Status_BEV) != 1) - { - // SRS Ctl is modified only if Status_EXL and Status_BEV are not set - MiscReg srs = tc->readMiscReg(ArmISA::SRSCtl); - uint8_t CSS,ESS; - CSS = bits(srs,SRSCtl_CSS_HI,SRSCtl_CSS_LO); - ESS = bits(srs,SRSCtl_ESS_HI,SRSCtl_ESS_LO); - // Move CSS to PSS - replaceBits(srs,SRSCtl_PSS_HI,SRSCtl_PSS_LO,CSS); - // Move ESS to CSS - replaceBits(srs,SRSCtl_CSS_HI,SRSCtl_CSS_LO,ESS); - tc->setMiscRegNoEffect(ArmISA::SRSCtl,srs); - //tc->setShadowSet(ESS); - } - - // set EXL bit (don't care if it is already set!) - replaceBits(stat,Status_EXL_HI,Status_EXL_LO,1); - tc->setMiscRegNoEffect(ArmISA::Status,stat); - - // write EPC - // warn("Set EPC to %x\n",tc->readPC()); - // CHECK ME or FIXME or FIX ME or POSSIBLE HACK - // Check to see if the exception occurred in the branch delay slot - DPRINTF(Arm,"PC: %x, NextPC: %x, NNPC: %x\n",tc->readPC(),tc->readNextPC(),tc->readNextNPC()); - int C_BD=0; - if(tc->readPC() + sizeof(MachInst) != tc->readNextPC()){ - tc->setMiscRegNoEffect(ArmISA::EPC,tc->readPC()-sizeof(MachInst)); - // In the branch delay slot? set CAUSE_31 - C_BD = 1; - } else { - tc->setMiscRegNoEffect(ArmISA::EPC,tc->readPC()); - // In the branch delay slot? reset CAUSE_31 - C_BD = 0; - } - - // Set Cause_EXCCODE field - MiscReg cause = tc->readMiscReg(ArmISA::Cause); - replaceBits(cause,Cause_EXCCODE_HI,Cause_EXCCODE_LO,ExcCode); - replaceBits(cause,Cause_BD_HI,Cause_BD_LO,C_BD); - replaceBits(cause,Cause_CE_HI,Cause_CE_LO,0); - tc->setMiscRegNoEffect(ArmISA::Cause,cause); + // 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; } -void ArithmeticFault::invoke(ThreadContext *tc) -{ - DPRINTF(Arm,"%s encountered.\n", name()); - setExceptionState(tc,0xC); - - // Set new PC - Addr HandlerBase; - MiscReg stat = tc->readMiscReg(ArmISA::Status); - // Here, the handler is dependent on BEV, which is not modified by setExceptionState() - if(bits(stat,Status_BEV)==0){ // See MIPS ARM Vol 3, Revision 2, Page 38 - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); - }else{ - HandlerBase = 0xBFC00200; - } - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x \n",HandlerBase); +ExceptionClass +UndefinedInstruction::ec(ThreadContext *tc) const +{ + return (overrideEc != EC_INVALID) ? overrideEc : vals.ec; } - -void StoreAddressErrorFault::invoke(ThreadContext *tc) + + +HypervisorCall::HypervisorCall(ExtMachInst _machInst, uint32_t _imm) : + ArmFaultVals(_machInst, _imm) +{} + +ExceptionClass +HypervisorCall::ec(ThreadContext *tc) const { - DPRINTF(Arm,"%s encountered.\n", name()); - setExceptionState(tc,0x5); - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); - - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x \n",HandlerBase); - // warn("Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); - -} - -void TrapFault::invoke(ThreadContext *tc) -{ - DPRINTF(Arm,"%s encountered.\n", name()); - // warn("%s encountered.\n", name()); - setExceptionState(tc,0xD); - - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x \n",HandlerBase); - // warn("Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); -} - -void BreakpointFault::invoke(ThreadContext *tc) -{ - setExceptionState(tc,0x9); - - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x \n",HandlerBase); - // warn("Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); - -} - -void DtbInvalidFault::invoke(ThreadContext *tc) -{ - DPRINTF(Arm,"%s encountered.\n", name()); - // warn("%s encountered.\n", name()); - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); - MiscReg eh = tc->readMiscReg(ArmISA::EntryHi); - replaceBits(eh,EntryHi_ASID_HI,EntryHi_ASID_LO,EntryHi_Asid); - replaceBits(eh,EntryHi_VPN2_HI,EntryHi_VPN2_LO,EntryHi_VPN2); - replaceBits(eh,EntryHi_VPN2X_HI,EntryHi_VPN2X_LO,EntryHi_VPN2X); - tc->setMiscRegNoEffect(ArmISA::EntryHi,eh); - MiscReg ctxt = tc->readMiscReg(ArmISA::Context); - replaceBits(ctxt,Context_BadVPN2_HI,Context_BadVPN2_LO,Context_BadVPN2); - tc->setMiscRegNoEffect(ArmISA::Context,ctxt); - setExceptionState(tc,0x3); + return from64 ? EC_HVC_64 : vals.ec; +} - - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); -} - -void AddressErrorFault::invoke(ThreadContext *tc) +ExceptionClass +HypervisorTrap::ec(ThreadContext *tc) const { - DPRINTF(Arm,"%s encountered.\n", name()); - setExceptionState(tc,0x4); - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); + return (overrideEc != EC_INVALID) ? overrideEc : vals.ec; +} - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); +template +FaultOffset +ArmFaultVals::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; } -void ItbInvalidFault::invoke(ThreadContext *tc) +// 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) { - DPRINTF(Arm,"%s encountered.\n", name()); - setExceptionState(tc,0x2); - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); - MiscReg eh = tc->readMiscReg(ArmISA::EntryHi); - replaceBits(eh,EntryHi_ASID_HI,EntryHi_ASID_LO,EntryHi_Asid); - replaceBits(eh,EntryHi_VPN2_HI,EntryHi_VPN2_LO,EntryHi_VPN2); - replaceBits(eh,EntryHi_VPN2X_HI,EntryHi_VPN2X_LO,EntryHi_VPN2X); - tc->setMiscRegNoEffect(ArmISA::EntryHi,eh); - MiscReg ctxt = tc->readMiscReg(ArmISA::Context); - replaceBits(ctxt,Context_BadVPN2_HI,Context_BadVPN2_LO,Context_BadVPN2); - tc->setMiscRegNoEffect(ArmISA::Context,ctxt); + if (FullSystem) { + ArmFault::invoke(tc, inst); + return; + } +} +ExceptionClass +SecureMonitorCall::ec(ThreadContext *tc) const +{ + return (from64 ? EC_SMC_64 : vals.ec); +} - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); - DPRINTF(Arm,"Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); +ExceptionClass +SupervisorTrap::ec(ThreadContext *tc) const +{ + return (overrideEc != EC_INVALID) ? overrideEc : vals.ec; } -void ItbRefillFault::invoke(ThreadContext *tc) +ExceptionClass +SecureMonitorTrap::ec(ThreadContext *tc) const { - DPRINTF(Arm,"%s encountered (%x).\n", name(),BadVAddr); - Addr HandlerBase; - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); - MiscReg eh = tc->readMiscReg(ArmISA::EntryHi); - replaceBits(eh,EntryHi_ASID_HI,EntryHi_ASID_LO,EntryHi_Asid); - replaceBits(eh,EntryHi_VPN2_HI,EntryHi_VPN2_LO,EntryHi_VPN2); - replaceBits(eh,EntryHi_VPN2X_HI,EntryHi_VPN2X_LO,EntryHi_VPN2X); - tc->setMiscRegNoEffect(ArmISA::EntryHi,eh); - MiscReg ctxt = tc->readMiscReg(ArmISA::Context); - replaceBits(ctxt,Context_BadVPN2_HI,Context_BadVPN2_LO,Context_BadVPN2); - tc->setMiscRegNoEffect(ArmISA::Context,ctxt); + return (overrideEc != EC_INVALID) ? overrideEc : + (from64 ? EC_SMC_64 : vals.ec); +} - MiscReg stat = tc->readMiscReg(ArmISA::Status); - // Since handler depends on EXL bit, must check EXL bit before setting it!! - if(bits(stat,Status_EXL)==1){ // See MIPS ARM Vol 3, Revision 2, Page 38 - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - }else{ - HandlerBase = tc->readMiscReg(ArmISA::EBase); // Offset 0x000 - } +template +void +AbortFault::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); + TTBCR M5_VAR_USED 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; + } + } - setExceptionState(tc,0x2); - setHandlerPC(HandlerBase,tc); + if (source == ArmFault::AsynchronousExternalAbort) { + tc->getCpuPtr()->clearInterrupt(tc->threadId(), INT_ABT, 0); + } + // Get effective fault source encoding + CPSR cpsr = tc->readMiscReg(MISCREG_CPSR); + FSR fsr = getFsr(tc); + + // source must be determined BEFORE invoking generic routines which will + // try to set hsr etc. and are based upon source! + ArmFaultVals::invoke(tc, inst); + + if (!this->to64) { // AArch32 + 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 { + 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::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::getFaultAddrReg64(), faultAddr); + } + } } -void DtbRefillFault::invoke(ThreadContext *tc) +template +FSR +AbortFault::getFsr(ThreadContext *tc) { - // Set new PC - DPRINTF(Arm,"%s encountered.\n", name()); - Addr HandlerBase; - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); - MiscReg eh = tc->readMiscReg(ArmISA::EntryHi); - replaceBits(eh,EntryHi_ASID_HI,EntryHi_ASID_LO,EntryHi_Asid); - replaceBits(eh,EntryHi_VPN2_HI,EntryHi_VPN2_LO,EntryHi_VPN2); - replaceBits(eh,EntryHi_VPN2X_HI,EntryHi_VPN2X_LO,EntryHi_VPN2X); - tc->setMiscRegNoEffect(ArmISA::EntryHi,eh); - MiscReg ctxt = tc->readMiscReg(ArmISA::Context); - replaceBits(ctxt,Context_BadVPN2_HI,Context_BadVPN2_LO,Context_BadVPN2); - tc->setMiscRegNoEffect(ArmISA::Context,ctxt); + FSR fsr = 0; + + if (((CPSR) tc->readMiscRegNoEffect(MISCREG_CPSR)).width) { + // AArch32 + assert(tranMethod != ArmFault::UnknownTran); + if (tranMethod == ArmFault::LpaeTran) { + srcEncoded = ArmFault::longDescFaultSources[source]; + fsr.status = srcEncoded; + fsr.lpae = 1; + } else { + srcEncoded = ArmFault::shortDescFaultSources[source]; + fsr.fsLow = bits(srcEncoded, 3, 0); + fsr.fsHigh = bits(srcEncoded, 4); + fsr.domain = static_cast(domain); + } + fsr.wnr = (write ? 1 : 0); + fsr.ext = 0; + } else { + // AArch64 + srcEncoded = ArmFault::aarch64FaultSources[source]; + } + if (srcEncoded == ArmFault::FaultSourceInvalid) { + panic("Invalid fault source\n"); + } + return fsr; +} + +template +bool +AbortFault::abortDisable(ThreadContext *tc) +{ + if (ArmSystem::haveSecurity(tc)) { + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + return (!scr.ns || scr.aw); + } + return true; +} + +template +void +AbortFault::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; + } +} - MiscReg stat = tc->readMiscReg(ArmISA::Status); - // Since handler depends on EXL bit, must check EXL bit before setting it!! - if(bits(stat,Status_EXL)==1){ // See MIPS ARM Vol 3, Revision 2, Page 38 - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - }else{ - HandlerBase = tc->readMiscReg(ArmISA::EBase); // Offset 0x000 - } +template +uint32_t +AbortFault::iss() const +{ + uint32_t val; + val = srcEncoded & 0x3F; + val |= write << 6; + val |= s1ptw << 7; + return (val); +} - setExceptionState(tc,0x3); +template +bool +AbortFault::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)); +} - setHandlerPC(HandlerBase,tc); +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::vals.ec; + + CPSR spsr = tc->readMiscReg(MISCREG_SPSR_HYP); + if (spsr.mode == MODE_HYP) { + ec = ((ExceptionClass) (((uint32_t) ec) + 1)); + } + return ec; + } } -void TLBModifiedFault::invoke(ThreadContext *tc) +bool +PrefetchAbort::routeToMonitor(ThreadContext *tc) const { - DPRINTF(Arm,"%s encountered.\n", name()); - tc->setMiscRegNoEffect(ArmISA::BadVAddr,BadVAddr); - MiscReg eh = tc->readMiscReg(ArmISA::EntryHi); - replaceBits(eh,EntryHi_ASID_HI,EntryHi_ASID_LO,EntryHi_Asid); - replaceBits(eh,EntryHi_VPN2_HI,EntryHi_VPN2_LO,EntryHi_VPN2); - replaceBits(eh,EntryHi_VPN2X_HI,EntryHi_VPN2X_LO,EntryHi_VPN2X); - tc->setMiscRegNoEffect(ArmISA::EntryHi,eh); - MiscReg ctxt = tc->readMiscReg(ArmISA::Context); - replaceBits(ctxt,Context_BadVPN2_HI,Context_BadVPN2_LO,Context_BadVPN2); - tc->setMiscRegNoEffect(ArmISA::Context,ctxt); + SCR scr = 0; + if (from64) + scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); + else + scr = tc->readMiscRegNoEffect(MISCREG_SCR); - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setExceptionState(tc,0x1); - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); + return scr.ea && !isMMUFault(); +} +bool +PrefetchAbort::routeToHyp(ThreadContext *tc) const +{ + bool toHyp; + + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); + HDCR hdcr = tc->readMiscRegNoEffect(MISCREG_HDCR); + + // if in Hyp mode then stay in Hyp mode + toHyp = scr.ns && (cpsr.mode == MODE_HYP); + // otherwise, check whether to take to Hyp mode through Hyp Trap vector + toHyp |= (stage2 || + ( (source == DebugEvent) && hdcr.tde && (cpsr.mode != MODE_HYP)) || + ( (source == SynchronousExternalAbort) && hcr.tge && (cpsr.mode == MODE_USER)) + ) && !inSecureState(tc); + return toHyp; } -void SystemCallFault::invoke(ThreadContext *tc) +ExceptionClass +DataAbort::ec(ThreadContext *tc) const { - DPRINTF(Arm,"%s encountered.\n", name()); - setExceptionState(tc,0x8); + 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::vals.ec; + + CPSR spsr = tc->readMiscReg(MISCREG_SPSR_HYP); + if (spsr.mode == MODE_HYP) { + ec = ((ExceptionClass) (((uint32_t) ec) + 1)); + } + return ec; + } +} - // Set new PC - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); - // warn("Exception Handler At: %x \n",HandlerBase); - // warn("Exception Handler At: %x , EPC set to %x\n",HandlerBase,tc->readMiscReg(ArmISA::EPC)); +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(); } -void InterruptFault::invoke(ThreadContext *tc) +bool +DataAbort::routeToHyp(ThreadContext *tc) const { -#if FULL_SYSTEM - DPRINTF(Arm,"%s encountered.\n", name()); - //RegFile *Reg = tc->getRegFilePtr(); // Get pointer to the register fil - setExceptionState(tc,0x0A); - Addr HandlerBase; + bool toHyp; + + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); + HDCR hdcr = tc->readMiscRegNoEffect(MISCREG_HDCR); + + // if in Hyp mode then stay in Hyp mode + toHyp = scr.ns && (cpsr.mode == MODE_HYP); + // otherwise, check whether to take to Hyp mode through Hyp Trap vector + toHyp |= (stage2 || + ( (cpsr.mode != MODE_HYP) && ( ((source == AsynchronousExternalAbort) && hcr.amo) || + ((source == DebugEvent) && hdcr.tde) ) + ) || + ( (cpsr.mode == MODE_USER) && hcr.tge && + ((source == AlignmentFault) || + (source == SynchronousExternalAbort)) + ) + ) && !inSecureState(tc); + 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::iss(); + // 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 && (!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); +} - uint8_t IV = bits(tc->readMiscRegNoEffect(ArmISA::Cause),Cause_IV); - if (IV)// Offset 200 for release 2 - HandlerBase= 0x20 + vect() + tc->readMiscRegNoEffect(ArmISA::EBase); - else//Ofset at 180 for release 1 - HandlerBase= vect() + tc->readMiscRegNoEffect(ArmISA::EBase); +void +DataAbort::annotate(AnnotationIDs id, uint64_t val) +{ + AbortFault::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; + // Just ignore unknown ID's + default: + break; + } +} - setHandlerPC(HandlerBase,tc); -#endif +void +VirtualDataAbort::invoke(ThreadContext *tc, const StaticInstPtr &inst) +{ + AbortFault::invoke(tc, inst); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + hcr.va = 0; + tc->setMiscRegNoEffect(MISCREG_HCR, hcr); } -#endif // FULL_SYSTEM +bool +Interrupt::routeToMonitor(ThreadContext *tc) const +{ + assert(ArmSystem::haveSecurity(tc)); + SCR scr = 0; + if (from64) + scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); + else + scr = tc->readMiscRegNoEffect(MISCREG_SCR); + return scr.irq; +} -void ResetFault::invoke(ThreadContext *tc) +bool +Interrupt::routeToHyp(ThreadContext *tc) const { -#if FULL_SYSTEM - DPRINTF(Arm,"%s encountered.\n", name()); - /* All reset activity must be invoked from here */ - tc->setPC(vect()); - tc->setNextPC(vect()+sizeof(MachInst)); - tc->setNextNPC(vect()+sizeof(MachInst)+sizeof(MachInst)); - DPRINTF(Arm,"(%x) - ResetFault::invoke : PC set to %x",(unsigned)tc,(unsigned)tc->readPC()); -#endif + bool toHyp; + + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); + // Determine whether IRQs are routed to Hyp mode. + toHyp = (!scr.irq && hcr.imo && !inSecureState(tc)) || + (cpsr.mode == MODE_HYP); + return toHyp; +} - // Set Coprocessor 1 (Floating Point) To Usable - //tc->setMiscReg(ArmISA::Status, ArmISA::Status | 0x20000000); +bool +Interrupt::abortDisable(ThreadContext *tc) +{ + if (ArmSystem::haveSecurity(tc)) { + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + return (!scr.ns || scr.aw); + } + return true; } -void ReservedInstructionFault::invoke(ThreadContext *tc) +VirtualInterrupt::VirtualInterrupt() +{} + +bool +FastInterrupt::routeToMonitor(ThreadContext *tc) const { -#if FULL_SYSTEM - DPRINTF(Arm,"%s encountered.\n", name()); - //RegFile *Reg = tc->getRegFilePtr(); // Get pointer to the register fil - setExceptionState(tc,0x0A); - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscRegNoEffect(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); -#else - panic("%s encountered.\n", name()); -#endif + assert(ArmSystem::haveSecurity(tc)); + SCR scr = 0; + if (from64) + scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); + else + scr = tc->readMiscRegNoEffect(MISCREG_SCR); + return scr.fiq; } -void ThreadFault::invoke(ThreadContext *tc) +bool +FastInterrupt::routeToHyp(ThreadContext *tc) const { - DPRINTF(Arm,"%s encountered.\n", name()); - panic("%s encountered.\n", name()); + bool toHyp; + + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + CPSR cpsr = tc->readMiscRegNoEffect(MISCREG_CPSR); + // Determine whether IRQs are routed to Hyp mode. + toHyp = (!scr.fiq && hcr.fmo && !inSecureState(tc)) || + (cpsr.mode == MODE_HYP); + return toHyp; } -void DspStateDisabledFault::invoke(ThreadContext *tc) +bool +FastInterrupt::abortDisable(ThreadContext *tc) { - DPRINTF(Arm,"%s encountered.\n", name()); - panic("%s encountered.\n", name()); + if (ArmSystem::haveSecurity(tc)) { + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + return (!scr.ns || scr.aw); + } + return true; } -void CoprocessorUnusableFault::invoke(ThreadContext *tc) +bool +FastInterrupt::fiqDisable(ThreadContext *tc) { -#if FULL_SYSTEM - DPRINTF(Arm,"%s encountered.\n", name()); - setExceptionState(tc,0xb); - /* The ID of the coprocessor causing the exception is stored in CoprocessorUnusableFault::coProcID */ - MiscReg cause = tc->readMiscReg(ArmISA::Cause); - replaceBits(cause,Cause_CE_HI,Cause_CE_LO,coProcID); - tc->setMiscRegNoEffect(ArmISA::Cause,cause); + if (ArmSystem::haveVirtualization(tc)) { + return true; + } else if (ArmSystem::haveSecurity(tc)) { + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR); + return (!scr.ns || scr.fw); + } + return true; +} - Addr HandlerBase; - HandlerBase= vect() + tc->readMiscReg(ArmISA::EBase); // Offset 0x180 - General Exception Vector - setHandlerPC(HandlerBase,tc); +VirtualFastInterrupt::VirtualFastInterrupt() +{} - // warn("Status: %x, Cause: %x\n",tc->readMiscReg(ArmISA::Status),tc->readMiscReg(ArmISA::Cause)); -#else - warn("%s (CP%d) encountered.\n", name(), coProcID); -#endif +void +PCAlignmentFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) +{ + ArmFaultVals::invoke(tc, inst); + assert(from64); + // Set the FAR + tc->setMiscReg(getFaultAddrReg64(), faultPC); } -} // namespace ArmISA +SPAlignmentFault::SPAlignmentFault() +{} + +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::haveSecurity(tc)); + assert(from64); + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); + return scr.ea; +} + +bool +SystemError::routeToHyp(ThreadContext *tc) const +{ + bool toHyp; + assert(from64); + + SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); + HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR); + + toHyp = (!scr.ea && hcr.amo && !inSecureState(tc)) || + (!scr.ea && !scr.rw && !hcr.amo && !inSecureState(tc)); + return toHyp; +} + +void +FlushPipe::invoke(ThreadContext *tc, const StaticInstPtr &inst) { + DPRINTF(Faults, "Invoking FlushPipe Fault\n"); + + // Set the PC to the next instruction of the faulting instruction. + // Net effect is simply squashing all instructions behind and + // start refetching from the next instruction. + PCState pc = tc->pcState(); + assert(inst); + inst->advancePC(pc); + tc->pcState(pc); +} + +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; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class ArmFaultVals; +template class AbortFault; +template class AbortFault; +template class AbortFault; + + +IllegalInstSetStateFault::IllegalInstSetStateFault() +{} + + +} // namespace ArmISA