Subversion Repositories HelenOS

Rev

Rev 3026 | Rev 3424 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2894 svoboda 1
/*
2
 * Copyright (c) 2008 Jiri Svoboda
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 *
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
14
 * - The name of the author may not be used to endorse or promote products
15
 *   derived from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
 
2887 svoboda 29
/** @addtogroup generic
30
 * @{
31
 */
32
 
33
/**
34
 * @file
2894 svoboda 35
 * @brief   Udebug operations.
2887 svoboda 36
 */
37
 
38
#include <console/klog.h>
39
#include <proc/task.h>
40
#include <proc/thread.h>
41
#include <arch.h>
42
#include <errno.h>
43
#include <syscall/copy.h>
44
#include <ipc/ipc.h>
45
#include <udebug/udebug.h>
46
#include <udebug/udebug_ops.h>
47
 
48
/**
49
 * Prepare a thread for a debugging operation.
50
 *
3018 svoboda 51
 * Simply put, return thread t with t->udebug.lock held,
2887 svoboda 52
 * but only if it verifies all conditions.
53
 *
54
 * Specifically, verifies that thread t exists, is a userspace thread,
2898 svoboda 55
 * and belongs to the current task (TASK). Verifies, that the thread
56
 * has (or hasn't) go according to having_go (typically false).
3026 svoboda 57
 * It also locks t->udebug.lock, making sure that t->udebug.debug_active
58
 * is true - that the thread is in a valid debugging session.
2887 svoboda 59
 *
3026 svoboda 60
 * With this verified and the t->udebug.lock mutex held, it is ensured
61
 * that the thread cannot leave the debugging session, let alone cease
62
 * to exist.
63
 *
64
 * In this function, holding the TASK->udebug.lock mutex prevents the
65
 * thread from leaving the debugging session, while relaxing from
66
 * the t->lock spinlock to the t->udebug.lock mutex.
67
 *
2887 svoboda 68
 * Returns EOK if all went well, or an error code otherwise.
69
 */
2898 svoboda 70
static int _thread_op_begin(thread_t *t, bool having_go)
2887 svoboda 71
{
72
    task_id_t taskid;
3026 svoboda 73
    ipl_t ipl;
2887 svoboda 74
 
75
    taskid = TASK->taskid;
76
 
3026 svoboda 77
    mutex_lock(&TASK->udebug.lock);
78
 
79
    /* thread_exists() must be called with threads_lock held */
80
    ipl = interrupts_disable();
2887 svoboda 81
    spinlock_lock(&threads_lock);
82
 
83
    if (!thread_exists(t)) {
84
        spinlock_unlock(&threads_lock);
3026 svoboda 85
        interrupts_restore(ipl);
86
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 87
        return ENOENT;
88
    }
89
 
3026 svoboda 90
    /* t->lock is enough to ensure the thread's existence */
2887 svoboda 91
    spinlock_lock(&t->lock);
3026 svoboda 92
    spinlock_unlock(&threads_lock);
2887 svoboda 93
 
94
    /* Verify that 't' is a userspace thread */
95
    if ((t->flags & THREAD_FLAG_USPACE) == 0) {
96
        /* It's not, deny its existence */
3026 svoboda 97
        spinlock_unlock(&t->lock);
98
        interrupts_restore(ipl);
99
        mutex_unlock(&TASK->udebug.lock);
100
        return ENOENT;
2887 svoboda 101
    }
102
 
3026 svoboda 103
    /* Verify debugging state */
104
    if (t->udebug.debug_active != true) {
2898 svoboda 105
        /* Not in debugging session or undesired GO state */
3026 svoboda 106
        spinlock_unlock(&t->lock);
107
        interrupts_restore(ipl);
108
        mutex_unlock(&TASK->udebug.lock);
109
        return ENOENT;
2887 svoboda 110
    }
111
 
3026 svoboda 112
    /*
113
     * Since the thread has debug_active == true, TASK->udebug.lock
114
     * is enough to ensure its existence and that debug_active remains
115
     * true.
116
     */
2887 svoboda 117
    spinlock_unlock(&t->lock);
3026 svoboda 118
    interrupts_restore(ipl);
2887 svoboda 119
 
3026 svoboda 120
    /* Only mutex TASK->udebug.lock left */
121
 
122
    /* Now verify that the thread belongs to the current task */
123
    if (t->task != TASK) {
124
        /* No such thread belonging this task*/
125
        mutex_unlock(&TASK->udebug.lock);
126
        return ENOENT;
127
    }
2887 svoboda 128
 
3026 svoboda 129
    /*
130
     * Now we need to grab the thread's debug lock for synchronization
131
     * of the threads stoppability/stop state.
132
     */
133
    mutex_lock(&t->udebug.lock);
2887 svoboda 134
 
3026 svoboda 135
    /* The big task mutex is no longer needed */
136
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 137
 
3026 svoboda 138
    if (!t->udebug.stop != having_go) {
139
        /* Not in debugging session or undesired GO state */
140
        mutex_unlock(&t->udebug.lock);
141
        return EINVAL;
142
    }
2887 svoboda 143
 
3026 svoboda 144
    /* Only t->udebug.lock left */
145
 
146
    return EOK; /* All went well */
2887 svoboda 147
}
148
 
