blob: c73782e96613ab04fc2bdcd0336c5094d8629571 [file] [log] [blame]
/*
* Copyright (c) 2021 Arm Limited
* Copyright (c) 2019 Metempsy Technology LSC
* 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.
*
* 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.
*/
#ifndef __ARCH_ARM_SELF_DEBUG_HH__
#define __ARCH_ARM_SELF_DEBUG_HH__
#include "arch/arm/faults.hh"
#include "arch/arm/regs/misc.hh"
#include "arch/arm/system.hh"
#include "arch/arm/types.hh"
#include "arch/arm/utility.hh"
#include "arch/generic/tlb.hh"
#include "cpu/thread_context.hh"
namespace gem5
{
class ThreadContext;
namespace ArmISA
{
class SelfDebug;
class BrkPoint
{
private:
MiscRegIndex ctrlRegIndex;
MiscRegIndex valRegIndex;
SelfDebug * conf;
bool isCntxtAware;
bool VMID16enabled;
Addr activePc;
bool enable;
int maxAddrSize;
bool onUse;
public:
friend class SelfDebug;
BrkPoint(MiscRegIndex ctrl_index, MiscRegIndex val_index,
SelfDebug* _conf, bool ctx_aw, bool lva,
bool vmid16, bool aarch32):
ctrlRegIndex(ctrl_index), valRegIndex(val_index),
conf(_conf), isCntxtAware(ctx_aw),
VMID16enabled(vmid16), activePc(0x0), enable(false)
{
maxAddrSize = lva ? 52: 48 ;
maxAddrSize = aarch32 ? 31 : maxAddrSize;
onUse = false;
}
bool testLinkedBk(ThreadContext *tc, Addr vaddr, ExceptionLevel el);
bool test(ThreadContext *tc, Addr pc, ExceptionLevel el, DBGBCR ctr,
bool from_link);
protected:
inline Addr
getAddrfromReg(ThreadContext *tc) const
{
return bits(tc->readMiscReg(valRegIndex), maxAddrSize, 2);
}
inline RegVal
getContextfromReg(ThreadContext *tc, bool ctxid1) const
{
if (ctxid1)
return bits(tc->readMiscReg(valRegIndex), 31, 0);
else
return bits(tc->readMiscReg(valRegIndex), 63, 32);
}
vmid_t getVMIDfromReg(ThreadContext *tc, bool vs);
public:
bool testAddrMatch(ThreadContext *tc, Addr pc, uint8_t bas);
bool testAddrMissMatch(ThreadContext *tc, Addr pc, uint8_t bas);
bool testContextMatch(ThreadContext *tc, bool ctx1, bool low_ctx);
bool testContextMatch(ThreadContext *tc, bool ctx1);
bool testVMIDMatch(ThreadContext *tc);
const DBGBCR
getControlReg(ThreadContext *tc)
{
return tc->readMiscReg(ctrlRegIndex);
}
bool isEnabled(ThreadContext* tc, ExceptionLevel el,
uint8_t hmc, uint8_t ssc, uint8_t pmc);
bool
isActive(Addr vaddr)
{
if (vaddr == activePc) {
activePc = 0x0;
return false;
} else {
activePc = vaddr;
return true;
}
}
inline void
updateControl(DBGBCR val)
{
enable = val.e == 0x1;
}
};
class WatchPoint
{
private:
MiscRegIndex ctrlRegIndex;
MiscRegIndex valRegIndex;
SelfDebug * conf;
bool enable;
int maxAddrSize;
public:
friend class SelfDebug;
WatchPoint(MiscRegIndex ctrl_index, MiscRegIndex val_index,
SelfDebug* _conf, bool lva, bool aarch32) :
ctrlRegIndex(ctrl_index),
valRegIndex(val_index), conf(_conf), enable(false)
{
maxAddrSize = lva ? 52: 48 ;
maxAddrSize = aarch32 ? 31 : maxAddrSize;
}
bool compareAddress(ThreadContext *tc, Addr in_addr,
uint8_t bas, uint8_t mask, unsigned size);
inline Addr
getAddrfromReg(ThreadContext *tc)
{
return bits(tc->readMiscReg(valRegIndex), maxAddrSize, 0);
}
inline bool
isDoubleAligned(Addr addr)
{
return addr & 0x4;
}
inline void
updateControl(DBGWCR val)
{
enable = val.e == 0x1;
}
bool isEnabled(ThreadContext* tc, ExceptionLevel el, bool hmc,
uint8_t ssc, uint8_t pac);
bool test(ThreadContext *tc, Addr addr, ExceptionLevel el, bool& wrt,
bool atomic, unsigned size);
};
class SoftwareStep
{
private:
static const uint8_t INACTIVE_STATE = 0;
static const uint8_t ACTIVE_PENDING_STATE = 1;
static const uint8_t ACTIVE_NOT_PENDING_STATE = 2;
bool bSS;
int stateSS;
SelfDebug *conf;
bool steppedLdx;
bool prevSteppedLdx;
bool cpsrD;
public:
friend class SelfDebug;
SoftwareStep(SelfDebug *s)
: bSS(false), stateSS(INACTIVE_STATE),
conf(s), steppedLdx(false)
{}
bool debugExceptionReturnSS(ThreadContext *tc, CPSR spsr,
ExceptionLevel dest);
bool advanceSS(ThreadContext *tc);
void
setLdx()
{
prevSteppedLdx = steppedLdx;
steppedLdx = true;
}
void
clearLdx()
{
prevSteppedLdx = steppedLdx;
steppedLdx = false;
}
bool
getLdx() const
{
return prevSteppedLdx;
}
};
class VectorCatch
{
private:
bool vcmatch;
SelfDebug *conf;
std::vector<Fault *> vectorTypes();
public:
VectorCatch(bool _vcmatch, SelfDebug* s) : vcmatch(_vcmatch), conf(s)
{}
bool addressMatching(ThreadContext *tc, Addr addr, ExceptionLevel el);
bool exceptionTrapping(ThreadContext *tc, ExceptionLevel el,
ArmFault* fault);
bool isVCMatch() const { return vcmatch; }
private:
Addr
getVectorBase(ThreadContext *tc, bool monitor)
{
if (monitor) {
return tc->readMiscReg(MISCREG_MVBAR) & ~0x1F;
}
SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR_EL1);
if (sctlr.v) {
return (Addr) 0xFFFF0000;
} else {
Addr vbar = tc->readMiscReg(MISCREG_VBAR) & ~0x1F;
return vbar;
}
}
};
class SelfDebug
{
private:
std::vector<BrkPoint> arBrkPoints;
std::vector<WatchPoint> arWatchPoints;
SoftwareStep * softStep;
VectorCatch * vcExcpt;
bool initialized;
bool enableTdeTge; // MDCR_EL2.TDE || HCR_EL2.TGE
bool mde; // MDSCR_EL1.MDE, DBGDSCRext.MDBGen
bool sdd; // MDCR_EL3.SDD
bool kde; // MDSCR_EL1.KDE
bool oslk; // OS lock flag
bool aarch32; // updates with stage1 aarch64/32
bool to32;
public:
SelfDebug()
: initialized(false), enableTdeTge(false),
mde(false), sdd(false), kde(false), oslk(false)
{
softStep = new SoftwareStep(this);
}
~SelfDebug()
{
delete softStep;
delete vcExcpt;
}
Fault testDebug(ThreadContext *tc, const RequestPtr &req,
BaseMMU::Mode mode);
protected:
Fault testBreakPoints(ThreadContext *tc, Addr vaddr);
Fault testWatchPoints(ThreadContext *tc, Addr vaddr, bool write,
bool atomic, unsigned size, bool cm);
Fault triggerException(ThreadContext * tc, Addr vaddr);
Fault triggerWatchpointException(ThreadContext *tc, Addr vaddr,
bool write, bool cm);
public:
Fault testVectorCatch(ThreadContext *tc, Addr addr, ArmFault* flt);
bool enabled() const { return mde || softStep->bSS; };
inline BrkPoint*
getBrkPoint(uint8_t index)
{
return &arBrkPoints[index];
}
static inline bool
securityStateMatch(ThreadContext *tc, uint8_t ssc, bool hmc)
{
switch (ssc) {
case 0x0: return true;
case 0x1: return !isSecure(tc);
case 0x2: return isSecure(tc);
case 0x3:
{
bool b = hmc? true: isSecure(tc);
return b;
}
default: panic("Unreachable value");
}
return false;
}
bool isDebugEnabledForEL64(ThreadContext *tc, ExceptionLevel el,
bool secure, bool mask);
bool isDebugEnabledForEL32(ThreadContext *tc, ExceptionLevel el,
bool secure, bool mask);
void
activateDebug()
{
for (auto &p: arBrkPoints){
p.onUse = false;
}
}
inline bool
isDebugEnabled(ThreadContext *tc)
{
CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
ExceptionLevel el = (ExceptionLevel) currEL(tc);
if (aarch32) {
return isDebugEnabledForEL32(tc, el, isSecure(tc),
(bool)cpsr.d == 1);
} else {
return isDebugEnabledForEL64(tc, el, isSecure(tc),
(bool)cpsr.d == 1 );
}
}
inline void
setbSDD(RegVal val)
{
sdd = bits(val, 16);
}
inline void
setMDSCRvals(RegVal val)
{
mde = bits(val, 15);
kde = bits(val, 13);
softStep->bSS = bits(val, 0);
}
inline void
setMDBGen(RegVal val)
{
mde = bits(val, 15);
}
inline void
setenableTDETGE(HCR hcr, HDCR mdcr)
{
enableTdeTge = (mdcr.tde == 0x1 || hcr.tge == 0x1);
}
inline void
updateOSLock(RegVal val)
{
oslk = bool(bits(val, 0));
}
inline void
updateDBGBCR(int index, DBGBCR val)
{
arBrkPoints[index].updateControl(val);
}
inline void
updateDBGWCR(int index, DBGWCR val)
{
arWatchPoints[index].updateControl(val);
}
inline void
setDebugMask(bool mask)
{
softStep->cpsrD = mask;
}
inline bool
isAArch32() const
{
return aarch32;
}
inline void
setAArch32(ThreadContext *tc)
{
ExceptionLevel from_el = (ExceptionLevel) currEL(tc);
if (from_el == EL0)
aarch32 = ELIs32(tc, EL0) && ELIs32(tc, EL1);
else
aarch32 = ELIs32(tc, from_el);
return;
}
SoftwareStep *
getSstep()
{
return softStep;
}
VectorCatch*
getVectorCatch(ThreadContext *tc)
{
if (!initialized)
init(tc);
return vcExcpt;
}
bool
targetAArch32(ThreadContext *tc)
{
ExceptionLevel ELd = debugTargetFrom(tc, isSecure(tc));
return ELIs32(tc, ELd) && aarch32;
}
void init(ThreadContext *tc);
};
} // namespace ArmISA
} // namespace gem5
#endif