Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 2848 → Rev 2849

/branches/tracing/kernel/generic/src/udebug/udebug_ipc.c
18,20 → 18,16
#include <udebug/udebug_ipc.h>
 
/**
* Get and lock a phone's callee task.
* Get a phone's callee task id.
*
* This will return a pointer to the task to which the phone
* is connected. It will lock the task, making sure it exists.
* This will return the id of the task to which the phone
* is connected.
*
* Interrupts must be already disabled.
*
* (TODO: make sure the udebug-cleanup of the task hasn't
* started yet)
*/
static task_t *get_lock_callee_task(phone_t *phone)
static task_id_t get_callee_task_id(phone_t *phone)
{
answerbox_t *box;
task_t *ta;
task_id_t taskid;
 
spinlock_lock(&phone->lock);
43,13 → 39,31
box = phone->callee;
spinlock_lock(&box->lock);
ta = box->task;
taskid = ta->taskid;
taskid = box->task->taskid;
spinlock_unlock(&box->lock);
spinlock_unlock(&phone->lock);
 
/* Locking decoupled using taskid */
return taskid;
}
 
/**
* Get and lock a phone's callee task.
*
* This will return a pointer to the task to which the phone
* is connected. It will lock the task, making sure it exists.
*
* Interrupts must be already disabled.
*
* (TODO: make sure the udebug-cleanup of the task hasn't
* started yet)
*/
static task_t *get_lock_callee_task(phone_t *phone)
{
task_t *ta;
task_id_t taskid;
 
taskid = get_callee_task_id(phone);
 
spinlock_lock(&tasks_lock);
ta = task_find_by_id(taskid);
if (ta == NULL) {
63,35 → 77,97
return ta;
}
 
/**
* Verify that thread t is valid for debugging ops.
/**
* Prepare a thread for a debugging operation.
*
* Verifies that t belongs to task ta and that debugging operations
* may be used on it.
* Simply put, return thread t with t->debug_lock held,
* but only if it verifies all conditions.
*
* Thread t's lock must already be held and interrupts must be disabled.
* Specifically, verifies that thread t exists, is a userspace thread,
* belongs to the callee of 'phone'. It also locks t->debug_lock,
* making sure that t->debug_active is true - that the thread is
* in a valid debugging session.
*
* Returns EOK if all went well, or an error code otherwise.
* Interrupts must be already disabled when calling this function.
*
* Note: This function sports complicated locking.
*/
static int verify_thread(thread_t *t, task_t *ta)
static int _thread_op_begin(phone_t *phone, thread_t *t)
{
/* Verify that 't' exists and belongs to task 'ta' */
if (!thread_exists(t) || (t->task != ta)) {
int rc;
task_id_t taskid;
int task_match;
DEADLOCK_PROBE_INIT(p_tasklock);
 
taskid = get_callee_task_id(phone);
 
/* Need to lock down the thread and than it's owner task */
grab_locks:
spinlock_lock(&threads_lock);
 
if (!thread_exists(t)) {
spinlock_unlock(&threads_lock);
return ENOENT;
}
 
spinlock_lock(&t->debug_lock);
spinlock_lock(&t->lock);
if (!spinlock_trylock(&t->task->lock)) {
spinlock_unlock(&t->lock);
spinlock_unlock(&t->debug_lock);
DEADLOCK_PROBE(p_tasklock, DEADLOCK_THRESHOLD);
goto grab_locks; /* avoid deadlock */
}
 
/* Now verify that it's the callee */
task_match = (t->task->taskid == taskid);
 
spinlock_unlock(&t->task->lock);
 
if (!task_match) {
/* No such thread belonging to callee */
rc = ENOENT;
goto error_exit;
}
 
/* Verify that 't' is a userspace thread */
if ((t->flags & THREAD_FLAG_USPACE) == 0) {
/* It's not, deny its existence */
return ENOENT;
rc = ENOENT;
goto error_exit;
}
 
if ((t->debug_active != true) || (t->debug_stop != true)) {
/* Not in debugging session or already has GO */
return EBUSY;
rc = ENOENT;
goto error_exit;
}
 
return EOK;
spinlock_unlock(&threads_lock);
spinlock_unlock(&t->lock);
 
/* Only t->debug_lock left */
 
return EOK; /* All went well */
 
 
/* Executed when a check on the thread fails */
error_exit:
spinlock_unlock(&t->lock);
spinlock_unlock(&t->debug_lock);
spinlock_unlock(&threads_lock);
 
/* No locks left here */
return rc; /* Some errors occured */
}
 
static void _thread_op_end(thread_t *t)
{
spinlock_unlock(&t->debug_lock);
}
 
static int udebug_rp_begin(call_t *call, phone_t *phone)
{
task_t *ta;
229,45 → 305,22
static int udebug_rp_go(call_t *call, phone_t *phone)
{
thread_t *t;
task_t *ta;
ipl_t ipl;
int rc;
 
klog_printf("debug_go()");
 
t = (thread_t *)IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
ta = get_lock_callee_task(phone);
spinlock_unlock(&ta->lock);
// TODO: don't lock ta
 
t = (thread_t *) IPC_GET_ARG2(call->data);
 
spinlock_lock(&threads_lock);
if (!thread_exists(t)) {
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return ENOENT;
}
 
spinlock_lock(&t->debug_lock);
 
/* Verify that thread t may be operated on */
rc = verify_thread(t, ta);
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
spinlock_unlock(&t->debug_lock);
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return rc;
}
 
/*
* Since t->debug_active == true and t->debug_lock is held,
* we can safely release threads_lock and t will continue
* to exist (and will stay in debug_active state)
*/
spinlock_unlock(&threads_lock);
 
t->debug_go_call = call;
t->debug_stop = false;
 
276,7 → 329,7
*/
waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
 
spinlock_unlock(&t->debug_lock);
_thread_op_end(t);
interrupts_restore(ipl);
 
return 0; /* no backsend */
285,7 → 338,6
static int udebug_rp_args_read(call_t *call, phone_t *phone)
{
thread_t *t;
task_t *ta;
void *uspace_buffer;
int rc;
ipl_t ipl;
293,44 → 345,23
 
klog_printf("debug_args_read()");
 
t = (thread_t *)IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
ta = get_lock_callee_task(phone);
klog_printf("task %llu", ta->taskid);
spinlock_unlock(&ta->lock);
 
t = (thread_t *) IPC_GET_ARG2(call->data);
 
spinlock_lock(&threads_lock);
 
if (!thread_exists(t)) {
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return ENOENT;
}
 
spinlock_lock(&t->debug_lock);
 
/* Verify that thread t may be operated on */
rc = verify_thread(t, ta);
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
spinlock_unlock(&t->debug_lock);
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return rc;
}
 
/*
* We can now safely release threads_lock as debug_active == true
* and t->debug_lock is held.
*/
spinlock_unlock(&threads_lock);
 
//FIXME: additionally we need to verify that we are inside a syscall
 
/* Copy to a local buffer before releasing the lock */
memcpy(buffer, t->syscall_args, 6 * sizeof(unative_t));
 
spinlock_unlock(&t->debug_lock);
_thread_op_end(t);
interrupts_restore(ipl);
 
/* Now copy to userspace */
339,7 → 370,6
 
rc = copy_to_uspace(uspace_buffer, buffer, 6 * sizeof(unative_t));
if (rc != 0) {
spinlock_unlock(&ta->lock);
klog_printf("debug_args_read() - copy failed");
return rc;
}
351,7 → 381,6
static int udebug_rp_regs_read(call_t *call, phone_t *phone)
{
thread_t *t;
task_t *ta;
void *uspace_buffer;
unative_t to_copy;
int rc;
361,41 → 390,20
 
klog_printf("debug_regs_read()");
 
ta = get_lock_callee_task(phone);
spinlock_unlock(&ta->lock);
//FIXME: don't lock ta
t = (thread_t *) IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
spinlock_lock(&threads_lock);
 
t = (thread_t *) IPC_GET_ARG2(call->data);
 
if (!thread_exists(t)) {
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return ENOENT;
}
 
spinlock_lock(&t->debug_lock);
 
/* Verify that thread t may be operated on */
rc = verify_thread(t, ta);
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
spinlock_unlock(&t->debug_lock);
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return rc;
}
 
/*
* We can now safely release threads_lock as debug_active == true
* and t->debug_lock is held.
*/
spinlock_unlock(&threads_lock);
 
state = t->uspace_state;
if (state == NULL) {
spinlock_unlock(&threads_lock);
_thread_op_end(t);
interrupts_restore(ipl);
klog_printf("debug_regs_read() - istate not available");
return EBUSY;
403,7 → 411,7
 
/* Copy to a local buffer so that we can release the lock */
memcpy(&state_copy, state, sizeof(state_copy));
spinlock_unlock(&t->debug_lock);
_thread_op_end(t);
interrupts_restore(ipl);
 
uspace_buffer = (void *)IPC_GET_ARG3(call->data);
423,10 → 431,11
return 1; /* actually need becksend with retval 0 */
}
 
 
 
static int udebug_rp_regs_write(call_t *call, phone_t *phone)
{
thread_t *t;
task_t *ta;
void *uspace_data;
unative_t to_copy;
int rc;
448,30 → 457,14
return rc;
}
 
ta = get_lock_callee_task(phone);
spinlock_unlock(&ta->lock);
//FIXME: don't lock ta
 
/* Now try to change the thread's uspace_state */
 
ipl = interrupts_disable();
spinlock_lock(&threads_lock);
 
t = (thread_t *) IPC_GET_ARG2(call->data);
 
if (!thread_exists(t)) {
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return ENOENT;
}
 
spinlock_lock(&t->debug_lock);
 
/* Verify that thread t may be operated on */
rc = verify_thread(t, ta);
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
spinlock_unlock(&t->debug_lock);
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
return rc;
}
478,7 → 471,7
 
state = t->uspace_state;
if (state == NULL) {
spinlock_unlock(&t->debug_lock);
_thread_op_end(t);
interrupts_restore(ipl);
klog_printf("debug_regs_write() - istate not available");
return EBUSY;
486,7 → 479,7
 
memcpy(t->uspace_state, &data_copy, sizeof(t->uspace_state));
 
spinlock_unlock(&t->debug_lock);
_thread_op_end(t);
interrupts_restore(ipl);
 
/* Set answer values */