Subversion Repositories HelenOS

Rev

Rev 4393 | 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.
3471 svoboda 36
 *
37
 * Udebug operations on tasks and threads are implemented here. The
38
 * functions defined here are called from the udebug_ipc module
39
 * when servicing udebug IPC messages.
2887 svoboda 40
 */
41
 
3471 svoboda 42
#include <debug.h>
2887 svoboda 43
#include <proc/task.h>
44
#include <proc/thread.h>
45
#include <arch.h>
46
#include <errno.h>
4377 svoboda 47
#include <print.h>
2887 svoboda 48
#include <syscall/copy.h>
49
#include <ipc/ipc.h>
50
#include <udebug/udebug.h>
51
#include <udebug/udebug_ops.h>
52
 
53
/**
54
 * Prepare a thread for a debugging operation.
55
 *
3018 svoboda 56
 * Simply put, return thread t with t->udebug.lock held,
2887 svoboda 57
 * but only if it verifies all conditions.
58
 *
59
 * Specifically, verifies that thread t exists, is a userspace thread,
2898 svoboda 60
 * and belongs to the current task (TASK). Verifies, that the thread
3606 svoboda 61
 * is (or is not) go according to being_go (typically false).
3684 svoboda 62
 * It also locks t->udebug.lock, making sure that t->udebug.active
3026 svoboda 63
 * is true - that the thread is in a valid debugging session.
2887 svoboda 64
 *
3026 svoboda 65
 * With this verified and the t->udebug.lock mutex held, it is ensured
66
 * that the thread cannot leave the debugging session, let alone cease
67
 * to exist.
68
 *
69
 * In this function, holding the TASK->udebug.lock mutex prevents the
70
 * thread from leaving the debugging session, while relaxing from
71
 * the t->lock spinlock to the t->udebug.lock mutex.
72
 *
3471 svoboda 73
 * @param t     Pointer, need not at all be valid.
3606 svoboda 74
 * @param being_go  Required thread state.
3471 svoboda 75
 *
2887 svoboda 76
 * Returns EOK if all went well, or an error code otherwise.
77
 */
3606 svoboda 78
static int _thread_op_begin(thread_t *t, bool being_go)
2887 svoboda 79
{
80
    task_id_t taskid;
3026 svoboda 81
    ipl_t ipl;
2887 svoboda 82
 
83
    taskid = TASK->taskid;
84
 
3026 svoboda 85
    mutex_lock(&TASK->udebug.lock);
86
 
87
    /* thread_exists() must be called with threads_lock held */
88
    ipl = interrupts_disable();
2887 svoboda 89
    spinlock_lock(&threads_lock);
90
 
91
    if (!thread_exists(t)) {
92
        spinlock_unlock(&threads_lock);
3026 svoboda 93
        interrupts_restore(ipl);
94
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 95
        return ENOENT;
96
    }
97
 
3026 svoboda 98
    /* t->lock is enough to ensure the thread's existence */
2887 svoboda 99
    spinlock_lock(&t->lock);
3026 svoboda 100
    spinlock_unlock(&threads_lock);
2887 svoboda 101
 
3606 svoboda 102
    /* Verify that 't' is a userspace thread. */
2887 svoboda 103
    if ((t->flags & THREAD_FLAG_USPACE) == 0) {
104
        /* It's not, deny its existence */
3026 svoboda 105
        spinlock_unlock(&t->lock);
106
        interrupts_restore(ipl);
107
        mutex_unlock(&TASK->udebug.lock);
108
        return ENOENT;
2887 svoboda 109
    }
110
 
3606 svoboda 111
    /* Verify debugging state. */
3684 svoboda 112
    if (t->udebug.active != true) {
2898 svoboda 113
        /* Not in debugging session or undesired GO state */
3026 svoboda 114
        spinlock_unlock(&t->lock);
115
        interrupts_restore(ipl);
116
        mutex_unlock(&TASK->udebug.lock);
117
        return ENOENT;
2887 svoboda 118
    }
119
 
3026 svoboda 120
    /*
3684 svoboda 121
     * Since the thread has active == true, TASK->udebug.lock
122
     * is enough to ensure its existence and that active remains
3026 svoboda 123
     * true.
124
     */
2887 svoboda 125
    spinlock_unlock(&t->lock);
3026 svoboda 126
    interrupts_restore(ipl);
2887 svoboda 127
 
3606 svoboda 128
    /* Only mutex TASK->udebug.lock left. */
3026 svoboda 129
 
3606 svoboda 130
    /* Now verify that the thread belongs to the current task. */
3026 svoboda 131
    if (t->task != TASK) {
132
        /* No such thread belonging this task*/
133
        mutex_unlock(&TASK->udebug.lock);
134
        return ENOENT;
135
    }
2887 svoboda 136
 
3026 svoboda 137
    /*
138
     * Now we need to grab the thread's debug lock for synchronization
139
     * of the threads stoppability/stop state.
140
     */
141
    mutex_lock(&t->udebug.lock);
2887 svoboda 142
 
3606 svoboda 143
    /* The big task mutex is no longer needed. */
3026 svoboda 144
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 145
 
3606 svoboda 146
    if (t->udebug.go != being_go) {
147
        /* Not in debugging session or undesired GO state. */
3026 svoboda 148
        mutex_unlock(&t->udebug.lock);
149
        return EINVAL;
150
    }
2887 svoboda 151
 
3606 svoboda 152
    /* Only t->udebug.lock left. */
3026 svoboda 153
 
3606 svoboda 154
    return EOK; /* All went well. */
2887 svoboda 155
}
156
 
