blob: 859fd3489961b3b5b3345ddc0d195c0800dd45ae [file] [log] [blame]
/*
* Copyright (c) 2010, 2012-2013, 2016-2018 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.
*
* Authors: Ali Saidi
* Gabe Black
* Giacomo Gabrielli
* Thomas Grocutt
*/
#ifndef __ARM_FAULTS_HH__
#define __ARM_FAULTS_HH__
#include "arch/arm/miscregs.hh"
#include "arch/arm/pagetable.hh"
#include "arch/arm/types.hh"
#include "base/logging.hh"
#include "sim/faults.hh"
#include "sim/full_system.hh"
// The design of the "name" and "vect" functions is in sim/faults.hh
namespace ArmISA
{
typedef Addr FaultOffset;
class ArmFault : public FaultBase
{
protected:
ExtMachInst machInst;
uint32_t issRaw;
// Helper variables for ARMv8 exception handling
bool from64; // True if the exception is generated from the AArch64 state
bool to64; // True if the exception is taken in AArch64 state
ExceptionLevel fromEL; // Source exception level
ExceptionLevel toEL; // Target exception level
OperatingMode fromMode; // Source operating mode (aarch32)
OperatingMode toMode; // Next operating mode (aarch32)
// This variable is true if the above fault specific informations
// have been updated. This is to prevent that a client is using their
// un-updated default constructed value.
bool faultUpdated;
bool hypRouted; // True if the fault has been routed to Hypervisor
virtual Addr getVector(ThreadContext *tc);
Addr getVector64(ThreadContext *tc);
public:
/// Generic fault source enums used to index into
/// {short/long/aarch64}DescFaultSources[] to get the actual encodings based
/// on the current register width state and the translation table format in
/// use
enum FaultSource
{
AlignmentFault = 0,
InstructionCacheMaintenance, // Short-desc. format only
SynchExtAbtOnTranslTableWalkLL,
SynchPtyErrOnTranslTableWalkLL = SynchExtAbtOnTranslTableWalkLL + 4,
TranslationLL = SynchPtyErrOnTranslTableWalkLL + 4,
AccessFlagLL = TranslationLL + 4,
DomainLL = AccessFlagLL + 4,
PermissionLL = DomainLL + 4,
DebugEvent = PermissionLL + 4,
SynchronousExternalAbort,
TLBConflictAbort, // Requires LPAE
SynchPtyErrOnMemoryAccess,
AsynchronousExternalAbort,
AsynchPtyErrOnMemoryAccess,
AddressSizeLL, // AArch64 only
// Not real faults. These are faults to allow the translation function
// to inform the memory access function not to proceed for a prefetch
// that misses in the TLB or that targets an uncacheable address
PrefetchTLBMiss = AddressSizeLL + 4,
PrefetchUncacheable,
NumFaultSources,
FaultSourceInvalid = 0xff
};
/// Encodings of the fault sources when the short-desc. translation table
/// format is in use (ARM ARM Issue C B3.13.3)
static uint8_t shortDescFaultSources[NumFaultSources];
/// Encodings of the fault sources when the long-desc. translation table
/// format is in use (ARM ARM Issue C B3.13.3)
static uint8_t longDescFaultSources[NumFaultSources];
/// Encodings of the fault sources in AArch64 state
static uint8_t aarch64FaultSources[NumFaultSources];
enum AnnotationIDs
{
S1PTW, // DataAbort, PrefetchAbort: Stage 1 Page Table Walk,
OVA, // DataAbort, PrefetchAbort: stage 1 Virtual Address for stage 2 faults
SAS, // DataAbort: Syndrome Access Size
SSE, // DataAbort: Syndrome Sign Extend
SRT, // DataAbort: Syndrome Register Transfer
// AArch64 only
SF, // DataAbort: width of the accessed register is SixtyFour
AR // DataAbort: Acquire/Release semantics
};
enum TranMethod
{
LpaeTran,
VmsaTran,
UnknownTran
};
struct FaultVals
{
const FaultName name;
const FaultOffset offset;
// Offsets used for exceptions taken in AArch64 state
const uint16_t currELTOffset;
const uint16_t currELHOffset;
const uint16_t lowerEL64Offset;
const uint16_t lowerEL32Offset;
const OperatingMode nextMode;
const uint8_t armPcOffset;
const uint8_t thumbPcOffset;
// The following two values are used in place of armPcOffset and
// thumbPcOffset when the exception return address is saved into ELR
// registers (exceptions taken in HYP mode or in AArch64 state)
const uint8_t armPcElrOffset;
const uint8_t thumbPcElrOffset;
const bool hypTrappable;
const bool abortDisable;
const bool fiqDisable;
// Exception class used to appropriately set the syndrome register
// (exceptions taken in HYP mode or in AArch64 state)
const ExceptionClass ec;
FaultStat count;
FaultVals(const FaultName& name_, const FaultOffset& offset_,
const uint16_t& currELTOffset_, const uint16_t& currELHOffset_,
const uint16_t& lowerEL64Offset_,
const uint16_t& lowerEL32Offset_,
const OperatingMode& nextMode_, const uint8_t& armPcOffset_,
const uint8_t& thumbPcOffset_, const uint8_t& armPcElrOffset_,
const uint8_t& thumbPcElrOffset_, const bool& hypTrappable_,
const bool& abortDisable_, const bool& fiqDisable_,
const ExceptionClass& ec_)
: name(name_), offset(offset_), currELTOffset(currELTOffset_),
currELHOffset(currELHOffset_), lowerEL64Offset(lowerEL64Offset_),
lowerEL32Offset(lowerEL32Offset_), nextMode(nextMode_),
armPcOffset(armPcOffset_), thumbPcOffset(thumbPcOffset_),
armPcElrOffset(armPcElrOffset_), thumbPcElrOffset(thumbPcElrOffset_),
hypTrappable(hypTrappable_), abortDisable(abortDisable_),
fiqDisable(fiqDisable_), ec(ec_) {}
};
ArmFault(ExtMachInst _machInst = 0, uint32_t _iss = 0) :
machInst(_machInst), issRaw(_iss), from64(false), to64(false),
fromEL(EL0), toEL(EL0), fromMode(MODE_UNDEFINED),
faultUpdated(false), hypRouted(false) {}
// Returns the actual syndrome register to use based on the target
// exception level
MiscRegIndex getSyndromeReg64() const;
// Returns the actual fault address register to use based on the target
// exception level
MiscRegIndex getFaultAddrReg64() const;
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
void invoke64(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr);
void update(ThreadContext *tc);
virtual void annotate(AnnotationIDs id, uint64_t val) {}
virtual FaultStat& countStat() = 0;
virtual FaultOffset offset(ThreadContext *tc) = 0;
virtual FaultOffset offset64(ThreadContext *tc) = 0;
virtual OperatingMode nextMode() = 0;
virtual bool routeToMonitor(ThreadContext *tc) const = 0;
virtual bool routeToHyp(ThreadContext *tc) const { return false; }
virtual uint8_t armPcOffset(bool isHyp) = 0;
virtual uint8_t thumbPcOffset(bool isHyp) = 0;
virtual uint8_t armPcElrOffset() = 0;
virtual uint8_t thumbPcElrOffset() = 0;
virtual bool abortDisable(ThreadContext *tc) = 0;
virtual bool fiqDisable(ThreadContext *tc) = 0;
virtual ExceptionClass ec(ThreadContext *tc) const = 0;
virtual uint32_t iss() const = 0;
virtual bool isStage2() const { return false; }
virtual FSR getFsr(ThreadContext *tc) const { return 0; }
virtual void setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg);
};
template<typename T>
class ArmFaultVals : public ArmFault
{
protected:
static FaultVals vals;
public:
ArmFaultVals<T>(ExtMachInst _machInst = 0, uint32_t _iss = 0) :
ArmFault(_machInst, _iss) {}
FaultName name() const override { return vals.name; }
FaultStat & countStat() override { return vals.count; }
FaultOffset offset(ThreadContext *tc) override;
FaultOffset offset64(ThreadContext *tc) override;
OperatingMode nextMode() override { return vals.nextMode; }
virtual bool routeToMonitor(ThreadContext *tc) const override {
return false;
}
uint8_t armPcOffset(bool isHyp) override {
return isHyp ? vals.armPcElrOffset
: vals.armPcOffset;
}
uint8_t thumbPcOffset(bool isHyp) override {
return isHyp ? vals.thumbPcElrOffset
: vals.thumbPcOffset;
}
uint8_t armPcElrOffset() override { return vals.armPcElrOffset; }
uint8_t thumbPcElrOffset() override { return vals.thumbPcElrOffset; }
bool abortDisable(ThreadContext* tc) override { return vals.abortDisable; }
bool fiqDisable(ThreadContext* tc) override { return vals.fiqDisable; }
ExceptionClass ec(ThreadContext *tc) const override { return vals.ec; }
uint32_t iss() const override { return issRaw; }
};
class Reset : public ArmFaultVals<Reset>
{
protected:
Addr getVector(ThreadContext *tc) override;
public:
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
};
class UndefinedInstruction : public ArmFaultVals<UndefinedInstruction>
{
protected:
bool unknown;
bool disabled;
ExceptionClass overrideEc;
const char *mnemonic;
public:
UndefinedInstruction(ExtMachInst _machInst,
bool _unknown,
const char *_mnemonic = NULL,
bool _disabled = false) :
ArmFaultVals<UndefinedInstruction>(_machInst),
unknown(_unknown), disabled(_disabled),
overrideEc(EC_INVALID), mnemonic(_mnemonic)
{}
UndefinedInstruction(ExtMachInst _machInst, uint32_t _iss,
ExceptionClass _overrideEc, const char *_mnemonic = NULL) :
ArmFaultVals<UndefinedInstruction>(_machInst, _iss),
unknown(false), disabled(true), overrideEc(_overrideEc),
mnemonic(_mnemonic)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
bool routeToHyp(ThreadContext *tc) const override;
ExceptionClass ec(ThreadContext *tc) const override;
uint32_t iss() const override;
};
class SupervisorCall : public ArmFaultVals<SupervisorCall>
{
protected:
ExceptionClass overrideEc;
public:
SupervisorCall(ExtMachInst _machInst, uint32_t _iss,
ExceptionClass _overrideEc = EC_INVALID) :
ArmFaultVals<SupervisorCall>(_machInst, _iss),
overrideEc(_overrideEc)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
bool routeToHyp(ThreadContext *tc) const override;
ExceptionClass ec(ThreadContext *tc) const override;
uint32_t iss() const override;
};
class SecureMonitorCall : public ArmFaultVals<SecureMonitorCall>
{
public:
SecureMonitorCall(ExtMachInst _machInst) :
ArmFaultVals<SecureMonitorCall>(_machInst)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
ExceptionClass ec(ThreadContext *tc) const override;
uint32_t iss() const override;
};
class SupervisorTrap : public ArmFaultVals<SupervisorTrap>
{
protected:
ExtMachInst machInst;
ExceptionClass overrideEc;
public:
SupervisorTrap(ExtMachInst _machInst, uint32_t _iss,
ExceptionClass _overrideEc = EC_INVALID) :
ArmFaultVals<SupervisorTrap>(_machInst, _iss),
overrideEc(_overrideEc)
{}
bool routeToHyp(ThreadContext *tc) const override;
uint32_t iss() const override;
ExceptionClass ec(ThreadContext *tc) const override;
};
class SecureMonitorTrap : public ArmFaultVals<SecureMonitorTrap>
{
protected:
ExtMachInst machInst;
ExceptionClass overrideEc;
public:
SecureMonitorTrap(ExtMachInst _machInst, uint32_t _iss,
ExceptionClass _overrideEc = EC_INVALID) :
ArmFaultVals<SecureMonitorTrap>(_machInst, _iss),
overrideEc(_overrideEc)
{}
ExceptionClass ec(ThreadContext *tc) const override;
};
class HypervisorCall : public ArmFaultVals<HypervisorCall>
{
public:
HypervisorCall(ExtMachInst _machInst, uint32_t _imm);
ExceptionClass ec(ThreadContext *tc) const override;
};
class HypervisorTrap : public ArmFaultVals<HypervisorTrap>
{
protected:
ExtMachInst machInst;
ExceptionClass overrideEc;
public:
HypervisorTrap(ExtMachInst _machInst, uint32_t _iss,
ExceptionClass _overrideEc = EC_INVALID) :
ArmFaultVals<HypervisorTrap>(_machInst, _iss),
overrideEc(_overrideEc)
{}
ExceptionClass ec(ThreadContext *tc) const override;
};
template <class T>
class AbortFault : public ArmFaultVals<T>
{
protected:
/**
* The virtual address the fault occured at. If 2 stages of
* translation are being used then this is the intermediate
* physical address that is the starting point for the second
* stage of translation.
*/
Addr faultAddr;
/**
* Original virtual address. If the fault was generated on the
* second stage of translation then this variable stores the
* virtual address used in the original stage 1 translation.
*/
Addr OVAddr;
bool write;
TlbEntry::DomainType domain;
uint8_t source;
uint8_t srcEncoded;
bool stage2;
bool s1ptw;
ArmFault::TranMethod tranMethod;
public:
AbortFault(Addr _faultAddr, bool _write, TlbEntry::DomainType _domain,
uint8_t _source, bool _stage2,
ArmFault::TranMethod _tranMethod = ArmFault::UnknownTran) :
faultAddr(_faultAddr), OVAddr(0), write(_write),
domain(_domain), source(_source), srcEncoded(0),
stage2(_stage2), s1ptw(false), tranMethod(_tranMethod)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
FSR getFsr(ThreadContext *tc) const override;
uint8_t getFaultStatusCode(ThreadContext *tc) const;
bool abortDisable(ThreadContext *tc) override;
uint32_t iss() const override;
bool isStage2() const override { return stage2; }
void annotate(ArmFault::AnnotationIDs id, uint64_t val) override;
void setSyndrome(ThreadContext *tc, MiscRegIndex syndrome_reg) override;
bool isMMUFault() const;
};
class PrefetchAbort : public AbortFault<PrefetchAbort>
{
public:
static const MiscRegIndex FsrIndex = MISCREG_IFSR;
static const MiscRegIndex FarIndex = MISCREG_IFAR;
static const MiscRegIndex HFarIndex = MISCREG_HIFAR;
PrefetchAbort(Addr _addr, uint8_t _source, bool _stage2 = false,
ArmFault::TranMethod _tranMethod = ArmFault::UnknownTran) :
AbortFault<PrefetchAbort>(_addr, false, TlbEntry::DomainType::NoAccess,
_source, _stage2, _tranMethod)
{}
ExceptionClass ec(ThreadContext *tc) const override;
// @todo: external aborts should be routed if SCR.EA == 1
bool routeToMonitor(ThreadContext *tc) const override;
bool routeToHyp(ThreadContext *tc) const override;
};
class DataAbort : public AbortFault<DataAbort>
{
public:
static const MiscRegIndex FsrIndex = MISCREG_DFSR;
static const MiscRegIndex FarIndex = MISCREG_DFAR;
static const MiscRegIndex HFarIndex = MISCREG_HDFAR;
bool isv;
uint8_t sas;
uint8_t sse;
uint8_t srt;
// AArch64 only
bool sf;
bool ar;
DataAbort(Addr _addr, TlbEntry::DomainType _domain, bool _write, uint8_t _source,
bool _stage2 = false, ArmFault::TranMethod _tranMethod = ArmFault::UnknownTran) :
AbortFault<DataAbort>(_addr, _write, _domain, _source, _stage2,
_tranMethod),
isv(false), sas (0), sse(0), srt(0), sf(false), ar(false)
{}
ExceptionClass ec(ThreadContext *tc) const override;
// @todo: external aborts should be routed if SCR.EA == 1
bool routeToMonitor(ThreadContext *tc) const override;
bool routeToHyp(ThreadContext *tc) const override;
uint32_t iss() const override;
void annotate(AnnotationIDs id, uint64_t val) override;
};
class VirtualDataAbort : public AbortFault<VirtualDataAbort>
{
public:
static const MiscRegIndex FsrIndex = MISCREG_DFSR;
static const MiscRegIndex FarIndex = MISCREG_DFAR;
static const MiscRegIndex HFarIndex = MISCREG_HDFAR;
VirtualDataAbort(Addr _addr, TlbEntry::DomainType _domain, bool _write,
uint8_t _source) :
AbortFault<VirtualDataAbort>(_addr, _write, _domain, _source, false)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst) override;
};
class Interrupt : public ArmFaultVals<Interrupt>
{
public:
bool routeToMonitor(ThreadContext *tc) const override;
bool routeToHyp(ThreadContext *tc) const override;
bool abortDisable(ThreadContext *tc) override;
};
class VirtualInterrupt : public ArmFaultVals<VirtualInterrupt>
{
public:
VirtualInterrupt();
};
class FastInterrupt : public ArmFaultVals<FastInterrupt>
{
public:
bool routeToMonitor(ThreadContext *tc) const override;
bool routeToHyp(ThreadContext *tc) const override;
bool abortDisable(ThreadContext *tc) override;
bool fiqDisable(ThreadContext *tc) override;
};
class VirtualFastInterrupt : public ArmFaultVals<VirtualFastInterrupt>
{
public:
VirtualFastInterrupt();
};
/// PC alignment fault (AArch64 only)
class PCAlignmentFault : public ArmFaultVals<PCAlignmentFault>
{
protected:
/// The unaligned value of the PC
Addr faultPC;
public:
PCAlignmentFault(Addr _faultPC) : faultPC(_faultPC)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
bool routeToHyp(ThreadContext *tc) const override;
};
/// Stack pointer alignment fault (AArch64 only)
class SPAlignmentFault : public ArmFaultVals<SPAlignmentFault>
{
public:
SPAlignmentFault();
};
/// System error (AArch64 only)
class SystemError : public ArmFaultVals<SystemError>
{
public:
SystemError();
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
bool routeToMonitor(ThreadContext *tc) const override;
bool routeToHyp(ThreadContext *tc) const override;
};
/// System error (AArch64 only)
class SoftwareBreakpoint : public ArmFaultVals<SoftwareBreakpoint>
{
public:
SoftwareBreakpoint(ExtMachInst _mach_inst, uint32_t _iss);
bool routeToHyp(ThreadContext *tc) const override;
ExceptionClass ec(ThreadContext *tc) const override;
};
// A fault that flushes the pipe, excluding the faulting instructions
class ArmSev : public ArmFaultVals<ArmSev>
{
public:
ArmSev () {}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override;
};
/// Illegal Instruction Set State fault (AArch64 only)
class IllegalInstSetStateFault : public ArmFaultVals<IllegalInstSetStateFault>
{
public:
IllegalInstSetStateFault();
};
/*
* Explicitly declare template static member variables to avoid warnings
* in some clang versions
*/
template<> ArmFault::FaultVals ArmFaultVals<Reset>::vals;
template<> ArmFault::FaultVals ArmFaultVals<UndefinedInstruction>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SupervisorCall>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SecureMonitorCall>::vals;
template<> ArmFault::FaultVals ArmFaultVals<HypervisorCall>::vals;
template<> ArmFault::FaultVals ArmFaultVals<PrefetchAbort>::vals;
template<> ArmFault::FaultVals ArmFaultVals<DataAbort>::vals;
template<> ArmFault::FaultVals ArmFaultVals<VirtualDataAbort>::vals;
template<> ArmFault::FaultVals ArmFaultVals<HypervisorTrap>::vals;
template<> ArmFault::FaultVals ArmFaultVals<Interrupt>::vals;
template<> ArmFault::FaultVals ArmFaultVals<VirtualInterrupt>::vals;
template<> ArmFault::FaultVals ArmFaultVals<FastInterrupt>::vals;
template<> ArmFault::FaultVals ArmFaultVals<VirtualFastInterrupt>::vals;
template<> ArmFault::FaultVals ArmFaultVals<IllegalInstSetStateFault>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SupervisorTrap>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SecureMonitorTrap>::vals;
template<> ArmFault::FaultVals ArmFaultVals<PCAlignmentFault>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SPAlignmentFault>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SystemError>::vals;
template<> ArmFault::FaultVals ArmFaultVals<SoftwareBreakpoint>::vals;
template<> ArmFault::FaultVals ArmFaultVals<ArmSev>::vals;
} // namespace ArmISA
#endif // __ARM_FAULTS_HH__