Rev 2823 | Rev 2825 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2813 | svoboda | 1 | /** @addtogroup generic |
2 | * @{ |
||
3 | */ |
||
4 | |||
5 | /** |
||
6 | * @file |
||
7 | * @brief Tdebug. |
||
8 | */ |
||
9 | |||
10 | #include <console/klog.h> |
||
11 | #include <proc/task.h> |
||
12 | #include <proc/thread.h> |
||
2815 | svoboda | 13 | #include <arch.h> |
2813 | svoboda | 14 | #include <errno.h> |
15 | #include <ipc/ipc.h> |
||
16 | #include <syscall/copy.h> |
||
17 | #include <udebug/udebug.h> |
||
18 | #include <udebug/udebug_ipc.h> |
||
19 | |||
2823 | svoboda | 20 | /** |
21 | * Get and lock a phone's callee task. |
||
22 | * |
||
23 | * This will return a pointer to the task to which the phone |
||
24 | * is connected. It will lock the task, making sure it exists. |
||
25 | * (TODO: make sure the udebug-cleanup of the task hasn't |
||
26 | * started yet) |
||
27 | */ |
||
2813 | svoboda | 28 | static task_t *get_lock_callee_task(phone_t *phone) |
29 | { |
||
2823 | svoboda | 30 | answerbox_t *box; |
2813 | svoboda | 31 | task_t *ta; |
2823 | svoboda | 32 | task_id_t taskid; |
33 | ipl_t ipl; |
||
2813 | svoboda | 34 | |
2823 | svoboda | 35 | ipl = interrupts_disable(); |
36 | spinlock_lock(&phone->lock); |
||
37 | if (phone->state != IPC_PHONE_CONNECTED) { |
||
38 | spinlock_unlock(&phone->lock); |
||
39 | interrupts_restore(ipl); |
||
40 | return NULL; |
||
41 | } |
||
2813 | svoboda | 42 | |
2823 | svoboda | 43 | box = phone->callee; |
44 | |||
45 | spinlock_lock(&box->lock); |
||
46 | ta = box->task; |
||
47 | taskid = ta->taskid; |
||
48 | spinlock_unlock(&box->lock); |
||
49 | spinlock_unlock(&phone->lock); |
||
2813 | svoboda | 50 | |
2823 | svoboda | 51 | /* Locking decoupled using taskid */ |
52 | |||
53 | spinlock_lock(&tasks_lock); |
||
54 | ta = task_find_by_id(taskid); |
||
55 | if (ta == NULL) { |
||
56 | spinlock_unlock(&tasks_lock); |
||
57 | interrupts_restore(ipl); |
||
58 | return NULL; |
||
59 | } |
||
60 | |||
2813 | svoboda | 61 | spinlock_lock(&ta->lock); |
2823 | svoboda | 62 | spinlock_unlock(&tasks_lock); |
63 | interrupts_restore(ipl); |
||
2813 | svoboda | 64 | |
65 | return ta; |
||
66 | } |
||
67 | |||
68 | static int udebug_rp_begin(call_t *call, phone_t *phone) |
||
69 | { |
||
70 | task_t *ta; |
||
2823 | svoboda | 71 | ipl_t ipl; |
2813 | svoboda | 72 | |
73 | klog_printf("debug_begin()"); |
||
74 | |||
2823 | svoboda | 75 | ipl = interrupts_disable(); |
2813 | svoboda | 76 | ta = get_lock_callee_task(phone); |
77 | klog_printf("debugging task %llu", ta->taskid); |
||
78 | |||
79 | if (ta->being_debugged != false) { |
||
80 | spinlock_unlock(&ta->lock); |
||
2823 | svoboda | 81 | interrupts_restore(ipl); |
2813 | svoboda | 82 | klog_printf("debug_begin(): busy error"); |
83 | return EBUSY; |
||
84 | } |
||
85 | |||
86 | ta->being_debugged = true; |
||
87 | ta->stop_request = true; |
||
88 | ta->debug_begin_call = call; |
||
89 | |||
90 | if (ta->not_stoppable_count == 0) { |
||
91 | ta->debug_begin_call = NULL; |
||
92 | ta->stop_request = false; |
||
93 | spinlock_unlock(&ta->lock); |
||
2823 | svoboda | 94 | interrupts_restore(ipl); |
2813 | svoboda | 95 | klog_printf("debug_begin(): immediate backsend"); |
96 | return 1; /* actually we need backsend with 0 retval */ |
||
97 | } |
||
98 | |||
99 | spinlock_unlock(&ta->lock); |
||
2823 | svoboda | 100 | interrupts_restore(ipl); |
2813 | svoboda | 101 | |
102 | klog_printf("debug_begin() done (wait for stoppability)"); |
||
103 | return 0; |
||
104 | } |
||
105 | |||
106 | static int udebug_rp_go(call_t *call, phone_t *phone) |
||
107 | { |
||
108 | thread_t *t; |
||
109 | task_t *ta; |
||
2823 | svoboda | 110 | ipl_t ipl; |
2813 | svoboda | 111 | |
112 | klog_printf("debug_go()"); |
||
113 | ta = get_lock_callee_task(phone); |
||
114 | |||
2816 | svoboda | 115 | // FIXME: must save this in thread struct, not task struct!!! |
2813 | svoboda | 116 | ta->debug_go_call = call; |
2823 | svoboda | 117 | spinlock_unlock(&ta->lock); |
118 | |||
2816 | svoboda | 119 | t = (thread_t *) IPC_GET_ARG2(call->data); |
2823 | svoboda | 120 | |
121 | ipl = interrupts_disable(); |
||
122 | spinlock_lock(&threads_lock); |
||
2816 | svoboda | 123 | if (!thread_exists(t)) { |
2823 | svoboda | 124 | spinlock_unlock(&threads_lock); |
125 | interrupts_restore(ipl); |
||
2813 | svoboda | 126 | return ENOENT; |
127 | } |
||
128 | |||
129 | waitq_wakeup(&t->go_wq, WAKEUP_FIRST); |
||
2823 | svoboda | 130 | spinlock_unlock(&threads_lock); |
131 | interrupts_restore(ipl); |
||
2813 | svoboda | 132 | |
133 | return 0; /* no backsend */ |
||
134 | } |
||
135 | |||
136 | static int udebug_rp_args_read(call_t *call, phone_t *phone) |
||
137 | { |
||
138 | thread_t *t; |
||
139 | task_t *ta; |
||
140 | void *uspace_buffer; |
||
141 | unative_t to_copy; |
||
142 | int rc; |
||
2823 | svoboda | 143 | ipl_t ipl; |
2813 | svoboda | 144 | |
145 | klog_printf("debug_args_read()"); |
||
146 | // FIXME: verify task/thread state |
||
147 | |||
148 | ta = get_lock_callee_task(phone); |
||
149 | klog_printf("task %llu", ta->taskid); |
||
2823 | svoboda | 150 | spinlock_unlock(&ta->lock); |
151 | |||
2816 | svoboda | 152 | t = (thread_t *) IPC_GET_ARG2(call->data); |
2823 | svoboda | 153 | |
154 | ipl = interrupts_disable(); |
||
155 | spinlock_lock(&threads_lock); |
||
156 | |||
2816 | svoboda | 157 | if (!thread_exists(t)) { |
2823 | svoboda | 158 | spinlock_unlock(&threads_lock); |
159 | interrupts_restore(ipl); |
||
2813 | svoboda | 160 | return ENOENT; |
161 | } |
||
162 | |||
2823 | svoboda | 163 | // FIXME: copy to a local buffer before releasing the lock |
164 | spinlock_unlock(&threads_lock); |
||
165 | interrupts_restore(ipl); |
||
166 | |||
2813 | svoboda | 167 | uspace_buffer = (void *)IPC_GET_ARG3(call->data); |
168 | to_copy = IPC_GET_ARG4(call->data); |
||
169 | if (to_copy > 6 * sizeof(unative_t)) to_copy = 6 * sizeof(unative_t); |
||
170 | |||
171 | rc = copy_to_uspace(uspace_buffer, t->syscall_args, to_copy); |
||
172 | if (rc != 0) { |
||
173 | spinlock_unlock(&ta->lock); |
||
174 | klog_printf("debug_args_read() - copy failed"); |
||
175 | return rc; |
||
176 | } |
||
177 | |||
178 | IPC_SET_ARG1(call->data, to_copy); |
||
179 | |||
180 | klog_printf("debug_args_read() done"); |
||
181 | return 1; /* actually need becksend with retval 0 */ |
||
182 | } |
||
183 | |||
2817 | svoboda | 184 | static int udebug_rp_regs_read(call_t *call, phone_t *phone) |
185 | { |
||
186 | thread_t *t; |
||
187 | task_t *ta; |
||
188 | void *uspace_buffer; |
||
189 | unative_t to_copy; |
||
190 | int rc; |
||
191 | istate_t *state; |
||
2824 | svoboda | 192 | istate_t state_copy; |
193 | ipl_t ipl; |
||
2817 | svoboda | 194 | |
195 | klog_printf("debug_regs_read()"); |
||
196 | // FIXME: verify task/thread state |
||
197 | |||
2824 | svoboda | 198 | ta = get_lock_callee_task(phone); |
199 | spinlock_unlock(&ta->lock); |
||
2817 | svoboda | 200 | |
2824 | svoboda | 201 | ipl = interrupts_disable(); |
202 | spinlock_lock(&threads_lock); |
||
203 | |||
2817 | svoboda | 204 | t = (thread_t *) IPC_GET_ARG2(call->data); |
205 | if (!thread_exists(t)) { |
||
206 | return ENOENT; |
||
207 | } |
||
208 | |||
2824 | svoboda | 209 | state = t->uspace_state; |
210 | if (state == NULL) { |
||
211 | spinlock_unlock(&threads_lock); |
||
212 | interrupts_restore(ipl); |
||
213 | klog_printf("debug_regs_read() - istate not available"); |
||
214 | return EBUSY; |
||
215 | } |
||
216 | |||
217 | /* Copy to a local buffer so that we can release the lock */ |
||
218 | memcpy(&state_copy, state, sizeof(state_copy)); |
||
219 | spinlock_unlock(&threads_lock); |
||
220 | interrupts_restore(ipl); |
||
221 | |||
2817 | svoboda | 222 | uspace_buffer = (void *)IPC_GET_ARG3(call->data); |
223 | to_copy = IPC_GET_ARG4(call->data); |
||
224 | if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t); |
||
225 | |||
2824 | svoboda | 226 | rc = copy_to_uspace(uspace_buffer, &state_copy, to_copy); |
2817 | svoboda | 227 | if (rc != 0) { |
228 | spinlock_unlock(&ta->lock); |
||
229 | klog_printf("debug_regs_read() - copy failed"); |
||
230 | return rc; |
||
231 | } |
||
232 | |||
233 | IPC_SET_ARG1(call->data, to_copy); |
||
234 | IPC_SET_ARG2(call->data, sizeof(istate_t)); |
||
235 | |||
236 | klog_printf("debug_regs_read() done"); |
||
237 | return 1; /* actually need becksend with retval 0 */ |
||
238 | } |
||
239 | |||
240 | static int udebug_rp_regs_write(call_t *call, phone_t *phone) |
||
241 | { |
||
242 | thread_t *t; |
||
243 | task_t *ta; |
||
244 | void *uspace_data; |
||
245 | unative_t to_copy; |
||
246 | int rc; |
||
247 | istate_t *state; |
||
2824 | svoboda | 248 | istate_t data_copy; |
249 | ipl_t ipl; |
||
2817 | svoboda | 250 | |
251 | klog_printf("debug_regs_write()"); |
||
252 | |||
2824 | svoboda | 253 | /* First copy to a local buffer */ |
2817 | svoboda | 254 | |
255 | uspace_data = (void *)IPC_GET_ARG3(call->data); |
||
256 | to_copy = IPC_GET_ARG4(call->data); |
||
257 | if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t); |
||
258 | |||
2824 | svoboda | 259 | rc = copy_from_uspace(&data_copy, uspace_data, to_copy); |
2817 | svoboda | 260 | if (rc != 0) { |
261 | klog_printf("debug_regs_write() - copy failed"); |
||
262 | return rc; |
||
263 | } |
||
264 | |||
2824 | svoboda | 265 | // FIXME: verify task/thread state |
266 | |||
267 | ta = get_lock_callee_task(phone); |
||
2817 | svoboda | 268 | spinlock_unlock(&ta->lock); |
269 | |||
2824 | svoboda | 270 | /* Now try to change the thread's uspace_state */ |
271 | |||
272 | ipl = interrupts_disable(); |
||
273 | spinlock_lock(&threads_lock); |
||
274 | |||
275 | t = (thread_t *) IPC_GET_ARG2(call->data); |
||
276 | if (!thread_exists(t)) { |
||
277 | spinlock_unlock(&threads_lock); |
||
278 | interrupts_restore(ipl); |
||
279 | return ENOENT; |
||
280 | } |
||
281 | |||
282 | state = t->uspace_state; |
||
283 | if (state == NULL) { |
||
284 | spinlock_unlock(&threads_lock); |
||
285 | interrupts_restore(ipl); |
||
286 | klog_printf("debug_regs_write() - istate not available"); |
||
287 | return EBUSY; |
||
288 | } |
||
289 | |||
290 | memcpy(t->uspace_state, &data_copy, sizeof(t->uspace_state)); |
||
291 | |||
292 | spinlock_unlock(&threads_lock); |
||
293 | interrupts_restore(ipl); |
||
294 | |||
295 | /* Set answer values */ |
||
296 | |||
2817 | svoboda | 297 | IPC_SET_ARG1(call->data, to_copy); |
298 | IPC_SET_ARG2(call->data, sizeof(istate_t)); |
||
299 | |||
300 | klog_printf("debug_regs_write() done"); |
||
301 | return 1; /* actually need becksend with retval 0 */ |
||
302 | } |
||
303 | |||
2813 | svoboda | 304 | static int udebug_rp_thread_read(call_t *call, phone_t *phone) |
305 | { |
||
306 | thread_t *t; |
||
307 | link_t *cur; |
||
308 | task_t *ta; |
||
309 | unative_t *uspace_buffer; |
||
310 | unative_t to_copy; |
||
311 | int rc; |
||
2824 | svoboda | 312 | unsigned total_bytes; |
2813 | svoboda | 313 | unsigned buf_size; |
314 | unative_t tid; |
||
2824 | svoboda | 315 | unsigned num_threads, copied_ids; |
316 | ipl_t ipl; |
||
317 | unative_t *buffer; |
||
318 | int flags; |
||
2813 | svoboda | 319 | |
320 | klog_printf("debug_thread_read()"); |
||
321 | // FIXME: verify task/thread state |
||
322 | |||
2824 | svoboda | 323 | ipl = interrupts_disable(); |
2813 | svoboda | 324 | ta = get_lock_callee_task(phone); |
325 | |||
2824 | svoboda | 326 | /* Count the threads first */ |
327 | |||
328 | num_threads = 0; |
||
2813 | svoboda | 329 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
2824 | svoboda | 330 | /* Count all threads, to be on the safe side */ |
331 | ++num_threads; |
||
332 | } |
||
333 | |||
334 | /* Allocate a buffer and copy down the threads' ids */ |
||
335 | buffer = malloc(num_threads * sizeof(unative_t), 0); // ??? |
||
336 | |||
337 | copied_ids = 0; |
||
338 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
||
2813 | svoboda | 339 | t = list_get_instance(cur, thread_t, th_link); |
340 | |||
2824 | svoboda | 341 | spinlock_lock(&t->lock); |
342 | flags = t->flags; |
||
343 | spinlock_unlock(&t->lock); |
||
344 | |||
2813 | svoboda | 345 | /* Not interested in kernel threads */ |
2824 | svoboda | 346 | if ((flags & THREAD_FLAG_USPACE) != 0) { |
347 | /* Using thread struct pointer for identification */ |
||
348 | tid = (unative_t) t; |
||
349 | buffer[copied_ids++] = tid; |
||
350 | } |
||
351 | } |
||
2813 | svoboda | 352 | |
2824 | svoboda | 353 | spinlock_unlock(&ta->lock); |
354 | interrupts_restore(ipl); |
||
2813 | svoboda | 355 | |
2824 | svoboda | 356 | /* Now copy to userspace */ |
2813 | svoboda | 357 | |
2824 | svoboda | 358 | uspace_buffer = (void *)IPC_GET_ARG2(call->data); |
359 | buf_size = IPC_GET_ARG3(call->data); |
||
2813 | svoboda | 360 | |
2824 | svoboda | 361 | total_bytes = copied_ids * sizeof(unative_t); |
362 | |||
363 | if (buf_size > total_bytes) |
||
364 | to_copy = total_bytes; |
||
365 | else |
||
366 | to_copy = buf_size; |
||
367 | |||
368 | rc = copy_to_uspace(uspace_buffer, buffer, to_copy); |
||
369 | free(buffer); |
||
370 | |||
371 | if (rc != 0) { |
||
372 | klog_printf("debug_thread_read() - copy failed"); |
||
373 | return rc; |
||
2813 | svoboda | 374 | } |
375 | |||
2824 | svoboda | 376 | IPC_SET_ARG1(call->data, to_copy); |
377 | IPC_SET_ARG2(call->data, total_bytes); |
||
2813 | svoboda | 378 | |
379 | klog_printf("debug_thread_read() done"); |
||
380 | return 1; /* actually need becksend with retval 0 */ |
||
381 | } |
||
382 | |||
2818 | svoboda | 383 | static int udebug_rp_mem_write(call_t *call, phone_t *phone) |
384 | { |
||
385 | void *uspace_data; |
||
386 | unative_t to_copy; |
||
387 | int rc; |
||
388 | void *buffer; |
||
389 | |||
390 | klog_printf("udebug_rp_mem_write()"); |
||
391 | // FIXME: verify task/thread state |
||
392 | |||
393 | uspace_data = (void *)IPC_GET_ARG2(call->data); |
||
394 | to_copy = IPC_GET_ARG4(call->data); |
||
395 | |||
396 | buffer = malloc(to_copy, 0); // ??? |
||
397 | |||
398 | rc = copy_from_uspace(buffer, uspace_data, to_copy); |
||
399 | if (rc != 0) { |
||
400 | klog_printf(" - copy failed"); |
||
401 | return rc; |
||
402 | } |
||
403 | |||
404 | call->buffer = buffer; |
||
405 | |||
406 | klog_printf(" - done"); |
||
407 | return 1; /* actually need becksend with retval 0 */ |
||
408 | } |
||
409 | |||
410 | |||
2813 | svoboda | 411 | int udebug_request_preprocess(call_t *call, phone_t *phone) |
412 | { |
||
413 | int rc; |
||
414 | |||
415 | switch (IPC_GET_ARG1(call->data)) { |
||
416 | case UDEBUG_M_BEGIN: |
||
417 | rc = udebug_rp_begin(call, phone); |
||
418 | return rc; |
||
419 | case UDEBUG_M_GO: |
||
420 | rc = udebug_rp_go(call, phone); |
||
421 | return rc; |
||
422 | case UDEBUG_M_ARGS_READ: |
||
423 | rc = udebug_rp_args_read(call, phone); |
||
424 | return rc; |
||
2817 | svoboda | 425 | case UDEBUG_M_REGS_READ: |
426 | rc = udebug_rp_regs_read(call, phone); |
||
427 | return rc; |
||
428 | case UDEBUG_M_REGS_WRITE: |
||
429 | rc = udebug_rp_regs_write(call, phone); |
||
430 | return rc; |
||
2813 | svoboda | 431 | case UDEBUG_M_THREAD_READ: |
432 | rc = udebug_rp_thread_read(call, phone); |
||
2819 | svoboda | 433 | return rc; |
2818 | svoboda | 434 | case UDEBUG_M_MEM_WRITE: |
435 | rc = udebug_rp_mem_write(call, phone); |
||
2813 | svoboda | 436 | return rc; |
437 | default: |
||
438 | break; |
||
439 | } |
||
440 | |||
441 | return 0; |
||
442 | } |
||
443 | |||
2815 | svoboda | 444 | static void udebug_receive_mem_read(call_t *call) |
445 | { |
||
446 | unative_t uspace_dst; |
||
447 | void *uspace_ptr; |
||
448 | unsigned size; |
||
449 | void *buffer; |
||
450 | int rc; |
||
2813 | svoboda | 451 | |
2815 | svoboda | 452 | klog_printf("debug_mem_read()"); |
453 | uspace_dst = IPC_GET_ARG2(call->data); |
||
454 | uspace_ptr = (void *)IPC_GET_ARG3(call->data); |
||
455 | size = IPC_GET_ARG4(call->data); |
||
456 | |||
457 | buffer = malloc(size, 0); // ??? |
||
458 | klog_printf("debug_mem_read: src=%u, size=%u", uspace_ptr, size); |
||
459 | |||
460 | /* NOTE: this is not strictly from a syscall... but that shouldn't |
||
461 | * be a problem */ |
||
462 | rc = copy_from_uspace(buffer, uspace_ptr, size); |
||
463 | if (rc) { |
||
464 | IPC_SET_RETVAL(call->data, rc); |
||
465 | return; |
||
466 | } |
||
467 | |||
468 | klog_printf("first word: %u", *((unative_t *)buffer)); |
||
469 | |||
470 | IPC_SET_RETVAL(call->data, 0); |
||
471 | /* Hack: ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that |
||
472 | same code in process_answer() can be used |
||
473 | (no way to distinguish method in answer) */ |
||
474 | IPC_SET_ARG1(call->data, uspace_dst); |
||
475 | IPC_SET_ARG2(call->data, size); |
||
476 | call->buffer = buffer; |
||
477 | |||
478 | ipc_answer(&TASK->kernel_box, call); |
||
479 | } |
||
480 | |||
2818 | svoboda | 481 | static void udebug_receive_mem_write(call_t *call) |
482 | { |
||
483 | void *uspace_dst; |
||
484 | unsigned size; |
||
485 | void *buffer; |
||
486 | int rc; |
||
487 | |||
488 | klog_printf("udebug_receive_mem_write()"); |
||
489 | uspace_dst = (void *)IPC_GET_ARG3(call->data); |
||
490 | size = IPC_GET_ARG4(call->data); |
||
491 | |||
492 | buffer = call->buffer; |
||
493 | klog_printf("dst=%u, size=%u", uspace_dst, size); |
||
494 | |||
495 | /* NOTE: this is not strictly from a syscall... but that shouldn't |
||
496 | * be a problem */ |
||
497 | rc = copy_to_uspace(uspace_dst, buffer, size); |
||
498 | if (rc) { |
||
499 | IPC_SET_RETVAL(call->data, rc); |
||
500 | return; |
||
501 | } |
||
502 | |||
503 | IPC_SET_RETVAL(call->data, 0); |
||
504 | |||
505 | free(call->buffer); |
||
506 | call->buffer = NULL; |
||
507 | |||
508 | ipc_answer(&TASK->kernel_box, call); |
||
509 | } |
||
510 | |||
511 | |||
2815 | svoboda | 512 | /** |
513 | * Handle a debug call received on the kernel answerbox. |
||
514 | * |
||
515 | * This is called by the kbox servicing thread. |
||
516 | */ |
||
517 | void udebug_call_receive(call_t *call) |
||
518 | { |
||
519 | int debug_method; |
||
520 | |||
521 | debug_method = IPC_GET_ARG1(call->data); |
||
522 | |||
523 | switch (debug_method) { |
||
524 | case UDEBUG_M_MEM_READ: |
||
525 | udebug_receive_mem_read(call); |
||
526 | break; |
||
2818 | svoboda | 527 | case UDEBUG_M_MEM_WRITE: |
528 | udebug_receive_mem_write(call); |
||
529 | break; |
||
2815 | svoboda | 530 | } |
531 | } |
||
532 | |||
2813 | svoboda | 533 | /** @} |
534 | */ |