Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 2885 → Rev 2886

/branches/tracing/kernel/generic/src/udebug/udebug_ipc.c
18,35 → 18,6
#include <udebug/udebug_ipc.h>
 
/**
* Get a phone's callee task id.
*
* This will return the id of the task to which the phone
* is connected.
*
* Interrupts must be already disabled.
*/
static task_id_t get_callee_task_id(phone_t *phone)
{
answerbox_t *box;
task_id_t taskid;
 
spinlock_lock(&phone->lock);
if (phone->state != IPC_PHONE_CONNECTED) {
spinlock_unlock(&phone->lock);
return NULL;
}
 
box = phone->callee;
spinlock_lock(&box->lock);
taskid = box->task->taskid;
spinlock_unlock(&box->lock);
spinlock_unlock(&phone->lock);
 
return taskid;
}
 
/**
* Prepare a thread for a debugging operation.
*
* Simply put, return thread t with t->debug_lock held,
53,7 → 24,7
* but only if it verifies all conditions.
*
* Specifically, verifies that thread t exists, is a userspace thread,
* belongs to the callee of 'phone'. It also locks t->debug_lock,
* and belongs to the current task (TASK). It also locks t->debug_lock,
* making sure that t->debug_active is true - that the thread is
* in a valid debugging session.
*
62,17 → 33,14
*
* Note: This function sports complicated locking.
*/
static int _thread_op_begin(phone_t *phone, thread_t *t)
static int _thread_op_begin(thread_t *t)
{
int rc;
task_id_t taskid;
int task_match;
DEADLOCK_PROBE_INIT(p_tasklock);
 
taskid = get_callee_task_id(phone);
taskid = TASK->taskid;
 
/* Need to lock down the thread and than it's owner task */
grab_locks:
/* Must lock threads_lock to ensure continued existence of the thread */
spinlock_lock(&threads_lock);
 
if (!thread_exists(t)) {
83,19 → 51,8
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) {
/* Now verify that it's the current task */
if (t->task != TASK) {
/* No such thread belonging to callee */
rc = ENOENT;
goto error_exit;
132,204 → 89,37
return rc; /* Some errors occured */
}
 
 
static void _thread_op_end(thread_t *t)
{
spinlock_unlock(&t->debug_lock);
}
 
static int udebug_rp_go(call_t *call, phone_t *phone)
{
thread_t *t;
ipl_t ipl;
int rc;
 
klog_printf("debug_go()");
 
t = (thread_t *)IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
interrupts_restore(ipl);
return rc;
}
 
t->debug_go_call = call;
t->debug_stop = false;
t->cur_event = 0; /* none */
 
/*
* Neither t's lock nor threads_lock may be held during wakeup
*/
waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
 
_thread_op_end(t);
interrupts_restore(ipl);
 
return 0; /* no backsend */
}
 
static int udebug_rp_args_read(call_t *call, phone_t *phone)
{
thread_t *t;
void *uspace_buffer;
int rc;
ipl_t ipl;
unative_t buffer[6];
 
klog_printf("debug_args_read()");
 
t = (thread_t *)IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
interrupts_restore(ipl);
return rc;
}
 
/* Additionally we need to verify that we are inside a syscall */
if (t->cur_event != UDEBUG_EVENT_SYSCALL) {
_thread_op_end(t);
interrupts_restore(ipl);
return EINVAL;
}
 
/* Copy to a local buffer before releasing the lock */
memcpy(buffer, t->syscall_args, 6 * sizeof(unative_t));
 
_thread_op_end(t);
interrupts_restore(ipl);
 
/* Now copy to userspace */
 
uspace_buffer = (void *)IPC_GET_ARG3(call->data);
 
rc = copy_to_uspace(uspace_buffer, buffer, 6 * sizeof(unative_t));
if (rc != 0) {
klog_printf("debug_args_read() - copy failed");
return rc;
}
 
klog_printf("debug_args_read() done");
return 1; /* actually need becksend with retval 0 */
}
 
static int udebug_rp_regs_read(call_t *call, phone_t *phone)
{
thread_t *t;
void *uspace_buffer;
unative_t to_copy;
int rc;
istate_t *state;
istate_t state_copy;
ipl_t ipl;
 
klog_printf("debug_regs_read()");
 
t = (thread_t *) IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
interrupts_restore(ipl);
return rc;
}
 
state = t->uspace_state;
if (state == NULL) {
_thread_op_end(t);
interrupts_restore(ipl);
klog_printf("debug_regs_read() - istate not available");
return EBUSY;
}
 
/* Copy to a local buffer so that we can release the lock */
memcpy(&state_copy, state, sizeof(state_copy));
_thread_op_end(t);
interrupts_restore(ipl);
 
