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