/branches/dynload/kernel/kernel.config |
---|
144,6 → 144,8 |
# Virtually indexed D-cache support |
! [ARCH=sparc64] CONFIG_VIRT_IDX_DCACHE (y/n) |
# Support for userspace debuggers |
! CONFIG_UDEBUG (n/y) |
## Debugging configuration directives |
/branches/dynload/kernel/generic/include/proc/task.h |
---|
52,6 → 52,7 |
#include <arch/cpu.h> |
#include <mm/tlb.h> |
#include <proc/scheduler.h> |
#include <udebug/udebug.h> |
struct thread; |
93,6 → 94,20 |
* certain extent. |
*/ |
atomic_t active_calls; |
#ifdef CONFIG_UDEBUG |
/** Debugging stuff */ |
udebug_task_t udebug; |
/** Kernel answerbox */ |
answerbox_t kernel_box; |
/** Thread used to service kernel answerbox */ |
struct thread *kb_thread; |
/** Kbox thread creation vs. begin of cleanup mutual exclusion */ |
mutex_t kb_cleanup_lock; |
/** True if cleanup of kbox has already started */ |
bool kb_finished; |
#endif |
/** Architecture specific task data. */ |
task_arch_t arch; |
/branches/dynload/kernel/generic/include/proc/thread.h |
---|
46,6 → 46,7 |
#include <arch/cpu.h> |
#include <mm/tlb.h> |
#include <proc/uarg.h> |
#include <udebug/udebug.h> |
#define THREAD_STACK_SIZE STACK_SIZE |
#define THREAD_NAME_BUFLEN 20 |
203,6 → 204,12 |
/** Thread's kernel stack. */ |
uint8_t *kstack; |
#ifdef CONFIG_UDEBUG |
/** Debugging stuff */ |
udebug_thread_t udebug; |
#endif |
} thread_t; |
/** Thread list lock. |
/branches/dynload/kernel/generic/include/udebug/udebug.h |
---|
0,0 → 1,218 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup generic |
* @{ |
*/ |
/** @file |
*/ |
#ifndef KERN_UDEBUG_H_ |
#define KERN_UDEBUG_H_ |
#include <ipc/ipc.h> |
typedef enum { /* udebug_method_t */ |
/** Start debugging the recipient. |
* Causes all threads in the receiving task to stop. When they |
* are all stoped, an answer with retval 0 is generated. |
*/ |
UDEBUG_M_BEGIN = 1, |
/** Finish debugging the recipient. |
* Answers all pending GO and GUARD messages. |
*/ |
UDEBUG_M_END, |
/** Set which events should be captured. |
*/ |
UDEBUG_M_SET_EVMASK, |
/** Make sure the debugged task is still there. |
* This message is answered when the debugged task dies |
* or the debugging session ends. |
*/ |
UDEBUG_M_GUARD, |
/** Run a thread until a debugging event occurs. |
* This message is answered when the thread stops |
* in a debugging event. |
* |
* - ARG2 - id of the thread to run |
*/ |
UDEBUG_M_GO, |
/** Stop a thread being debugged. |
* Creates a special STOP event in the thread, causing |
* it to answer a pending GO message (if any). |
*/ |
UDEBUG_M_STOP, |
/** Read arguments of a syscall. |
* |
* - ARG2 - thread identification |
* - ARG3 - destination address in the caller's address space |
* |
*/ |
UDEBUG_M_ARGS_READ, |
/** Read the list of the debugged tasks's threads. |
* |
* - ARG2 - destination address in the caller's address space |
* - ARG3 - size of receiving buffer in bytes |
* |
* The kernel fills the buffer with a series of sysarg_t values |
* (thread ids). On answer, the kernel will set: |
* |
* - ARG2 - number of bytes that were actually copied |
* - ARG3 - number of bytes of the complete data |
* |
*/ |
UDEBUG_M_THREAD_READ, |
/** Read the debugged tasks's memory. |
* |
* - ARG2 - destination address in the caller's address space |
* - ARG3 - source address in the recipient's address space |
* - ARG4 - size of receiving buffer in bytes |
* |
*/ |
UDEBUG_M_MEM_READ, |
} udebug_method_t; |
typedef enum { |
UDEBUG_EVENT_FINISHED = 1, /**< Debuging session has finished */ |
UDEBUG_EVENT_STOP, /**< Stopped on DEBUG_STOP request */ |
UDEBUG_EVENT_SYSCALL_B, /**< Before beginning syscall execution */ |
UDEBUG_EVENT_SYSCALL_E, /**< After finishing syscall execution */ |
UDEBUG_EVENT_THREAD_B, /**< The task created a new thread */ |
UDEBUG_EVENT_THREAD_E /**< A thread exited */ |
} udebug_event_t; |
#define UDEBUG_EVMASK(event) (1 << ((event) - 1)) |
typedef enum { |
UDEBUG_EM_FINISHED = UDEBUG_EVMASK(UDEBUG_EVENT_FINISHED), |
UDEBUG_EM_STOP = UDEBUG_EVMASK(UDEBUG_EVENT_STOP), |
UDEBUG_EM_SYSCALL_B = UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_B), |
UDEBUG_EM_SYSCALL_E = UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_E), |
UDEBUG_EM_THREAD_B = UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_B), |
UDEBUG_EM_THREAD_E = UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_E), |
UDEBUG_EM_ALL = |
UDEBUG_EVMASK(UDEBUG_EVENT_FINISHED) | |
UDEBUG_EVMASK(UDEBUG_EVENT_STOP) | |
UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_B) | |
UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_E) | |
UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_B) | |
UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_E) |
} udebug_evmask_t; |
#ifdef KERNEL |
#include <synch/mutex.h> |
#include <arch/interrupt.h> |
#include <atomic.h> |
typedef enum { |
/** Task is not being debugged */ |
UDEBUG_TS_INACTIVE, |
/** BEGIN operation in progress (waiting for threads to stop) */ |
UDEBUG_TS_BEGINNING, |
/** Debugger fully connected */ |
UDEBUG_TS_ACTIVE, |
/** Task is shutting down, no more debug activities allowed */ |
UDEBUG_TS_SHUTDOWN |
} udebug_task_state_t; |
/** Debugging part of task_t structure. |
*/ |
typedef struct { |
/** Synchronize debug ops on this task / access to this structure */ |
mutex_t lock; |
char *lock_owner; |
udebug_task_state_t dt_state; |
call_t *begin_call; |
int not_stoppable_count; |
struct task *debugger; |
udebug_evmask_t evmask; |
} udebug_task_t; |
/** Debugging part of thread_t structure. |
*/ |
typedef struct { |
/** |
* Prevent deadlock with udebug_before_thread_runs() in interrupt |
* handler, without actually disabling interrupts. |
* ==0 means "unlocked", >0 means "locked" |
*/ |
atomic_t int_lock; |
/** Synchronize debug ops on this thread / access to this structure */ |
mutex_t lock; |
waitq_t go_wq; |
call_t *go_call; |
unative_t syscall_args[6]; |
/** What type of event are we stopped in or 0 if none */ |
udebug_event_t cur_event; |
bool stop; |
bool stoppable; |
bool debug_active; /**< In a debugging session */ |
} udebug_thread_t; |
struct task; |
struct thread; |
void udebug_task_init(udebug_task_t *ut); |
void udebug_thread_initialize(udebug_thread_t *ut); |
void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, |
unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, |
bool end_variant); |
void udebug_thread_b_event(struct thread *t); |
void udebug_thread_e_event(void); |
void udebug_stoppable_begin(void); |
void udebug_stoppable_end(void); |
void udebug_before_thread_runs(void); |
int udebug_task_cleanup(struct task *ta); |
#endif |
#endif |
/** @} |
*/ |
/branches/dynload/kernel/generic/include/udebug/udebug_ops.h |
---|
0,0 → 1,55 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup generic |
* @{ |
*/ |
/** @file |
*/ |
#ifndef KERN_UDEBUG_OPS_H_ |
#define KERN_UDEBUG_OPS_H_ |
#include <ipc/ipc.h> |
int udebug_begin(call_t *call); |
int udebug_end(void); |
int udebug_set_evmask(udebug_evmask_t mask); |
int udebug_go(thread_t *t, call_t *call); |
int udebug_stop(thread_t *t, call_t *call); |
int udebug_thread_read(void **buffer, size_t buf_size, size_t *n); |
int udebug_args_read(thread_t *t, void **buffer); |
int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer); |
#endif |
/** @} |
*/ |
/branches/dynload/kernel/generic/include/udebug/udebug_ipc.h |
---|
0,0 → 1,47 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup generic |
* @{ |
*/ |
/** @file |
*/ |
#ifndef KERN_UDEBUG_IPC_H_ |
#define KERN_UDEBUG_IPC_H_ |
#include <ipc/ipc.h> |
int udebug_request_preprocess(call_t *call, phone_t *phone); |
void udebug_call_receive(call_t *call); |
#endif |
/** @} |
*/ |
/branches/dynload/kernel/generic/include/syscall/syscall.h |
---|
79,7 → 79,7 |
SYS_DEBUG_PUTINT, |
SYS_DEBUG_ENABLE_CONSOLE, |
SYS_IPC_CONNECT_KBOX, |
SYSCALL_END |
} syscall_t; |
/branches/dynload/kernel/generic/include/ipc/sysipc.h |
---|
57,6 → 57,7 |
unative_t sys_ipc_register_irq(inr_t inr, devno_t devno, unative_t method, |
irq_code_t *ucode); |
unative_t sys_ipc_unregister_irq(inr_t inr, devno_t devno); |
unative_t sys_ipc_connect_kbox(sysarg64_t *task_id); |
#endif |
/branches/dynload/kernel/generic/include/ipc/ipc.h |
---|
195,6 → 195,12 |
*/ |
#define IPC_M_DATA_READ 7 |
/** Debug the recipient. |
* - ARG1 - specifies the debug method (from udebug_method_t) |
* - other arguments are specific to the debug method |
*/ |
#define IPC_M_DEBUG_ALL 8 |
/* Well-known methods */ |
#define IPC_M_LAST_SYSTEM 511 |
#define IPC_M_PING 512 |
307,6 → 313,8 |
extern int ipc_phone_hangup(phone_t *); |
extern void ipc_backsend_err(phone_t *, call_t *, unative_t); |
extern void ipc_print_task(task_id_t); |
extern void ipc_answerbox_slam_phones(answerbox_t *, bool); |
extern void ipc_cleanup_call_list(link_t *); |
extern answerbox_t *ipc_phone_0; |
/branches/dynload/kernel/generic/include/ipc/kbox.h |
---|
0,0 → 1,46 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup genericipc |
* @{ |
*/ |
/** @file |
*/ |
#ifndef KERN_IPC_KBOX_H_ |
#define KERN_IPC_KBOX_H_ |
#include <typedefs.h> |
extern int ipc_connect_kbox(task_id_t); |
extern void ipc_kbox_cleanup(void); |
#endif |
/** @} |
*/ |
/branches/dynload/kernel/generic/src/main/uinit.c |
---|
46,7 → 46,9 |
#include <userspace.h> |
#include <mm/slab.h> |
#include <arch.h> |
#include <udebug/udebug.h> |
/** Thread used to bring up userspace thread. |
* |
* @param arg Pointer to structure containing userspace entry and stack |
65,6 → 67,10 |
* deployed for the event of forceful task termination. |
*/ |
thread_detach(THREAD); |
#ifdef CONFIG_UDEBUG |
udebug_stoppable_end(); |
#endif |
uarg.uspace_entry = ((uspace_arg_t *) arg)->uspace_entry; |
uarg.uspace_stack = ((uspace_arg_t *) arg)->uspace_stack; |
/branches/dynload/kernel/generic/src/proc/task.c |
---|
155,7 → 155,18 |
ta->capabilities = 0; |
ta->cycles = 0; |
#ifdef CONFIG_UDEBUG |
/* Init debugging stuff */ |
udebug_task_init(&ta->udebug); |
/* Init kbox stuff */ |
ipc_answerbox_init(&ta->kernel_box, ta); |
ta->kb_thread = NULL; |
mutex_initialize(&ta->kb_cleanup_lock, MUTEX_PASSIVE); |
ta->kb_finished = false; |
#endif |
ipc_answerbox_init(&ta->answerbox, ta); |
for (i = 0; i < IPC_MAX_PHONES; i++) |
ipc_phone_init(&ta->phones[i]); |
/branches/dynload/kernel/generic/src/proc/thread.c |
---|
180,6 → 180,10 |
return -1; |
} |
#ifdef CONFIG_UDEBUG |
mutex_initialize(&t->udebug.lock, MUTEX_PASSIVE); |
#endif |
return 0; |
} |
347,6 → 351,11 |
avltree_node_initialize(&t->threads_tree_node); |
t->threads_tree_node.key = (uintptr_t) t; |
#ifdef CONFIG_UDEBUG |
/* Init debugging stuff */ |
udebug_thread_initialize(&t->udebug); |
#endif |
/* might depend on previous initialization */ |
thread_create_arch(t); |
409,12 → 418,17 |
ipl_t ipl; |
/* |
* Attach to the current task. |
* Attach to the specified task. |
*/ |
ipl = interrupts_disable(); |
spinlock_lock(&task->lock); |
atomic_inc(&task->refcount); |
atomic_inc(&task->lifecount); |
/* Must not count kbox thread into lifecount */ |
if (t->flags & THREAD_FLAG_USPACE) |
atomic_inc(&task->lifecount); |
list_append(&t->th_link, &task->th_head); |
spinlock_unlock(&task->lock); |
437,14 → 451,19 |
{ |
ipl_t ipl; |
if (atomic_predec(&TASK->lifecount) == 0) { |
/* |
* We are the last thread in the task that still has not exited. |
* With the exception of the moment the task was created, new |
* threads can only be created by threads of the same task. |
* We are safe to perform cleanup. |
*/ |
if (THREAD->flags & THREAD_FLAG_USPACE) { |
if (THREAD->flags & THREAD_FLAG_USPACE) { |
#ifdef CONFIG_UDEBUG |
/* Generate udebug THREAD_E event */ |
udebug_thread_e_event(); |
#endif |
if (atomic_predec(&TASK->lifecount) == 0) { |
/* |
* We are the last userspace thread in the task that |
* still has not exited. With the exception of the |
* moment the task was created, new userspace threads |
* can only be created by threads of the same task. |
* We are safe to perform cleanup. |
*/ |
ipc_cleanup(); |
futex_cleanup(); |
LOG("Cleanup of task %" PRIu64" completed.", TASK->taskid); |
741,6 → 760,11 |
thread_attach(t, TASK); |
thread_ready(t); |
#ifdef CONFIG_UDEBUG |
/* Generate udebug THREAD_B event */ |
udebug_thread_b_event(t); |
#endif |
return 0; |
} else |
free(kernel_uarg); |
/branches/dynload/kernel/generic/src/mm/as.c |
---|
385,7 → 385,7 |
if (pages < area->pages) { |
bool cond; |
uintptr_t start_free = area->base + pages*PAGE_SIZE; |
uintptr_t start_free = area->base + pages * PAGE_SIZE; |
/* |
* Shrinking the area. |
395,7 → 395,7 |
/* |
* Start TLB shootdown sequence. |
*/ |
tlb_shootdown_start(TLB_INVL_PAGES, AS->asid, area->base + |
tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base + |
pages * PAGE_SIZE, area->pages - pages); |
/* |
/branches/dynload/kernel/generic/src/syscall/syscall.c |
---|
53,6 → 53,7 |
#include <syscall/copy.h> |
#include <sysinfo/sysinfo.h> |
#include <console/console.h> |
#include <udebug/udebug.h> |
/** Print using kernel facility |
* |
108,9 → 109,16 |
{ |
unative_t rc; |
if (id < SYSCALL_END) |
#ifdef CONFIG_UDEBUG |
udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, 0, false); |
#endif |
if (id < SYSCALL_END) { |
#ifdef CONFIG_UDEBUG |
udebug_stoppable_begin(); |
#endif |
rc = syscall_table[id](a1, a2, a3, a4, a5, a6); |
else { |
} else { |
printf("Task %" PRIu64": Unknown syscall %#" PRIxn, TASK->taskid, id); |
task_kill(TASK->taskid); |
thread_exit(); |
118,6 → 126,11 |
if (THREAD->interrupted) |
thread_exit(); |
#ifdef CONFIG_UDEBUG |
udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, rc, true); |
udebug_stoppable_end(); |
#endif |
return rc; |
} |
173,7 → 186,9 |
/* Debug calls */ |
(syshandler_t) sys_debug_putint, |
(syshandler_t) sys_debug_enable_console |
(syshandler_t) sys_debug_enable_console, |
(syshandler_t) sys_ipc_connect_kbox |
}; |
/** @} |
/branches/dynload/kernel/generic/src/ipc/kbox.c |
---|
0,0 → 1,220 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup genericipc |
* @{ |
*/ |
/** @file |
*/ |
#include <synch/synch.h> |
#include <synch/spinlock.h> |
#include <synch/mutex.h> |
#include <ipc/ipc.h> |
#include <ipc/ipcrsc.h> |
#include <arch.h> |
#include <errno.h> |
#include <debug.h> |
#include <udebug/udebug_ipc.h> |
#include <ipc/kbox.h> |
void ipc_kbox_cleanup(void) |
{ |
bool have_kb_thread; |
/* Only hold kb_cleanup_lock while setting kb_finished - this is enough */ |
mutex_lock(&TASK->kb_cleanup_lock); |
TASK->kb_finished = true; |
mutex_unlock(&TASK->kb_cleanup_lock); |
have_kb_thread = (TASK->kb_thread != NULL); |
/* From now on nobody will try to connect phones or attach kbox threads */ |
/* |
* Disconnect all phones connected to our kbox. Passing true for |
* notify_box causes a HANGUP message to be inserted for each |
* disconnected phone. This ensures the kbox thread is going to |
* wake up and terminate. |
*/ |
ipc_answerbox_slam_phones(&TASK->kernel_box, have_kb_thread); |
if (have_kb_thread) { |
LOG("join kb_thread..\n"); |
thread_join(TASK->kb_thread); |
thread_detach(TASK->kb_thread); |
LOG("join done\n"); |
TASK->kb_thread = NULL; |
} |
/* Answer all messages in 'calls' and 'dispatched_calls' queues */ |
spinlock_lock(&TASK->kernel_box.lock); |
ipc_cleanup_call_list(&TASK->kernel_box.dispatched_calls); |
ipc_cleanup_call_list(&TASK->kernel_box.calls); |
spinlock_unlock(&TASK->kernel_box.lock); |
} |
static void kbox_thread_proc(void *arg) |
{ |
call_t *call; |
int method; |
bool done; |
ipl_t ipl; |
(void)arg; |
LOG("kbox_thread_proc()\n"); |
done = false; |
while (!done) { |
call = ipc_wait_for_call(&TASK->kernel_box, SYNCH_NO_TIMEOUT, |
SYNCH_FLAGS_NONE); |
if (call != NULL) { |
method = IPC_GET_METHOD(call->data); |
if (method == IPC_M_DEBUG_ALL) { |
udebug_call_receive(call); |
} |
if (method == IPC_M_PHONE_HUNGUP) { |
LOG("kbox: handle hangup message\n"); |
/* Was it our debugger, who hung up? */ |
if (call->sender == TASK->udebug.debugger) { |
/* Terminate debugging session (if any) */ |
LOG("kbox: terminate debug session\n"); |
ipl = interrupts_disable(); |
spinlock_lock(&TASK->lock); |
udebug_task_cleanup(TASK); |
spinlock_unlock(&TASK->lock); |
interrupts_restore(ipl); |
} else { |
LOG("kbox: was not debugger\n"); |
} |
LOG("kbox: continue with hangup message\n"); |
IPC_SET_RETVAL(call->data, 0); |
ipc_answer(&TASK->kernel_box, call); |
ipl = interrupts_disable(); |
spinlock_lock(&TASK->lock); |
spinlock_lock(&TASK->answerbox.lock); |
if (list_empty(&TASK->answerbox.connected_phones)) { |
/* Last phone has been disconnected */ |
TASK->kb_thread = NULL; |
done = true; |
LOG("phone list is empty\n"); |
} |
spinlock_unlock(&TASK->answerbox.lock); |
spinlock_unlock(&TASK->lock); |
interrupts_restore(ipl); |
} |
} |
} |
LOG("kbox: finished\n"); |
} |
/** |
* Connect phone to a task kernel-box specified by id. |
* |
* Note that this is not completely atomic. For optimisation reasons, |
* The task might start cleaning up kbox after the phone has been connected |
* and before a kbox thread has been created. This must be taken into account |
* in the cleanup code. |
* |
* @return Phone id on success, or negative error code. |
*/ |
int ipc_connect_kbox(task_id_t taskid) |
{ |
int newphid; |
task_t *ta; |
thread_t *kb_thread; |
ipl_t ipl; |
ipl = interrupts_disable(); |
spinlock_lock(&tasks_lock); |
ta = task_find_by_id(taskid); |
if (ta == NULL) { |
spinlock_unlock(&tasks_lock); |
interrupts_restore(ipl); |
return ENOENT; |
} |
atomic_inc(&ta->refcount); |
spinlock_unlock(&tasks_lock); |
interrupts_restore(ipl); |
mutex_lock(&ta->kb_cleanup_lock); |
if (atomic_predec(&ta->refcount) == 0) { |
mutex_unlock(&ta->kb_cleanup_lock); |
task_destroy(ta); |
return ENOENT; |
} |
if (ta->kb_finished != false) { |
mutex_unlock(&ta->kb_cleanup_lock); |
return EINVAL; |
} |
newphid = phone_alloc(); |
if (newphid < 0) { |
mutex_unlock(&ta->kb_cleanup_lock); |
return ELIMIT; |
} |
/* Connect the newly allocated phone to the kbox */ |
ipc_phone_connect(&TASK->phones[newphid], &ta->kernel_box); |
if (ta->kb_thread != NULL) { |
mutex_unlock(&ta->kb_cleanup_lock); |
return newphid; |
} |
/* Create a kbox thread */ |
kb_thread = thread_create(kbox_thread_proc, NULL, ta, 0, "kbox", false); |
if (!kb_thread) { |
mutex_unlock(&ta->kb_cleanup_lock); |
return ENOMEM; |
} |
ta->kb_thread = kb_thread; |
thread_ready(kb_thread); |
mutex_unlock(&ta->kb_cleanup_lock); |
return newphid; |
} |
/** @} |
*/ |
/branches/dynload/kernel/generic/src/ipc/sysipc.c |
---|
42,8 → 42,9 |
#include <ipc/sysipc.h> |
#include <ipc/irq.h> |
#include <ipc/ipcrsc.h> |
#include <ipc/kbox.h> |
#include <udebug/udebug_ipc.h> |
#include <arch/interrupt.h> |
#include <print.h> |
#include <syscall/copy.h> |
#include <security/cap.h> |
#include <mm/as.h> |
295,10 → 296,11 |
/** Called before the request is sent. |
* |
* @param call Call structure with the request. |
* @param phone Phone that the call will be sent through. |
* |
* @return Return 0 on success, ELIMIT or EPERM on error. |
*/ |
static int request_preprocess(call_t *call) |
static int request_preprocess(call_t *call, phone_t *phone) |
{ |
int newphid; |
size_t size; |
340,6 → 342,10 |
return rc; |
} |
break; |
#ifdef CONFIG_UDEBUG |
case IPC_M_DEBUG_ALL: |
return udebug_request_preprocess(call, phone); |
#endif |
default: |
break; |
} |
369,6 → 375,7 |
if (call->buffer) { |
/* This must be an affirmative answer to IPC_M_DATA_READ. */ |
/* or IPC_M_DEBUG_ALL/UDEBUG_M_MEM_READ... */ |
uintptr_t dst = IPC_GET_ARG1(call->data); |
size_t size = IPC_GET_ARG2(call->data); |
int rc = copy_to_uspace((void *) dst, call->buffer, size); |
399,7 → 406,13 |
return -1; |
} |
IPC_SET_ARG5(call->data, phoneid); |
} |
} |
switch (IPC_GET_METHOD(call->data)) { |
case IPC_M_DEBUG_ALL: |
return -1; |
default: |
break; |
} |
return 0; |
} |
441,7 → 454,7 |
IPC_SET_ARG4(call.data, 0); |
IPC_SET_ARG5(call.data, 0); |
if (!(res = request_preprocess(&call))) { |
if (!(res = request_preprocess(&call, phone))) { |
rc = ipc_call_sync(phone, &call); |
if (rc != EOK) |
return rc; |
481,7 → 494,7 |
GET_CHECK_PHONE(phone, phoneid, return ENOENT); |
if (!(res = request_preprocess(&call))) { |
if (!(res = request_preprocess(&call, phone))) { |
rc = ipc_call_sync(phone, &call); |
if (rc != EOK) |
return rc; |
550,7 → 563,7 |
*/ |
IPC_SET_ARG5(call->data, 0); |
if (!(res = request_preprocess(call))) |
if (!(res = request_preprocess(call, phone))) |
ipc_call(phone, call); |
else |
ipc_backsend_err(phone, call, res); |
584,7 → 597,7 |
ipc_call_free(call); |
return (unative_t) rc; |
} |
if (!(res = request_preprocess(call))) |
if (!(res = request_preprocess(call, phone))) |
ipc_call(phone, call); |
else |
ipc_backsend_err(phone, call, res); |
868,5 → 881,30 |
return 0; |
} |
#include <console/console.h> |
/** |
* Syscall connect to a task by id. |
* |
* @return Phone id on success, or negative error code. |
*/ |
unative_t sys_ipc_connect_kbox(sysarg64_t *uspace_taskid_arg) |
{ |
#ifdef CONFIG_UDEBUG |
sysarg64_t taskid_arg; |
int rc; |
rc = copy_from_uspace(&taskid_arg, uspace_taskid_arg, sizeof(sysarg64_t)); |
if (rc != 0) |
return (unative_t) rc; |
LOG("sys_ipc_connect_kbox(%" PRIu64 ")\n", taskid_arg.value); |
return ipc_connect_kbox(taskid_arg.value); |
#else |
return (unative_t) ENOTSUP; |
#endif |
} |
/** @} |
*/ |
/branches/dynload/kernel/generic/src/ipc/ipc.c |
---|
43,6 → 43,7 |
#include <synch/waitq.h> |
#include <synch/synch.h> |
#include <ipc/ipc.h> |
#include <ipc/kbox.h> |
#include <errno.h> |
#include <mm/slab.h> |
#include <arch.h> |
51,6 → 52,7 |
#include <debug.h> |
#include <print.h> |
#include <console/console.h> |
#include <proc/thread.h> |
#include <arch/interrupt.h> |
#include <ipc/irq.h> |
427,7 → 429,7 |
* |
* @param lst Head of the list to be cleaned up. |
*/ |
static void ipc_cleanup_call_list(link_t *lst) |
void ipc_cleanup_call_list(link_t *lst) |
{ |
call_t *call; |
442,33 → 444,31 |
} |
} |
/** Cleans up all IPC communication of the current task. |
/** Disconnects all phones connected to an answerbox. |
* |
* Note: ipc_hangup sets returning answerbox to TASK->answerbox, you |
* have to change it as well if you want to cleanup other tasks than TASK. |
* @param box Answerbox to disconnect phones from. |
* @param notify_box If true, the answerbox will get a hangup message for |
* each disconnected phone. |
*/ |
void ipc_cleanup(void) |
void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box) |
{ |
int i; |
call_t *call; |
phone_t *phone; |
DEADLOCK_PROBE_INIT(p_phonelck); |
ipl_t ipl; |
call_t *call; |
/* Disconnect all our phones ('ipc_phone_hangup') */ |
for (i = 0; i < IPC_MAX_PHONES; i++) |
ipc_phone_hangup(&TASK->phones[i]); |
call = notify_box ? ipc_call_alloc(0) : NULL; |
/* Disconnect all connected irqs */ |
ipc_irq_cleanup(&TASK->answerbox); |
/* Disconnect all phones connected to our answerbox */ |
restart_phones: |
spinlock_lock(&TASK->answerbox.lock); |
while (!list_empty(&TASK->answerbox.connected_phones)) { |
phone = list_get_instance(TASK->answerbox.connected_phones.next, |
ipl = interrupts_disable(); |
spinlock_lock(&box->lock); |
while (!list_empty(&box->connected_phones)) { |
phone = list_get_instance(box->connected_phones.next, |
phone_t, link); |
if (SYNCH_FAILED(mutex_trylock(&phone->lock))) { |
spinlock_unlock(&TASK->answerbox.lock); |
spinlock_unlock(&box->lock); |
interrupts_restore(ipl); |
DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD); |
goto restart_phones; |
} |
475,13 → 475,69 |
/* Disconnect phone */ |
ASSERT(phone->state == IPC_PHONE_CONNECTED); |
list_remove(&phone->link); |
phone->state = IPC_PHONE_SLAMMED; |
list_remove(&phone->link); |
if (notify_box) { |
mutex_unlock(&phone->lock); |
spinlock_unlock(&box->lock); |
interrupts_restore(ipl); |
/* |
* Send one message to the answerbox for each |
* phone. Used to make sure the kbox thread |
* wakes up after the last phone has been |
* disconnected. |
*/ |
IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP); |
call->flags |= IPC_CALL_DISCARD_ANSWER; |
_ipc_call(phone, box, call); |
/* Allocate another call in advance */ |
call = ipc_call_alloc(0); |
/* Must start again */ |
goto restart_phones; |
} |
mutex_unlock(&phone->lock); |
} |
spinlock_unlock(&box->lock); |
interrupts_restore(ipl); |
/* Free unused call */ |
if (call) ipc_call_free(call); |
} |
/** Cleans up all IPC communication of the current task. |
* |
* Note: ipc_hangup sets returning answerbox to TASK->answerbox, you |
* have to change it as well if you want to cleanup other tasks than TASK. |
*/ |
void ipc_cleanup(void) |
{ |
int i; |
call_t *call; |
/* Disconnect all our phones ('ipc_phone_hangup') */ |
for (i = 0; i < IPC_MAX_PHONES; i++) |
ipc_phone_hangup(&TASK->phones[i]); |
/* Disconnect all connected irqs */ |
ipc_irq_cleanup(&TASK->answerbox); |
/* Disconnect all phones connected to our regular answerbox */ |
ipc_answerbox_slam_phones(&TASK->answerbox, false); |
#ifdef CONFIG_UDEBUG |
/* Clean up kbox thread and communications */ |
ipc_kbox_cleanup(); |
#endif |
/* Answer all messages in 'calls' and 'dispatched_calls' queues */ |
spinlock_lock(&TASK->answerbox.lock); |
ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls); |
ipc_cleanup_call_list(&TASK->answerbox.calls); |
spinlock_unlock(&TASK->answerbox.lock); |
/branches/dynload/kernel/generic/src/udebug/udebug_ipc.c |
---|
0,0 → 1,294 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup generic |
* @{ |
*/ |
/** |
* @file |
* @brief Udebug IPC message handling. |
*/ |
#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_ops.h> |
#include <udebug/udebug_ipc.h> |
int udebug_request_preprocess(call_t *call, phone_t *phone) |
{ |
switch (IPC_GET_ARG1(call->data)) { |
/* future UDEBUG_M_REGS_WRITE, UDEBUG_M_MEM_WRITE: */ |
default: |
break; |
} |
return 0; |
} |
static void udebug_receive_begin(call_t *call) |
{ |
int rc; |
rc = udebug_begin(call); |
if (rc < 0) { |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
return; |
} |
if (rc != 0) { |
IPC_SET_RETVAL(call->data, 0); |
ipc_answer(&TASK->kernel_box, call); |
} |
} |
static void udebug_receive_end(call_t *call) |
{ |
int rc; |
rc = udebug_end(); |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
} |
static void udebug_receive_set_evmask(call_t *call) |
{ |
int rc; |
udebug_evmask_t mask; |
mask = IPC_GET_ARG2(call->data); |
rc = udebug_set_evmask(mask); |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
} |
static void udebug_receive_go(call_t *call) |
{ |
thread_t *t; |
int rc; |
t = (thread_t *)IPC_GET_ARG2(call->data); |
rc = udebug_go(t, call); |
if (rc < 0) { |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
return; |
} |
} |
static void udebug_receive_stop(call_t *call) |
{ |
thread_t *t; |
int rc; |
t = (thread_t *)IPC_GET_ARG2(call->data); |
rc = udebug_stop(t, call); |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
} |
static void udebug_receive_thread_read(call_t *call) |
{ |
unative_t uspace_addr; |
unative_t to_copy; |
unsigned total_bytes; |
unsigned buf_size; |
void *buffer; |
size_t n; |
int rc; |
uspace_addr = IPC_GET_ARG2(call->data); /* Destination address */ |
buf_size = IPC_GET_ARG3(call->data); /* Dest. buffer size */ |
/* |
* Read thread list. Variable n will be filled with actual number |
* of threads times thread-id size. |
*/ |
rc = udebug_thread_read(&buffer, buf_size, &n); |
if (rc < 0) { |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
return; |
} |
total_bytes = n; |
/* Copy MAX(buf_size, total_bytes) bytes */ |
if (buf_size > total_bytes) |
to_copy = total_bytes; |
else |
to_copy = buf_size; |
/* |
* Make use of call->buffer to transfer data to caller's userspace |
*/ |
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 = buffer; |
ipc_answer(&TASK->kernel_box, call); |
} |
static void udebug_receive_args_read(call_t *call) |
{ |
thread_t *t; |
unative_t uspace_addr; |
int rc; |
void *buffer; |
t = (thread_t *)IPC_GET_ARG2(call->data); |
rc = udebug_args_read(t, &buffer); |
if (rc != EOK) { |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
return; |
} |
/* |
* 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 = buffer; |
ipc_answer(&TASK->kernel_box, call); |
} |
static void udebug_receive_mem_read(call_t *call) |
{ |
unative_t uspace_dst; |
unative_t uspace_src; |
unsigned size; |
void *buffer; |
int rc; |
uspace_dst = IPC_GET_ARG2(call->data); |
uspace_src = IPC_GET_ARG3(call->data); |
size = IPC_GET_ARG4(call->data); |
rc = udebug_mem_read(uspace_src, size, &buffer); |
if (rc < 0) { |
IPC_SET_RETVAL(call->data, rc); |
ipc_answer(&TASK->kernel_box, call); |
return; |
} |
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_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); |
if (debug_method != UDEBUG_M_BEGIN) { |
/* |
* Verify that the sender is this task's debugger. |
* Note that this is the only thread that could change |
* TASK->debugger. Therefore no locking is necessary |
* and the sender can be safely considered valid until |
* control exits this function. |
*/ |
if (TASK->udebug.debugger != call->sender) { |
IPC_SET_RETVAL(call->data, EINVAL); |
ipc_answer(&TASK->kernel_box, call); |
return; |
} |
} |
switch (debug_method) { |
case UDEBUG_M_BEGIN: |
udebug_receive_begin(call); |
break; |
case UDEBUG_M_END: |
udebug_receive_end(call); |
break; |
case UDEBUG_M_SET_EVMASK: |
udebug_receive_set_evmask(call); |
break; |
case UDEBUG_M_GO: |
udebug_receive_go(call); |
break; |
case UDEBUG_M_STOP: |
udebug_receive_stop(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_MEM_READ: |
udebug_receive_mem_read(call); |
break; |
} |
} |
/** @} |
*/ |
/branches/dynload/kernel/generic/src/udebug/udebug.c |
---|
0,0 → 1,502 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup generic |
* @{ |
*/ |
/** |
* @file |
* @brief Udebug. |
* |
* Functions in this file are executed directly in each thread, which |
* may or may not be the subject of debugging. The udebug_stoppable_begin/end() |
* functions are also executed in the clock interrupt handler. To avoid |
* deadlock, functions in this file are protected from the interrupt |
* by locking the recursive lock THREAD->udebug.int_lock (just an atomic |
* variable). This prevents udebug_stoppable_begin/end() from being |
* executed in the interrupt handler (they are skipped). |
* |
* Functions in udebug_ops.c and udebug_ipc.c execute in different threads, |
* so they needn't be protected from the (preemptible) interrupt-initiated |
* code. |
*/ |
#include <synch/waitq.h> |
#include <debug.h> |
#include <udebug/udebug.h> |
#include <errno.h> |
#include <arch.h> |
static inline void udebug_int_lock(void) |
{ |
atomic_inc(&THREAD->udebug.int_lock); |
} |
static inline void udebug_int_unlock(void) |
{ |
atomic_dec(&THREAD->udebug.int_lock); |
} |
void udebug_task_init(udebug_task_t *ut) |
{ |
mutex_initialize(&ut->lock, MUTEX_PASSIVE); |
ut->dt_state = UDEBUG_TS_INACTIVE; |
ut->begin_call = NULL; |
ut->not_stoppable_count = 0; |
ut->evmask = 0; |
} |
void udebug_thread_initialize(udebug_thread_t *ut) |
{ |
mutex_initialize(&ut->lock, MUTEX_PASSIVE); |
waitq_initialize(&ut->go_wq); |
/* |
* At the beginning the thread is stoppable, so int_lock be set, too. |
*/ |
atomic_set(&ut->int_lock, 1); |
ut->go_call = NULL; |
ut->stop = true; |
ut->stoppable = true; |
ut->debug_active = false; |
ut->cur_event = 0; /* none */ |
} |
static void udebug_wait_for_go(waitq_t *wq) |
{ |
int rc; |
ipl_t ipl; |
ipl = waitq_sleep_prepare(wq); |
wq->missed_wakeups = 0; /* Enforce blocking. */ |
rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE); |
waitq_sleep_finish(wq, rc, ipl); |
} |
/** Do a preliminary check that a debugging session is in progress. |
* |
* This only requires the THREAD->udebug.lock mutex (and not |
* TASK->udebug.lock mutex). For an undebugged task, this will |
* never block (while there could be collisions by different threads |
* on the TASK mutex), thus improving SMP perormance for undebugged tasks. |
*/ |
static bool udebug_thread_precheck(void) |
{ |
bool res; |
mutex_lock(&THREAD->udebug.lock); |
res = THREAD->udebug.debug_active; |
mutex_unlock(&THREAD->udebug.lock); |
return res; |
} |
void udebug_stoppable_begin(void) |
{ |
int nsc; |
call_t *db_call, *go_call; |
ASSERT(THREAD); |
ASSERT(TASK); |
udebug_int_lock(); |
/* Early check for undebugged tasks */ |
if (!udebug_thread_precheck()) { |
udebug_int_unlock(); |
return; |
} |
mutex_lock(&TASK->udebug.lock); |
nsc = --TASK->udebug.not_stoppable_count; |
/* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */ |
mutex_lock(&THREAD->udebug.lock); |
ASSERT(THREAD->udebug.stoppable == false); |
THREAD->udebug.stoppable = true; |
if (TASK->udebug.dt_state == UDEBUG_TS_BEGINNING && nsc == 0) { |
/* |
* This was the last non-stoppable thread. Reply to |
* DEBUG_BEGIN call. |
*/ |
db_call = TASK->udebug.begin_call; |
ASSERT(db_call); |
TASK->udebug.dt_state = UDEBUG_TS_ACTIVE; |
TASK->udebug.begin_call = NULL; |
IPC_SET_RETVAL(db_call->data, 0); |
ipc_answer(&TASK->answerbox, db_call); |
} else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) { |
/* |
* Active debugging session |
*/ |
if (THREAD->udebug.debug_active && THREAD->udebug.stop) { |
/* |
* Thread was requested to stop - answer go call |
*/ |
/* Make sure nobody takes this call away from us */ |
go_call = THREAD->udebug.go_call; |
THREAD->udebug.go_call = NULL; |
ASSERT(go_call); |
IPC_SET_RETVAL(go_call->data, 0); |
IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP); |
THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; |
ipc_answer(&TASK->answerbox, go_call); |
} |
} |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
} |
void udebug_stoppable_end(void) |
{ |
/* Early check for undebugged tasks */ |
if (!udebug_thread_precheck()) { |
udebug_int_unlock(); |
return; |
} |
restart: |
mutex_lock(&TASK->udebug.lock); |
mutex_lock(&THREAD->udebug.lock); |
if (THREAD->udebug.debug_active && |
THREAD->udebug.stop == true) { |
TASK->udebug.begin_call = NULL; |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
udebug_wait_for_go(&THREAD->udebug.go_wq); |
goto restart; |
/* must try again - have to lose stoppability atomically */ |
} else { |
++TASK->udebug.not_stoppable_count; |
ASSERT(THREAD->udebug.stoppable == true); |
THREAD->udebug.stoppable = false; |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
} |
udebug_int_unlock(); |
} |
/** Upon being scheduled to run, check if the current thread should stop. |
* |
* This function is called from clock(). Preemption is enabled. |
* interrupts are disabled, but since this is called after |
* being scheduled-in, we can enable them, if we're careful enough |
* not to allow arbitrary recursion or deadlock with the thread context. |
*/ |
void udebug_before_thread_runs(void) |
{ |
ipl_t ipl; |
return; |
ASSERT(!PREEMPTION_DISABLED); |
/* |
* Prevent agains re-entering, such as when preempted inside this |
* function. |
*/ |
if (atomic_get(&THREAD->udebug.int_lock) != 0) |
return; |
udebug_int_lock(); |
ipl = interrupts_enable(); |
/* Now we're free to do whatever we need (lock mutexes, sleep, etc.) */ |
/* Check if we're supposed to stop */ |
udebug_stoppable_begin(); |
udebug_stoppable_end(); |
interrupts_restore(ipl); |
udebug_int_unlock(); |
} |
void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3, |
unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc, |
bool end_variant) |
{ |
call_t *call; |
udebug_event_t etype; |
etype = end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B; |
udebug_int_lock(); |
/* Early check for undebugged tasks */ |
if (!udebug_thread_precheck()) { |
udebug_int_unlock(); |
return; |
} |
mutex_lock(&TASK->udebug.lock); |
mutex_lock(&THREAD->udebug.lock); |
/* Must only generate events when in debugging session and have go */ |
if (THREAD->udebug.debug_active != true || |
THREAD->udebug.stop == true || |
(TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) { |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
return; |
} |
//printf("udebug_syscall_event\n"); |
call = THREAD->udebug.go_call; |
THREAD->udebug.go_call = NULL; |
IPC_SET_RETVAL(call->data, 0); |
IPC_SET_ARG1(call->data, etype); |
IPC_SET_ARG2(call->data, id); |
IPC_SET_ARG3(call->data, rc); |
//printf("udebug_syscall_event/ipc_answer\n"); |
THREAD->udebug.syscall_args[0] = a1; |
THREAD->udebug.syscall_args[1] = a2; |
THREAD->udebug.syscall_args[2] = a3; |
THREAD->udebug.syscall_args[3] = a4; |
THREAD->udebug.syscall_args[4] = a5; |
THREAD->udebug.syscall_args[5] = a6; |
/* |
* Make sure udebug.stop is true when going to sleep |
* in case we get woken up by DEBUG_END. (At which |
* point it must be back to the initial true value). |
*/ |
THREAD->udebug.stop = true; |
THREAD->udebug.cur_event = etype; |
ipc_answer(&TASK->answerbox, call); |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
udebug_wait_for_go(&THREAD->udebug.go_wq); |
udebug_int_unlock(); |
} |
void udebug_thread_b_event(struct thread *t) |
{ |
call_t *call; |
udebug_int_lock(); |
mutex_lock(&TASK->udebug.lock); |
mutex_lock(&THREAD->udebug.lock); |
LOG("udebug_thread_b_event\n"); |
LOG("- check state\n"); |
/* Must only generate events when in debugging session */ |
if (THREAD->udebug.debug_active != true) { |
LOG("- debug_active: %s, udebug.stop: %s\n", |
THREAD->udebug.debug_active ? "yes(+)" : "no(-)", |
THREAD->udebug.stop ? "yes(-)" : "no(+)"); |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
return; |
} |
LOG("- trigger event\n"); |
call = THREAD->udebug.go_call; |
THREAD->udebug.go_call = NULL; |
IPC_SET_RETVAL(call->data, 0); |
IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B); |
IPC_SET_ARG2(call->data, (unative_t)t); |
/* |
* Make sure udebug.stop is true when going to sleep |
* in case we get woken up by DEBUG_END. (At which |
* point it must be back to the initial true value). |
*/ |
THREAD->udebug.stop = true; |
THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B; |
ipc_answer(&TASK->answerbox, call); |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
LOG("- sleep\n"); |
udebug_wait_for_go(&THREAD->udebug.go_wq); |
udebug_int_unlock(); |
} |
void udebug_thread_e_event(void) |
{ |
call_t *call; |
udebug_int_lock(); |
mutex_lock(&TASK->udebug.lock); |
mutex_lock(&THREAD->udebug.lock); |
LOG("udebug_thread_e_event\n"); |
LOG("- check state\n"); |
/* Must only generate events when in debugging session */ |
if (THREAD->udebug.debug_active != true) { |
/* printf("- debug_active: %s, udebug.stop: %s\n", |
THREAD->udebug.debug_active ? "yes(+)" : "no(-)", |
THREAD->udebug.stop ? "yes(-)" : "no(+)");*/ |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
return; |
} |
LOG("- trigger event\n"); |
call = THREAD->udebug.go_call; |
THREAD->udebug.go_call = NULL; |
IPC_SET_RETVAL(call->data, 0); |
IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E); |
/* Prevent any further debug activity in thread */ |
THREAD->udebug.debug_active = false; |
THREAD->udebug.cur_event = 0; /* none */ |
THREAD->udebug.stop = true; /* set to initial value */ |
ipc_answer(&TASK->answerbox, call); |
mutex_unlock(&THREAD->udebug.lock); |
mutex_unlock(&TASK->udebug.lock); |
/* Leave int_lock enabled */ |
/* This event does not sleep - debugging has finished in this thread */ |
} |
/** |
* Terminate task debugging session. |
* |
* \param ta->udebug.lock must be already locked. |
* \return Zero on success or negative error code. |
*/ |
int udebug_task_cleanup(struct task *ta) |
{ |
thread_t *t; |
link_t *cur; |
int flags; |
ipl_t ipl; |
LOG("udebug_task_cleanup()\n"); |
LOG("task %" PRIu64 "\n", ta->taskid); |
udebug_int_lock(); |
if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING && |
ta->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
LOG("udebug_task_cleanup(): task not being debugged\n"); |
return EINVAL; |
} |
/* Finish debugging of all userspace threads */ |
for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) { |
t = list_get_instance(cur, thread_t, th_link); |
mutex_lock(&t->udebug.lock); |
ipl = interrupts_disable(); |
spinlock_lock(&t->lock); |
flags = t->flags; |
spinlock_unlock(&t->lock); |
interrupts_restore(ipl); |
/* Only process userspace threads */ |
if ((flags & THREAD_FLAG_USPACE) != 0) { |
/* Prevent any further debug activity in thread */ |
t->udebug.debug_active = false; |
t->udebug.cur_event = 0; /* none */ |
/* Still has go? */ |
if (t->udebug.stop == false) { |
/* |
* Yes, so clear go. As debug_active == false, |
* this doesn't affect anything. |
*/ |
t->udebug.stop = true; |
/* Answer GO call */ |
LOG("answer GO call with EVENT_FINISHED\n"); |
IPC_SET_RETVAL(t->udebug.go_call->data, 0); |
IPC_SET_ARG1(t->udebug.go_call->data, UDEBUG_EVENT_FINISHED); |
ipc_answer(&ta->answerbox, t->udebug.go_call); |
t->udebug.go_call = NULL; |
} else { |
/* |
* Debug_stop is already at initial value. |
* Yet this means the thread needs waking up. |
*/ |
/* |
* t's lock must not be held when calling |
* waitq_wakeup. |
*/ |
waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST); |
} |
} |
mutex_unlock(&t->udebug.lock); |
} |
ta->udebug.dt_state = UDEBUG_TS_INACTIVE; |
ta->udebug.debugger = NULL; |
udebug_int_unlock(); |
return 0; |
} |
/** @} |
*/ |
/branches/dynload/kernel/generic/src/udebug/udebug_ops.c |
---|
0,0 → 1,436 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
/** @addtogroup generic |
* @{ |
*/ |
/** |
* @file |
* @brief Udebug operations. |
*/ |
#include <debug.h> |
#include <proc/task.h> |
#include <proc/thread.h> |
#include <arch.h> |
#include <errno.h> |
#include <syscall/copy.h> |
#include <ipc/ipc.h> |
#include <udebug/udebug.h> |
#include <udebug/udebug_ops.h> |
/** |
* Prepare a thread for a debugging operation. |
* |
* Simply put, return thread t with t->udebug.lock held, |
* but only if it verifies all conditions. |
* |
* Specifically, verifies that thread t exists, is a userspace thread, |
* and belongs to the current task (TASK). Verifies, that the thread |
* has (or hasn't) go according to having_go (typically false). |
* It also locks t->udebug.lock, making sure that t->udebug.debug_active |
* is true - that the thread is in a valid debugging session. |
* |
* With this verified and the t->udebug.lock mutex held, it is ensured |
* that the thread cannot leave the debugging session, let alone cease |
* to exist. |
* |
* In this function, holding the TASK->udebug.lock mutex prevents the |
* thread from leaving the debugging session, while relaxing from |
* the t->lock spinlock to the t->udebug.lock mutex. |
* |
* Returns EOK if all went well, or an error code otherwise. |
*/ |
static int _thread_op_begin(thread_t *t, bool having_go) |
{ |
task_id_t taskid; |
ipl_t ipl; |
taskid = TASK->taskid; |
mutex_lock(&TASK->udebug.lock); |
/* thread_exists() must be called with threads_lock held */ |
ipl = interrupts_disable(); |
spinlock_lock(&threads_lock); |
if (!thread_exists(t)) { |
spinlock_unlock(&threads_lock); |
interrupts_restore(ipl); |
mutex_unlock(&TASK->udebug.lock); |
return ENOENT; |
} |
/* t->lock is enough to ensure the thread's existence */ |
spinlock_lock(&t->lock); |
spinlock_unlock(&threads_lock); |
/* Verify that 't' is a userspace thread */ |
if ((t->flags & THREAD_FLAG_USPACE) == 0) { |
/* It's not, deny its existence */ |
spinlock_unlock(&t->lock); |
interrupts_restore(ipl); |
mutex_unlock(&TASK->udebug.lock); |
return ENOENT; |
} |
/* Verify debugging state */ |
if (t->udebug.debug_active != true) { |
/* Not in debugging session or undesired GO state */ |
spinlock_unlock(&t->lock); |
interrupts_restore(ipl); |
mutex_unlock(&TASK->udebug.lock); |
return ENOENT; |
} |
/* |
* Since the thread has debug_active == true, TASK->udebug.lock |
* is enough to ensure its existence and that debug_active remains |
* true. |
*/ |
spinlock_unlock(&t->lock); |
interrupts_restore(ipl); |
/* Only mutex TASK->udebug.lock left */ |
/* Now verify that the thread belongs to the current task */ |
if (t->task != TASK) { |
/* No such thread belonging this task*/ |
mutex_unlock(&TASK->udebug.lock); |
return ENOENT; |
} |
/* |
* Now we need to grab the thread's debug lock for synchronization |
* of the threads stoppability/stop state. |
*/ |
mutex_lock(&t->udebug.lock); |
/* The big task mutex is no longer needed */ |
mutex_unlock(&TASK->udebug.lock); |
if (!t->udebug.stop != having_go) { |
/* Not in debugging session or undesired GO state */ |
mutex_unlock(&t->udebug.lock); |
return EINVAL; |
} |
/* Only t->udebug.lock left */ |
return EOK; /* All went well */ |
} |
static void _thread_op_end(thread_t *t) |
{ |
mutex_unlock(&t->udebug.lock); |
} |
/** |
* \return 0 (ok, but not done yet), 1 (done) or negative error code. |
*/ |
int udebug_begin(call_t *call) |
{ |
int reply; |
thread_t *t; |
link_t *cur; |
LOG("udebug_begin()\n"); |
mutex_lock(&TASK->udebug.lock); |
LOG("debugging task %llu\n", TASK->taskid); |
if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) { |
mutex_unlock(&TASK->udebug.lock); |
LOG("udebug_begin(): busy error\n"); |
return EBUSY; |
} |
TASK->udebug.dt_state = UDEBUG_TS_BEGINNING; |
TASK->udebug.begin_call = call; |
TASK->udebug.debugger = call->sender; |
if (TASK->udebug.not_stoppable_count == 0) { |
TASK->udebug.dt_state = UDEBUG_TS_ACTIVE; |
TASK->udebug.begin_call = NULL; |
reply = 1; /* immediate reply */ |
} else { |
reply = 0; /* no reply */ |
} |
/* Set udebug.debug_active on all of the task's userspace threads */ |
for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) { |
t = list_get_instance(cur, thread_t, th_link); |
mutex_lock(&t->udebug.lock); |
if ((t->flags & THREAD_FLAG_USPACE) != 0) |
t->udebug.debug_active = true; |
mutex_unlock(&t->udebug.lock); |
} |
mutex_unlock(&TASK->udebug.lock); |
LOG("udebug_begin() done (%s)\n", |
reply ? "reply" : "stoppability wait"); |
return reply; |
} |
int udebug_end(void) |
{ |
int rc; |
LOG("udebug_end()\n"); |
mutex_lock(&TASK->udebug.lock); |
LOG("task %" PRIu64 "\n", TASK->taskid); |
rc = udebug_task_cleanup(TASK); |
mutex_unlock(&TASK->udebug.lock); |
return rc; |
} |
int udebug_set_evmask(udebug_evmask_t mask) |
{ |
LOG("udebug_set_mask()\n"); |
mutex_lock(&TASK->udebug.lock); |
if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
mutex_unlock(&TASK->udebug.lock); |
LOG("udebug_set_mask(): not active debuging session\n"); |
return EINVAL; |
} |
TASK->udebug.evmask = mask; |
mutex_unlock(&TASK->udebug.lock); |
return 0; |
} |
int udebug_go(thread_t *t, call_t *call) |
{ |
int rc; |
/* On success, this will lock t->udebug.lock */ |
rc = _thread_op_begin(t, false); |
if (rc != EOK) { |
return rc; |
} |
t->udebug.go_call = call; |
t->udebug.stop = false; |
t->udebug.cur_event = 0; /* none */ |
/* |
* Neither t's lock nor threads_lock may be held during wakeup |
*/ |
waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST); |
_thread_op_end(t); |
return 0; |
} |
int udebug_stop(thread_t *t, call_t *call) |
{ |
int rc; |
LOG("udebug_stop()\n"); |
mutex_lock(&TASK->udebug.lock); |
/* |
* On success, this will lock t->udebug.lock. Note that this makes sure |
* the thread is not stopped. |
*/ |
rc = _thread_op_begin(t, true); |
if (rc != EOK) { |
return rc; |
} |
/* Take GO away from the thread */ |
t->udebug.stop = true; |
if (!t->udebug.stoppable) { |
/* Answer will be sent when the thread becomes stoppable */ |
_thread_op_end(t); |
return 0; |
} |
/* |
* Answer GO call |
*/ |
LOG("udebug_stop - answering go call\n"); |
/* Make sure nobody takes this call away from us */ |
call = t->udebug.go_call; |
t->udebug.go_call = NULL; |
IPC_SET_RETVAL(call->data, 0); |
IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP); |
LOG("udebug_stop/ipc_answer\n"); |
THREAD->udebug.cur_event = UDEBUG_EVENT_STOP; |
_thread_op_end(t); |
ipc_answer(&TASK->answerbox, call); |
mutex_unlock(&TASK->udebug.lock); |
LOG("udebog_stop/done\n"); |
return 0; |
} |
int udebug_thread_read(void **buffer, size_t buf_size, size_t *n) |
{ |
thread_t *t; |
link_t *cur; |
unative_t tid; |
unsigned copied_ids; |
ipl_t ipl; |
unative_t *id_buffer; |
int flags; |
size_t max_ids; |
LOG("udebug_thread_read()\n"); |
/* Allocate a buffer to hold thread IDs */ |
id_buffer = malloc(buf_size, 0); |
mutex_lock(&TASK->udebug.lock); |
/* Verify task state */ |
if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
mutex_unlock(&TASK->udebug.lock); |
return EINVAL; |
} |
ipl = interrupts_disable(); |
spinlock_lock(&TASK->lock); |
/* Copy down the thread IDs */ |
max_ids = buf_size / sizeof(unative_t); |
copied_ids = 0; |
/* FIXME: make sure the thread isn't past debug shutdown... */ |
for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) { |
/* Do not write past end of buffer */ |
if (copied_ids >= max_ids) break; |
t = list_get_instance(cur, thread_t, th_link); |
spinlock_lock(&t->lock); |
flags = t->flags; |
spinlock_unlock(&t->lock); |
/* Not interested in kernel threads */ |
if ((flags & THREAD_FLAG_USPACE) != 0) { |
/* Using thread struct pointer as identification hash */ |
tid = (unative_t) t; |
id_buffer[copied_ids++] = tid; |
} |
} |
spinlock_unlock(&TASK->lock); |
interrupts_restore(ipl); |
mutex_unlock(&TASK->udebug.lock); |
*buffer = id_buffer; |
*n = copied_ids * sizeof(unative_t); |
return 0; |
} |
int udebug_args_read(thread_t *t, void **buffer) |
{ |
int rc; |
unative_t *arg_buffer; |
/* Prepare a buffer to hold the arguments */ |
arg_buffer = malloc(6 * sizeof(unative_t), 0); |
/* On success, this will lock t->udebug.lock */ |
rc = _thread_op_begin(t, false); |
if (rc != EOK) { |
return rc; |
} |
/* Additionally we need to verify that we are inside a syscall */ |
if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B && |
t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) { |
_thread_op_end(t); |
return EINVAL; |
} |
/* Copy to a local buffer before releasing the lock */ |
memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t)); |
_thread_op_end(t); |
*buffer = arg_buffer; |
return 0; |
} |
int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer) |
{ |
void *data_buffer; |
int rc; |
/* Verify task state */ |
mutex_lock(&TASK->udebug.lock); |
if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) { |
mutex_unlock(&TASK->udebug.lock); |
return EBUSY; |
} |
data_buffer = malloc(n, 0); |
/* NOTE: this is not strictly from a syscall... but that shouldn't |
* be a problem */ |
rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n); |
mutex_unlock(&TASK->udebug.lock); |
if (rc != 0) return rc; |
*buffer = data_buffer; |
return 0; |
} |
/** @} |
*/ |
/branches/dynload/kernel/Makefile |
---|
150,6 → 150,10 |
endif |
endif |
ifeq ($(CONFIG_UDEBUG),y) |
DEFS += -DCONFIG_UDEBUG |
endif |
## Simple detection for the type of the host system |
# |
HOST = $(shell uname) |
283,6 → 287,17 |
generic/src/security/cap.c \ |
generic/src/sysinfo/sysinfo.c |
## Udebug interface sources |
# |
ifeq ($(CONFIG_UDEBUG),y) |
GENERIC_SOURCES += \ |
generic/src/ipc/kbox.c \ |
generic/src/udebug/udebug.c \ |
generic/src/udebug/udebug_ops.c \ |
generic/src/udebug/udebug_ipc.c |
endif |
## Test sources |
# |
/branches/dynload/kernel/arch/arm32/include/interrupt.h |
---|
37,6 → 37,7 |
#define KERN_arm32_INTERRUPT_H_ |
#include <arch/types.h> |
#include <arch/exception.h> |
/** Initial size of exception dispatch table. */ |
#define IVT_ITEMS 6 |