3471 svoboda 157
/** End debugging operation on a thread. */
2887 svoboda 158
static void _thread_op_end(thread_t *t)
159
{
3026 svoboda 160
    mutex_unlock(&t->udebug.lock);
2887 svoboda 161
}
162
 
3471 svoboda 163
/** Begin debugging the current task.
164
 *
165
 * Initiates a debugging session for the current task (and its threads).
166
 * When the debugging session has started a reply will be sent to the
167
 * UDEBUG_BEGIN call. This may happen immediately in this function if
168
 * all the threads in this task are stoppable at the moment and in this
169
 * case the function returns 1.
170
 *
171
 * Otherwise the function returns 0 and the reply will be sent as soon as
172
 * all the threads become stoppable (i.e. they can be considered stopped).
173
 *
174
 * @param call  The BEGIN call we are servicing.
175
 * @return  0 (OK, but not done yet), 1 (done) or negative error code.
2887 svoboda 176
 */
177
int udebug_begin(call_t *call)
178
{
179
    int reply;
180
 
181
    thread_t *t;
182
    link_t *cur;
183
 
4692 svoboda 184
    LOG("Debugging task %llu", TASK->taskid);
3016 svoboda 185
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 186
 
3014 svoboda 187
    if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
3016 svoboda 188
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 189
        return EBUSY;
190
    }
191
 
3014 svoboda 192
    TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
193
    TASK->udebug.begin_call = call;
194
    TASK->udebug.debugger = call->sender;
2887 svoboda 195
 
3014 svoboda 196
    if (TASK->udebug.not_stoppable_count == 0) {
197
        TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
198
        TASK->udebug.begin_call = NULL;
2887 svoboda 199
        reply = 1; /* immediate reply */
200
    } else {
201
        reply = 0; /* no reply */
202
    }
203
 
3684 svoboda 204
    /* Set udebug.active on all of the task's userspace threads. */
2887 svoboda 205
 
206
    for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
207
        t = list_get_instance(cur, thread_t, th_link);
208
 
3026 svoboda 209
        mutex_lock(&t->udebug.lock);
2887 svoboda 210
        if ((t->flags & THREAD_FLAG_USPACE) != 0)
3684 svoboda 211
            t->udebug.active = true;
3026 svoboda 212
        mutex_unlock(&t->udebug.lock);
2887 svoboda 213
    }
214
 
3016 svoboda 215
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 216
    return reply;
217
}
218
 
3471 svoboda 219
/** Finish debugging the current task.
220
 *
221
 * Closes the debugging session for the current task.
222
 * @return Zero on success or negative error code.
223
 */
2887 svoboda 224
int udebug_end(void)
225
{
226
    int rc;
227
 
4692 svoboda 228
    LOG("Task %" PRIu64, TASK->taskid);
2887 svoboda 229
 
3016 svoboda 230
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 231
    rc = udebug_task_cleanup(TASK);
3016 svoboda 232
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 233
 
3016 svoboda 234
    return rc;
2887 svoboda 235
}
236
 
3471 svoboda 237
/** Set the event mask.
238
 *
239
 * Sets the event mask that determines which events are enabled.
240
 *
241
 * @param mask  Or combination of events that should be enabled.
242
 * @return  Zero on success or negative error code.
243
 */
2899 svoboda 244
int udebug_set_evmask(udebug_evmask_t mask)
245
{
4692 svoboda 246
    LOG("mask = 0x%x", mask);
2899 svoboda 247
 
3016 svoboda 248
    mutex_lock(&TASK->udebug.lock);
2899 svoboda 249
 
3014 svoboda 250
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
3016 svoboda 251
        mutex_unlock(&TASK->udebug.lock);
2899 svoboda 252
        return EINVAL;
253
    }
