1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #include <arch/registers.h>
3 #include <arch/breakpoint.h>
6 #define DEBUG_REGISTER_COUNT 4
8 /* Each enable field is 2 bits and starts at bit 0 */
9 #define DEBUG_CTRL_ENABLE_SHIFT(index) (2 * (index))
10 #define DEBUG_CTRL_ENABLE_MASK(index) (0x3 << DEBUG_CTRL_ENABLE_SHIFT(index))
11 #define DEBUG_CTRL_ENABLE(index, enable) ((enable) << DEBUG_CTRL_ENABLE_SHIFT(index))
13 /* Each breakpoint has a length and type, each is two bits and start at bit 16 */
14 #define DEBUG_CTRL_LT_SHIFT(index) (4 * (index) + 16)
15 #define DEBUG_CTRL_LT_MASK(index) (0xf << DEBUG_CTRL_LT_SHIFT(index))
16 #define DEBUG_CTRL_LT(index, len, type) ((((len) << 2 | (type))) << DEBUG_CTRL_LT_SHIFT(index))
18 /* Each field is one bit, starting at bit 0 */
19 #define DEBUG_STATUS_BP_HIT_MASK(index) (1 << (index))
20 #define DEBUG_STATUS_GET_BP_HIT(index, value) \
21 (((value) & DEBUG_STATUS_BP_HIT_MASK(index)) >> (index))
23 /* Breakpoint lengths values */
24 #define DEBUG_CTRL_LEN_1 0x0
25 #define DEBUG_CTRL_LEN_2 0x1
26 #define DEBUG_CTRL_LEN_8 0x2
27 #define DEBUG_CTRL_LEN_4 0x3
29 /* Breakpoint enable values */
30 #define DEBUG_CTRL_ENABLE_LOCAL 0x1
31 #define DEBUG_CTRL_ENABLE_GLOBAL 0x2
33 /* eflags/rflags bit to continue execution after hitting an instruction breakpoint */
34 #define FLAGS_RESUME (1 << 16)
38 enum breakpoint_type type
;
39 breakpoint_handler handler
;
42 static struct breakpoint breakpoints
[DEBUG_REGISTER_COUNT
];
44 static inline bool debug_write_addr_reg(int index
, uintptr_t value
)
48 asm("mov %0, %%dr0" ::"r"(value
));
52 asm("mov %0, %%dr1" ::"r"(value
));
56 asm("mov %0, %%dr2" ::"r"(value
));
60 asm("mov %0, %%dr3" ::"r"(value
));
70 static inline uintptr_t debug_read_status(void)
74 asm("mov %%dr6, %0" : "=r"(ret
));
78 static inline void debug_write_status(uintptr_t value
)
80 asm("mov %0, %%dr6" ::"r"(value
));
83 static inline uintptr_t debug_read_control(void)
87 asm("mov %%dr7, %0" : "=r"(ret
));
91 static inline void debug_write_control(uintptr_t value
)
93 asm("mov %0, %%dr7" ::"r"(value
));
96 static enum breakpoint_result
allocate_breakpoint(struct breakpoint_handle
*out_handle
,
97 enum breakpoint_type type
)
99 for (int i
= 0; i
< DEBUG_REGISTER_COUNT
; i
++) {
100 if (breakpoints
[i
].allocated
)
103 breakpoints
[i
].allocated
= true;
104 breakpoints
[i
].handler
= NULL
;
105 breakpoints
[i
].type
= type
;
107 return BREAKPOINT_RES_OK
;
110 return BREAKPOINT_RES_NONE_AVAILABLE
;
113 static enum breakpoint_result
validate_handle(struct breakpoint_handle handle
)
117 if (bp
< 0 || bp
>= DEBUG_REGISTER_COUNT
|| !breakpoints
[bp
].allocated
)
118 return BREAKPOINT_RES_INVALID_HANDLE
;
120 return BREAKPOINT_RES_OK
;
123 enum breakpoint_result
breakpoint_create_instruction(struct breakpoint_handle
*out_handle
,
126 enum breakpoint_result res
=
127 allocate_breakpoint(out_handle
, BREAKPOINT_TYPE_INSTRUCTION
);
129 if (res
!= BREAKPOINT_RES_OK
)
132 int bp
= out_handle
->bp
;
133 if (!debug_write_addr_reg(bp
, (uintptr_t)virt_addr
))
134 return BREAKPOINT_RES_INVALID_HANDLE
;
136 uintptr_t control
= debug_read_control();
137 control
&= ~DEBUG_CTRL_LT_MASK(bp
);
138 control
|= DEBUG_CTRL_LT(bp
, DEBUG_CTRL_LEN_1
, BREAKPOINT_TYPE_INSTRUCTION
);
139 debug_write_control(control
);
140 return BREAKPOINT_RES_OK
;
143 enum breakpoint_result
breakpoint_create_data(struct breakpoint_handle
*out_handle
,
144 void *virt_addr
, size_t len
, bool write_only
)
146 uintptr_t len_value
= 0;
150 len_value
= DEBUG_CTRL_LEN_1
;
154 len_value
= DEBUG_CTRL_LEN_2
;
158 len_value
= DEBUG_CTRL_LEN_4
;
162 /* Only supported on 64-bit CPUs */
164 return BREAKPOINT_RES_INVALID_LENGTH
;
165 len_value
= DEBUG_CTRL_LEN_8
;
169 return BREAKPOINT_RES_INVALID_LENGTH
;
172 enum breakpoint_type type
=
173 write_only
? BREAKPOINT_TYPE_DATA_WRITE
: BREAKPOINT_TYPE_DATA_RW
;
174 enum breakpoint_result res
= allocate_breakpoint(out_handle
, type
);
175 if (res
!= BREAKPOINT_RES_OK
)
178 int bp
= out_handle
->bp
;
179 if (!debug_write_addr_reg(bp
, (uintptr_t)virt_addr
))
180 return BREAKPOINT_RES_INVALID_HANDLE
;
182 uintptr_t control
= debug_read_control();
183 control
&= ~DEBUG_CTRL_LT_MASK(bp
);
184 control
|= DEBUG_CTRL_LT(bp
, len_value
, type
);
185 debug_write_control(control
);
186 return BREAKPOINT_RES_OK
;
189 enum breakpoint_result
breakpoint_remove(struct breakpoint_handle handle
)
191 enum breakpoint_result res
= validate_handle(handle
);
193 if (res
!= BREAKPOINT_RES_OK
)
195 breakpoint_enable(handle
, false);
198 breakpoints
[bp
].allocated
= false;
199 return BREAKPOINT_RES_OK
;
202 enum breakpoint_result
breakpoint_enable(struct breakpoint_handle handle
, bool enabled
)
204 enum breakpoint_result res
= validate_handle(handle
);
206 if (res
!= BREAKPOINT_RES_OK
)
209 uintptr_t control
= debug_read_control();
211 control
&= ~DEBUG_CTRL_ENABLE_MASK(bp
);
213 control
|= DEBUG_CTRL_ENABLE(bp
, DEBUG_CTRL_ENABLE_GLOBAL
);
214 debug_write_control(control
);
215 return BREAKPOINT_RES_OK
;
218 enum breakpoint_result
breakpoint_get_type(struct breakpoint_handle handle
,
219 enum breakpoint_type
*type
)
221 enum breakpoint_result res
= validate_handle(handle
);
223 if (res
!= BREAKPOINT_RES_OK
)
226 *type
= breakpoints
[handle
.bp
].type
;
227 return BREAKPOINT_RES_OK
;
230 enum breakpoint_result
breakpoint_set_handler(struct breakpoint_handle handle
,
231 breakpoint_handler handler
)
233 enum breakpoint_result res
= validate_handle(handle
);
235 if (res
!= BREAKPOINT_RES_OK
)
238 breakpoints
[handle
.bp
].handler
= handler
;
239 return BREAKPOINT_RES_OK
;
242 static enum breakpoint_result
is_breakpoint_hit(struct breakpoint_handle handle
, bool *out_hit
)
244 enum breakpoint_result res
= validate_handle(handle
);
246 if (res
!= BREAKPOINT_RES_OK
)
249 uintptr_t status
= debug_read_status();
250 *out_hit
= DEBUG_STATUS_GET_BP_HIT(handle
.bp
, status
);
252 return BREAKPOINT_RES_OK
;
255 int breakpoint_dispatch_handler(struct eregs
*info
)
257 bool instr_bp_hit
= 0;
259 for (int i
= 0; i
< DEBUG_REGISTER_COUNT
; i
++) {
260 struct breakpoint_handle handle
= { i
};
262 enum breakpoint_type type
;
264 if (is_breakpoint_hit(handle
, &hit
) != BREAKPOINT_RES_OK
|| !hit
)
267 if (breakpoint_get_type(handle
, &type
) != BREAKPOINT_RES_OK
)
270 instr_bp_hit
|= type
== BREAKPOINT_TYPE_INSTRUCTION
;
272 /* Call the breakpoint handler. */
273 if (breakpoints
[handle
.bp
].handler
) {
274 int ret
= breakpoints
[handle
.bp
].handler(handle
, info
);
275 /* A non-zero return value indicates a fatal error. */
281 /* Clear hit breakpoints. */
282 uintptr_t status
= debug_read_status();
283 for (int i
= 0; i
< DEBUG_REGISTER_COUNT
; i
++) {
284 status
&= ~DEBUG_STATUS_BP_HIT_MASK(i
);
286 debug_write_status(status
);
289 /* Set the resume flag so the same breakpoint won't be hit immediately. */
291 info
->rflags
|= FLAGS_RESUME
;
293 info
->eflags
|= FLAGS_RESUME
;