Skip to content

Commit edffa33

Browse files
zhangqingmychenhuacai
authored andcommitted
LoongArch: Add hardware breakpoints/watchpoints support
Use perf framework to manage hardware instruction and data breakpoints. LoongArch defines hardware watchpoint functions for instruction fetch and memory load/store operations. After the software configures hardware watchpoints, the processor hardware will monitor the access address of the instruction fetch and load/store operation, and trigger an exception of the watchpoint when it meets the conditions set by the watchpoint. The hardware monitoring points for instruction fetching and load/store operations each have a register for the overall configuration of all monitoring points, a register for recording the status of all monitoring points, and four registers required for configuration of each watchpoint individually. Signed-off-by: Qing Zhang <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent 35c94fa commit edffa33

File tree

9 files changed

+713
-22
lines changed

9 files changed

+713
-22
lines changed

arch/loongarch/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ config LOONGARCH
101101
select HAVE_FUNCTION_GRAPH_TRACER
102102
select HAVE_FUNCTION_TRACER
103103
select HAVE_GENERIC_VDSO
104+
select HAVE_HW_BREAKPOINT if PERF_EVENTS
104105
select HAVE_IOREMAP_PROT
105106
select HAVE_IRQ_EXIT_ON_IRQ_STACK
106107
select HAVE_IRQ_TIME_ACCOUNTING
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2022-2023 Loongson Technology Corporation Limited
4+
*/
5+
#ifndef __ASM_HW_BREAKPOINT_H
6+
#define __ASM_HW_BREAKPOINT_H
7+
8+
#include <asm/loongarch.h>
9+
10+
#ifdef __KERNEL__
11+
12+
/* Breakpoint */
13+
#define LOONGARCH_BREAKPOINT_EXECUTE (0 << 0)
14+
15+
/* Watchpoints */
16+
#define LOONGARCH_BREAKPOINT_LOAD (1 << 0)
17+
#define LOONGARCH_BREAKPOINT_STORE (1 << 1)
18+
19+
struct arch_hw_breakpoint_ctrl {
20+
u32 __reserved : 28,
21+
len : 2,
22+
type : 2;
23+
};
24+
25+
struct arch_hw_breakpoint {
26+
u64 address;
27+
u64 mask;
28+
struct arch_hw_breakpoint_ctrl ctrl;
29+
};
30+
31+
/* Lengths */
32+
#define LOONGARCH_BREAKPOINT_LEN_1 0b11
33+
#define LOONGARCH_BREAKPOINT_LEN_2 0b10
34+
#define LOONGARCH_BREAKPOINT_LEN_4 0b01
35+
#define LOONGARCH_BREAKPOINT_LEN_8 0b00
36+
37+
/*
38+
* Limits.
39+
* Changing these will require modifications to the register accessors.
40+
*/
41+
#define LOONGARCH_MAX_BRP 8
42+
#define LOONGARCH_MAX_WRP 8
43+
44+
/* Virtual debug register bases. */
45+
#define CSR_CFG_ADDR 0
46+
#define CSR_CFG_MASK (CSR_CFG_ADDR + LOONGARCH_MAX_BRP)
47+
#define CSR_CFG_CTRL (CSR_CFG_MASK + LOONGARCH_MAX_BRP)
48+
#define CSR_CFG_ASID (CSR_CFG_CTRL + LOONGARCH_MAX_WRP)
49+
50+
/* Debug register names. */
51+
#define LOONGARCH_CSR_NAME_ADDR ADDR
52+
#define LOONGARCH_CSR_NAME_MASK MASK
53+
#define LOONGARCH_CSR_NAME_CTRL CTRL
54+
#define LOONGARCH_CSR_NAME_ASID ASID
55+
56+
/* Accessor macros for the debug registers. */
57+
#define LOONGARCH_CSR_WATCH_READ(N, REG, T, VAL) \
58+
do { \
59+
if (T == 0) \
60+
VAL = csr_read64(LOONGARCH_CSR_##IB##N##REG); \
61+
else \
62+
VAL = csr_read64(LOONGARCH_CSR_##DB##N##REG); \
63+
} while (0)
64+
65+
#define LOONGARCH_CSR_WATCH_WRITE(N, REG, T, VAL) \
66+
do { \
67+
if (T == 0) \
68+
csr_write64(VAL, LOONGARCH_CSR_##IB##N##REG); \
69+
else \
70+
csr_write64(VAL, LOONGARCH_CSR_##DB##N##REG); \
71+
} while (0)
72+
73+
/* Exact number */
74+
#define CSR_FWPC_NUM 0x3f
75+
#define CSR_MWPC_NUM 0x3f
76+
77+
#define CTRL_PLV_ENABLE 0x1e
78+
79+
#define MWPnCFG3_LoadEn 8
80+
#define MWPnCFG3_StoreEn 9
81+
82+
#define MWPnCFG3_Type_mask 0x3
83+
#define MWPnCFG3_Size_mask 0x3
84+
85+
static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl)
86+
{
87+
return (ctrl.len << 10) | (ctrl.type << 8);
88+
}
89+
90+
static inline void decode_ctrl_reg(u32 reg, struct arch_hw_breakpoint_ctrl *ctrl)
91+
{
92+
reg >>= 8;
93+
ctrl->type = reg & MWPnCFG3_Type_mask;
94+
reg >>= 2;
95+
ctrl->len = reg & MWPnCFG3_Size_mask;
96+
}
97+
98+
struct task_struct;
99+
struct notifier_block;
100+
struct perf_event;
101+
struct perf_event_attr;
102+
103+
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
104+
int *gen_len, int *gen_type, int *offset);
105+
extern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);
106+
extern int hw_breakpoint_arch_parse(struct perf_event *bp,
107+
const struct perf_event_attr *attr,
108+
struct arch_hw_breakpoint *hw);
109+
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
110+
unsigned long val, void *data);
111+
112+
extern int arch_install_hw_breakpoint(struct perf_event *bp);
113+
extern void arch_uninstall_hw_breakpoint(struct perf_event *bp);
114+
extern int hw_breakpoint_slots(int type);
115+
extern void hw_breakpoint_pmu_read(struct perf_event *bp);
116+
117+
void breakpoint_handler(struct pt_regs *regs);
118+
void watchpoint_handler(struct pt_regs *regs);
119+
120+
#ifdef CONFIG_HAVE_HW_BREAKPOINT
121+
extern void ptrace_hw_copy_thread(struct task_struct *task);
122+
extern void hw_breakpoint_thread_switch(struct task_struct *next);
123+
#else
124+
static inline void ptrace_hw_copy_thread(struct task_struct *task)
125+
{
126+
}
127+
static inline void hw_breakpoint_thread_switch(struct task_struct *next)
128+
{
129+
}
130+
#endif
131+
132+
/* Determine number of BRP registers available. */
133+
static inline int get_num_brps(void)
134+
{
135+
return csr_read64(LOONGARCH_CSR_FWPC) & CSR_FWPC_NUM;
136+
}
137+
138+
/* Determine number of WRP registers available. */
139+
static inline int get_num_wrps(void)
140+
{
141+
return csr_read64(LOONGARCH_CSR_MWPC) & CSR_MWPC_NUM;
142+
}
143+
144+
#endif /* __KERNEL__ */
145+
#endif /* __ASM_BREAKPOINT_H */

