#ifndef _RISCV_TRIGGERS_H #define _RISCV_TRIGGERS_H #include #include #include "decode.h" namespace triggers { typedef enum { OPERATION_EXECUTE, OPERATION_STORE, OPERATION_LOAD, } operation_t; typedef enum { ACTION_DEBUG_EXCEPTION = MCONTROL_ACTION_DEBUG_EXCEPTION, ACTION_DEBUG_MODE = MCONTROL_ACTION_DEBUG_MODE, ACTION_TRACE_START = MCONTROL_ACTION_TRACE_START, ACTION_TRACE_STOP = MCONTROL_ACTION_TRACE_STOP, ACTION_TRACE_EMIT = MCONTROL_ACTION_TRACE_EMIT, ACTION_MAXVAL = MCONTROL_ACTION_TRACE_EMIT } action_t; typedef enum { TIMING_BEFORE = 0, TIMING_AFTER = 1 } timing_t; typedef enum { SSELECT_IGNORE = 0, SSELECT_SCONTEXT = 1, SSELECT_ASID = 2, SSELECT_MAXVAL = 2 } sselect_t; typedef enum { MHSELECT_MODE_IGNORE, MHSELECT_MODE_MCONTEXT, MHSELECT_MODE_VMID, } mhselect_mode_t; struct match_result_t { match_result_t(const timing_t t=TIMING_BEFORE, const action_t a=ACTION_DEBUG_EXCEPTION) { timing = t; action = a; } timing_t timing; action_t action; }; class matched_t { public: matched_t(triggers::operation_t operation, reg_t address, action_t action) : operation(operation), address(address), action(action) {} triggers::operation_t operation; reg_t address; action_t action; }; class trigger_t { public: virtual ~trigger_t() {}; virtual reg_t tdata1_read(const processor_t * const proc) const noexcept = 0; virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept = 0; reg_t tdata2_read(const processor_t * const proc) const noexcept; void tdata2_write(processor_t * const proc, const reg_t val) noexcept; reg_t tdata3_read(const processor_t * const proc) const noexcept; void tdata3_write(processor_t * const proc, const reg_t val) noexcept; virtual bool get_dmode() const = 0; virtual bool get_chain() const { return false; } virtual bool get_execute() const { return false; } virtual bool get_store() const { return false; } virtual bool get_load() const { return false; } virtual action_t get_action() const { return ACTION_DEBUG_EXCEPTION; } virtual std::optional detect_memory_access_match(processor_t UNUSED * const proc, operation_t UNUSED operation, reg_t UNUSED address, std::optional UNUSED data) noexcept { return std::nullopt; } virtual std::optional detect_trap_match(processor_t UNUSED * const proc, const trap_t UNUSED & t) noexcept { return std::nullopt; } bool textra_match(processor_t * const proc) const noexcept; protected: action_t legalize_action(reg_t val) const noexcept; bool mode_match(state_t * const state) const noexcept; reg_t tdata2; bool vs = false; bool vu = false; bool m = false; bool s = false; bool u = false; private: unsigned legalize_mhselect(bool h_enabled) const noexcept; struct mhselect_interpretation { const unsigned mhselect; const mhselect_mode_t mode; const std::optional shift_mhvalue; unsigned compare_val(const unsigned mhvalue) const { return shift_mhvalue.value() ? (mhvalue << 1 | mhselect >> 2) : mhvalue; }; }; mhselect_interpretation interpret_mhselect(bool h_enabled) const noexcept { static unsigned warlize_if_h[8] = { 0, 1, 2, 0, 4, 5, 6, 4 }; // 3,7 downgrade static unsigned warlize_no_h[8] = { 0, 0, 0, 0, 4, 4, 4, 4 }; // only 0,4 legal static std::optional table[8] = { mhselect_interpretation{ 0, MHSELECT_MODE_IGNORE, std::nullopt }, mhselect_interpretation{ 1, MHSELECT_MODE_MCONTEXT, true }, mhselect_interpretation{ 2, MHSELECT_MODE_VMID, true }, std::nullopt, mhselect_interpretation{ 4, MHSELECT_MODE_MCONTEXT, false }, mhselect_interpretation{ 5, MHSELECT_MODE_MCONTEXT, true }, mhselect_interpretation{ 6, MHSELECT_MODE_VMID, true }, std::nullopt }; assert(mhselect < 8); unsigned legal = h_enabled ? warlize_if_h[mhselect] : warlize_no_h[mhselect]; assert(legal < 8); return table[legal].value(); } sselect_t sselect; unsigned svalue; unsigned sbytemask; unsigned mhselect; unsigned mhvalue; }; class disabled_trigger_t : public trigger_t { public: virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override; virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; virtual bool get_dmode() const override { return dmode; } private: bool dmode; }; class itrigger_t : public trigger_t { public: virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override; virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; bool get_dmode() const override { return dmode; } virtual action_t get_action() const override { return action; } virtual std::optional detect_trap_match(processor_t * const proc, const trap_t& t) noexcept override; private: bool dmode; bool hit; bool nmi; action_t action; }; class etrigger_t : public trigger_t { public: virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override; virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; bool get_dmode() const override { return dmode; } virtual action_t get_action() const override { return action; } virtual std::optional detect_trap_match(processor_t * const proc, const trap_t& t) noexcept override; private: bool dmode; bool hit; action_t action; }; class mcontrol_common_t : public trigger_t { public: typedef enum { MATCH_EQUAL = MCONTROL_MATCH_EQUAL, MATCH_NAPOT = MCONTROL_MATCH_NAPOT, MATCH_GE = MCONTROL_MATCH_GE, MATCH_LT = MCONTROL_MATCH_LT, MATCH_MASK_LOW = MCONTROL_MATCH_MASK_LOW, MATCH_MASK_HIGH = MCONTROL_MATCH_MASK_HIGH } match_t; virtual bool get_dmode() const override { return dmode; } virtual bool get_chain() const override { return chain; } virtual bool get_execute() const override { return execute; } virtual bool get_store() const override { return store; } virtual bool get_load() const override { return load; } virtual action_t get_action() const override { return action; } virtual std::optional detect_memory_access_match(processor_t * const proc, operation_t operation, reg_t address, std::optional data) noexcept override; private: bool simple_match(unsigned xlen, reg_t value) const; protected: match_t legalize_match(reg_t val) const noexcept; bool dmode = false; action_t action = ACTION_DEBUG_EXCEPTION; bool hit = false; bool select = false; bool timing = false; bool chain = false; match_t match = MATCH_EQUAL; bool execute = false; bool store = false; bool load = false; }; class mcontrol_t : public mcontrol_common_t { public: virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override; virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; }; class mcontrol6_t : public mcontrol_common_t { public: virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override; virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; }; class module_t { public: module_t(unsigned count); ~module_t(); reg_t tdata1_read(unsigned index) const noexcept; bool tdata1_write(unsigned index, const reg_t val) noexcept; reg_t tdata2_read(unsigned index) const noexcept; bool tdata2_write(unsigned index, const reg_t val) noexcept; reg_t tdata3_read(unsigned index) const noexcept; bool tdata3_write(unsigned index, const reg_t val) noexcept; reg_t tinfo_read(unsigned index) const noexcept; unsigned count() const { return triggers.size(); } std::optional detect_memory_access_match(operation_t operation, reg_t address, std::optional data) noexcept; std::optional detect_trap_match(const trap_t& t) noexcept; processor_t *proc; private: std::vector triggers; }; }; #endif