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; |