arch/loongarch/include/asm/loongarch.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -970,85 +970,85 @@ static __always_inline void iocsr_write64(u64 val, u32 reg)
970970

971971
#define LOONGARCH_CSR_DB0ADDR 0x310 /* data breakpoint 0 address */
972972
#define LOONGARCH_CSR_DB0MASK 0x311 /* data breakpoint 0 mask */
973-
#define LOONGARCH_CSR_DB0CTL 0x312 /* data breakpoint 0 control */
973+
#define LOONGARCH_CSR_DB0CTRL 0x312 /* data breakpoint 0 control */
974974
#define LOONGARCH_CSR_DB0ASID 0x313 /* data breakpoint 0 asid */
975975

976976
#define LOONGARCH_CSR_DB1ADDR 0x318 /* data breakpoint 1 address */
977977
#define LOONGARCH_CSR_DB1MASK 0x319 /* data breakpoint 1 mask */
978-
#define LOONGARCH_CSR_DB1CTL 0x31a /* data breakpoint 1 control */
978+
#define LOONGARCH_CSR_DB1CTRL 0x31a /* data breakpoint 1 control */
979979
#define LOONGARCH_CSR_DB1ASID 0x31b /* data breakpoint 1 asid */
980980

981981
#define LOONGARCH_CSR_DB2ADDR 0x320 /* data breakpoint 2 address */
982982
#define LOONGARCH_CSR_DB2MASK 0x321 /* data breakpoint 2 mask */
983-
#define LOONGARCH_CSR_DB2CTL 0x322 /* data breakpoint 2 control */
983+
#define LOONGARCH_CSR_DB2CTRL 0x322 /* data breakpoint 2 control */
984984
#define LOONGARCH_CSR_DB2ASID 0x323 /* data breakpoint 2 asid */
985985

986986
#define LOONGARCH_CSR_DB3ADDR 0x328 /* data breakpoint 3 address */
987987
#define LOONGARCH_CSR_DB3MASK 0x329 /* data breakpoint 3 mask */
988-
#define LOONGARCH_CSR_DB3CTL 0x32a /* data breakpoint 3 control */
988+
#define LOONGARCH_CSR_DB3CTRL 0x32a /* data breakpoint 3 control */
989989
#define LOONGARCH_CSR_DB3ASID 0x32b /* data breakpoint 3 asid */
990990

