| /* |
| * Copyright (c) 2010, 2012-2014, 2016-2019, 2022 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, ExceptionClass::UNKNOWN |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<UndefinedInstruction>::vals( |
| "Undefined Instruction", 0x004, 0x000, 0x200, 0x400, 0x600, MODE_UNDEFINED, |
| 4, 2, 0, 0, true, false, false, ExceptionClass::UNKNOWN |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<SupervisorCall>::vals( |
| "Supervisor Call", 0x008, 0x000, 0x200, 0x400, 0x600, MODE_SVC, |
| 4, 2, 4, 2, true, false, false, ExceptionClass::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, ExceptionClass::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, ExceptionClass::HVC |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<PrefetchAbort>::vals( |
| "Prefetch Abort", 0x00C, 0x000, 0x200, 0x400, 0x600, MODE_ABORT, |
| 4, 4, 0, 0, true, true, false, ExceptionClass::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, ExceptionClass::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, ExceptionClass::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, ExceptionClass::UNKNOWN |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<SecureMonitorTrap>::vals( |
| "Secure Monitor Trap", 0x004, 0x000, 0x200, 0x400, 0x600, MODE_MON, |
| 4, 2, 0, 0, false, false, false, ExceptionClass::UNKNOWN |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<Interrupt>::vals( |
| "IRQ", 0x018, 0x080, 0x280, 0x480, 0x680, MODE_IRQ, |
| 4, 4, 0, 0, false, true, false, ExceptionClass::UNKNOWN |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<VirtualInterrupt>::vals( |
| "Virtual IRQ", 0x018, 0x080, 0x280, 0x480, 0x680, MODE_IRQ, |
| 4, 4, 0, 0, false, true, false, ExceptionClass::INVALID |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<FastInterrupt>::vals( |
| "FIQ", 0x01C, 0x100, 0x300, 0x500, 0x700, MODE_FIQ, |
| 4, 4, 0, 0, false, true, true, ExceptionClass::UNKNOWN |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<VirtualFastInterrupt>::vals( |
| "Virtual FIQ", 0x01C, 0x100, 0x300, 0x500, 0x700, MODE_FIQ, |
| 4, 4, 0, 0, false, true, true, ExceptionClass::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, ExceptionClass::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, ExceptionClass::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, ExceptionClass::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, ExceptionClass::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, ExceptionClass::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, ExceptionClass::SOFTWARE_BREAKPOINT |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<HardwareBreakpoint>::vals( |
| "Hardware Breakpoint", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC, |
| 0, 0, 0, 0, true, false, false, ExceptionClass::HW_BREAKPOINT |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<Watchpoint>::vals( |
| "Watchpoint", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC, |
| 0, 0, 0, 0, true, false, false, ExceptionClass::WATCHPOINT |
| ); |
| template<> ArmFault::FaultVals ArmFaultVals<SoftwareStepFault>::vals( |
| "SoftwareStep", 0x000, 0x000, 0x200, 0x400, 0x600, MODE_SVC, |
| 0, 0, 0, 0, true, false, false, ExceptionClass::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, ExceptionClass::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) |
| { |
| ESR esr = 0; |
| uint32_t exc_class = (uint32_t) ec(tc); |
| uint32_t iss_val = iss(); |
| |
| assert(!from64 || ArmSystem::highestELIs64(tc)); |
| |
| esr.ec = exc_class; |
| esr.il = il(tc); |
| |
| // 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) { |
| ConditionCode cond_code = (ConditionCode) (uint32_t) machInst.condCode; |
| // If its on unconditional instruction report with a cond code of |
| // 0xE, ie the unconditional code |
| esr.cond_iss.cv = 1; |
| esr.cond_iss.cond = (cond_code == COND_UC) ? COND_AL : cond_code; |
| } |
| esr.cond_iss.iss = bits(iss_val, 19, 0); |
| } else { |
| esr.iss = iss_val; |
| } |
| tc->setMiscReg(syndrome_reg, esr); |
| } |
| |
| 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 |
| if (HaveExt(tc, ArmExtension::FEAT_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; |
| |
| SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR); |
| SCR scr = tc->readMiscReg(MISCREG_SCR_EL3); |
| CPSR saved_cpsr = tc->readMiscReg(MISCREG_CPSR); |
| saved_cpsr.nz = tc->getReg(cc_reg::Nz); |
| saved_cpsr.c = tc->getReg(cc_reg::C); |
| saved_cpsr.v = tc->getReg(cc_reg::V); |
| saved_cpsr.ge = tc->getReg(cc_reg::Ge); |
| |
| [[maybe_unused]] Addr cur_pc = tc->pcState().as<PCState>().pc(); |
| ITSTATE it = tc->pcState().as<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_EL3); |
| if (scr.ns) { |
| scr.ns = 0; |
| tc->setMiscRegNoEffect(MISCREG_SCR_EL3, 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->setReg(int_reg::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) != ExceptionClass::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->getReg(int_reg::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->getReg(cc_reg::Nz); |
| spsr.c = tc->getReg(cc_reg::C); |
| spsr.v = tc->getReg(cc_reg::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->getReg(cc_reg::Ge); |
| ITSTATE it = tc->pcState().as<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().instAddr(); |
| 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 && !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_EL2); |
| 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 == ExceptionClass::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; |
| } |
| |
| // Advance the PC since that won't happen automatically. |
| PCState pc = tc->pcState().as<PCState>(); |
| assert(inst); |
| inst->advancePC(pc); |
| tc->pcState(pc); |
| |
| // As of now, there isn't a 32 bit thumb version of this instruction. |
| assert(!machInst.bigThumb); |
| tc->getSystemPtr()->workload->syscall(tc); |
| } |
| |
| bool |
| SupervisorCall::routeToHyp(ThreadContext *tc) const |
| { |
| HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); |
| return fromEL == EL2 || |
| (EL2Enabled(tc) && fromEL == EL0 && hcr.tge); |
| } |
| |
| ExceptionClass |
| SupervisorCall::ec(ThreadContext *tc) const |
| { |
| return (overrideEc != ExceptionClass::INVALID) ? overrideEc : |
| (from64 ? ExceptionClass::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 ExceptionClass::UNKNOWN; |
| else |
| return (overrideEc != ExceptionClass::INVALID) ? overrideEc : vals.ec; |
| } |
| |
| |
| HypervisorCall::HypervisorCall(ExtMachInst mach_inst, uint32_t _imm) : |
| ArmFaultVals<HypervisorCall>(mach_inst, _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 ? ExceptionClass::HVC_64 : vals.ec; |
| } |
| |
| ExceptionClass |
| HypervisorTrap::ec(ThreadContext *tc) const |
| { |
| return (overrideEc != ExceptionClass::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 ? ExceptionClass::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 ExceptionClass::UNKNOWN; |
| else |
| return (overrideEc != ExceptionClass::INVALID) ? overrideEc : vals.ec; |
| } |
| |
| ExceptionClass |
| SecureMonitorTrap::ec(ThreadContext *tc) const |
| { |
| return (overrideEc != ExceptionClass::INVALID) ? overrideEc : |
| (from64 ? ExceptionClass::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_EL3); |
| 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> |
| 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 ExceptionClass::PREFETCH_ABORT_CURR_EL; |
| else |
| return ExceptionClass::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; |
| } |
| } |
| |
| uint32_t |
| PrefetchAbort::iss() const |
| { |
| ESR esr = 0; |
| auto& iss = esr.instruction_abort_iss; |
| |
| iss.ifsc = srcEncoded & 0x3F; |
| iss.s1ptw = s1ptw; |
| |
| return iss; |
| } |
| |
| bool |
| PrefetchAbort::routeToMonitor(ThreadContext *tc) const |
| { |
| SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); |
| return scr.ea && !isMMUFault(); |
| } |
| |
| bool |
| PrefetchAbort::routeToHyp(ThreadContext *tc) const |
| { |
| bool toHyp; |
| |
| HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); |
| 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 ExceptionClass::DATA_ABORT_CURR_EL; |
| else |
| return ExceptionClass::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::il(ThreadContext *tc) const |
| { |
| return !isv? true : AbortFault<DataAbort>::il(tc); |
| } |
| |
| bool |
| DataAbort::routeToMonitor(ThreadContext *tc) const |
| { |
| SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); |
| return scr.ea && !isMMUFault(); |
| } |
| |
| bool |
| DataAbort::routeToHyp(ThreadContext *tc) const |
| { |
| bool toHyp; |
| |
| HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); |
| HDCR hdcr = tc->readMiscRegNoEffect(MISCREG_HDCR); |
| |
| bool amo = hcr.amo; |
| if (hcr.tge == 1) |
| amo = (!HaveExt(tc, ArmExtension::FEAT_VHE) || 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 |
| { |
| ESR esr = 0; |
| auto& iss = esr.data_abort_iss; |
| |
| iss.dfsc = srcEncoded & 0x3F; |
| iss.wnr = write; |
| iss.s1ptw = s1ptw; |
| iss.cm = cm; |
| |
| // 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)) { |
| iss.isv = isv; |
| if (isv) { |
| iss.sas = sas; |
| iss.sse = sse; |
| iss.srt = srt; |
| // AArch64 only. These assignments are safe on AArch32 as well |
| // because these vars are initialized to false |
| iss.sf = sf; |
| iss.ar = ar; |
| } |
| } |
| return iss; |
| } |
| |
| 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_EL2); |
| hcr.va = 0; |
| tc->setMiscRegNoEffect(MISCREG_HCR_EL2, hcr); |
| } |
| |
| bool |
| Interrupt::routeToMonitor(ThreadContext *tc) const |
| { |
| assert(ArmSystem::haveEL(tc, EL3)); |
| SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); |
| return scr.irq; |
| } |
| |
| bool |
| Interrupt::routeToHyp(ThreadContext *tc) const |
| { |
| HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); |
| 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_EL3); |
| return (!scr.ns || scr.aw); |
| } |
| return true; |
| } |
| |
| VirtualInterrupt::VirtualInterrupt() |
| {} |
| |
| bool |
| FastInterrupt::routeToMonitor(ThreadContext *tc) const |
| { |
| assert(ArmSystem::haveEL(tc, EL3)); |
| SCR scr = tc->readMiscRegNoEffect(MISCREG_SCR_EL3); |
| return scr.fiq; |
| } |
| |
| bool |
| FastInterrupt::routeToHyp(ThreadContext *tc) const |
| { |
| HCR hcr = tc->readMiscRegNoEffect(MISCREG_HCR_EL2); |
| 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_EL3); |
| 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_EL3); |
| 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 ? ExceptionClass::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 ExceptionClass::HW_BREAKPOINT_CURR_EL; |
| else |
| return ExceptionClass::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 |
| { |
| ESR esr = 0; |
| auto& iss = esr.watchpoint_iss; |
| iss.dfsc = 0b100010; |
| iss.cm = cm; |
| iss.wnr = write; |
| 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 ExceptionClass::WATCHPOINT_CURR_EL; |
| else |
| return ExceptionClass::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 ExceptionClass::SOFTWARE_STEP_CURR_EL; |
| else |
| return ExceptionClass::SOFTWARE_STEP_LOWER_EL; |
| } |
| |
| uint32_t |
| SoftwareStepFault::iss() const |
| { |
| ESR esr = 0; |
| auto& iss = esr.software_step_iss; |
| iss.ifsc = 0b100010; |
| iss.isv = stepped; |
| iss.ex = isldx; |
| 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 |