uspace_buffer = (void *)IPC_GET_ARG3(call->data);
to_copy = IPC_GET_ARG4(call->data);
if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t);
 
rc = copy_to_uspace(uspace_buffer, &state_copy, to_copy);
if (rc != 0) {
klog_printf("debug_regs_read() - copy failed");
return rc;
}
 
IPC_SET_ARG1(call->data, to_copy);
IPC_SET_ARG2(call->data, sizeof(istate_t));
 
klog_printf("debug_regs_read() done");
return 1; /* actually need becksend with retval 0 */
}
 
 
 
static int udebug_rp_regs_write(call_t *call, phone_t *phone)
{
thread_t *t;
void *uspace_data;
unative_t to_copy;
int rc;
istate_t *state;
istate_t data_copy;
ipl_t ipl;
void *buffer;
 
klog_printf("debug_regs_write()");
 
/* First copy to a local buffer */
 
uspace_data = (void *)IPC_GET_ARG3(call->data);
to_copy = IPC_GET_ARG4(call->data);
if (to_copy > sizeof(istate_t)) to_copy = sizeof(istate_t);
 
rc = copy_from_uspace(&data_copy, uspace_data, to_copy);
buffer = malloc(to_copy, 0); // ???
 
rc = copy_from_uspace(buffer, uspace_data, to_copy);
if (rc != 0) {
klog_printf("debug_regs_write() - copy failed");
return rc;
}
 
/* Now try to change the thread's uspace_state */
call->buffer = buffer;
 
ipl = interrupts_disable();
t = (thread_t *) IPC_GET_ARG2(call->data);
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(phone, t);
if (rc != EOK) {
interrupts_restore(ipl);
return rc;
}
 
state = t->uspace_state;
if (state == NULL) {
_thread_op_end(t);
interrupts_restore(ipl);
klog_printf("debug_regs_write() - istate not available");
return EBUSY;
}
 
memcpy(t->uspace_state, &data_copy, sizeof(t->uspace_state));
 
_thread_op_end(t);
interrupts_restore(ipl);
 
/* Set answer values */
 
IPC_SET_ARG1(call->data, to_copy);
IPC_SET_ARG2(call->data, sizeof(istate_t));
 
klog_printf("debug_regs_write() done");
return 1; /* actually need becksend with retval 0 */
klog_printf(" - done");
return 0; /* No backsend */
}
 
static int udebug_rp_mem_write(call_t *call, phone_t *phone)
364,15 → 154,6
int rc;
 
switch (IPC_GET_ARG1(call->data)) {
case UDEBUG_M_GO:
rc = udebug_rp_go(call, phone);
return rc;
case UDEBUG_M_ARGS_READ:
rc = udebug_rp_args_read(call, phone);
return rc;
case UDEBUG_M_REGS_READ:
rc = udebug_rp_regs_read(call, phone);
return rc;
case UDEBUG_M_REGS_WRITE:
rc = udebug_rp_regs_write(call, phone);
return rc;
469,6 → 250,44
ipc_answer(&TASK->kernel_box, call);
}
 