991991
#define LOONGARCH_CSR_DB4ADDR 0x330 /* data breakpoint 4 address */
992992
#define LOONGARCH_CSR_DB4MASK 0x331 /* data breakpoint 4 maks */
993-
#define LOONGARCH_CSR_DB4CTL 0x332 /* data breakpoint 4 control */
993+
#define LOONGARCH_CSR_DB4CTRL 0x332 /* data breakpoint 4 control */
994994
#define LOONGARCH_CSR_DB4ASID 0x333 /* data breakpoint 4 asid */
995995

996996
#define LOONGARCH_CSR_DB5ADDR 0x338 /* data breakpoint 5 address */
997997
#define LOONGARCH_CSR_DB5MASK 0x339 /* data breakpoint 5 mask */
998-
#define LOONGARCH_CSR_DB5CTL 0x33a /* data breakpoint 5 control */
998+
#define LOONGARCH_CSR_DB5CTRL 0x33a /* data breakpoint 5 control */
999999
#define LOONGARCH_CSR_DB5ASID 0x33b /* data breakpoint 5 asid */
10001000

10011001
#define LOONGARCH_CSR_DB6ADDR 0x340 /* data breakpoint 6 address */
10021002
#define LOONGARCH_CSR_DB6MASK 0x341 /* data breakpoint 6 mask */
1003-
#define LOONGARCH_CSR_DB6CTL 0x342 /* data breakpoint 6 control */
1003+
#define LOONGARCH_CSR_DB6CTRL 0x342 /* data breakpoint 6 control */
10041004
#define LOONGARCH_CSR_DB6ASID 0x343 /* data breakpoint 6 asid */
10051005

10061006
#define LOONGARCH_CSR_DB7ADDR 0x348 /* data breakpoint 7 address */
10071007
#define LOONGARCH_CSR_DB7MASK 0x349 /* data breakpoint 7 mask */
1008-
#define LOONGARCH_CSR_DB7CTL 0x34a /* data breakpoint 7 control */
1008+
#define LOONGARCH_CSR_DB7CTRL 0x34a /* data breakpoint 7 control */
10091009
#define LOONGARCH_CSR_DB7ASID 0x34b /* data breakpoint 7 asid */
10101010

10111011
#define LOONGARCH_CSR_FWPC 0x380 /* instruction breakpoint config */
10121012
#define LOONGARCH_CSR_FWPS 0x381 /* instruction breakpoint status */
10131013

10141014
#define LOONGARCH_CSR_IB0ADDR 0x390 /* inst breakpoint 0 address */
10151015
#define LOONGARCH_CSR_IB0MASK 0x391 /* inst breakpoint 0 mask */
1016-
#define LOONGARCH_CSR_IB0CTL 0x392 /* inst breakpoint 0 control */
1016+
#define LOONGARCH_CSR_IB0CTRL 0x392 /* inst breakpoint 0 control */
10171017
#define LOONGARCH_CSR_IB0ASID 0x393 /* inst breakpoint 0 asid */
10181018

10191019
#define LOONGARCH_CSR_IB1ADDR 0x398 /* inst breakpoint 1 address */
10201020
#define LOONGARCH_CSR_IB1MASK 0x399 /* inst breakpoint 1 mask */
1021-
#define LOONGARCH_CSR_IB1CTL 0x39a /* inst breakpoint 1 control */
1021+
#define LOONGARCH_CSR_IB1CTRL 0x39a /* inst breakpoint 1 control */
10221022
#define LOONGARCH_CSR_IB1ASID 0x39b /* inst breakpoint 1 asid */
10231023

10241024
#define LOONGARCH_CSR_IB2ADDR 0x3a0 /* inst breakpoint 2 address */
10251025
#define LOONGARCH_CSR_IB2MASK 0x3a1 /* inst breakpoint 2 mask */
1026-
#define LOONGARCH_CSR_IB2CTL 0x3a2 /* inst breakpoint 2 control */
1026+
#define LOONGARCH_CSR_IB2CTRL 0x3a2 /* inst breakpoint 2 control */
10271027
#define LOONGARCH_CSR_IB2ASID 0x3a3 /* inst breakpoint 2 asid */
10281028

10291029
#define LOONGARCH_CSR_IB3ADDR 0x3a8 /* inst breakpoint 3 address */
10301030
#define LOONGARCH_CSR_IB3MASK 0x3a9 /* breakpoint 3 mask */
1031-
#define LOONGARCH_CSR_IB3CTL 0x3aa /* inst breakpoint 3 control */
1031+
#define LOONGARCH_CSR_IB3CTRL 0x3aa /* inst breakpoint 3 control */
10321032
#define LOONGARCH_CSR_IB3ASID 0x3ab /* inst breakpoint 3 asid */
10331033

