/* * 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/miscregs.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" 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); } inline uint32_t getVMIDfromReg(ThreadContext *tc); 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 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: SoftwareStep(SelfDebug *s) : bSS(false), stateSS(INACTIVE_STATE), conf(s), steppedLdx(false) {} bool debugExceptionReturnSS(ThreadContext *tc, CPSR spsr, ExceptionLevel dest, bool aarch32); bool advanceSS(ThreadContext *tc); inline void setCPSRD(bool val) { cpsrD = val; } inline void setEnableSS(bool val) { bSS = val; } 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 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 arBrkPoints; std::vector arWatchPoints; SoftwareStep * softStep; VectorCatch * vcExcpt; bool initialized; bool enableTdeTge; // MDCR_EL2.TDE || HCR_EL2.TGE // THIS is MDSCR_EL1.MDE in aarch64 and DBGDSCRext.MDBGen in aarch32 bool enableFlag; bool bSDD; // MDCR_EL3.SDD bool bKDE; // MDSCR_EL1.KDE bool oslk; // OS lock flag bool aarch32; // updates with stage1 aarch64/32 bool to32; public: SelfDebug() : initialized(false), enableTdeTge(false), enableFlag(false), bSDD(false), bKDE(false), oslk(false) { softStep = new SoftwareStep(this); } ~SelfDebug() { delete softStep; delete vcExcpt; } Fault testDebug(ThreadContext *tc, const RequestPtr &req, BaseTLB::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); 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 !inSecureState(tc); case 0x2: return inSecureState(tc); case 0x3: { bool b = hmc? true: inSecureState(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, inSecureState(tc), (bool)cpsr.d == 1); } else { return isDebugEnabledForEL64(tc, el, inSecureState(tc), (bool)cpsr.d == 1 ); } } inline void setbSDD(RegVal val) { bSDD = bits(val, 16); } inline void setMDSCRvals(RegVal val) { enableFlag = bits(val, 15); bKDE = bits(val, 13); softStep->setEnableSS((bool)bits(val, 0)); } inline void setMDBGen(RegVal val) { enableFlag = 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->setCPSRD(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, inSecureState(tc)); return ELIs32(tc, ELd) && aarch32; } void init(ThreadContext *tc); }; } #endif