static void udebug_receive_go(call_t *call)
{
thread_t *t;
ipl_t ipl;
int rc;
 
klog_printf("debug_go()");
 
t = (thread_t *)IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(t);
if (rc != EOK) {
interrupts_restore(ipl);
 
IPC_SET_RETVAL(call->data, rc);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
t->debug_go_call = call;
t->debug_stop = false;
t->cur_event = 0; /* none */
 
/*
* Neither t's lock nor threads_lock may be held during wakeup
*/
waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
 
_thread_op_end(t);
interrupts_restore(ipl);
 
/* No reply */
}
 
 
static void udebug_receive_thread_read(call_t *call)
{
thread_t *t;
555,7 → 374,193
ipc_answer(&TASK->kernel_box, call);
}
 
static void udebug_receive_args_read(call_t *call)
{
thread_t *t;
unative_t uspace_addr;
int rc;
ipl_t ipl;
unative_t *buffer;
 
klog_printf("debug_args_read()");
 
t = (thread_t *)IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(t);
if (rc != EOK) {
interrupts_restore(ipl);
IPC_SET_RETVAL(call->data, rc);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
/* Additionally we need to verify that we are inside a syscall */
if (t->cur_event != UDEBUG_EVENT_SYSCALL) {
_thread_op_end(t);
interrupts_restore(ipl);
 
IPC_SET_RETVAL(call->data, EINVAL);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
/* Copy to a local buffer before releasing the lock */
buffer = malloc(6 * sizeof(unative_t), 0); // ???
memcpy(buffer, t->syscall_args, 6 * sizeof(unative_t));
 
_thread_op_end(t);
interrupts_restore(ipl);
 
/*
* Make use of call->buffer to transfer data to caller's userspace
*/
 
uspace_addr = IPC_GET_ARG3(call->data);
 
IPC_SET_RETVAL(call->data, 0);
/* ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
same code in process_answer() can be used
(no way to distinguish method in answer) */
IPC_SET_ARG1(call->data, uspace_addr);
IPC_SET_ARG2(call->data, 6 * sizeof(unative_t));
call->buffer = (void *)buffer;
 
ipc_answer(&TASK->kernel_box, call);
}
 
static void udebug_receive_regs_read(call_t *call)
{
thread_t *t;
unative_t uspace_addr;
unative_t to_copy;
unative_t buf_size;
unative_t total_bytes;
istate_t *state;
void *buffer;
int rc;
ipl_t ipl;
 
klog_printf("debug_regs_read()");
 
t = (thread_t *) IPC_GET_ARG2(call->data);
 
ipl = interrupts_disable();
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(t);
if (rc != EOK) {
interrupts_restore(ipl);
 
IPC_SET_RETVAL(call->data, rc);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
state = t->uspace_state;
if (state == NULL) {
_thread_op_end(t);
interrupts_restore(ipl);
klog_printf("debug_regs_read() - istate not available");
 
IPC_SET_RETVAL(call->data, EBUSY);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
/* Copy to an allocated buffer */
buffer = malloc(sizeof(istate_t), 0); // ???
memcpy(buffer, state, sizeof(istate_t));
 
_thread_op_end(t);
interrupts_restore(ipl);
 
/*
* Make use of call->buffer to transfer data to caller's userspace
*/
 
uspace_addr = IPC_GET_ARG3(call->data);
buf_size = IPC_GET_ARG4(call->data);
 
total_bytes = sizeof(istate_t);
 
if (buf_size > total_bytes)
to_copy = total_bytes;
else
to_copy = buf_size;
 
IPC_SET_RETVAL(call->data, 0);
/* ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
same code in process_answer() can be used
(no way to distinguish method in answer) */
IPC_SET_ARG1(call->data, uspace_addr);
IPC_SET_ARG2(call->data, to_copy);
 
IPC_SET_ARG3(call->data, total_bytes);
call->buffer = (void *)buffer;
 
ipc_answer(&TASK->kernel_box, call);
}
 
static void udebug_receive_regs_write(call_t *call)
{
thread_t *t;
void *uspace_data;
unative_t to_copy;
int rc;
istate_t *state;
ipl_t ipl;
 
klog_printf("debug_regs_write()");
 
uspace_data = (void *)IPC_GET_ARG3(call->data);
to_copy = IPC_GET_ARG4(call->data);
 
/* Try to change the thread's uspace_state */
 
ipl = interrupts_disable();
t = (thread_t *) IPC_GET_ARG2(call->data);
 
/* On success, this will lock t->debug_lock */
rc = _thread_op_begin(t);
if (rc != EOK) {
interrupts_restore(ipl);
 
IPC_SET_RETVAL(call->data, rc);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
state = t->uspace_state;
if (state == NULL) {
_thread_op_end(t);
interrupts_restore(ipl);
klog_printf("debug_regs_write() - istate not available");
 
IPC_SET_RETVAL(call->data, EBUSY);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
memcpy(t->uspace_state, call->buffer, sizeof(t->uspace_state));
 
_thread_op_end(t);
interrupts_restore(ipl);
 
/* Set answer values */
 
IPC_SET_ARG1(call->data, to_copy);
IPC_SET_ARG2(call->data, sizeof(istate_t));
 
IPC_SET_RETVAL(call->data, 0);
ipc_answer(&TASK->kernel_box, call);
 
klog_printf("debug_regs_write() done");
}
 
 
static void udebug_receive_mem_read(call_t *call)
{
unative_t uspace_dst;
577,6 → 582,7
rc = copy_from_uspace(buffer, uspace_ptr, size);
if (rc) {
IPC_SET_RETVAL(call->data, rc);
ipc_answer(&TASK->kernel_box, call);
return;
}
 
656,9 → 662,21
case UDEBUG_M_END:
udebug_receive_end(call);
break;
case UDEBUG_M_GO:
udebug_receive_go(call);
break;
case UDEBUG_M_THREAD_READ:
udebug_receive_thread_read(call);
break;
case UDEBUG_M_ARGS_READ:
udebug_receive_args_read(call);
break;
case UDEBUG_M_REGS_READ:
udebug_receive_regs_read(call);
break;
case UDEBUG_M_REGS_WRITE:
udebug_receive_regs_write(call);
break;
case UDEBUG_M_MEM_READ:
udebug_receive_mem_read(call);
break;