//----------------------------------------------------------------------------
//
// Breakpoint support.
//
// Copyright (C) Microsoft Corporation, 1997-2001.
//
//----------------------------------------------------------------------------

#ifndef _BRKPT_HPP_
#define _BRKPT_HPP_

// Ways in which a breakpoint can be hit.  There's full
// match, hit but ignored and not hit.
#define BREAKPOINT_HIT         0
#define BREAKPOINT_HIT_IGNORED 1
#define BREAKPOINT_NOT_HIT     2

//----------------------------------------------------------------------------
//
// Breakpoint.
//
//----------------------------------------------------------------------------

#define BREAKPOINT_EXTERNAL_MODIFY_FLAGS \
    (DEBUG_BREAKPOINT_GO_ONLY | DEBUG_BREAKPOINT_ENABLED | \
     DEBUG_BREAKPOINT_ADDER_ONLY)
#define BREAKPOINT_EXTERNAL_FLAGS \
    (BREAKPOINT_EXTERNAL_MODIFY_FLAGS | DEBUG_BREAKPOINT_DEFERRED)

// Internal flags.
#define BREAKPOINT_KD_INTERNAL          0x80000000
#define BREAKPOINT_KD_COUNT_ONLY        0x40000000
#define BREAKPOINT_VIRT_ADDR            0x20000000
#define BREAKPOINT_INSERTED             0x10000000
#define BREAKPOINT_IN_LIST              0x08000000
#define BREAKPOINT_HIDDEN               0x04000000
#define BREAKPOINT_NOTIFY               0x02000000

// Internal types.
#define EXBS_NONE              0x00000000
#define EXBS_BREAKPOINT_DATA   0x00000001
#define EXBS_BREAKPOINT_CODE   0x00000002
#define EXBS_BREAKPOINT_ANY    0x00000003
#define EXBS_STEP_INSTRUCTION  0x00000004
#define EXBS_STEP_BRANCH       0x00000008
#define EXBS_STEP_ANY          0x0000000c
#define EXBS_ANY               0xffffffff

enum BreakpointMatchAction
{
    BREAKPOINT_ALLOW_MATCH,
    BREAKPOINT_WARN_MATCH,
    BREAKPOINT_REMOVE_MATCH
};
    
