162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2022-2023 Loongson Technology Corporation Limited 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#ifndef __ASM_HW_BREAKPOINT_H 662306a36Sopenharmony_ci#define __ASM_HW_BREAKPOINT_H 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/loongarch.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#ifdef __KERNEL__ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* Breakpoint */ 1362306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_EXECUTE (0 << 0) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Watchpoints */ 1662306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_LOAD (1 << 0) 1762306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_STORE (1 << 1) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct arch_hw_breakpoint_ctrl { 2062306a36Sopenharmony_ci u32 __reserved : 28, 2162306a36Sopenharmony_ci len : 2, 2262306a36Sopenharmony_ci type : 2; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct arch_hw_breakpoint { 2662306a36Sopenharmony_ci u64 address; 2762306a36Sopenharmony_ci u64 mask; 2862306a36Sopenharmony_ci struct arch_hw_breakpoint_ctrl ctrl; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Lengths */ 3262306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_LEN_1 0b11 3362306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_LEN_2 0b10 3462306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_LEN_4 0b01 3562306a36Sopenharmony_ci#define LOONGARCH_BREAKPOINT_LEN_8 0b00 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Limits. 3962306a36Sopenharmony_ci * Changing these will require modifications to the register accessors. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define LOONGARCH_MAX_BRP 8 4262306a36Sopenharmony_ci#define LOONGARCH_MAX_WRP 8 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Virtual debug register bases. */ 4562306a36Sopenharmony_ci#define CSR_CFG_ADDR 0 4662306a36Sopenharmony_ci#define CSR_CFG_MASK (CSR_CFG_ADDR + LOONGARCH_MAX_BRP) 4762306a36Sopenharmony_ci#define CSR_CFG_CTRL (CSR_CFG_MASK + LOONGARCH_MAX_BRP) 4862306a36Sopenharmony_ci#define CSR_CFG_ASID (CSR_CFG_CTRL + LOONGARCH_MAX_WRP) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Debug register names. */ 5162306a36Sopenharmony_ci#define LOONGARCH_CSR_NAME_ADDR ADDR 5262306a36Sopenharmony_ci#define LOONGARCH_CSR_NAME_MASK MASK 5362306a36Sopenharmony_ci#define LOONGARCH_CSR_NAME_CTRL CTRL 5462306a36Sopenharmony_ci#define LOONGARCH_CSR_NAME_ASID ASID 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Accessor macros for the debug registers. */ 5762306a36Sopenharmony_ci#define LOONGARCH_CSR_WATCH_READ(N, REG, T, VAL) \ 5862306a36Sopenharmony_cido { \ 5962306a36Sopenharmony_ci if (T == 0) \ 6062306a36Sopenharmony_ci VAL = csr_read64(LOONGARCH_CSR_##IB##N##REG); \ 6162306a36Sopenharmony_ci else \ 6262306a36Sopenharmony_ci VAL = csr_read64(LOONGARCH_CSR_##DB##N##REG); \ 6362306a36Sopenharmony_ci} while (0) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define LOONGARCH_CSR_WATCH_WRITE(N, REG, T, VAL) \ 6662306a36Sopenharmony_cido { \ 6762306a36Sopenharmony_ci if (T == 0) \ 6862306a36Sopenharmony_ci csr_write64(VAL, LOONGARCH_CSR_##IB##N##REG); \ 6962306a36Sopenharmony_ci else \ 7062306a36Sopenharmony_ci csr_write64(VAL, LOONGARCH_CSR_##DB##N##REG); \ 7162306a36Sopenharmony_ci} while (0) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Exact number */ 7462306a36Sopenharmony_ci#define CSR_FWPC_NUM 0x3f 7562306a36Sopenharmony_ci#define CSR_MWPC_NUM 0x3f 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define CTRL_PLV_ENABLE 0x1e 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define MWPnCFG3_LoadEn 8 8062306a36Sopenharmony_ci#define MWPnCFG3_StoreEn 9 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define MWPnCFG3_Type_mask 0x3 8362306a36Sopenharmony_ci#define MWPnCFG3_Size_mask 0x3 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return (ctrl.len << 10) | (ctrl.type << 8); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline void decode_ctrl_reg(u32 reg, struct arch_hw_breakpoint_ctrl *ctrl) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci reg >>= 8; 9362306a36Sopenharmony_ci ctrl->type = reg & MWPnCFG3_Type_mask; 9462306a36Sopenharmony_ci reg >>= 2; 9562306a36Sopenharmony_ci ctrl->len = reg & MWPnCFG3_Size_mask; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct task_struct; 9962306a36Sopenharmony_cistruct notifier_block; 10062306a36Sopenharmony_cistruct perf_event; 10162306a36Sopenharmony_cistruct perf_event_attr; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciextern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, 10462306a36Sopenharmony_ci int *gen_len, int *gen_type, int *offset); 10562306a36Sopenharmony_ciextern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw); 10662306a36Sopenharmony_ciextern int hw_breakpoint_arch_parse(struct perf_event *bp, 10762306a36Sopenharmony_ci const struct perf_event_attr *attr, 10862306a36Sopenharmony_ci struct arch_hw_breakpoint *hw); 10962306a36Sopenharmony_ciextern int hw_breakpoint_exceptions_notify(struct notifier_block *unused, 11062306a36Sopenharmony_ci unsigned long val, void *data); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciextern int arch_install_hw_breakpoint(struct perf_event *bp); 11362306a36Sopenharmony_ciextern void arch_uninstall_hw_breakpoint(struct perf_event *bp); 11462306a36Sopenharmony_ciextern int hw_breakpoint_slots(int type); 11562306a36Sopenharmony_ciextern void hw_breakpoint_pmu_read(struct perf_event *bp); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_civoid breakpoint_handler(struct pt_regs *regs); 11862306a36Sopenharmony_civoid watchpoint_handler(struct pt_regs *regs); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#ifdef CONFIG_HAVE_HW_BREAKPOINT 12162306a36Sopenharmony_ciextern void ptrace_hw_copy_thread(struct task_struct *task); 12262306a36Sopenharmony_ciextern void hw_breakpoint_thread_switch(struct task_struct *next); 12362306a36Sopenharmony_ci#else 12462306a36Sopenharmony_cistatic inline void ptrace_hw_copy_thread(struct task_struct *task) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_cistatic inline void hw_breakpoint_thread_switch(struct task_struct *next) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci#endif 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Determine number of BRP registers available. */ 13362306a36Sopenharmony_cistatic inline int get_num_brps(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return csr_read64(LOONGARCH_CSR_FWPC) & CSR_FWPC_NUM; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* Determine number of WRP registers available. */ 13962306a36Sopenharmony_cistatic inline int get_num_wrps(void) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return csr_read64(LOONGARCH_CSR_MWPC) & CSR_MWPC_NUM; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#endif /* __KERNEL__ */ 14562306a36Sopenharmony_ci#endif /* __ASM_BREAKPOINT_H */ 146