10341034
#define LOONGARCH_CSR_IB4ADDR 0x3b0 /* inst breakpoint 4 address */
10351035
#define LOONGARCH_CSR_IB4MASK 0x3b1 /* inst breakpoint 4 mask */
1036-
#define LOONGARCH_CSR_IB4CTL 0x3b2 /* inst breakpoint 4 control */
1036+
#define LOONGARCH_CSR_IB4CTRL 0x3b2 /* inst breakpoint 4 control */
10371037
#define LOONGARCH_CSR_IB4ASID 0x3b3 /* inst breakpoint 4 asid */
10381038

10391039
#define LOONGARCH_CSR_IB5ADDR 0x3b8 /* inst breakpoint 5 address */
10401040
#define LOONGARCH_CSR_IB5MASK 0x3b9 /* inst breakpoint 5 mask */
1041-
#define LOONGARCH_CSR_IB5CTL 0x3ba /* inst breakpoint 5 control */
1041+
#define LOONGARCH_CSR_IB5CTRL 0x3ba /* inst breakpoint 5 control */
10421042
#define LOONGARCH_CSR_IB5ASID 0x3bb /* inst breakpoint 5 asid */
10431043

10441044
#define LOONGARCH_CSR_IB6ADDR 0x3c0 /* inst breakpoint 6 address */
10451045
#define LOONGARCH_CSR_IB6MASK 0x3c1 /* inst breakpoint 6 mask */
1046-
#define LOONGARCH_CSR_IB6CTL 0x3c2 /* inst breakpoint 6 control */
1046+
#define LOONGARCH_CSR_IB6CTRL 0x3c2 /* inst breakpoint 6 control */
10471047
#define LOONGARCH_CSR_IB6ASID 0x3c3 /* inst breakpoint 6 asid */
10481048

10491049
#define LOONGARCH_CSR_IB7ADDR 0x3c8 /* inst breakpoint 7 address */
10501050
#define LOONGARCH_CSR_IB7MASK 0x3c9 /* inst breakpoint 7 mask */
1051-
#define LOONGARCH_CSR_IB7CTL 0x3ca /* inst breakpoint 7 control */
1051+
#define LOONGARCH_CSR_IB7CTRL 0x3ca /* inst breakpoint 7 control */
10521052
#define LOONGARCH_CSR_IB7ASID 0x3cb /* inst breakpoint 7 asid */
10531053

10541054
#define LOONGARCH_CSR_DEBUG 0x500 /* debug config */

arch/loongarch/include/asm/processor.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <asm/cpu.h>
1313
#include <asm/cpu-info.h>
14+
#include <asm/hw_breakpoint.h>
1415
#include <asm/loongarch.h>
1516
#include <asm/vdso/processor.h>
1617
#include <uapi/asm/ptrace.h>
@@ -127,10 +128,14 @@ struct thread_struct {
127128
struct loongarch_vdso_info *vdso;
128129

129130
/*
130-
* FPU & vector registers, must be at last because
131-
* they are conditionally copied at fork().
131+
* FPU & vector registers, must be at the last of inherited
132+
* context because they are conditionally copied at fork().
132133
*/
133134
struct loongarch_fpu fpu FPU_ALIGN;
135+
136+
/* Hardware breakpoints pinned to this task. */
137+
struct perf_event *hbp_break[LOONGARCH_MAX_BRP];
138+
struct perf_event *hbp_watch[LOONGARCH_MAX_WRP];
134139
};
135140

136141
#define thread_saved_ra(tsk) (tsk->thread.sched_ra)
@@ -172,6 +177,8 @@ struct thread_struct {
172177
.fcc = 0, \
173178
.fpr = {{{0,},},}, \
174179
}, \
180+
.hbp_break = {0}, \
181+
.hbp_watch = {0}, \
175182
}
176183

177184
struct task_struct;
@@ -184,10 +191,6 @@ extern unsigned long boot_option_idle_override;
184191
*/
185192
extern void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp);
186193

187-
static inline void flush_thread(void)
188-
{
189-
}
190-
191194
unsigned long __get_wchan(struct task_struct *p);
192195

193196
#define __KSTK_TOS(tsk) ((unsigned long)task_stack_page(tsk) + \

arch/loongarch/include/asm/switch_to.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev,
3434
#define switch_to(prev, next, last) \
3535
do { \
3636
lose_fpu_inatomic(1, prev); \
37+
hw_breakpoint_thread_switch(next); \
3738
(last) = __switch_to(prev, next, task_thread_info(next), \
3839
__builtin_return_address(0), __builtin_frame_address(0)); \
3940
} while (0)

arch/loongarch/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o
5050
obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
5151

5252
obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o
53+
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
5354

5455
CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS)

0 commit comments

Comments
 (0)