class Breakpoint
    : public IDebugBreakpoint
{
public:
    Breakpoint* m_Next;
    Breakpoint* m_Prev;
    ULONG m_Id;
    ULONG m_BreakType;
    ULONG m_Flags;
    ULONG m_DataSize;
    ULONG m_DataAccessType;
    ULONG m_PassCount;
    ULONG m_CurPassCount;
    PCSTR m_Command;
    PTHREAD_INFO m_MatchThread;
    PPROCESS_INFO m_Process;
    PCSTR m_OffsetExpr;
    DebugClient* m_Adder;
    ULONG64 m_MatchThreadData;
    ULONG64 m_MatchProcessData;

    Breakpoint(DebugClient* Adder, ULONG Id, ULONG Type, ULONG ProcType);
    ~Breakpoint(void);

    // IUnknown.
    STDMETHOD(QueryInterface)(
        THIS_
        IN REFIID InterfaceId,
        OUT PVOID* Interface
        );
    STDMETHOD_(ULONG, AddRef)(
        THIS
        );
    STDMETHOD_(ULONG, Release)(
        THIS
        );
    
    // IDebugBreakpoint.
    STDMETHOD(GetId)(
        THIS_
        OUT PULONG Id
        );
    STDMETHOD(GetType)(
        THIS_
        OUT PULONG BreakType,
        OUT PULONG ProcType
        );
    STDMETHOD(GetAdder)(
        THIS_
        OUT PDEBUG_CLIENT* Adder
        );
    STDMETHOD(GetFlags)(
        THIS_
        OUT PULONG Flags
        );
    STDMETHOD(AddFlags)(
        THIS_
        IN ULONG Flags
        );
    STDMETHOD(RemoveFlags)(
        THIS_
        IN ULONG Flags
        );
    STDMETHOD(SetFlags)(
        THIS_
        IN ULONG Flags
        );
    STDMETHOD(GetOffset)(
        THIS_
        OUT PULONG64 Offset
        );
    STDMETHOD(SetOffset)(
        THIS_
        IN ULONG64 Offset
        );
    STDMETHOD(GetDataParameters)(
        THIS_
        OUT PULONG Size,
        OUT PULONG AccessType
        );
    STDMETHOD(SetDataParameters)(
        THIS_
        IN ULONG Size,
        IN ULONG AccessType
        );
    STDMETHOD(GetPassCount)(
        THIS_
        OUT PULONG Count
        );
    STDMETHOD(SetPassCount)(
        THIS_
        IN ULONG Count
        );
    STDMETHOD(GetCurrentPassCount)(
        THIS_
        OUT PULONG Count
        );
    STDMETHOD(GetMatchThreadId)(
        THIS_
        OUT PULONG Id
        );
    STDMETHOD(SetMatchThreadId)(
        THIS_
        IN ULONG Id
        );
    STDMETHOD(GetCommand)(
        THIS_
        OUT OPTIONAL PSTR Buffer,
        IN ULONG BufferSize,
        OUT OPTIONAL PULONG CommandSize
        );
    STDMETHOD(SetCommand)(
        THIS_
        IN PCSTR Command
        );
    STDMETHOD(GetOffsetExpression)(
        THIS_
        OUT OPTIONAL PSTR Buffer,
        IN ULONG BufferSize,
        OUT OPTIONAL PULONG ExpressionSize
        );
    STDMETHOD(SetOffsetExpression)(
        THIS_
        IN PCSTR Expression
        );
    STDMETHOD(GetParameters)(
        THIS_
        OUT PDEBUG_BREAKPOINT_PARAMETERS Params
        );
    // Breakpoint.
    virtual HRESULT Validate(void) = 0;
    virtual HRESULT Insert(void) = 0;
    virtual HRESULT Remove(void) = 0;
    virtual ULONG IsHit(PADDR Addr) = 0;

    // Must resturn true if in case of THIS breakpoint hit
    // Pc points to the instruction caused the hit
    virtual BOOL PcAtHit() = 0;
    
    PADDR GetAddr(void)
    {
        return &m_Addr;
    }
    
    BOOL PassHit(void)
    {
        if (--m_CurPassCount == 0)
        {
            m_CurPassCount = 1;
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
    ULONG GetProcType(void)
    {
        return m_ProcType;
    }
    void SetProcType(ULONG ProcType)
    {
        m_ProcType = ProcType;
        m_ProcIndex = MachineTypeIndex(ProcType);
    }
    ULONG GetProcIndex(void)
    {
        return m_ProcIndex;
    }
    
    void ForceFlatAddr(void)
    {
        NotFlat(m_Addr);
        ComputeFlatAddress(&m_Addr, NULL);
    }

    // Breakpoint is enabled, not deferred, not internal.
    BOOL IsNormalEnabled(void)
    {
        return (m_Flags & (DEBUG_BREAKPOINT_ENABLED |
                           BREAKPOINT_KD_INTERNAL |
                           DEBUG_BREAKPOINT_DEFERRED)) ==
            DEBUG_BREAKPOINT_ENABLED;
    }

    void LinkIntoList(void);
    void UnlinkFromList(void);
    void UpdateInternal(void);
    BOOL EvalOffsetExpr(void);
    HRESULT SetAddr(PADDR Addr, BreakpointMatchAction MatchAction);
    // Matches breakpoints if they have the same insertion effect.
    // Used when determining whether a breakpoint needs to be
    // inserted or if another breakpoint is already covering the break.
    BOOL IsInsertionMatch(Breakpoint* Match);
    // Matches breakpoints if they have an insertion match and
    // if they match publicly, such as between flags, hiddenness
    // and so on.  Used when determining whether a user breakpoint
    // redefines an existing breakpoint.
    BOOL IsPublicMatch(Breakpoint* Match);
    // Check m_Match* fields against current state.
    BOOL MatchesCurrentState(void);

protected:
    // ProcType is private so that ProcType and ProcIndex can
    // be kept in sync.
    ULONG m_ProcType;
    MachineIndex m_ProcIndex;
    // Address is private to force users to go through SetAddr.
    ADDR m_Addr;
    ULONG m_CommandLen;
    ULONG m_OffsetExprLen;
    UCHAR m_InsertStorage[MAX_BREAKPOINT_LENGTH];
};

//----------------------------------------------------------------------------
//
// CodeBreakpoint.
//
//----------------------------------------------------------------------------

class CodeBreakpoint :
    public Breakpoint
{
public:
    CodeBreakpoint(DebugClient* Adder, ULONG Id, ULONG ProcType)
        : Breakpoint(Adder, Id, DEBUG_BREAKPOINT_CODE, ProcType)
    {
        m_Flags |= BREAKPOINT_VIRT_ADDR;
    }

    // Breakpoint.
    virtual HRESULT Validate(void);
    virtual HRESULT Insert(void);
    virtual HRESULT Remove(void);
    virtual ULONG IsHit(PADDR Addr);
    virtual BOOL PcAtHit()
    {
        return TRUE;
    }
};

//----------------------------------------------------------------------------
//
// DataBreakpoint.
//
//----------------------------------------------------------------------------

class DataBreakpoint :
    public Breakpoint
{
public:
    DataBreakpoint(DebugClient* Adder, ULONG Id, ULONG ProcType)
        : Breakpoint(Adder, Id, DEBUG_BREAKPOINT_DATA, ProcType) {}

    // Breakpoint.
    virtual HRESULT Insert(void);
    virtual HRESULT Remove(void);

    // DataBreakpoint.
    static void ClearThreadDataBreaks(PTHREAD_INFO Thread);
    void AddToThread(PTHREAD_INFO Thread);
};

//----------------------------------------------------------------------------
//
// X86DataBreakpoint.
//
//----------------------------------------------------------------------------

class X86DataBreakpoint :
    public DataBreakpoint
{
public:
    X86DataBreakpoint(DebugClient* Adder, ULONG Id,
                      ULONG Cr4Reg, ULONG Dr6Reg, ULONG ProcType)
        : DataBreakpoint(Adder, Id, ProcType)
    {
        m_Dr7Bits = 0;
        m_Cr4Reg = Cr4Reg;
        m_Dr6Reg = Dr6Reg;
    }

    // Breakpoint.
    virtual HRESULT Validate(void);
    virtual ULONG IsHit(PADDR Addr);

    virtual BOOL PcAtHit()
    {
        return FALSE;
    }

private:
    // Precomputed enable bits.
    ULONG m_Dr7Bits;
    
    // Register indices for getting breakpoint-related information.
    ULONG m_Cr4Reg;
    ULONG m_Dr6Reg;

    friend class X86MachineInfo;
    friend class Amd64MachineInfo;
};

//----------------------------------------------------------------------------
//
// Ia64DataBreakpoint.
//
//----------------------------------------------------------------------------

class Ia64DataBreakpoint :
    public DataBreakpoint
{
public:
    Ia64DataBreakpoint(DebugClient* Adder, ULONG Id)
        : DataBreakpoint(Adder, Id, IMAGE_FILE_MACHINE_IA64)
    {
        m_Control = 0;
    }

    // Breakpoint.
    virtual HRESULT Validate(void);
    virtual ULONG IsHit(PADDR Addr);
    virtual BOOL PcAtHit()
    {
        return TRUE;
    }

    static ULONG64 GetControl(ULONG AccessType, ULONG Size);

private:
    ULONG64 m_Control;

    friend class Ia64MachineInfo;
};

//----------------------------------------------------------------------------
//
// X86OnIa64DataBreakpoint.
//
//----------------------------------------------------------------------------
class X86OnIa64DataBreakpoint :
    public X86DataBreakpoint
{
public:
    X86OnIa64DataBreakpoint(DebugClient* Adder, ULONG Id);

    // Breakpoint.
    virtual HRESULT Validate(void);
    virtual ULONG IsHit(PADDR Addr);

private:
    ULONG64 m_Control;

    friend class Ia64MachineInfo;
};

extern BOOL g_BreakpointListChanged;
extern BOOL g_UpdateDataBreakpoints;
extern BOOL g_DataBreakpointsChanged;
extern BOOL g_BreakpointsSuspended;
extern BOOL g_DeferDefined;
extern Breakpoint* g_DeferBp;
extern Breakpoint* g_StepTraceBp;
extern CHAR g_StepTraceCmdState;
extern ADDR g_PrevRelatedPc;

HRESULT BreakpointInit(void);

HRESULT InsertBreakpoints(void);
BOOL RemoveBreakpoints(void);

HRESULT AddBreakpoint(DebugClient* Client,
                      ULONG Type,
                      ULONG DesiredId,
                      Breakpoint** Bp);
void RemoveBreakpoint(Breakpoint* Bp);
void RemoveProcessBreakpoints(PPROCESS_INFO Process);
void RemoveThreadBreakpoints(PTHREAD_INFO Thread);
void RemoveAllKernelBreakpoints(void);
void RemoveAllBreakpoints(ULONG Reason);

Breakpoint* GetBreakpointByIndex(DebugClient* Client, ULONG Index);
Breakpoint* GetBreakpointById(DebugClient* Client, ULONG Id);
Breakpoint* CheckMatchingBreakpoints(Breakpoint* Match, BOOL PUBLIC,
                                     ULONG IncFlags);
Breakpoint* CheckBreakpointHit(PPROCESS_INFO Process,
                               Breakpoint* Start, PADDR Addr,
                               ULONG ExbsType, ULONG IncFlags, ULONG ExcFlags,
                               PULONG HitType,
                               BOOL SetLastBreakpointHit);
ULONG NotifyHitBreakpoints(ULONG EventStatus);

void EvaluateOffsetExpressions(PPROCESS_INFO Process, ULONG Flags);

#define BPCMDS_FORCE_DISABLE 0x00000001
#define BPCMDS_ONE_LINE      0x00000002
#define BPCMDS_EXPR_ONLY     0x00000004
#define BPCMDS_MODULE_HINT   0x00000008

void ChangeBreakpointState(DebugClient* Client, PPROCESS_INFO ForProcess,
                           ULONG Id, UCHAR StateChange);
void ListBreakpoints(DebugClient* Client, PPROCESS_INFO ForProcess,
                     ULONG Id);
void ListBreakpointsAsCommands(DebugClient* Client, PPROCESS_INFO Process,
                               ULONG Flags);
PDEBUG_BREAKPOINT ParseBpCmd(DebugClient* Client, UCHAR Type,
                             PTHREAD_INFO Thread);
BOOL CheckBreakpointInsertedInRange(PPROCESS_INFO Process,
                                    ULONG64 Start, ULONG64 End);

void DbgKdpAcquireHardwareBp(PDBGKD_CONTROL_REQUEST BpRequest);
void DbgKdpReleaseHardwareBp(PDBGKD_CONTROL_REQUEST BpRequest);

#endif // #ifndef _BRKPT_HPP_