Rev 3016 | Rev 3026 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 3016 | Rev 3018 | ||
---|---|---|---|
Line 46... | Line 46... | ||
46 | #include <udebug/udebug_ops.h> |
46 | #include <udebug/udebug_ops.h> |
47 | 47 | ||
48 | /** |
48 | /** |
49 | * Prepare a thread for a debugging operation. |
49 | * Prepare a thread for a debugging operation. |
50 | * |
50 | * |
51 | * Simply put, return thread t with t->debug_lock held, |
51 | * Simply put, return thread t with t->udebug.lock held, |
52 | * but only if it verifies all conditions. |
52 | * but only if it verifies all conditions. |
53 | * |
53 | * |
54 | * Specifically, verifies that thread t exists, is a userspace thread, |
54 | * Specifically, verifies that thread t exists, is a userspace thread, |
55 | * and belongs to the current task (TASK). Verifies, that the thread |
55 | * and belongs to the current task (TASK). Verifies, that the thread |
56 | * has (or hasn't) go according to having_go (typically false). |
56 | * has (or hasn't) go according to having_go (typically false). |
57 | * It also locks t->debug_lock, making sure that t->debug_active is true |
57 | * It also locks t->udebug.lock, making sure that t->udebug.debug_active is true |
58 | * - that the thread is in a valid debugging session. |
58 | * - that the thread is in a valid debugging session. |
59 | * |
59 | * |
60 | * Returns EOK if all went well, or an error code otherwise. |
60 | * Returns EOK if all went well, or an error code otherwise. |
61 | * Interrupts must be already disabled when calling this function. |
61 | * Interrupts must be already disabled when calling this function. |
62 | * |
62 | * |
Line 75... | Line 75... | ||
75 | if (!thread_exists(t)) { |
75 | if (!thread_exists(t)) { |
76 | spinlock_unlock(&threads_lock); |
76 | spinlock_unlock(&threads_lock); |
77 | return ENOENT; |
77 | return ENOENT; |
78 | } |
78 | } |
79 | 79 | ||
80 | spinlock_lock(&t->debug_lock); |
80 | spinlock_lock(&t->udebug.lock); |
81 | spinlock_lock(&t->lock); |
81 | spinlock_lock(&t->lock); |
82 | 82 | ||
83 | /* Now verify that it's the current task */ |
83 | /* Now verify that it's the current task */ |
84 | if (t->task != TASK) { |
84 | if (t->task != TASK) { |
85 | /* No such thread belonging to callee */ |
85 | /* No such thread belonging to callee */ |
Line 92... | Line 92... | ||
92 | /* It's not, deny its existence */ |
92 | /* It's not, deny its existence */ |
93 | rc = ENOENT; |
93 | rc = ENOENT; |
94 | goto error_exit; |
94 | goto error_exit; |
95 | } |
95 | } |
96 | 96 | ||
97 | if ((t->debug_active != true) || (!t->debug_stop != having_go)) { |
97 | if ((t->udebug.debug_active != true) || (!t->udebug.stop != having_go)) { |
98 | /* Not in debugging session or undesired GO state */ |
98 | /* Not in debugging session or undesired GO state */ |
99 | rc = EINVAL; |
99 | rc = EINVAL; |
100 | goto error_exit; |
100 | goto error_exit; |
101 | } |
101 | } |
102 | 102 | ||
103 | spinlock_unlock(&threads_lock); |
103 | spinlock_unlock(&threads_lock); |
104 | spinlock_unlock(&t->lock); |
104 | spinlock_unlock(&t->lock); |
105 | 105 | ||
106 | /* Only t->debug_lock left */ |
106 | /* Only t->udebug.lock left */ |
107 | 107 | ||
108 | return EOK; /* All went well */ |
108 | return EOK; /* All went well */ |
109 | 109 | ||
110 | 110 | ||
111 | /* Executed when a check on the thread fails */ |
111 | /* Executed when a check on the thread fails */ |
112 | error_exit: |
112 | error_exit: |
113 | spinlock_unlock(&t->lock); |
113 | spinlock_unlock(&t->lock); |
114 | spinlock_unlock(&t->debug_lock); |
114 | spinlock_unlock(&t->udebug.lock); |
115 | spinlock_unlock(&threads_lock); |
115 | spinlock_unlock(&threads_lock); |
116 | 116 | ||
117 | /* No locks left here */ |
117 | /* No locks left here */ |
118 | return rc; /* Some errors occured */ |
118 | return rc; /* Some errors occured */ |
119 | } |
119 | } |
120 | 120 | ||
121 | 121 | ||
122 | static void _thread_op_end(thread_t *t) |
122 | static void _thread_op_end(thread_t *t) |
123 | { |
123 | { |
124 | spinlock_unlock(&t->debug_lock); |
124 | spinlock_unlock(&t->udebug.lock); |
125 | } |
125 | } |
126 | 126 | ||
127 | /** |
127 | /** |
128 | * \return 0 (ok, but not done yet), 1 (done) or negative error code. |
128 | * \return 0 (ok, but not done yet), 1 (done) or negative error code. |
129 | */ |
129 | */ |
Line 157... | Line 157... | ||
157 | reply = 1; /* immediate reply */ |
157 | reply = 1; /* immediate reply */ |
158 | } else { |
158 | } else { |
159 | reply = 0; /* no reply */ |
159 | reply = 0; /* no reply */ |
160 | } |
160 | } |
161 | 161 | ||
162 | /* Set debug_active on all of the task's userspace threads */ |
162 | /* Set udebug.debug_active on all of the task's userspace threads */ |
163 | 163 | ||
164 | for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) { |
164 | for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) { |
165 | t = list_get_instance(cur, thread_t, th_link); |
165 | t = list_get_instance(cur, thread_t, th_link); |
166 | 166 | ||
167 | ipl = interrupts_disable(); |
167 | ipl = interrupts_disable(); |
168 | spinlock_lock(&t->debug_lock); |
168 | spinlock_lock(&t->udebug.lock); |
169 | if ((t->flags & THREAD_FLAG_USPACE) != 0) |
169 | if ((t->flags & THREAD_FLAG_USPACE) != 0) |
170 | t->debug_active = true; |
170 | t->udebug.debug_active = true; |
171 | spinlock_unlock(&t->debug_lock); |
171 | spinlock_unlock(&t->udebug.lock); |
172 | interrupts_restore(ipl); |
172 | interrupts_restore(ipl); |
173 | } |
173 | } |
174 | 174 | ||
175 | mutex_unlock(&TASK->udebug.lock); |
175 | mutex_unlock(&TASK->udebug.lock); |
176 | 176 | ||
Line 226... | Line 226... | ||
226 | 226 | ||
227 | // klog_printf("udebug_go()"); |
227 | // klog_printf("udebug_go()"); |
228 | 228 | ||
229 | ipl = interrupts_disable(); |
229 | ipl = interrupts_disable(); |
230 | 230 | ||
231 | /* On success, this will lock t->debug_lock */ |
231 | /* On success, this will lock t->udebug.lock */ |
232 | rc = _thread_op_begin(t, false); |
232 | rc = _thread_op_begin(t, false); |
233 | if (rc != EOK) { |
233 | if (rc != EOK) { |
234 | interrupts_restore(ipl); |
234 | interrupts_restore(ipl); |
235 | return rc; |
235 | return rc; |
236 | } |
236 | } |
237 | 237 | ||
238 | t->debug_go_call = call; |
238 | t->udebug.go_call = call; |
239 | t->debug_stop = false; |
239 | t->udebug.stop = false; |
240 | t->cur_event = 0; /* none */ |
240 | t->udebug.cur_event = 0; /* none */ |
241 | 241 | ||
242 | /* |
242 | /* |
243 | * Neither t's lock nor threads_lock may be held during wakeup |
243 | * Neither t's lock nor threads_lock may be held during wakeup |
244 | */ |
244 | */ |
245 | waitq_wakeup(&t->go_wq, WAKEUP_FIRST); |
245 | waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST); |
246 | 246 | ||
247 | _thread_op_end(t); |
247 | _thread_op_end(t); |
248 | interrupts_restore(ipl); |
248 | interrupts_restore(ipl); |
249 | 249 | ||
250 | return 0; |
250 | return 0; |
Line 259... | Line 259... | ||
259 | mutex_lock(&TASK->udebug.lock); |
259 | mutex_lock(&TASK->udebug.lock); |
260 | 260 | ||
261 | ipl = interrupts_disable(); |
261 | ipl = interrupts_disable(); |
262 | 262 | ||
263 | /* |
263 | /* |
264 | * On success, this will lock t->debug_lock. Note that this makes sure |
264 | * On success, this will lock t->udebug.lock. Note that this makes sure |
265 | * the thread is not stopped. |
265 | * the thread is not stopped. |
266 | */ |
266 | */ |
267 | rc = _thread_op_begin(t, true); |
267 | rc = _thread_op_begin(t, true); |
268 | if (rc != EOK) { |
268 | if (rc != EOK) { |
269 | interrupts_restore(ipl); |
269 | interrupts_restore(ipl); |
270 | return rc; |
270 | return rc; |
271 | } |
271 | } |
272 | 272 | ||
273 | /* Take GO away from the thread */ |
273 | /* Take GO away from the thread */ |
274 | t->debug_stop = true; |
274 | t->udebug.stop = true; |
275 | 275 | ||
276 | if (!t->debug_stoppable) { |
276 | if (!t->udebug.stoppable) { |
277 | /* Answer will be sent when the thread becomes stoppable */ |
277 | /* Answer will be sent when the thread becomes stoppable */ |
278 | _thread_op_end(t); |
278 | _thread_op_end(t); |
279 | interrupts_restore(ipl); |
279 | interrupts_restore(ipl); |
280 | return 0; |
280 | return 0; |
281 | } |
281 | } |
Line 284... | Line 284... | ||
284 | * Answer GO call |
284 | * Answer GO call |
285 | */ |
285 | */ |
286 | klog_printf("udebug_stop - answering go call"); |
286 | klog_printf("udebug_stop - answering go call"); |
287 | 287 | ||
288 | /* Make sure nobody takes this call away from us */ |
288 | /* Make sure nobody takes this call away from us */ |
289 | call = t->debug_go_call; |
289 | call = t->udebug.go_call; |
290 | t->debug_go_call = NULL; |
290 | t->udebug.go_call = NULL; |
291 | 291 | ||
292 | IPC_SET_RETVAL(call->data, 0); |
292 | IPC_SET_RETVAL(call->data, 0); |
293 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP); |
293 | IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP); |
294 | klog_printf("udebug_stop/ipc_answer"); |
294 | klog_printf("udebug_stop/ipc_answer"); |
295 | 295 | ||
296 | THREAD->cur_event = UDEBUG_EVENT_STOP; |
296 | THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; |
297 | 297 | ||
298 | _thread_op_end(t); |
298 | _thread_op_end(t); |
299 | interrupts_restore(ipl); |
299 | interrupts_restore(ipl); |
300 | 300 | ||
301 | ipc_answer(&TASK->answerbox, call); |
301 | ipc_answer(&TASK->answerbox, call); |
Line 376... | Line 376... | ||
376 | /* Prepare a buffer to hold the arguments */ |
376 | /* Prepare a buffer to hold the arguments */ |
377 | arg_buffer = malloc(6 * sizeof(unative_t), 0); |
377 | arg_buffer = malloc(6 * sizeof(unative_t), 0); |
378 | 378 | ||
379 | ipl = interrupts_disable(); |
379 | ipl = interrupts_disable(); |
380 | 380 | ||
381 | /* On success, this will lock t->debug_lock */ |
381 | /* On success, this will lock t->udebug.lock */ |
382 | rc = _thread_op_begin(t, false); |
382 | rc = _thread_op_begin(t, false); |
383 | if (rc != EOK) { |
383 | if (rc != EOK) { |
384 | interrupts_restore(ipl); |
384 | interrupts_restore(ipl); |
385 | return rc; |
385 | return rc; |
386 | } |
386 | } |
387 | 387 | ||
388 | /* Additionally we need to verify that we are inside a syscall */ |
388 | /* Additionally we need to verify that we are inside a syscall */ |
389 | if (t->cur_event != UDEBUG_EVENT_SYSCALL_B && |
389 | if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B && |
390 | t->cur_event != UDEBUG_EVENT_SYSCALL_E) { |
390 | t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) { |
391 | _thread_op_end(t); |
391 | _thread_op_end(t); |
392 | interrupts_restore(ipl); |
392 | interrupts_restore(ipl); |
393 | 393 | ||
394 | return EINVAL; |
394 | return EINVAL; |
395 | } |
395 | } |
396 | 396 | ||
397 | /* Copy to a local buffer before releasing the lock */ |
397 | /* Copy to a local buffer before releasing the lock */ |
398 | memcpy(arg_buffer, t->syscall_args, 6 * sizeof(unative_t)); |
398 | memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t)); |
399 | 399 | ||
400 | _thread_op_end(t); |
400 | _thread_op_end(t); |
401 | interrupts_restore(ipl); |
401 | interrupts_restore(ipl); |
402 | 402 | ||
403 | *buffer = arg_buffer; |
403 | *buffer = arg_buffer; |
Line 412... | Line 412... | ||
412 | 412 | ||
413 | klog_printf("udebug_regs_read()"); |
413 | klog_printf("udebug_regs_read()"); |
414 | 414 | ||
415 | ipl = interrupts_disable(); |
415 | ipl = interrupts_disable(); |
416 | 416 | ||
417 | /* On success, this will lock t->debug_lock */ |
417 | /* On success, this will lock t->udebug.lock */ |
418 | rc = _thread_op_begin(t, false); |
418 | rc = _thread_op_begin(t, false); |
419 | if (rc != EOK) { |
419 | if (rc != EOK) { |
420 | interrupts_restore(ipl); |
420 | interrupts_restore(ipl); |
421 | return rc; |
421 | return rc; |
422 | } |
422 | } |
423 | 423 | ||
424 | state = t->uspace_state; |
424 | state = t->udebug.uspace_state; |
425 | if (state == NULL) { |
425 | if (state == NULL) { |
426 | _thread_op_end(t); |
426 | _thread_op_end(t); |
427 | interrupts_restore(ipl); |
427 | interrupts_restore(ipl); |
428 | klog_printf("udebug_regs_read() - istate not available"); |
428 | klog_printf("udebug_regs_read() - istate not available"); |
429 | return EBUSY; |
429 | return EBUSY; |
Line 448... | Line 448... | ||
448 | 448 | ||
449 | /* Try to change the thread's uspace_state */ |
449 | /* Try to change the thread's uspace_state */ |
450 | 450 | ||
451 | ipl = interrupts_disable(); |
451 | ipl = interrupts_disable(); |
452 | 452 | ||
453 | /* On success, this will lock t->debug_lock */ |
453 | /* On success, this will lock t->udebug.lock */ |
454 | rc = _thread_op_begin(t, false); |
454 | rc = _thread_op_begin(t, false); |
455 | if (rc != EOK) { |
455 | if (rc != EOK) { |
456 | klog_printf("error locking thread"); |
456 | klog_printf("error locking thread"); |
457 | interrupts_restore(ipl); |
457 | interrupts_restore(ipl); |
458 | return rc; |
458 | return rc; |
459 | } |
459 | } |
460 | 460 | ||
461 | state = t->uspace_state; |
461 | state = t->udebug.uspace_state; |
462 | if (state == NULL) { |
462 | if (state == NULL) { |
463 | _thread_op_end(t); |
463 | _thread_op_end(t); |
464 | interrupts_restore(ipl); |
464 | interrupts_restore(ipl); |
465 | klog_printf("udebug_regs_write() - istate not available"); |
465 | klog_printf("udebug_regs_write() - istate not available"); |
466 | 466 | ||
467 | return EBUSY; |
467 | return EBUSY; |
468 | } |
468 | } |
469 | 469 | ||
470 | memcpy(t->uspace_state, buffer, sizeof(istate_t)); |
470 | memcpy(t->udebug.uspace_state, buffer, sizeof(istate_t)); |
471 | 471 | ||
472 | _thread_op_end(t); |
472 | _thread_op_end(t); |
473 | interrupts_restore(ipl); |
473 | interrupts_restore(ipl); |
474 | 474 | ||
475 | return 0; |
475 | return 0; |