149
 
150
static void _thread_op_end(thread_t *t)
151
{
3026 svoboda 152
    mutex_unlock(&t->udebug.lock);
2887 svoboda 153
}
154
 
155
/**
156
 * \return 0 (ok, but not done yet), 1 (done) or negative error code.
157
 */
158
int udebug_begin(call_t *call)
159
{
160
    int reply;
161
 
162
    thread_t *t;
163
    link_t *cur;
164
 
165
    klog_printf("udebug_begin()");
166
 
3016 svoboda 167
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 168
    klog_printf("debugging task %llu", TASK->taskid);
169
 
3014 svoboda 170
    if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
3016 svoboda 171
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 172
        klog_printf("udebug_begin(): busy error");
173
 
174
        return EBUSY;
175
    }
176
 
3014 svoboda 177
    TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
178
    TASK->udebug.begin_call = call;
179
    TASK->udebug.debugger = call->sender;
2887 svoboda 180
 
3014 svoboda 181
    if (TASK->udebug.not_stoppable_count == 0) {
182
        TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
183
        TASK->udebug.begin_call = NULL;
2887 svoboda 184
        reply = 1; /* immediate reply */
185
    } else {
186
        reply = 0; /* no reply */
187
    }
188
 
3018 svoboda 189
    /* Set udebug.debug_active on all of the task's userspace threads */
2887 svoboda 190
 
191
    for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
192
        t = list_get_instance(cur, thread_t, th_link);
193
 
3026 svoboda 194
        mutex_lock(&t->udebug.lock);
2887 svoboda 195
        if ((t->flags & THREAD_FLAG_USPACE) != 0)
3018 svoboda 196
            t->udebug.debug_active = true;
3026 svoboda 197
        mutex_unlock(&t->udebug.lock);
2887 svoboda 198
    }
199
 
3016 svoboda 200
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 201
 
202
    klog_printf("udebug_begin() done (%s)",
203
        reply ? "reply" : "stoppability wait");
204
 
205
    return reply;
206
}
207
 
208
int udebug_end(void)
209
{
210
    int rc;
211
 
212
    klog_printf("udebug_end()");
213
 
3016 svoboda 214
    mutex_lock(&TASK->udebug.lock);
215
    klog_printf("task %llu", TASK->taskid);
2887 svoboda 216
 
217
    rc = udebug_task_cleanup(TASK);
218
 
3016 svoboda 219
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 220
 
3016 svoboda 221
    return rc;
2887 svoboda 222
}
223
 
2899 svoboda 224
int udebug_set_evmask(udebug_evmask_t mask)
225
{
226
    klog_printf("udebug_set_mask()");
227
 
228
    klog_printf("debugging task %llu", TASK->taskid);
229
 
3016 svoboda 230
    mutex_lock(&TASK->udebug.lock);
2899 svoboda 231
 
3014 svoboda 232
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
3016 svoboda 233
        mutex_unlock(&TASK->udebug.lock);
2899 svoboda 234
        klog_printf("udebug_set_mask(): not active debuging session");
235
 
236
        return EINVAL;
237
    }
238
 
3014 svoboda 239
    TASK->udebug.evmask = mask;
2899 svoboda 240
 
3016 svoboda 241
    mutex_unlock(&TASK->udebug.lock);
2899 svoboda 242
 
243
    return 0;
244
}
245
 
246
 
2887 svoboda 247
int udebug_go(thread_t *t, call_t *call)
248
{
249
    int rc;
250
 
2913 svoboda 251
//  klog_printf("udebug_go()");
2887 svoboda 252
 
3018 svoboda 253
    /* On success, this will lock t->udebug.lock */
2898 svoboda 254
    rc = _thread_op_begin(t, false);
2887 svoboda 255
    if (rc != EOK) {
256
        return rc;
257
    }
258
 
3018 svoboda 259
    t->udebug.go_call = call;
260
    t->udebug.stop = false;
261
    t->udebug.cur_event = 0;    /* none */
2887 svoboda 262
 
263
    /*
264
     * Neither t's lock nor threads_lock may be held during wakeup
265
     */
3018 svoboda 266
    waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
2887 svoboda 267
 
268
    _thread_op_end(t);
269
 
270
    return 0;
271
}
272
 