254
 
3014 svoboda 255
    TASK->udebug.evmask = mask;
3016 svoboda 256
    mutex_unlock(&TASK->udebug.lock);
2899 svoboda 257
 
258
    return 0;
259
}
260
 
3471 svoboda 261
/** Give thread GO.
262
 *
3606 svoboda 263
 * Upon recieving a go message, the thread is given GO. Being GO
3471 svoboda 264
 * means the thread is allowed to execute userspace code (until
265
 * a debugging event or STOP occurs, at which point the thread loses GO.
266
 *
267
 * @param t The thread to operate on (unlocked and need not be valid).
268
 * @param call  The GO call that we are servicing.
269
 */
2887 svoboda 270
int udebug_go(thread_t *t, call_t *call)
271
{
272
    int rc;
273
 
3606 svoboda 274
    /* On success, this will lock t->udebug.lock. */
2898 svoboda 275
    rc = _thread_op_begin(t, false);
2887 svoboda 276
    if (rc != EOK) {
277
        return rc;
278
    }
279
 
3018 svoboda 280
    t->udebug.go_call = call;
3606 svoboda 281
    t->udebug.go = true;
3018 svoboda 282
    t->udebug.cur_event = 0;    /* none */
2887 svoboda 283
 
284
    /*
3606 svoboda 285
     * Neither t's lock nor threads_lock may be held during wakeup.
2887 svoboda 286
     */
3018 svoboda 287
    waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
2887 svoboda 288
 
289
    _thread_op_end(t);
290
 
291
    return 0;
292
}
293
 
3471 svoboda 294
/** Stop a thread (i.e. take its GO away)
295
 *
296
 * Generates a STOP event as soon as the thread becomes stoppable (i.e.
297
 * can be considered stopped).
298
 *
299
 * @param t The thread to operate on (unlocked and need not be valid).
300
 * @param call  The GO call that we are servicing.
301
 */
2898 svoboda 302
int udebug_stop(thread_t *t, call_t *call)
303
{
304
    int rc;
2887 svoboda 305
 
4692 svoboda 306
    LOG("udebug_stop()");
2898 svoboda 307
 
308
    /*
3018 svoboda 309
     * On success, this will lock t->udebug.lock. Note that this makes sure
2898 svoboda 310
     * the thread is not stopped.
311
     */
312
    rc = _thread_op_begin(t, true);
313
    if (rc != EOK) {
314
        return rc;
315
    }
316
 
3606 svoboda 317
    /* Take GO away from the thread. */
318
    t->udebug.go = false;
2898 svoboda 319
 
3606 svoboda 320
    if (t->udebug.stoppable != true) {
321
        /* Answer will be sent when the thread becomes stoppable. */
2898 svoboda 322
        _thread_op_end(t);
323
        return 0;
324
    }
325
 
326
    /*
3606 svoboda 327
     * Answer GO call.
2898 svoboda 328
     */
329
 
3606 svoboda 330
    /* Make sure nobody takes this call away from us. */
3018 svoboda 331
    call = t->udebug.go_call;
332
    t->udebug.go_call = NULL;
2898 svoboda 333
 
334
    IPC_SET_RETVAL(call->data, 0);
335
    IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
336
 
3018 svoboda 337
    THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
3016 svoboda 338
 
2898 svoboda 339
    _thread_op_end(t);
340
 
3606 svoboda 341
    mutex_lock(&TASK->udebug.lock);
2898 svoboda 342
    ipc_answer(&TASK->answerbox, call);
3016 svoboda 343
    mutex_unlock(&TASK->udebug.lock);
2898 svoboda 344
 
345
    return 0;
346
}
347
 
3471 svoboda 348
/** Read the list of userspace threads in the current task.
349
 *
350
 * The list takes the form of a sequence of thread hashes (i.e. the pointers
351
 * to thread structures). A buffer of size @a buf_size is allocated and
352
 * a pointer to it written to @a buffer. The sequence of hashes is written
353
 * into this buffer.
354
 *
355
 * If the sequence is longer than @a buf_size bytes, only as much hashes
356
 * as can fit are copied. The number of thread hashes copied is stored
357
 * in @a n.
358
 *
359
 * The rationale for having @a buf_size is that this function is only
360
 * used for servicing the THREAD_READ message, which always specifies
361
 * a maximum size for the userspace buffer.
362
 *
363
 * @param buffer    The buffer for storing thread hashes.
364
 * @param buf_size  Buffer size in bytes.
365
 * @param n     The actual number of hashes copied will be stored here.
366
 */
