| /* |
| * 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 SelfDebug |
| { |
| private: |
| std::vector<BrkPoint> arBrkPoints; |
| std::vector<WatchPoint> arWatchPoints; |
| SoftwareStep * softStep; |
| |
| 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() |
| : softStep(nullptr), enableTdeTge(false), |
| mde(false), sdd(false), kde(false), oslk(false) |
| { |
| softStep = new SoftwareStep(this); |
| } |
| |
| ~SelfDebug() |
| { |
| delete softStep; |
| } |
| |
| 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: |
| 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; |
| } |
| |
| bool |
| targetAArch32(ThreadContext *tc) |
| { |
| ExceptionLevel ELd = debugTargetFrom(tc, isSecure(tc)); |
| return ELIs32(tc, ELd) && aarch32; |
| } |
| |
| void init(ThreadContext *tc); |
| }; |
| |
| } // namespace ArmISA |
| } // namespace gem5 |
| |
| #endif |