gem5/src/arch/arm/faults.hh
Dylan Johnson 2950a95672 arm: Add AArch64 hypervisor call instruction 'hvc'
This patch adds the AArch64 instruction hvc which raises an exception
from EL1 into EL2. The host OS uses this instruction to world switch
into the guest.

Change-Id: I930ee43f4f0abd4b35a68eb2a72e44e3ea6570be
2016-08-02 10:38:02 +01:00

572 lines
19 KiB
C++

/*
* Copyright (c) 2010, 2012-2013, 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.
*
* 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/misc.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
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;
};
ArmFault(ExtMachInst _machInst = 0, uint32_t _iss = 0) :
machInst(_machInst), issRaw(_iss), from64(false), to64(false),
fromEL(EL0), toEL(EL0), fromMode(MODE_UNDEFINED) {}
// 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);
void invoke64(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr);
virtual void annotate(AnnotationIDs id, uint64_t val) {}
virtual FaultStat& countStat() = 0;
virtual FaultOffset offset(ThreadContext *tc) = 0;
virtual FaultOffset offset64() = 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) { 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 { return vals.name; }
FaultStat & countStat() { return vals.count; }
FaultOffset offset(ThreadContext *tc);
FaultOffset
offset64()
{
if (toEL == fromEL) {
if (opModeIsT(fromMode))
return vals.currELTOffset;
return vals.currELHOffset;
} else {
if (from64)
return vals.lowerEL64Offset;
return vals.lowerEL32Offset;
}
}
OperatingMode nextMode() { return vals.nextMode; }
virtual bool routeToMonitor(ThreadContext *tc) const { return false; }
uint8_t armPcOffset(bool isHyp) { return isHyp ? vals.armPcElrOffset
: vals.armPcOffset; }
uint8_t thumbPcOffset(bool isHyp) { return isHyp ? vals.thumbPcElrOffset
: vals.thumbPcOffset; }
uint8_t armPcElrOffset() { return vals.armPcElrOffset; }
uint8_t thumbPcElrOffset() { return vals.thumbPcElrOffset; }
virtual bool abortDisable(ThreadContext* tc) { return vals.abortDisable; }
virtual bool fiqDisable(ThreadContext* tc) { return vals.fiqDisable; }
virtual ExceptionClass ec(ThreadContext *tc) const { return vals.ec; }
virtual uint32_t iss() const { return issRaw; }
};
class Reset : public ArmFaultVals<Reset>
{
public:
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr);
};
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);
bool routeToHyp(ThreadContext *tc) const;
ExceptionClass ec(ThreadContext *tc) const;
uint32_t iss() const;
};
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);
bool routeToHyp(ThreadContext *tc) const;
ExceptionClass ec(ThreadContext *tc) const;
uint32_t iss() const;
};
class SecureMonitorCall : public ArmFaultVals<SecureMonitorCall>
{
public:
SecureMonitorCall(ExtMachInst _machInst) :
ArmFaultVals<SecureMonitorCall>(_machInst)
{}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr);
ExceptionClass ec(ThreadContext *tc) const;
uint32_t iss() const;
};
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)
{}
ExceptionClass ec(ThreadContext *tc) const;
};
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;
};
class HypervisorCall : public ArmFaultVals<HypervisorCall>
{
public:
HypervisorCall(ExtMachInst _machInst, uint32_t _imm);
ExceptionClass ec(ThreadContext *tc) const;
};
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;
};
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);
FSR getFsr(ThreadContext *tc);
bool abortDisable(ThreadContext *tc);
uint32_t iss() const;
bool isStage2() const { return stage2; }
void annotate(ArmFault::AnnotationIDs id, uint64_t val);
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;
// @todo: external aborts should be routed if SCR.EA == 1
bool routeToMonitor(ThreadContext *tc) const;
bool routeToHyp(ThreadContext *tc) const;
};
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;
// @todo: external aborts should be routed if SCR.EA == 1
bool routeToMonitor(ThreadContext *tc) const;
bool routeToHyp(ThreadContext *tc) const;
uint32_t iss() const;
void annotate(AnnotationIDs id, uint64_t val);
};
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);
};
class Interrupt : public ArmFaultVals<Interrupt>
{
public:
bool routeToMonitor(ThreadContext *tc) const;
bool routeToHyp(ThreadContext *tc) const;
bool abortDisable(ThreadContext *tc);
};
class VirtualInterrupt : public ArmFaultVals<VirtualInterrupt>
{
public:
VirtualInterrupt();
};
class FastInterrupt : public ArmFaultVals<FastInterrupt>
{
public:
bool routeToMonitor(ThreadContext *tc) const;
bool routeToHyp(ThreadContext *tc) const;
bool abortDisable(ThreadContext *tc);
bool fiqDisable(ThreadContext *tc);
};
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);
};
/// 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);
bool routeToMonitor(ThreadContext *tc) const;
bool routeToHyp(ThreadContext *tc) const;
};
// A fault that flushes the pipe, excluding the faulting instructions
class FlushPipe : public ArmFaultVals<FlushPipe>
{
public:
FlushPipe() {}
void invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr);
};
// 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);
};
/// Illegal Instruction Set State fault (AArch64 only)
class IllegalInstSetStateFault : public ArmFaultVals<IllegalInstSetStateFault>
{
public:
IllegalInstSetStateFault();
};
} // namespace ArmISA
#endif // __ARM_FAULTS_HH__