2897 svoboda 367
int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
2887 svoboda 368
{
369
    thread_t *t;
370
    link_t *cur;
371
    unative_t tid;
2897 svoboda 372
    unsigned copied_ids;
2887 svoboda 373
    ipl_t ipl;
374
    unative_t *id_buffer;
375
    int flags;
2897 svoboda 376
    size_t max_ids;
2887 svoboda 377
 
4692 svoboda 378
    LOG("udebug_thread_read()");
2887 svoboda 379
 
2897 svoboda 380
    /* Allocate a buffer to hold thread IDs */
381
    id_buffer = malloc(buf_size, 0);
382
 
3016 svoboda 383
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 384
 
385
    /* Verify task state */
3014 svoboda 386
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
3016 svoboda 387
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 388
        return EINVAL;
389
    }
390
 
3016 svoboda 391
    ipl = interrupts_disable();
392
    spinlock_lock(&TASK->lock);
2897 svoboda 393
    /* Copy down the thread IDs */
2887 svoboda 394
 
2897 svoboda 395
    max_ids = buf_size / sizeof(unative_t);
396
    copied_ids = 0;
397
 
3026 svoboda 398
    /* FIXME: make sure the thread isn't past debug shutdown... */
2887 svoboda 399
    for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
2897 svoboda 400
        /* Do not write past end of buffer */
401
        if (copied_ids >= max_ids) break;
2887 svoboda 402
 
403
        t = list_get_instance(cur, thread_t, th_link);
404
 
405
        spinlock_lock(&t->lock);
406
        flags = t->flags;
407
        spinlock_unlock(&t->lock);
408
 
3606 svoboda 409
        /* Not interested in kernel threads. */
2887 svoboda 410
        if ((flags & THREAD_FLAG_USPACE) != 0) {
2897 svoboda 411
            /* Using thread struct pointer as identification hash */
2887 svoboda 412
            tid = (unative_t) t;
413
            id_buffer[copied_ids++] = tid;
414
        }
415
    }
416
 
417
    spinlock_unlock(&TASK->lock);
418
    interrupts_restore(ipl);
419
 
3016 svoboda 420
    mutex_unlock(&TASK->udebug.lock);
421
 
2887 svoboda 422
    *buffer = id_buffer;
423
    *n = copied_ids * sizeof(unative_t);
424
 
425
    return 0;
426
}
427
 
3471 svoboda 428
/** Read the arguments of a system call.
429
 *
430
 * The arguments of the system call being being executed are copied
431
 * to an allocated buffer and a pointer to it is written to @a buffer.
432
 * The size of the buffer is exactly such that it can hold the maximum number
433
 * of system-call arguments.
434
 *
435
 * Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event,
436
 * this function will fail with an EINVAL error code.
437
 *
438
 * @param buffer    The buffer for storing thread hashes.
439
 */
2887 svoboda 440
int udebug_args_read(thread_t *t, void **buffer)
441
{
442
    int rc;
443
    unative_t *arg_buffer;
444
 
3606 svoboda 445
    /* Prepare a buffer to hold the arguments. */
2896 svoboda 446
    arg_buffer = malloc(6 * sizeof(unative_t), 0);
447
 
3606 svoboda 448
    /* On success, this will lock t->udebug.lock. */
2898 svoboda 449
    rc = _thread_op_begin(t, false);
2887 svoboda 450
    if (rc != EOK) {
451
        return rc;
452
    }
453
 
3606 svoboda 454
    /* Additionally we need to verify that we are inside a syscall. */
3018 svoboda 455
    if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
456
        t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
2887 svoboda 457
        _thread_op_end(t);
458
        return EINVAL;
459
    }
460
 
3606 svoboda 461
    /* Copy to a local buffer before releasing the lock. */
3018 svoboda 462
    memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
2887 svoboda 463
 
464
    _thread_op_end(t);
465
 
466
    *buffer = arg_buffer;
467
    return 0;
468
}
469
 
