Rev 2842 | Rev 2849 | 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. |
||
| 2848 | svoboda | 25 | * |
| 26 | * Interrupts must be already disabled. |
||
| 27 | * |
||
| 2823 | svoboda | 28 | * (TODO: make sure the udebug-cleanup of the task hasn't |
| 29 | * started yet) |
||
| 30 | */ |
||
| 2813 | svoboda | 31 | static task_t *get_lock_callee_task(phone_t *phone) |
| 32 | { |
||
| 2823 | svoboda | 33 | answerbox_t *box; |
| 2813 | svoboda | 34 | task_t *ta; |
| 2823 | svoboda | 35 | task_id_t taskid; |
| 2813 | svoboda | 36 | |
| 2823 | svoboda | 37 | spinlock_lock(&phone->lock); |
| 38 | if (phone->state != IPC_PHONE_CONNECTED) { |
||
| 39 | spinlock_unlock(&phone->lock); |
||
| 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 | return NULL; |
||
| 58 | } |
||
| 59 | |||
| 2813 | svoboda | 60 | spinlock_lock(&ta->lock); |
| 2823 | svoboda | 61 | spinlock_unlock(&tasks_lock); |
| 2813 | svoboda | 62 | |
| 63 | return ta; |
||
| 64 | } |
||
| 65 | |||
| 2842 | svoboda | 66 | /** |
| 67 | * Verify that thread t is valid for debugging ops. |
||
| 68 | * |
||
| 69 | * Verifies that t belongs to task ta and that debugging operations |
||
| 70 | * may be used on it. |
||
| 71 | * |
||
| 72 | * Thread t's lock must already be held and interrupts must be disabled. |
||
| 73 | */ |
||
| 74 | static int verify_thread(thread_t *t, task_t *ta) |
||
| 2841 | svoboda | 75 | { |
| 76 | /* Verify that 't' exists and belongs to task 'ta' */ |
||
| 77 | if (!thread_exists(t) || (t->task != ta)) { |
||
| 78 | return ENOENT; |
||
| 79 | } |
||
| 80 | |||
| 2842 | svoboda | 81 | /* Verify that 't' is a userspace thread */ |
| 2841 | svoboda | 82 | if ((t->flags & THREAD_FLAG_USPACE) == 0) { |
| 83 | /* It's not, deny its existence */ |
||
| 84 | return ENOENT; |
||
| 85 | } |
||
| 86 | |||
| 87 | if ((t->debug_active != true) || (t->debug_stop != true)) { |
||
| 88 | /* Not in debugging session or already has GO */ |
||
| 89 | return EBUSY; |
||
| 90 | } |
||
| 91 | |||
| 92 | return EOK; |
||
| 93 | } |
||
| 94 | |||
| 2813 | svoboda | 95 | static int udebug_rp_begin(call_t *call, phone_t *phone) |
| 96 | { |
||
| 97 | task_t *ta; |
||
| 2823 | svoboda | 98 | ipl_t ipl; |
| 2827 | svoboda | 99 | int rc; |
| 2813 | svoboda | 100 | |
| 2827 | svoboda | 101 | thread_t *t; |
| 102 | link_t *cur; |
||
| 103 | |||
| 2813 | svoboda | 104 | klog_printf("debug_begin()"); |
| 105 | |||
| 2823 | svoboda | 106 | ipl = interrupts_disable(); |
| 2813 | svoboda | 107 | ta = get_lock_callee_task(phone); |
| 108 | klog_printf("debugging task %llu", ta->taskid); |
||
| 109 | |||
| 2825 | svoboda | 110 | if (ta->dt_state != UDEBUG_TS_INACTIVE) { |
| 2813 | svoboda | 111 | spinlock_unlock(&ta->lock); |
| 2823 | svoboda | 112 | interrupts_restore(ipl); |
| 2813 | svoboda | 113 | klog_printf("debug_begin(): busy error"); |
| 114 | return EBUSY; |
||
| 115 | } |
||
| 116 | |||
| 2825 | svoboda | 117 | ta->dt_state = UDEBUG_TS_BEGINNING; |
| 2813 | svoboda | 118 | ta->debug_begin_call = call; |
| 119 | |||
| 120 | if (ta->not_stoppable_count == 0) { |
||
| 2825 | svoboda | 121 | ta->dt_state = UDEBUG_TS_ACTIVE; |
| 2813 | svoboda | 122 | ta->debug_begin_call = NULL; |
| 2827 | svoboda | 123 | rc = 1; /* actually we need backsend with 0 retval */ |
| 124 | } else { |
||
| 125 | rc = 0; /* no backsend */ |
||
| 2813 | svoboda | 126 | } |
| 2827 | svoboda | 127 | |
| 128 | /* Set debug_active on all of the task's userspace threads */ |
||
| 2813 | svoboda | 129 | |
| 2827 | svoboda | 130 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
| 131 | t = list_get_instance(cur, thread_t, th_link); |
||
| 132 | |||
| 2848 | svoboda | 133 | spinlock_lock(&t->debug_lock); |
| 2827 | svoboda | 134 | if ((t->flags & THREAD_FLAG_USPACE) != 0) |
| 135 | t->debug_active = true; |
||
| 2848 | svoboda | 136 | spinlock_unlock(&t->debug_lock); |
| 2827 | svoboda | 137 | } |
| 138 | |||
| 2813 | svoboda | 139 | spinlock_unlock(&ta->lock); |
| 2823 | svoboda | 140 | interrupts_restore(ipl); |
| 2813 | svoboda | 141 | |
| 2827 | svoboda | 142 | klog_printf("debug_begin() done (%s)", |
| 143 | rc ? "backsend" : "stoppability wait"); |
||
| 144 | |||
| 145 | return rc; |
||
| 2813 | svoboda | 146 | } |
| 147 | |||
| 2834 | svoboda | 148 | static int udebug_rp_end(call_t *call, phone_t *phone) |
| 149 | { |
||
| 150 | task_t *ta; |
||
| 151 | ipl_t ipl; |
||
| 152 | |||
| 153 | thread_t *t; |
||
| 154 | link_t *cur; |
||
| 2848 | svoboda | 155 | int flags; |
| 2834 | svoboda | 156 | |
| 157 | klog_printf("udebug_rp_end()"); |
||
| 158 | |||
| 159 | ipl = interrupts_disable(); |
||
| 160 | ta = get_lock_callee_task(phone); |
||
| 161 | klog_printf("task %llu", ta->taskid); |
||
| 162 | |||
| 2835 | svoboda | 163 | if (ta->dt_state == UDEBUG_TS_BEGINNING && |
| 164 | ta->dt_state != UDEBUG_TS_ACTIVE) { |
||
| 2834 | svoboda | 165 | spinlock_unlock(&ta->lock); |
| 166 | interrupts_restore(ipl); |
||
| 2835 | svoboda | 167 | klog_printf("udebug_rp_begin(): task not being debugged"); |
| 2834 | svoboda | 168 | return EINVAL; |
| 169 | } |
||
| 170 | |||
| 2835 | svoboda | 171 | /* Finish debugging of all userspace threads */ |
| 2834 | svoboda | 172 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
| 173 | t = list_get_instance(cur, thread_t, th_link); |
||
| 174 | |||
| 175 | spinlock_lock(&t->lock); |
||
| 176 | |||
| 2848 | svoboda | 177 | flags = t->flags; |
| 178 | |||
| 179 | spinlock_lock(&t->debug_lock); |
||
| 180 | spinlock_unlock(&t->lock); |
||
| 181 | |||
| 2835 | svoboda | 182 | /* Only process userspace threads */ |
| 2848 | svoboda | 183 | if ((flags & THREAD_FLAG_USPACE) != 0) { |
| 2835 | svoboda | 184 | /* Prevent any further debug activity in thread */ |
| 185 | t->debug_active = false; |
||
| 2834 | svoboda | 186 | |
| 2835 | svoboda | 187 | /* Still has go? */ |
| 188 | if (t->debug_stop == false) { |
||
| 189 | /* |
||
| 190 | * Yes, so clear go. As debug_active == false, |
||
| 191 | * this doesn't affect anything. |
||
| 192 | */ |
||
| 193 | t->debug_stop = true; |
||
| 2834 | svoboda | 194 | |
| 2835 | svoboda | 195 | /* Answer GO call */ |
| 2838 | svoboda | 196 | klog_printf("answer GO call with EVENT_FINISHED"); |
| 197 | IPC_SET_RETVAL(t->debug_go_call->data, 0); |
||
| 198 | IPC_SET_ARG1(t->debug_go_call->data, UDEBUG_EVENT_FINISHED); |
||
| 2835 | svoboda | 199 | ipc_answer(&ta->answerbox, t->debug_go_call); |
| 200 | } else { |
||
| 201 | /* |
||
| 202 | * Debug_stop is already at initial value. |
||
| 203 | * Yet this means the thread needs waking up. |
||
| 204 | */ |
||
| 2848 | svoboda | 205 | |
| 206 | /* |
||
| 207 | * t's lock must not be held when calling |
||
| 208 | * waitq_wakeup. |
||
| 209 | */ |
||
| 2835 | svoboda | 210 | waitq_wakeup(&t->go_wq, WAKEUP_FIRST); |
| 211 | } |
||
| 212 | } |
||
| 2848 | svoboda | 213 | spinlock_unlock(&t->debug_lock); |
| 2834 | svoboda | 214 | } |
| 215 | |||
| 216 | ta->dt_state = UDEBUG_TS_INACTIVE; |
||
| 217 | |||
| 218 | spinlock_unlock(&ta->lock); |
||
| 219 | interrupts_restore(ipl); |
||
| 220 | |||
| 2838 | svoboda | 221 | IPC_SET_RETVAL(call->data, 0); |
| 222 | |||
| 2834 | svoboda | 223 | klog_printf("udebug_rp_end() done\n"); |
| 224 | |||
| 225 | return 1; |
||
| 226 | } |
||
| 227 | |||
| 228 | |||
| 2813 | svoboda | 229 | static int udebug_rp_go(call_t *call, phone_t *phone) |
| 230 | { |
||
| 231 | thread_t *t; |
||
| 232 | task_t *ta; |
||
| 2823 | svoboda | 233 | ipl_t ipl; |
| 2848 | svoboda | 234 | int rc; |
| 2813 | svoboda | 235 | |
| 236 | klog_printf("debug_go()"); |
||
| 2848 | svoboda | 237 | |
| 238 | ipl = interrupts_disable(); |
||
| 239 | |||
| 2813 | svoboda | 240 | ta = get_lock_callee_task(phone); |
| 2823 | svoboda | 241 | spinlock_unlock(&ta->lock); |
| 2826 | svoboda | 242 | // TODO: don't lock ta |
| 2823 | svoboda | 243 | |
| 2816 | svoboda | 244 | t = (thread_t *) IPC_GET_ARG2(call->data); |
| 2823 | svoboda | 245 | |
| 246 | spinlock_lock(&threads_lock); |
||
| 2848 | svoboda | 247 | if (!thread_exists(t)) { |
| 248 | spinlock_unlock(&threads_lock); |
||
| 249 | interrupts_restore(ipl); |
||
| 250 | return ENOENT; |
||
| 251 | } |
||
| 2825 | svoboda | 252 | |
| 2848 | svoboda | 253 | spinlock_lock(&t->debug_lock); |
| 2813 | svoboda | 254 | |
| 2848 | svoboda | 255 | /* Verify that thread t may be operated on */ |
| 256 | rc = verify_thread(t, ta); |
||
| 257 | if (rc != EOK) { |
||
| 258 | spinlock_unlock(&t->debug_lock); |
||
| 259 | spinlock_unlock(&threads_lock); |
||
| 260 | interrupts_restore(ipl); |
||
| 261 | return rc; |
||
| 262 | } |
||
| 263 | |||
| 264 | /* |
||
| 265 | * Since t->debug_active == true and t->debug_lock is held, |
||
| 266 | * we can safely release threads_lock and t will continue |
||
| 267 | * to exist (and will stay in debug_active state) |
||
| 268 | */ |
||
| 269 | spinlock_unlock(&threads_lock); |
||
| 270 | |||
| 2826 | svoboda | 271 | t->debug_go_call = call; |
| 2825 | svoboda | 272 | t->debug_stop = false; |
| 2848 | svoboda | 273 | |
| 274 | /* |
||
| 275 | * Neither t's lock nor threads_lock may be held during wakeup |
||
| 276 | */ |
||
| 2813 | svoboda | 277 | waitq_wakeup(&t->go_wq, WAKEUP_FIRST); |
| 2825 | svoboda | 278 | |
| 2848 | svoboda | 279 | spinlock_unlock(&t->debug_lock); |
| 2823 | svoboda | 280 | interrupts_restore(ipl); |
| 2813 | svoboda | 281 | |
| 282 | return 0; /* no backsend */ |
||
| 283 | } |
||
| 284 | |||
| 285 | static int udebug_rp_args_read(call_t *call, phone_t *phone) |
||
| 286 | { |
||
| 287 | thread_t *t; |
||
| 288 | task_t *ta; |
||
| 289 | void *uspace_buffer; |
||
| 290 | int rc; |
||
| 2823 | svoboda | 291 | ipl_t ipl; |
| 2827 | svoboda | 292 | unative_t buffer[6]; |
| 2813 | svoboda | 293 | |
| 294 | klog_printf("debug_args_read()"); |
||
| 295 | |||
| 2848 | svoboda | 296 | ipl = interrupts_disable(); |
| 2813 | svoboda | 297 | ta = get_lock_callee_task(phone); |
| 298 | klog_printf("task %llu", ta->taskid); |
||
| 2823 | svoboda | 299 | spinlock_unlock(&ta->lock); |
| 300 | |||
| 2816 | svoboda | 301 | t = (thread_t *) IPC_GET_ARG2(call->data); |
| 2823 | svoboda | 302 | |
| 303 | spinlock_lock(&threads_lock); |
||
| 304 | |||
| 2848 | svoboda | 305 | if (!thread_exists(t)) { |
| 306 | spinlock_unlock(&threads_lock); |
||
| 307 | interrupts_restore(ipl); |
||
| 308 | return ENOENT; |
||
| 309 | } |
||
| 310 | |||
| 311 | spinlock_lock(&t->debug_lock); |
||
| 312 | |||
| 313 | /* Verify that thread t may be operated on */ |
||
| 2842 | svoboda | 314 | rc = verify_thread(t, ta); |
| 2841 | svoboda | 315 | if (rc != EOK) { |
| 2848 | svoboda | 316 | spinlock_unlock(&t->debug_lock); |
| 2823 | svoboda | 317 | spinlock_unlock(&threads_lock); |
| 318 | interrupts_restore(ipl); |
||
| 2841 | svoboda | 319 | return rc; |
| 2813 | svoboda | 320 | } |
| 321 | |||
| 2848 | svoboda | 322 | /* |
| 323 | * We can now safely release threads_lock as debug_active == true |
||
| 324 | * and t->debug_lock is held. |
||
| 325 | */ |
||
| 326 | spinlock_unlock(&threads_lock); |
||
| 327 | |||
| 2827 | svoboda | 328 | //FIXME: additionally we need to verify that we are inside a syscall |
| 329 | |||
| 330 | /* Copy to a local buffer before releasing the lock */ |
||
| 331 | memcpy(buffer, t->syscall_args, 6 * sizeof(unative_t)); |
||
| 332 | |||
| 2848 | svoboda | 333 | spinlock_unlock(&t->debug_lock); |
| 2823 | svoboda | 334 | interrupts_restore(ipl); |
| 335 | |||
| 2827 | svoboda | 336 | /* Now copy to userspace */ |
| 337 | |||
| 2813 | svoboda | 338 | uspace_buffer = (void *)IPC_GET_ARG3(call->data); |
| 339 | |||
| 2833 | svoboda | 340 | rc = copy_to_uspace(uspace_buffer, buffer, 6 * sizeof(unative_t)); |
| 2813 | svoboda | 341 | if (rc != 0) { |
| 342 | spinlock_unlock(&ta->lock); |
||
| 343 | klog_printf("debug_args_read() - copy failed"); |
||
| 344 | return rc; |
||
| 345 | } |
||
| 346 | |||
| 347 | klog_printf("debug_args_read() done"); |
||
| 348 | return 1; /* actually need becksend with retval 0 */ |
||
| 349 | } |
||
| 350 | |||
| 2817 | svoboda | 351 | static int udebug_rp_regs_read(call_t *call, phone_t *phone) |
| 352 | { |
||
| 353 | thread_t *t; |
||
| 354 | task_t *ta; |
||
| 355 | void *uspace_buffer; |
||
| 356 | unative_t to_copy; |
||
| 357 | int rc; |
||
| 358 | istate_t *state; |
||
| 2824 | svoboda | 359 | istate_t state_copy; |
| 360 | ipl_t ipl; |
||
| 2817 | svoboda | 361 | |
| 362 | klog_printf("debug_regs_read()"); |
||
| 363 | |||
| 2824 | svoboda | 364 | ta = get_lock_callee_task(phone); |
| 365 | spinlock_unlock(&ta->lock); |
||
| 2827 | svoboda | 366 | //FIXME: don't lock ta |
| 2817 | svoboda | 367 | |
| 2824 | svoboda | 368 | ipl = interrupts_disable(); |
| 369 | spinlock_lock(&threads_lock); |
||
| 370 | |||
| 2817 | svoboda | 371 | t = (thread_t *) IPC_GET_ARG2(call->data); |
| 2827 | svoboda | 372 | |
| 2848 | svoboda | 373 | if (!thread_exists(t)) { |
| 374 | spinlock_unlock(&threads_lock); |
||
| 375 | interrupts_restore(ipl); |
||
| 376 | return ENOENT; |
||
| 377 | } |
||
| 378 | |||
| 379 | spinlock_lock(&t->debug_lock); |
||
| 380 | |||
| 381 | /* Verify that thread t may be operated on */ |
||
| 2842 | svoboda | 382 | rc = verify_thread(t, ta); |
| 2841 | svoboda | 383 | if (rc != EOK) { |
| 2848 | svoboda | 384 | spinlock_unlock(&t->debug_lock); |
| 2827 | svoboda | 385 | spinlock_unlock(&threads_lock); |
| 386 | interrupts_restore(ipl); |
||
| 2841 | svoboda | 387 | return rc; |
| 2817 | svoboda | 388 | } |
| 389 | |||
| 2848 | svoboda | 390 | /* |
| 391 | * We can now safely release threads_lock as debug_active == true |
||
| 392 | * and t->debug_lock is held. |
||
| 393 | */ |
||
| 394 | spinlock_unlock(&threads_lock); |
||
| 395 | |||
| 2824 | svoboda | 396 | state = t->uspace_state; |
| 397 | if (state == NULL) { |
||
| 398 | spinlock_unlock(&threads_lock); |
||
| 399 | interrupts_restore(ipl); |
||
| 400 | klog_printf("debug_regs_read() - istate not available"); |
||
| 401 | return EBUSY; |
||
| 402 | } |
||
| 403 | |||
| 404 | /* Copy to a local buffer so that we can release the lock */ |
||
| 405 | memcpy(&state_copy, state, sizeof(state_copy)); |
||
| 2848 | svoboda | 406 | spinlock_unlock(&t->debug_lock); |
| 2824 | svoboda | 407 | interrupts_restore(ipl); |
| 408 | |||
| 2817 | svoboda | 409 | uspace_buffer = (void *)IPC_GET_ARG3(call->data); |
| 410 | to_copy = IPC_GET_ARG4(call->data); |
||
| 411 | if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t); |
||
| 412 | |||
| 2824 | svoboda | 413 | rc = copy_to_uspace(uspace_buffer, &state_copy, to_copy); |
| 2817 | svoboda | 414 | if (rc != 0) { |
| 415 | klog_printf("debug_regs_read() - copy failed"); |
||
| 416 | return rc; |
||
| 417 | } |
||
| 418 | |||
| 419 | IPC_SET_ARG1(call->data, to_copy); |
||
| 420 | IPC_SET_ARG2(call->data, sizeof(istate_t)); |
||
| 421 | |||
| 422 | klog_printf("debug_regs_read() done"); |
||
| 423 | return 1; /* actually need becksend with retval 0 */ |
||
| 424 | } |
||
| 425 | |||
| 426 | static int udebug_rp_regs_write(call_t *call, phone_t *phone) |
||
| 427 | { |
||
| 428 | thread_t *t; |
||
| 429 | task_t *ta; |
||
| 430 | void *uspace_data; |
||
| 431 | unative_t to_copy; |
||
| 432 | int rc; |
||
| 433 | istate_t *state; |
||
| 2824 | svoboda | 434 | istate_t data_copy; |
| 435 | ipl_t ipl; |
||
| 2817 | svoboda | 436 | |
| 437 | klog_printf("debug_regs_write()"); |
||
| 438 | |||
| 2824 | svoboda | 439 | /* First copy to a local buffer */ |
| 2817 | svoboda | 440 | |
| 441 | uspace_data = (void *)IPC_GET_ARG3(call->data); |
||
| 442 | to_copy = IPC_GET_ARG4(call->data); |
||
| 443 | if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t); |
||
| 444 | |||
| 2824 | svoboda | 445 | rc = copy_from_uspace(&data_copy, uspace_data, to_copy); |
| 2817 | svoboda | 446 | if (rc != 0) { |
| 447 | klog_printf("debug_regs_write() - copy failed"); |
||
| 448 | return rc; |
||
| 449 | } |
||
| 450 | |||
| 2824 | svoboda | 451 | ta = get_lock_callee_task(phone); |
| 2817 | svoboda | 452 | spinlock_unlock(&ta->lock); |
| 2827 | svoboda | 453 | //FIXME: don't lock ta |
| 2817 | svoboda | 454 | |
| 2824 | svoboda | 455 | /* Now try to change the thread's uspace_state */ |
| 456 | |||
| 457 | ipl = interrupts_disable(); |
||
| 458 | spinlock_lock(&threads_lock); |
||
| 459 | |||
| 460 | t = (thread_t *) IPC_GET_ARG2(call->data); |
||
| 2827 | svoboda | 461 | |
| 2848 | svoboda | 462 | if (!thread_exists(t)) { |
| 463 | spinlock_unlock(&threads_lock); |
||
| 464 | interrupts_restore(ipl); |
||
| 465 | return ENOENT; |
||
| 466 | } |
||
| 467 | |||
| 468 | spinlock_lock(&t->debug_lock); |
||
| 469 | |||
| 470 | /* Verify that thread t may be operated on */ |
||
| 2842 | svoboda | 471 | rc = verify_thread(t, ta); |
| 2841 | svoboda | 472 | if (rc != EOK) { |
| 2848 | svoboda | 473 | spinlock_unlock(&t->debug_lock); |
| 2824 | svoboda | 474 | spinlock_unlock(&threads_lock); |
| 475 | interrupts_restore(ipl); |
||
| 2841 | svoboda | 476 | return rc; |
| 2824 | svoboda | 477 | } |
| 478 | |||
| 479 | state = t->uspace_state; |
||
| 480 | if (state == NULL) { |
||
| 2848 | svoboda | 481 | spinlock_unlock(&t->debug_lock); |
| 2824 | svoboda | 482 | interrupts_restore(ipl); |
| 483 | klog_printf("debug_regs_write() - istate not available"); |
||
| 484 | return EBUSY; |
||
| 485 | } |
||
| 486 | |||
| 487 | memcpy(t->uspace_state, &data_copy, sizeof(t->uspace_state)); |
||
| 488 | |||
| 2848 | svoboda | 489 | spinlock_unlock(&t->debug_lock); |
| 2824 | svoboda | 490 | interrupts_restore(ipl); |
| 491 | |||
| 492 | /* Set answer values */ |
||
| 493 | |||
| 2817 | svoboda | 494 | IPC_SET_ARG1(call->data, to_copy); |
| 495 | IPC_SET_ARG2(call->data, sizeof(istate_t)); |
||
| 496 | |||
| 497 | klog_printf("debug_regs_write() done"); |
||
| 498 | return 1; /* actually need becksend with retval 0 */ |
||
| 499 | } |
||
| 500 | |||
| 2813 | svoboda | 501 | static int udebug_rp_thread_read(call_t *call, phone_t *phone) |
| 502 | { |
||
| 503 | thread_t *t; |
||
| 504 | link_t *cur; |
||
| 505 | task_t *ta; |
||
| 506 | unative_t *uspace_buffer; |
||
| 507 | unative_t to_copy; |
||
| 508 | int rc; |
||
| 2824 | svoboda | 509 | unsigned total_bytes; |
| 2813 | svoboda | 510 | unsigned buf_size; |
| 511 | unative_t tid; |
||
| 2824 | svoboda | 512 | unsigned num_threads, copied_ids; |
| 513 | ipl_t ipl; |
||
| 514 | unative_t *buffer; |
||
| 515 | int flags; |
||
| 2813 | svoboda | 516 | |
| 517 | klog_printf("debug_thread_read()"); |
||
| 518 | |||
| 2824 | svoboda | 519 | ipl = interrupts_disable(); |
| 2813 | svoboda | 520 | ta = get_lock_callee_task(phone); |
| 521 | |||
| 2827 | svoboda | 522 | /* Verify task state */ |
| 523 | if (ta->dt_state != UDEBUG_TS_ACTIVE) { |
||
| 524 | spinlock_unlock(&ta->lock); |
||
| 525 | interrupts_restore(ipl); |
||
| 526 | return EBUSY; |
||
| 527 | } |
||
| 528 | |||
| 2824 | svoboda | 529 | /* Count the threads first */ |
| 530 | |||
| 531 | num_threads = 0; |
||
| 2813 | svoboda | 532 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
| 2824 | svoboda | 533 | /* Count all threads, to be on the safe side */ |
| 534 | ++num_threads; |
||
| 535 | } |
||
| 536 | |||
| 537 | /* Allocate a buffer and copy down the threads' ids */ |
||
| 538 | buffer = malloc(num_threads * sizeof(unative_t), 0); // ??? |
||
| 539 | |||
| 540 | copied_ids = 0; |
||
| 541 | for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
||
| 2813 | svoboda | 542 | t = list_get_instance(cur, thread_t, th_link); |
| 543 | |||
| 2824 | svoboda | 544 | spinlock_lock(&t->lock); |
| 545 | flags = t->flags; |
||
| 546 | spinlock_unlock(&t->lock); |
||
| 547 | |||
| 2813 | svoboda | 548 | /* Not interested in kernel threads */ |
| 2824 | svoboda | 549 | if ((flags & THREAD_FLAG_USPACE) != 0) { |
| 550 | /* Using thread struct pointer for identification */ |
||
| 551 | tid = (unative_t) t; |
||
| 552 | buffer[copied_ids++] = tid; |
||
| 553 | } |
||
| 554 | } |
||
| 2813 | svoboda | 555 | |
| 2824 | svoboda | 556 | spinlock_unlock(&ta->lock); |
| 557 | interrupts_restore(ipl); |
||
| 2813 | svoboda | 558 | |
| 2824 | svoboda | 559 | /* Now copy to userspace */ |
| 2813 | svoboda | 560 | |
| 2824 | svoboda | 561 | uspace_buffer = (void *)IPC_GET_ARG2(call->data); |
| 562 | buf_size = IPC_GET_ARG3(call->data); |
||
| 2813 | svoboda | 563 | |
| 2824 | svoboda | 564 | total_bytes = copied_ids * sizeof(unative_t); |
| 565 | |||
| 566 | if (buf_size > total_bytes) |
||
| 567 | to_copy = total_bytes; |
||
| 568 | else |
||
| 569 | to_copy = buf_size; |
||
| 570 | |||
| 571 | rc = copy_to_uspace(uspace_buffer, buffer, to_copy); |
||
| 572 | free(buffer); |
||
| 573 | |||
| 574 | if (rc != 0) { |
||
| 575 | klog_printf("debug_thread_read() - copy failed"); |
||
| 576 | return rc; |
||
| 2813 | svoboda | 577 | } |
| 578 | |||
| 2824 | svoboda | 579 | IPC_SET_ARG1(call->data, to_copy); |
| 580 | IPC_SET_ARG2(call->data, total_bytes); |
||
| 2813 | svoboda | 581 | |
| 582 | klog_printf("debug_thread_read() done"); |
||
| 583 | return 1; /* actually need becksend with retval 0 */ |
||
| 584 | } |
||
| 585 | |||
| 2818 | svoboda | 586 | static int udebug_rp_mem_write(call_t *call, phone_t *phone) |
| 587 | { |
||
| 588 | void *uspace_data; |
||
| 589 | unative_t to_copy; |
||
| 590 | int rc; |
||
| 591 | void *buffer; |
||
| 592 | |||
| 593 | klog_printf("udebug_rp_mem_write()"); |
||
| 594 | |||
| 595 | uspace_data = (void *)IPC_GET_ARG2(call->data); |
||
| 596 | to_copy = IPC_GET_ARG4(call->data); |
||
| 597 | |||
| 598 | buffer = malloc(to_copy, 0); // ??? |
||
| 599 | |||
| 600 | rc = copy_from_uspace(buffer, uspace_data, to_copy); |
||
| 601 | if (rc != 0) { |
||
| 602 | klog_printf(" - copy failed"); |
||
| 603 | return rc; |
||
| 604 | } |
||
| 605 | |||
| 606 | call->buffer = buffer; |
||
| 607 | |||
| 608 | klog_printf(" - done"); |
||
| 609 | return 1; /* actually need becksend with retval 0 */ |
||
| 610 | } |
||
| 611 | |||
| 612 | |||
| 2813 | svoboda | 613 | int udebug_request_preprocess(call_t *call, phone_t *phone) |
| 614 | { |
||
| 615 | int rc; |
||
| 616 | |||
| 617 | switch (IPC_GET_ARG1(call->data)) { |
||
| 618 | case UDEBUG_M_BEGIN: |
||
| 619 | rc = udebug_rp_begin(call, phone); |
||
| 620 | return rc; |
||
| 2834 | svoboda | 621 | case UDEBUG_M_END: |
| 622 | rc = udebug_rp_end(call, phone); |
||
| 623 | return rc; |
||
| 2813 | svoboda | 624 | case UDEBUG_M_GO: |
| 625 | rc = udebug_rp_go(call, phone); |
||
| 626 | return rc; |
||
| 627 | case UDEBUG_M_ARGS_READ: |
||
| 628 | rc = udebug_rp_args_read(call, phone); |
||
| 629 | return rc; |
||
| 2817 | svoboda | 630 | case UDEBUG_M_REGS_READ: |
| 631 | rc = udebug_rp_regs_read(call, phone); |
||
| 632 | return rc; |
||
| 633 | case UDEBUG_M_REGS_WRITE: |
||
| 634 | rc = udebug_rp_regs_write(call, phone); |
||
| 635 | return rc; |
||
| 2813 | svoboda | 636 | case UDEBUG_M_THREAD_READ: |
| 637 | rc = udebug_rp_thread_read(call, phone); |
||
| 2819 | svoboda | 638 | return rc; |
| 2818 | svoboda | 639 | case UDEBUG_M_MEM_WRITE: |
| 640 | rc = udebug_rp_mem_write(call, phone); |
||
| 2813 | svoboda | 641 | return rc; |
| 642 | default: |
||
| 643 | break; |
||
| 644 | } |
||
| 645 | |||
| 646 | return 0; |
||
| 647 | } |
||
| 648 | |||
| 2815 | svoboda | 649 | static void udebug_receive_mem_read(call_t *call) |
| 650 | { |
||
| 651 | unative_t uspace_dst; |
||
| 652 | void *uspace_ptr; |
||
| 653 | unsigned size; |
||
| 654 | void *buffer; |
||
| 655 | int rc; |
||
| 2813 | svoboda | 656 | |
| 2815 | svoboda | 657 | klog_printf("debug_mem_read()"); |
| 658 | uspace_dst = IPC_GET_ARG2(call->data); |
||
| 659 | uspace_ptr = (void *)IPC_GET_ARG3(call->data); |
||
| 660 | size = IPC_GET_ARG4(call->data); |
||
| 661 | |||
| 662 | buffer = malloc(size, 0); // ??? |
||
| 663 | klog_printf("debug_mem_read: src=%u, size=%u", uspace_ptr, size); |
||
| 664 | |||
| 665 | /* NOTE: this is not strictly from a syscall... but that shouldn't |
||
| 666 | * be a problem */ |
||
| 667 | rc = copy_from_uspace(buffer, uspace_ptr, size); |
||
| 668 | if (rc) { |
||
| 669 | IPC_SET_RETVAL(call->data, rc); |
||
| 670 | return; |
||
| 671 | } |
||
| 672 | |||
| 673 | klog_printf("first word: %u", *((unative_t *)buffer)); |
||
| 674 | |||
| 675 | IPC_SET_RETVAL(call->data, 0); |
||
| 676 | /* Hack: ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that |
||
| 677 | same code in process_answer() can be used |
||
| 678 | (no way to distinguish method in answer) */ |
||
| 679 | IPC_SET_ARG1(call->data, uspace_dst); |
||
| 680 | IPC_SET_ARG2(call->data, size); |
||
| 681 | call->buffer = buffer; |
||
| 682 | |||
| 683 | ipc_answer(&TASK->kernel_box, call); |
||
| 684 | } |
||
| 685 | |||
| 2818 | svoboda | 686 | static void udebug_receive_mem_write(call_t *call) |
| 687 | { |
||
| 688 | void *uspace_dst; |
||
| 689 | unsigned size; |
||
| 690 | void *buffer; |
||
| 691 | int rc; |
||
| 2827 | svoboda | 692 | udebug_task_state_t dts; |
| 2818 | svoboda | 693 | |
| 694 | klog_printf("udebug_receive_mem_write()"); |
||
| 2827 | svoboda | 695 | |
| 696 | /* Verify task state */ |
||
| 697 | spinlock_lock(&TASK->lock); |
||
| 698 | dts = TASK->dt_state; |
||
| 699 | spinlock_unlock(&TASK->lock); |
||
| 700 | |||
| 701 | if (dts != UDEBUG_TS_ACTIVE) { |
||
| 702 | IPC_SET_RETVAL(call->data, EBUSY); |
||
| 703 | ipc_answer(&TASK->kernel_box, call); |
||
| 704 | return; |
||
| 705 | } |
||
| 706 | |||
| 2818 | svoboda | 707 | uspace_dst = (void *)IPC_GET_ARG3(call->data); |
| 708 | size = IPC_GET_ARG4(call->data); |
||
| 709 | |||
| 710 | buffer = call->buffer; |
||
| 711 | klog_printf("dst=%u, size=%u", uspace_dst, size); |
||
| 712 | |||
| 713 | /* NOTE: this is not strictly from a syscall... but that shouldn't |
||
| 714 | * be a problem */ |
||
| 715 | rc = copy_to_uspace(uspace_dst, buffer, size); |
||
| 716 | if (rc) { |
||
| 717 | IPC_SET_RETVAL(call->data, rc); |
||
| 2827 | svoboda | 718 | ipc_answer(&TASK->kernel_box, call); |
| 2818 | svoboda | 719 | return; |
| 720 | } |
||
| 721 | |||
| 722 | IPC_SET_RETVAL(call->data, 0); |
||
| 723 | |||
| 724 | free(call->buffer); |
||
| 725 | call->buffer = NULL; |
||
| 726 | |||
| 727 | ipc_answer(&TASK->kernel_box, call); |
||
| 728 | } |
||
| 729 | |||
| 730 | |||
| 2815 | svoboda | 731 | /** |
| 732 | * Handle a debug call received on the kernel answerbox. |
||
| 733 | * |
||
| 734 | * This is called by the kbox servicing thread. |
||
| 735 | */ |
||
| 736 | void udebug_call_receive(call_t *call) |
||
| 737 | { |
||
| 738 | int debug_method; |
||
| 739 | |||
| 740 | debug_method = IPC_GET_ARG1(call->data); |
||
| 741 | |||
| 742 | switch (debug_method) { |
||
| 743 | case UDEBUG_M_MEM_READ: |
||
| 744 | udebug_receive_mem_read(call); |
||
| 745 | break; |
||
| 2818 | svoboda | 746 | case UDEBUG_M_MEM_WRITE: |
| 747 | udebug_receive_mem_write(call); |
||
| 748 | break; |
||
| 2815 | svoboda | 749 | } |
| 750 | } |
||
| 751 | |||
| 2813 | svoboda | 752 | /** @} |
| 753 | */ |