2898 svoboda 273
int udebug_stop(thread_t *t, call_t *call)
274
{
275
    int rc;
2887 svoboda 276
 
2898 svoboda 277
    klog_printf("udebug_stop()");
3016 svoboda 278
    mutex_lock(&TASK->udebug.lock);
2898 svoboda 279
 
280
    /*
3018 svoboda 281
     * On success, this will lock t->udebug.lock. Note that this makes sure
2898 svoboda 282
     * the thread is not stopped.
283
     */
284
    rc = _thread_op_begin(t, true);
285
    if (rc != EOK) {
286
        return rc;
287
    }
288
 
289
    /* Take GO away from the thread */
3018 svoboda 290
    t->udebug.stop = true;
2898 svoboda 291
 
3018 svoboda 292
    if (!t->udebug.stoppable) {
2898 svoboda 293
        /* Answer will be sent when the thread becomes stoppable */
294
        _thread_op_end(t);
295
        return 0;
296
    }
297
 
298
    /*
299
     * Answer GO call
300
     */
301
    klog_printf("udebug_stop - answering go call");
302
 
303
    /* Make sure nobody takes this call away from us */
3018 svoboda 304
    call = t->udebug.go_call;
305
    t->udebug.go_call = NULL;
2898 svoboda 306
 
307
    IPC_SET_RETVAL(call->data, 0);
308
    IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
309
    klog_printf("udebug_stop/ipc_answer");
310
 
3018 svoboda 311
    THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
3016 svoboda 312
 
2898 svoboda 313
    _thread_op_end(t);
314
 
315
    ipc_answer(&TASK->answerbox, call);
3016 svoboda 316
    mutex_unlock(&TASK->udebug.lock);
2898 svoboda 317
 
318
    klog_printf("udebog_stop/done");
319
    return 0;
320
}
321
 
2897 svoboda 322
int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
2887 svoboda 323
{
324
    thread_t *t;
325
    link_t *cur;
326
    unative_t tid;
2897 svoboda 327
    unsigned copied_ids;
2887 svoboda 328
    ipl_t ipl;
329
    unative_t *id_buffer;
330
    int flags;
2897 svoboda 331
    size_t max_ids;
2887 svoboda 332
 
333
    klog_printf("udebug_thread_read()");
334
 
2897 svoboda 335
    /* Allocate a buffer to hold thread IDs */
336
    id_buffer = malloc(buf_size, 0);
337
 
3016 svoboda 338
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 339
 
340
    /* Verify task state */
3014 svoboda 341
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
3016 svoboda 342
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 343
        return EINVAL;
344
    }
345
 
3016 svoboda 346
    ipl = interrupts_disable();
347
    spinlock_lock(&TASK->lock);
2897 svoboda 348
    /* Copy down the thread IDs */
2887 svoboda 349
 
2897 svoboda 350
    max_ids = buf_size / sizeof(unative_t);
351
    copied_ids = 0;
352
 
3026 svoboda 353
    /* FIXME: make sure the thread isn't past debug shutdown... */
2887 svoboda 354
    for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
2897 svoboda 355
        /* Do not write past end of buffer */
356
        if (copied_ids >= max_ids) break;
2887 svoboda 357
 
358
        t = list_get_instance(cur, thread_t, th_link);
359
 
360
        spinlock_lock(&t->lock);
361
        flags = t->flags;
362
        spinlock_unlock(&t->lock);
363
 
364
        /* Not interested in kernel threads */
365
        if ((flags & THREAD_FLAG_USPACE) != 0) {
2897 svoboda 366
            /* Using thread struct pointer as identification hash */
2887 svoboda 367
            tid = (unative_t) t;
368
            id_buffer[copied_ids++] = tid;
369
        }
370
    }
371
 
372
    spinlock_unlock(&TASK->lock);
373
    interrupts_restore(ipl);
374
 
3016 svoboda 375
    mutex_unlock(&TASK->udebug.lock);
376
 
2887 svoboda 377
    *buffer = id_buffer;
378
    *n = copied_ids * sizeof(unative_t);
379
 
380
    return 0;
381
}
382
 
383
int udebug_args_read(thread_t *t, void **buffer)
384
{
385
    int rc;
386
    unative_t *arg_buffer;
387
 
3026 svoboda 388
//  klog_printf("udebug_args_read()");
2887 svoboda 389
 
2896 svoboda 390
    /* Prepare a buffer to hold the arguments */
391
    arg_buffer = malloc(6 * sizeof(unative_t), 0);
392
 
3018 svoboda 393
    /* On success, this will lock t->udebug.lock */
2898 svoboda 394
    rc = _thread_op_begin(t, false);
2887 svoboda 395
    if (rc != EOK) {
396
        return rc;
397
    }
398
 
399
    /* Additionally we need to verify that we are inside a syscall */
3018 svoboda 400
    if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
401
        t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
2887 svoboda 402
        _thread_op_end(t);
403
        return EINVAL;
404
    }
