/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2022-2023 Loongson Technology Corporation Limited */ #ifndef __ASM_HW_BREAKPOINT_H #define __ASM_HW_BREAKPOINT_H #include #ifdef __KERNEL__ /* Breakpoint */ #define LOONGARCH_BREAKPOINT_EXECUTE (0 << 0) /* Watchpoints */ #define LOONGARCH_BREAKPOINT_LOAD (1 << 0) #define LOONGARCH_BREAKPOINT_STORE (1 << 1) struct arch_hw_breakpoint_ctrl { u32 __reserved : 28, len : 2, type : 2; }; struct arch_hw_breakpoint { u64 address; u64 mask; struct arch_hw_breakpoint_ctrl ctrl; }; /* Lengths */ #define LOONGARCH_BREAKPOINT_LEN_1 0b11 #define LOONGARCH_BREAKPOINT_LEN_2 0b10 #define LOONGARCH_BREAKPOINT_LEN_4 0b01 #define LOONGARCH_BREAKPOINT_LEN_8 0b00 /* * Limits. * Changing these will require modifications to the register accessors. */ #define LOONGARCH_MAX_BRP 8 #define LOONGARCH_MAX_WRP 8 /* Virtual debug register bases. */ #define CSR_CFG_ADDR 0 #define CSR_CFG_MASK (CSR_CFG_ADDR + LOONGARCH_MAX_BRP) #define CSR_CFG_CTRL (CSR_CFG_MASK + LOONGARCH_MAX_BRP) #define CSR_CFG_ASID (CSR_CFG_CTRL + LOONGARCH_MAX_WRP) /* Debug register names. */ #define LOONGARCH_CSR_NAME_ADDR ADDR #define LOONGARCH_CSR_NAME_MASK MASK #define LOONGARCH_CSR_NAME_CTRL CTRL #define LOONGARCH_CSR_NAME_ASID ASID /* Accessor macros for the debug registers. */ #define LOONGARCH_CSR_WATCH_READ(N, REG, T, VAL) \ do { \ if (T == 0) \ VAL = csr_read64(LOONGARCH_CSR_##IB##N##REG); \ else \ VAL = csr_read64(LOONGARCH_CSR_##DB##N##REG); \ } while (0) #define LOONGARCH_CSR_WATCH_WRITE(N, REG, T, VAL) \ do { \ if (T == 0) \ csr_write64(VAL, LOONGARCH_CSR_##IB##N##REG); \ else \ csr_write64(VAL, LOONGARCH_CSR_##DB##N##REG); \ } while (0) /* Exact number */ #define CSR_FWPC_NUM 0x3f #define CSR_MWPC_NUM 0x3f #define CTRL_PLV_ENABLE 0x1e #define MWPnCFG3_LoadEn 8 #define MWPnCFG3_StoreEn 9 #define MWPnCFG3_Type_mask 0x3 #define MWPnCFG3_Size_mask 0x3 static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl) { return (ctrl.len << 10) | (ctrl.type << 8); } static inline void decode_ctrl_reg(u32 reg, struct arch_hw_breakpoint_ctrl *ctrl) { reg >>= 8; ctrl->type = reg & MWPnCFG3_Type_mask; reg >>= 2; ctrl->len = reg & MWPnCFG3_Size_mask; } struct task_struct; struct notifier_block; struct perf_event; struct perf_event_attr; extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, int *gen_len, int *gen_type, int *offset); extern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw); extern int hw_breakpoint_arch_parse(struct perf_event *bp, const struct perf_event_attr *attr, struct arch_hw_breakpoint *hw); extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, unsigned long val, void *data); extern int arch_install_hw_breakpoint(struct perf_event *bp); extern void arch_uninstall_hw_breakpoint(struct perf_event *bp); extern int hw_breakpoint_slots(int type); extern void hw_breakpoint_pmu_read(struct perf_event *bp); void breakpoint_handler(struct pt_regs *regs); void watchpoint_handler(struct pt_regs *regs); #ifdef CONFIG_HAVE_HW_BREAKPOINT extern void ptrace_hw_copy_thread(struct task_struct *task); extern void hw_breakpoint_thread_switch(struct task_struct *next); #else static inline void ptrace_hw_copy_thread(struct task_struct *task) { } static inline void hw_breakpoint_thread_switch(struct task_struct *next) { } #endif /* Determine number of BRP registers available. */ static inline int get_num_brps(void) { return csr_read64(LOONGARCH_CSR_FWPC) & CSR_FWPC_NUM; } /* Determine number of WRP registers available. */ static inline int get_num_wrps(void) { return csr_read64(LOONGARCH_CSR_MWPC) & CSR_MWPC_NUM; } #endif /* __KERNEL__ */ #endif /* __ASM_BREAKPOINT_H */