/** @addtogroup generic
* @{
*/
/**
* @file
* @brief Tdebug.
*/
#include <console/klog.h>
#include <proc/task.h>
#include <proc/thread.h>
#include <arch.h>
#include <errno.h>
#include <ipc/ipc.h>
#include <syscall/copy.h>
#include <udebug/udebug.h>
#include <udebug/udebug_ipc.h>
static task_t *get_lock_callee_task(phone_t *phone)
{
answerbox_t *answerbox;
task_t *ta;
// FIXME: locking!!!
answerbox = phone->callee;
ta = answerbox->task;
spinlock_lock(&ta->lock);
return ta;
}
static thread_t *get_task_thread_by_id(task_t *ta, thread_id_t tid)
{
thread_t *t;
link_t *cur;
for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
t = list_get_instance(cur, thread_t, th_link);
if (tid == t->tid) return t;
}
return NULL;
}
static int udebug_rp_begin(call_t *call, phone_t *phone)
{
task_t *ta;
klog_printf("debug_begin()");
ta = get_lock_callee_task(phone);
klog_printf("debugging task %llu", ta->taskid);
if (ta->being_debugged != false) {
spinlock_unlock(&ta->lock);
klog_printf("debug_begin(): busy error");
return EBUSY;
}
ta->being_debugged = true;
ta->stop_request = true;
ta->debug_begin_call = call;
if (ta->not_stoppable_count == 0) {
ta->debug_begin_call = NULL;
ta->stop_request = false;
spinlock_unlock(&ta->lock);
klog_printf("debug_begin(): immediate backsend");
return 1; /* actually we need backsend with 0 retval */
}
spinlock_unlock(&ta->lock);
klog_printf("debug_begin() done (wait for stoppability)");
return 0;
}
static int udebug_rp_go(call_t *call, phone_t *phone)
{
thread_t *t;
task_t *ta;
klog_printf("debug_go()");
ta = get_lock_callee_task(phone);
ta->debug_go_call = call;
t = get_task_thread_by_id(ta, IPC_GET_ARG2(call->data));
if (t == NULL) {
spinlock_unlock(&ta->lock);
return ENOENT;
}
klog_printf("debug_go(): waitq_wakeup");
waitq_wakeup(&t->go_wq, WAKEUP_FIRST);
spinlock_unlock(&ta->lock);
return 0; /* no backsend */
}
static int udebug_rp_args_read(call_t *call, phone_t *phone)
{
thread_t *t;
task_t *ta;
void *uspace_buffer;
unative_t to_copy;
int rc;
klog_printf("debug_args_read()");
// FIXME: verify task/thread state
ta = get_lock_callee_task(phone);
klog_printf("task %llu", ta->taskid);
t = get_task_thread_by_id(ta, IPC_GET_ARG2(call->data));
if (t == NULL) {
spinlock_unlock(&ta->lock);
return ENOENT;
}
uspace_buffer = (void *)IPC_GET_ARG3(call->data);
to_copy = IPC_GET_ARG4(call->data);
if (to_copy > 6 * sizeof(unative_t)) to_copy = 6 * sizeof(unative_t);
rc = copy_to_uspace(uspace_buffer, t->syscall_args, to_copy);
if (rc != 0) {
spinlock_unlock(&ta->lock);
klog_printf("debug_args_read() - copy failed");
return rc;
}
spinlock_unlock(&ta->lock);
IPC_SET_ARG1(call->data, to_copy);
klog_printf("debug_args_read() done");
return 1; /* actually need becksend with retval 0 */
}
static int udebug_rp_thread_read(call_t *call, phone_t *phone)
{
thread_t *t;
link_t *cur;
task_t *ta;
unative_t *uspace_buffer;
unative_t to_copy;
int rc;
unsigned copied, total;
unsigned buf_size;
unative_t tid;
klog_printf("debug_thread_read()");
// FIXME: verify task/thread state
ta = get_lock_callee_task(phone);
klog_printf("task %llu", ta->taskid);
uspace_buffer = (void *)IPC_GET_ARG2(call->data);
buf_size = IPC_GET_ARG3(call->data);
copied = total = 0;
for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
t = list_get_instance(cur, thread_t, th_link);
/* Not interested in kernel threads */
if ((t->flags & THREAD_FLAG_USPACE) == 0)
continue;
//FIXME: id cropped!!
tid = (unative_t) t->tid;
to_copy = sizeof(unative_t);
if (copied + to_copy >= buf_size)
to_copy = buf_size - copied;
if (to_copy > 0) {
rc = copy_to_uspace(uspace_buffer, &tid, to_copy);
if (rc != 0) {
spinlock_unlock(&ta->lock);
klog_printf("debug_thread_read() - copy failed");
return rc;
}
}
++uspace_buffer;
total += sizeof(unative_t);
copied += to_copy;
}
spinlock_unlock(&ta->lock);
IPC_SET_ARG1(call->data, copied);
IPC_SET_ARG2(call->data, total);
klog_printf("debug_thread_read() done");
return 1; /* actually need becksend with retval 0 */
}
int udebug_request_preprocess(call_t *call, phone_t *phone)
{
int rc;
switch (IPC_GET_ARG1(call->data)) {
case UDEBUG_M_BEGIN:
rc = udebug_rp_begin(call, phone);
return rc;
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_THREAD_READ:
rc = udebug_rp_thread_read(call, phone);
return rc;
default:
break;
}
return 0;
}
static void udebug_receive_mem_read(call_t *call)
{
unative_t uspace_dst;
void *uspace_ptr;
unsigned size;
void *buffer;
int rc;
klog_printf("debug_mem_read()");
uspace_dst = IPC_GET_ARG2(call->data);
uspace_ptr = (void *)IPC_GET_ARG3(call->data);
size = IPC_GET_ARG4(call->data);
buffer
= malloc(size
, 0); // ???
klog_printf("debug_mem_read: src=%u, size=%u", uspace_ptr, size);
/* NOTE: this is not strictly from a syscall... but that shouldn't
* be a problem */
rc = copy_from_uspace(buffer, uspace_ptr, size);
if (rc) {
IPC_SET_RETVAL(call->data, rc);
return;
}
klog_printf("first word: %u", *((unative_t *)buffer));
IPC_SET_RETVAL(call->data, 0);
/* Hack: 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_dst);
IPC_SET_ARG2(call->data, size);
call->buffer = buffer;
ipc_answer(&TASK->kernel_box, call);
}
/**
* Handle a debug call received on the kernel answerbox.
*
* This is called by the kbox servicing thread.
*/
void udebug_call_receive(call_t *call)
{
int debug_method;
debug_method = IPC_GET_ARG1(call->data);
switch (debug_method) {
case UDEBUG_M_MEM_READ:
udebug_receive_mem_read(call);
break;
}
}
/** @}
*/