405
 
406
    /* Copy to a local buffer before releasing the lock */
3018 svoboda 407
    memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
2887 svoboda 408
 
409
    _thread_op_end(t);
410
 
411
    *buffer = arg_buffer;
412
    return 0;
413
}
414
 
2919 svoboda 415
int udebug_regs_read(thread_t *t, void *buffer)
2887 svoboda 416
{
417
    istate_t *state;
418
    int rc;
419
 
3026 svoboda 420
//  klog_printf("udebug_regs_read()");
2887 svoboda 421
 
3018 svoboda 422
    /* On success, this will lock t->udebug.lock */
2898 svoboda 423
    rc = _thread_op_begin(t, false);
2887 svoboda 424
    if (rc != EOK) {
425
        return rc;
426
    }
427
 
3018 svoboda 428
    state = t->udebug.uspace_state;
2887 svoboda 429
    if (state == NULL) {
430
        _thread_op_end(t);
431
        klog_printf("udebug_regs_read() - istate not available");
432
        return EBUSY;
433
    }
434
 
2896 svoboda 435
    /* Copy to the allocated buffer */
2919 svoboda 436
    memcpy(buffer, state, sizeof(istate_t));
2887 svoboda 437
 
438
    _thread_op_end(t);
439
 
440
    return 0;
441
}
442
 
443
int udebug_regs_write(thread_t *t, void *buffer)
444
{
445
    int rc;
446
    istate_t *state;
447
 
448
    klog_printf("udebug_regs_write()");
449
 
450
    /* Try to change the thread's uspace_state */
451
 
3018 svoboda 452
    /* On success, this will lock t->udebug.lock */
2898 svoboda 453
    rc = _thread_op_begin(t, false);
2887 svoboda 454
    if (rc != EOK) {
2919 svoboda 455
        klog_printf("error locking thread");
2887 svoboda 456
        return rc;
457
    }
458
 
3018 svoboda 459
    state = t->udebug.uspace_state;
2887 svoboda 460
    if (state == NULL) {
461
        _thread_op_end(t);
462
        klog_printf("udebug_regs_write() - istate not available");
463
        return EBUSY;
464
    }
465
 
3018 svoboda 466
    memcpy(t->udebug.uspace_state, buffer, sizeof(istate_t));
2887 svoboda 467
 
468
    _thread_op_end(t);
469
 
470
    return 0;
471
}
472
 
473
 
474
int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
475
{
476
    void *data_buffer;
477
    int rc;
478
 
3016 svoboda 479
    /* Verify task state */
480
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 481
 
3016 svoboda 482
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
483
        mutex_unlock(&TASK->udebug.lock);
484
        return EBUSY;
485
    }
486
 
2896 svoboda 487
    data_buffer = malloc(n, 0);
488
 
3016 svoboda 489
//  klog_printf("udebug_mem_read: src=%u, size=%u", uspace_addr, n);
2887 svoboda 490
 
491
    /* NOTE: this is not strictly from a syscall... but that shouldn't
492
     * be a problem */
493
    rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
3016 svoboda 494
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 495
 
3016 svoboda 496
    if (rc != 0) return rc;
497
 
2887 svoboda 498
    *buffer = data_buffer;
499
    return 0;
500
}
501
 
502
int udebug_mem_write(unative_t uspace_addr, void *data, size_t n)
503
{
504
    int rc;
505
 
506
    klog_printf("udebug_mem_write()");
507
 
3127 svoboda 508
    /* n must be positive */
509
    if (n < 1)
510
        return EINVAL;
511
 
2887 svoboda 512
    /* Verify task state */
3016 svoboda 513
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 514
 
3016 svoboda 515
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
516
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 517
        return EBUSY;
3016 svoboda 518
    }
2887 svoboda 519
 
520
    klog_printf("dst=%u, size=%u", uspace_addr, n);
521
 
522
    /* NOTE: this is not strictly from a syscall... but that shouldn't
523
     * be a problem */
3013 svoboda 524
//  rc = copy_to_uspace((void *)uspace_addr, data, n);
525
//  if (rc) return rc;
2887 svoboda 526
 
3013 svoboda 527
    rc = as_debug_write(uspace_addr, data, n);
3127 svoboda 528
 
529
    klog_printf("rc=%d\n", rc);
3013 svoboda 530
 
3016 svoboda 531
    mutex_unlock(&TASK->udebug.lock);
532
 
3013 svoboda 533
    return rc;
2887 svoboda 534
}
535
 
536
/** @}
537
 */