2919 svoboda 470
int udebug_regs_read(thread_t *t, void *buffer)
2887 svoboda 471
{
472
    istate_t *state;
473
    int rc;
474
 
4393 svoboda 475
    LOG("udebug_regs_read()");
2887 svoboda 476
 
3018 svoboda 477
    /* On success, this will lock t->udebug.lock */
2898 svoboda 478
    rc = _thread_op_begin(t, false);
2887 svoboda 479
    if (rc != EOK) {
480
        return rc;
481
    }
482
 
3018 svoboda 483
    state = t->udebug.uspace_state;
2887 svoboda 484
    if (state == NULL) {
485
        _thread_op_end(t);
3611 svoboda 486
        LOG("udebug_regs_read() - istate not available\n");
2887 svoboda 487
        return EBUSY;
488
    }
489
 
2896 svoboda 490
    /* Copy to the allocated buffer */
2919 svoboda 491
    memcpy(buffer, state, sizeof(istate_t));
2887 svoboda 492
 
493
    _thread_op_end(t);
494
 
4393 svoboda 495
    LOG("pc = 0x%" PRIp, istate_get_pc((istate_t *) buffer));
496
 
2887 svoboda 497
    return 0;
498
}
499
 
500
int udebug_regs_write(thread_t *t, void *buffer)
501
{
502
    int rc;
503
    istate_t *state;
504
 
3611 svoboda 505
    LOG("udebug_regs_write()\n");
2887 svoboda 506
 
507
    /* Try to change the thread's uspace_state */
508
 
3018 svoboda 509
    /* On success, this will lock t->udebug.lock */
2898 svoboda 510
    rc = _thread_op_begin(t, false);
2887 svoboda 511
    if (rc != EOK) {
3611 svoboda 512
        LOG("error locking thread\n");
2887 svoboda 513
        return rc;
514
    }
515
 
3018 svoboda 516
    state = t->udebug.uspace_state;
2887 svoboda 517
    if (state == NULL) {
518
        _thread_op_end(t);
3611 svoboda 519
        LOG("udebug_regs_write() - istate not available\n");
2887 svoboda 520
        return EBUSY;
521
    }
522
 
3018 svoboda 523
    memcpy(t->udebug.uspace_state, buffer, sizeof(istate_t));
2887 svoboda 524
 
525
    _thread_op_end(t);
526
 
527
    return 0;
528
}
529
 
3471 svoboda 530
/** Read the memory of the debugged task.
531
 *
532
 * Reads @a n bytes from the address space of the debugged task, starting
533
 * from @a uspace_addr. The bytes are copied into an allocated buffer
534
 * and a pointer to it is written into @a buffer.
535
 *
536
 * @param uspace_addr   Address from where to start reading.
537
 * @param n     Number of bytes to read.
538
 * @param buffer    For storing a pointer to the allocated buffer.
539
 */
2887 svoboda 540
int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
541
{
542
    void *data_buffer;
543
    int rc;
544
 
3016 svoboda 545
    /* Verify task state */
546
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 547
 
3016 svoboda 548
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
549
        mutex_unlock(&TASK->udebug.lock);
550
        return EBUSY;
551
    }
552
 
2896 svoboda 553
    data_buffer = malloc(n, 0);
554
 
2887 svoboda 555
    /* NOTE: this is not strictly from a syscall... but that shouldn't
556
     * be a problem */
557
    rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
3016 svoboda 558
    mutex_unlock(&TASK->udebug.lock);
2887 svoboda 559
 
3016 svoboda 560
    if (rc != 0) return rc;
561
 
2887 svoboda 562
    *buffer = data_buffer;
563
    return 0;
564
}
565
 
566
int udebug_mem_write(unative_t uspace_addr, void *data, size_t n)
567
{
568
    int rc;
569
 
3611 svoboda 570
    LOG("udebug_mem_write()\n");
2887 svoboda 571
 
3127 svoboda 572
    /* n must be positive */
573
    if (n < 1)
574
        return EINVAL;
575
 
2887 svoboda 576
    /* Verify task state */
3016 svoboda 577
    mutex_lock(&TASK->udebug.lock);
2887 svoboda 578
 
3016 svoboda 579
    if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
580
        mutex_unlock(&TASK->udebug.lock);
2887 svoboda 581
        return EBUSY;
3016 svoboda 582
    }
2887 svoboda 583
 
3611 svoboda 584
    LOG("dst=%u, size=%u\n", uspace_addr, n);
2887 svoboda 585
 
586
    /* NOTE: this is not strictly from a syscall... but that shouldn't
587
     * be a problem */
3013 svoboda 588
//  rc = copy_to_uspace((void *)uspace_addr, data, n);
589
//  if (rc) return rc;
2887 svoboda 590
 
3013 svoboda 591
    rc = as_debug_write(uspace_addr, data, n);
3127 svoboda 592
 
3611 svoboda 593
    LOG("rc=%d\n", rc);
3013 svoboda 594
 
3016 svoboda 595
    mutex_unlock(&TASK->udebug.lock);
596
 
3013 svoboda 597
    return rc;
2887 svoboda 598
}
599
 
600
/** @}
601
 */