xref: /haiku/src/system/kernel/arch/x86/arch_user_debugger.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
1 /*
2  * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <string.h>
7 
8 #include <debugger.h>
9 #include <driver_settings.h>
10 #include <int.h>
11 #include <thread.h>
12 #include <arch/user_debugger.h>
13 
14 //#define TRACE_ARCH_USER_DEBUGGER
15 #ifdef TRACE_ARCH_USER_DEBUGGER
16 #	define TRACE(x) dprintf x
17 #else
18 #	define TRACE(x) ;
19 #endif
20 
21 #define B_NO_MORE_BREAKPOINTS				B_ERROR
22 #define B_NO_MORE_WATCHPOINTS				B_ERROR
23 #define B_BAD_WATCHPOINT_ALIGNMENT			B_ERROR
24 #define B_WATCHPOINT_TYPE_NOT_SUPPORTED		B_ERROR
25 #define B_WATCHPOINT_LENGTH_NOT_SUPPORTED	B_ERROR
26 #define B_BREAKPOINT_NOT_FOUND				B_ERROR
27 #define B_WATCHPOINT_NOT_FOUND				B_ERROR
28 	// ToDo: Make those real error codes.
29 
30 // maps breakpoint slot index to LEN_i LSB number
31 static const uint32 sDR7Len[4] = {
32 	X86_DR7_LEN0_LSB, X86_DR7_LEN1_LSB, X86_DR7_LEN2_LSB, X86_DR7_LEN3_LSB
33 };
34 
35 // maps breakpoint slot index to R/W_i LSB number
36 static const uint32 sDR7RW[4] = {
37 	X86_DR7_RW0_LSB, X86_DR7_RW1_LSB, X86_DR7_RW2_LSB, X86_DR7_RW3_LSB
38 };
39 
40 // maps breakpoint slot index to L_i bit number
41 static const uint32 sDR7L[4] = {
42 	X86_DR7_L0, X86_DR7_L1, X86_DR7_L2, X86_DR7_L3
43 };
44 
45 // maps breakpoint slot index to B_i bit number
46 static const uint32 sDR6B[4] = {
47 	X86_DR6_B0, X86_DR6_B1, X86_DR6_B2, X86_DR6_B3
48 };
49 
50 // Enables a hack to make single stepping work under qemu. Set via kernel
51 // driver settings.
52 static bool sQEmuSingleStepHack = false;
53 
54 
55 void
56 arch_clear_team_debug_info(struct arch_team_debug_info *info)
57 {
58 	for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++)
59 		info->breakpoints[i].address = NULL;
60 
61 	info->dr7 = X86_BREAKPOINTS_DISABLED_DR7;
62 }
63 
64 
65 void
66 arch_destroy_team_debug_info(struct arch_team_debug_info *info)
67 {
68 	arch_clear_team_debug_info(info);
69 }
70 
71 
72 void
73 arch_clear_thread_debug_info(struct arch_thread_debug_info *info)
74 {
75 	info->flags = 0;
76 }
77 
78 
79 void
80 arch_destroy_thread_debug_info(struct arch_thread_debug_info *info)
81 {
82 	arch_clear_thread_debug_info(info);
83 }
84 
85 
86 void
87 arch_set_debug_cpu_state(const struct debug_cpu_state *cpuState)
88 {
89 	if (struct iframe *frame = i386_get_user_iframe()) {
90 		struct thread *thread = thread_get_current_thread();
91 
92 		i386_frstor(cpuState->extended_regs);
93 			// For this to be correct the calling function must not use these
94 			// registers (not even indirectly).
95 
96 //		frame->gs = cpuState->gs;
97 //		frame->fs = cpuState->fs;
98 //		frame->es = cpuState->es;
99 //		frame->ds = cpuState->ds;
100 		frame->edi = cpuState->edi;
101 		frame->esi = cpuState->esi;
102 		frame->ebp = cpuState->ebp;
103 		frame->esp = cpuState->esp;
104 		frame->ebx = cpuState->ebx;
105 		frame->edx = cpuState->edx;
106 		frame->ecx = cpuState->ecx;
107 		frame->eax = cpuState->eax;
108 //		frame->vector = cpuState->vector;
109 //		frame->error_code = cpuState->error_code;
110 		frame->eip = cpuState->eip;
111 //		frame->cs = cpuState->cs;
112 		frame->flags = (frame->flags & ~X86_EFLAGS_USER_SETTABLE_FLAGS)
113 			| (cpuState->eflags & X86_EFLAGS_USER_SETTABLE_FLAGS);
114 		frame->user_esp = cpuState->user_esp;
115 //		frame->user_ss = cpuState->user_ss;
116 	}
117 }
118 
119 void
120 arch_get_debug_cpu_state(struct debug_cpu_state *cpuState)
121 {
122 	if (struct iframe *frame = i386_get_user_iframe()) {
123 		struct thread *thread = thread_get_current_thread();
124 
125 		i386_fsave(cpuState->extended_regs);
126 			// For this to be correct the calling function must not use these
127 			// registers (not even indirectly).
128 
129 		cpuState->gs = frame->gs;
130 		cpuState->fs = frame->fs;
131 		cpuState->es = frame->es;
132 		cpuState->ds = frame->ds;
133 		cpuState->edi = frame->edi;
134 		cpuState->esi = frame->esi;
135 		cpuState->ebp = frame->ebp;
136 		cpuState->esp = frame->esp;
137 		cpuState->ebx = frame->ebx;
138 		cpuState->edx = frame->orig_edx;
139 		cpuState->ecx = frame->ecx;
140 		cpuState->eax = frame->orig_eax;
141 		cpuState->vector = frame->vector;
142 		cpuState->error_code = frame->error_code;
143 		cpuState->eip = frame->eip;
144 		cpuState->cs = frame->cs;
145 		cpuState->eflags = frame->flags;
146 		cpuState->user_esp = frame->user_esp;
147 		cpuState->user_ss = frame->user_ss;
148 	}
149 }
150 
151 static status_t
152 set_breakpoint(void *address, uint32 type, uint32 length)
153 {
154 	if (!address)
155 		return B_BAD_VALUE;
156 
157 	struct thread *thread = thread_get_current_thread();
158 
159 	status_t error = B_OK;
160 
161 	cpu_status state = disable_interrupts();
162 	GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
163 
164 	arch_team_debug_info &info = thread->team->debug_info.arch_info;
165 
166 	// check, if there is already a breakpoint at that address
167 	bool alreadySet = false;
168 	for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++) {
169 		if (info.breakpoints[i].address == address
170 			&& info.breakpoints[i].type == type) {
171 			alreadySet = true;
172 			break;
173 		}
174 	}
175 
176 	if (!alreadySet) {
177 		// find a free slot
178 		int32 slot = -1;
179 		for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++) {
180 			if (!info.breakpoints[i].address) {
181 				slot = i;
182 				break;
183 			}
184 		}
185 
186 		// init the breakpoint
187 		if (slot >= 0) {
188 			info.breakpoints[slot].address = address;
189 			info.breakpoints[slot].type = type;
190 			info.breakpoints[slot].length = length;
191 
192 			info.dr7 |= (length << sDR7Len[slot])
193 				| (type << sDR7RW[slot])
194 				| (1 << sDR7L[slot]);
195 		} else {
196 			if (type == X86_INSTRUCTION_BREAKPOINT)
197 				error = B_NO_MORE_BREAKPOINTS;
198 			else
199 				error = B_NO_MORE_WATCHPOINTS;
200 		}
201 	}
202 
203 	RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
204 	restore_interrupts(state);
205 
206 	return error;
207 }
208 
209 
210 static status_t
211 clear_breakpoint(void *address, bool watchpoint)
212 {
213 	if (!address)
214 		return B_BAD_VALUE;
215 
216 	struct thread *thread = thread_get_current_thread();
217 
218 	status_t error = B_OK;
219 
220 	cpu_status state = disable_interrupts();
221 	GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
222 
223 	arch_team_debug_info &info = thread->team->debug_info.arch_info;
224 
225 	// find the breakpoint
226 	int32 slot = -1;
227 	for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++) {
228 		if (info.breakpoints[i].address == address
229 			&& (watchpoint
230 				!= (info.breakpoints[i].type == X86_INSTRUCTION_BREAKPOINT))) {
231 			slot = i;
232 			break;
233 		}
234 	}
235 
236 	// clear the breakpoint
237 	if (slot >= 0) {
238 		info.breakpoints[slot].address = NULL;
239 
240 		info.dr7 &= ~((0x3 << sDR7Len[slot])
241 			| (0x3 << sDR7RW[slot])
242 			| (1 << sDR7L[slot]));
243 	} else {
244 		if (watchpoint)
245 			error = B_WATCHPOINT_NOT_FOUND;
246 		else
247 			error = B_BREAKPOINT_NOT_FOUND;
248 	}
249 
250 	RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
251 	restore_interrupts(state);
252 
253 	return error;
254 }
255 
256 
257 status_t
258 arch_set_breakpoint(void *address)
259 {
260 	return set_breakpoint(address, X86_INSTRUCTION_BREAKPOINT,
261 		X86_BREAKPOINT_LENGTH_1);
262 }
263 
264 
265 status_t
266 arch_clear_breakpoint(void *address)
267 {
268 	return clear_breakpoint(address, false);
269 }
270 
271 
272 status_t
273 arch_set_watchpoint(void *address, uint32 type, int32 length)
274 {
275 	// check type
276 	uint32 archType;
277 	switch (type) {
278 		case B_DATA_WRITE_WATCHPOINT:
279 			archType = X86_DATA_WRITE_BREAKPOINT;
280 			break;
281 		case B_DATA_READ_WRITE_WATCHPOINT:
282 			archType = X86_DATA_READ_WRITE_BREAKPOINT;
283 			break;
284 		case B_DATA_READ_WATCHPOINT:
285 		default:
286 			return B_WATCHPOINT_TYPE_NOT_SUPPORTED;
287 			break;
288 	}
289 
290 	// check length and alignment
291 	uint32 archLength;
292 	switch (length) {
293 		case 1:
294 			archLength = X86_BREAKPOINT_LENGTH_1;
295 			break;
296 		case 2:
297 			if ((uint32)address & 0x1)
298 				return B_BAD_WATCHPOINT_ALIGNMENT;
299 			archLength = X86_BREAKPOINT_LENGTH_2;
300 			break;
301 		case 4:
302 			if ((uint32)address & 0x3)
303 				return B_BAD_WATCHPOINT_ALIGNMENT;
304 			archLength = X86_BREAKPOINT_LENGTH_4;
305 			break;
306 		default:
307 			return B_WATCHPOINT_LENGTH_NOT_SUPPORTED;
308 	}
309 
310 	return set_breakpoint(address, archType, archLength);
311 }
312 
313 
314 status_t
315 arch_clear_watchpoint(void *address)
316 {
317 	return clear_breakpoint(address, false);
318 }
319 
320 
321 static inline void
322 install_breakpoints(const arch_team_debug_info &teamInfo)
323 {
324 	// set breakpoints
325 	asm("movl %0, %%dr0" : : "r"(teamInfo.breakpoints[0].address));
326 	asm("movl %0, %%dr1" : : "r"(teamInfo.breakpoints[1].address));
327 	asm("movl %0, %%dr2" : : "r"(teamInfo.breakpoints[2].address));
328 //	asm("movl %0, %%dr3" : : "r"(teamInfo.breakpoints[3].address));
329 		// DR3 is used to hold the current struct thread*.
330 
331 	// enable breakpoints
332 	asm("movl %0, %%dr7" : : "r"(teamInfo.dr7));
333 }
334 
335 
336 /**
337  *	Interrupts are enabled.
338  */
339 void
340 i386_init_user_debug_at_kernel_exit(struct iframe *frame)
341 {
342 	struct thread *thread = thread_get_current_thread();
343 
344 	cpu_status state = disable_interrupts();
345 	GRAB_THREAD_LOCK();
346 	GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
347 
348 	arch_team_debug_info &teamInfo = thread->team->debug_info.arch_info;
349 
350 	// install the breakpoints
351 	install_breakpoints(teamInfo);
352 	thread->debug_info.arch_info.flags |= X86_THREAD_DEBUG_DR7_SET;
353 
354 	// set/clear TF in EFLAGS depending on if single stepping is desired
355 	if (thread->debug_info.flags & B_THREAD_DEBUG_SINGLE_STEP)
356 		frame->flags |= (1 << X86_EFLAGS_TF);
357 	else
358 		frame->flags &= ~(1 << X86_EFLAGS_TF);
359 		// ToDo: Move into a function called from thread_hit_debug_event().
360 		// No need to have that here in the code executed for ever kernel->user
361 		// mode switch.
362 
363 	RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
364 	RELEASE_THREAD_LOCK();
365 	restore_interrupts(state);
366 }
367 
368 
369 /**
370  *	Interrupts may be enabled.
371  */
372 void
373 i386_exit_user_debug_at_kernel_entry()
374 {
375 	struct thread *thread = thread_get_current_thread();
376 
377 	cpu_status state = disable_interrupts();
378 	GRAB_THREAD_LOCK();
379 
380 	// disable breakpoints
381 	asm("movl %0, %%dr7" : : "r"(X86_BREAKPOINTS_DISABLED_DR7));
382 	thread->debug_info.arch_info.flags &= ~X86_THREAD_DEBUG_DR7_SET;
383 
384 	RELEASE_THREAD_LOCK();
385 	restore_interrupts(state);
386 }
387 
388 
389 /**
390  *	Interrupts are disabled and the thread lock is being held.
391  */
392 void
393 i386_reinit_user_debug_after_context_switch(struct thread *thread)
394 {
395 	if (thread->debug_info.arch_info.flags & X86_THREAD_DEBUG_DR7_SET) {
396 		GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
397 
398 		install_breakpoints(thread->team->debug_info.arch_info);
399 
400 		RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
401 	}
402 }
403 
404 
405 /**
406  *	Interrupts are disabled and will be enabled by the function.
407  */
408 int
409 i386_handle_debug_exception(struct iframe *frame)
410 {
411 	// get debug status and control registers
412 	uint32 dr6, dr7;
413 	asm("movl %%dr6, %0" : "=r"(dr6));
414 	asm("movl %%dr7, %0" : "=r"(dr7));
415 
416 	TRACE(("i386_handle_debug_exception(): DR6: %lx, DR7: %lx\n", dr6, dr7));
417 
418 	// check, which exception condition applies
419 	if (dr6 & X86_DR6_BREAKPOINT_MASK) {
420 		// breakpoint
421 
422 		// check which breakpoint was taken
423 		bool watchpoint = true;
424 		for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++) {
425 			if (dr6 & (1 << sDR6B[i])) {
426 				// If it is an instruction breakpoint, we need to set RF in
427 				// EFLAGS to prevent triggering the same exception
428 				// again (breakpoint instructions are triggered *before*
429 				// executing the instruction).
430 				uint32 type = (dr7 >> sDR7RW[i]) & 0x3;
431 				if (type == X86_INSTRUCTION_BREAKPOINT) {
432 					frame->flags |= (1 << X86_EFLAGS_RF);
433 					watchpoint = false;
434 				}
435 			}
436 		}
437 
438 		// enable interrupts and notify the debugger
439 		enable_interrupts();
440 
441 		if (watchpoint)
442 			user_debug_watchpoint_hit();
443 		else
444 			user_debug_breakpoint_hit(false);
445 
446 	} else if (dr6 & (1 << X86_DR6_BD)) {
447 		// general detect exception
448 		// Occurs only, if GD in DR7 is set (which we don't do) and someone
449 		// tries to write to the debug registers.
450 		dprintf("i386_handle_debug_exception(): ignoring spurious general "
451 			"detect exception\n");
452 
453 		enable_interrupts();
454 
455 	} else if ((dr6 & (1 << X86_DR6_BS)) || sQEmuSingleStepHack) {
456 		// single step
457 
458 		// enable interrupts and notify the debugger
459 		enable_interrupts();
460 
461 		user_debug_single_stepped();
462 
463 	} else if (dr6 & (1 << X86_DR6_BT)) {
464 		// task switch
465 		// Occurs only, if T in EFLAGS is set (which we don't do).
466 		dprintf("i386_handle_debug_exception(): ignoring spurious task switch "
467 			"exception\n");
468 
469 		enable_interrupts();
470 
471 	} else {
472 		dprintf("i386_handle_debug_exception(): ignoring spurious debug "
473 			"exception (no condition recognized)\n");
474 
475 		enable_interrupts();
476 	}
477 
478 	return B_HANDLED_INTERRUPT;
479 }
480 
481 
482 /**
483  *	Interrupts are disabled and will be enabled by the function.
484  */
485 int
486 i386_handle_breakpoint_exception(struct iframe *frame)
487 {
488 	TRACE(("i386_handle_breakpoint_exception()\n"));
489 
490 	enable_interrupts();
491 
492 	user_debug_breakpoint_hit(true);
493 
494 	return B_HANDLED_INTERRUPT;
495 }
496 
497 
498 void
499 i386_init_user_debug()
500 {
501 	// get debug settings
502 	if (void *handle = load_driver_settings("kernel")) {
503 		sQEmuSingleStepHack = get_driver_boolean_parameter(handle,
504 			"qemu_single_step_hack", false, false);;
505 
506 		unload_driver_settings(handle);
507 	}
508 }
509 
510