Subversion Repositories HelenOS

Compare Revisions

No changes between revisions

Ignore whitespace Rev 3491 → Rev 3492

/branches/sparc/kernel/kernel.config
149,6 → 149,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/sparc/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/sparc/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/sparc/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/sparc/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/sparc/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/sparc/kernel/generic/include/syscall/syscall.h
78,6 → 78,7
SYS_SYSINFO_VALUE,
SYS_DEBUG_ENABLE_CONSOLE,
SYS_IPC_CONNECT_KBOX,
SYSCALL_END
} syscall_t;
 
/branches/sparc/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/sparc/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/sparc/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/sparc/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/sparc/kernel/generic/src/console/cmd.c
398,17 → 398,17
.argc = 0
};
 
/* Data and methods for 'ipc_task' command */
static int cmd_ipc_task(cmd_arg_t *argv);
static cmd_arg_t ipc_task_argv = {
/* Data and methods for 'ipc' command */
static int cmd_ipc(cmd_arg_t *argv);
static cmd_arg_t ipc_argv = {
.type = ARG_TYPE_INT,
};
static cmd_info_t ipc_task_info = {
.name = "ipc_task",
.description = "ipc_task <taskid> Show IPC information of given task.",
.func = cmd_ipc_task,
static cmd_info_t ipc_info = {
.name = "ipc",
.description = "ipc <taskid> Show IPC information of given task.",
.func = cmd_ipc,
.argc = 1,
.argv = &ipc_task_argv
.argv = &ipc_argv
};
 
/* Data and methods for 'zone' command */
461,7 → 461,7
&uptime_info,
&halt_info,
&help_info,
&ipc_task_info,
&ipc_info,
&set4_info,
&slabs_info,
&symaddr_info,
937,7 → 937,7
*
* return Always 1
*/
int cmd_ipc_task(cmd_arg_t * argv) {
int cmd_ipc(cmd_arg_t * argv) {
ipc_print_task(argv[0].intval);
return 1;
}
/branches/sparc/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/sparc/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/sparc/kernel/generic/src/lib/memstr.c
34,12 → 34,10
* @file
* @brief Memory string operations.
*
* This file provides architecture independent functions
* to manipulate blocks of memory. These functions
* are optimized as much as generic functions of
* this type can be. However, architectures are
* free to provide even more optimized versions of these
* functions.
* This file provides architecture independent functions to manipulate blocks of
* memory. These functions are optimized as much as generic functions of this
* type can be. However, architectures are free to provide even more optimized
* versions of these functions.
*/
 
#include <memstr.h>
46,32 → 44,33
#include <arch/types.h>
#include <align.h>
 
/** Copy block of memory
/** Copy block of memory.
*
* Copy cnt bytes from src address to dst address.
* The copying is done word-by-word and then byte-by-byte.
* The source and destination memory areas cannot overlap.
* Copy cnt bytes from src address to dst address. The copying is done
* word-by-word and then byte-by-byte. The source and destination memory areas
* cannot overlap.
*
* @param src Origin address to copy from.
* @param dst Origin address to copy to.
* @param cnt Number of bytes to copy.
* @param src Source address to copy from.
* @param dst Destination address to copy to.
* @param cnt Number of bytes to copy.
*
* @return Destination address.
*/
void *_memcpy(void * dst, const void *src, size_t cnt)
void *_memcpy(void *dst, const void *src, size_t cnt)
{
unsigned int i, j;
if (ALIGN_UP((uintptr_t) src, sizeof(unative_t)) != (uintptr_t) src ||
ALIGN_UP((uintptr_t) dst, sizeof(unative_t)) != (uintptr_t) dst) {
ALIGN_UP((uintptr_t) dst, sizeof(unative_t)) != (uintptr_t) dst) {
for (i = 0; i < cnt; i++)
((uint8_t *) dst)[i] = ((uint8_t *) src)[i];
} else {
for (i = 0; i < cnt / sizeof(unative_t); i++)
((unative_t *) dst)[i] = ((unative_t *) src)[i];
for (j = 0; j < cnt % sizeof(unative_t); j++)
((uint8_t *)(((unative_t *) dst) + i))[j] = ((uint8_t *)(((unative_t *) src) + i))[j];
((uint8_t *)(((unative_t *) dst) + i))[j] =
((uint8_t *)(((unative_t *) src) + i))[j];
}
return (char *) dst;
79,12 → 78,12
 
/** Fill block of memory
*
* Fill cnt bytes at dst address with the value x.
* The filling is done byte-by-byte.
* Fill cnt bytes at dst address with the value x. The filling is done
* byte-by-byte.
*
* @param dst Origin address to fill.
* @param cnt Number of bytes to fill.
* @param x Value to fill.
* @param dst Destination address to fill.
* @param cnt Number of bytes to fill.
* @param x Value to fill.
*
*/
void _memsetb(void *dst, size_t cnt, uint8_t x)
96,14 → 95,14
p[i] = x;
}
 
/** Fill block of memory
/** Fill block of memory.
*
* Fill cnt words at dst address with the value x.
* The filling is done word-by-word.
* Fill cnt words at dst address with the value x. The filling is done
* word-by-word.
*
* @param dst Origin address to fill.
* @param cnt Number of words to fill.
* @param x Value to fill.
* @param dst Destination address to fill.
* @param cnt Number of words to fill.
* @param x Value to fill.
*
*/
void _memsetw(void *dst, size_t cnt, uint16_t x)
115,16 → 114,16
p[i] = x;
}
 
/** Copy string
/** Copy string.
*
* Copy string from src address to dst address.
* The copying is done char-by-char until the null
* character. The source and destination memory areas
* cannot overlap.
* Copy string from src address to dst address. The copying is done
* char-by-char until the null character. The source and destination memory
* areas cannot overlap.
*
* @param src Origin string to copy from.
* @param dst Origin string to copy to.
* @param src Source string to copy from.
* @param dst Destination string to copy to.
*
* @return Address of the destination string.
*/
char *strcpy(char *dest, const char *src)
{
/branches/sparc/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/sparc/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
*
101,9 → 102,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();
111,6 → 119,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;
}
165,7 → 178,9
(syshandler_t) sys_sysinfo_value,
/* Debug calls */
(syshandler_t) sys_debug_enable_console
(syshandler_t) sys_debug_enable_console,
 
(syshandler_t) sys_ipc_connect_kbox
};
 
/** @}
/branches/sparc/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/sparc/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);
809,11 → 822,16
 
ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
atomic_dec(&TASK->active_calls);
 
if (call->flags & IPC_CALL_DISCARD_ANSWER) {
ipc_call_free(call);
goto restart;
} else {
/*
* Decrement the counter of active calls only if the
* call is not an answer to IPC_M_PHONE_HUNGUP,
* which doesn't contribute to the counter.
*/
atomic_dec(&TASK->active_calls);
}
 
STRUCT_TO_USPACE(&calldata->args, &call->data.args);
868,5 → 886,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/sparc/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,70
/* 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);
517,7 → 574,13
(call->flags & IPC_CALL_NOTIF));
ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
atomic_dec(&TASK->active_calls);
/*
* Record the receipt of this call in the current task's counter
* of active calls. IPC_M_PHONE_HUNGUP calls do not contribute
* to this counter so do not record answers to them either.
*/
if (!(call->flags & IPC_CALL_DISCARD_ANSWER))
atomic_dec(&TASK->active_calls);
ipc_call_free(call);
}
}
/branches/sparc/kernel/generic/src/udebug/udebug_ipc.c
0,0 → 1,343
/*
* 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.
*
* This module handles udebug IPC messages and calls the appropriate
* functions from the udebug_ops module which implement them.
*/
#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;
}
 
/** Process a BEGIN call.
*
* Initiates a debugging session for the current task. The reply
* to this call may or may not be sent before this function returns.
*
* @param call The call structure.
*/
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 the initialization of the debugging session has finished,
* send a reply.
*/
if (rc != 0) {
IPC_SET_RETVAL(call->data, 0);
ipc_answer(&TASK->kernel_box, call);
}
}
 
/** Process an END call.
*
* Terminates the debugging session for the current task.
* @param call The call structure.
*/
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);
}
 
/** Process a SET_EVMASK call.
*
* Sets an event mask for the current debugging session.
* @param call The call structure.
*/
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);
}
 
 
/** Process a GO call.
*
* Resumes execution of the specified thread.
* @param call The call structure.
*/
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;
}
}
 
/** Process a STOP call.
*
* Suspends execution of the specified thread.
* @param call The call structure.
*/
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);
}
 
/** Process a THREAD_READ call.
*
* Reads the list of hashes of the (userspace) threads in the current task.
* @param call The call structure.
*/
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);
}
 
/** Process an ARGS_READ call.
*
* Reads the argument of a current syscall event (SYSCALL_B or SYSCALL_E).
* @param call The call structure.
*/
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);
}
 
/** Process an MEM_READ call.
*
* Reads memory of the current (debugged) task.
* @param call The call structure.
*/
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. Verifies that the sender
* is indeed the debugger and calls the appropriate processing function.
*/
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/sparc/kernel/generic/src/udebug/udebug.c
0,0 → 1,563
/*
* 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 hooks and data structure management.
*
* Udebug is an interface that makes userspace debuggers possible.
*
* 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);
}
 
/** Initialize udebug part of task structure.
*
* Called as part of task structure initialization.
* @param ut Pointer to the structure to initialize.
*/
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;
}
 
/** Initialize udebug part of thread structure.
*
* Called as part of thread structure initialization.
* @param ut Pointer to the structure to initialize.
*/
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 */
}
 
/** Wait for a GO message.
*
* When a debugging event occurs in a thread or the thread is stopped,
* this function is called to block the thread until a GO message
* is received.
*
* @param wq The wait queue used by the thread to wait for GO messages.
*/
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.
*
* @return True if the thread was in a debugging session when the function
* checked, false otherwise.
*/
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;
}
 
/** Start of stoppable section.
*
* A stoppable section is a section of code where if the thread can be stoped. In other words,
* if a STOP operation is issued, the thread is guaranteed not to execute
* any userspace instructions until the thread is resumed.
*
* Having stoppable sections is better than having stopping points, since
* a thread can be stopped even when it is blocked indefinitely in a system
* call (whereas it would not reach any stopping point).
*/
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);
}
 
/** End of a stoppable section.
*
* This is the point where the thread will block if it is stopped.
* (As, by definition, a stopped thread must not leave its stoppable section).
*/
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();
}
 
/** Syscall event hook.
*
* Must be called before and after servicing a system call. This generates
* a SYSCALL_B or SYSCALL_E event, depending on the value of @a end_variant.
*/
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();
}
 
/** Thread-creation event hook.
*
* Must be called when a new userspace thread is created in the debugged
* task. Generates a THREAD_B event.
*
* @param t Structure of the thread being created. Not locked, as the
* thread is not executing yet.
*/
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();
}
 
/** Thread-termination event hook.
*
* Must be called when the current thread is terminating.
* Generates a THREAD_E event.
*/
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.
*
* Gracefully terminates the debugging session for a task. If the debugger
* is still waiting for events on some threads, it will receive a
* FINISHED event for each of them.
*
* @param ta Task structure. 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/sparc/kernel/generic/src/udebug/udebug_ops.c
0,0 → 1,523
/*
* 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.
*
* Udebug operations on tasks and threads are implemented here. The
* functions defined here are called from the udebug_ipc module
* when servicing udebug IPC messages.
*/
#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.
*
* @param t Pointer, need not at all be valid.
* @param having_go Required thread state.
*
* 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 */
}
 
/** End debugging operation on a thread. */
static void _thread_op_end(thread_t *t)
{
mutex_unlock(&t->udebug.lock);
}
 
/** Begin debugging the current task.
*
* Initiates a debugging session for the current task (and its threads).
* When the debugging session has started a reply will be sent to the
* UDEBUG_BEGIN call. This may happen immediately in this function if
* all the threads in this task are stoppable at the moment and in this
* case the function returns 1.
*
* Otherwise the function returns 0 and the reply will be sent as soon as
* all the threads become stoppable (i.e. they can be considered stopped).
*
* @param call The BEGIN call we are servicing.
* @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;
}
 
/** Finish debugging the current task.
*
* Closes the debugging session for the current task.
* @return Zero on success or negative error code.
*/
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;
}
 
/** Set the event mask.
*
* Sets the event mask that determines which events are enabled.
*
* @param mask Or combination of events that should be enabled.
* @return Zero on success or negative error code.
*/
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;
}
 
/** Give thread GO.
*
* Upon recieving a go message, the thread is given GO. Having GO
* means the thread is allowed to execute userspace code (until
* a debugging event or STOP occurs, at which point the thread loses GO.
*
* @param t The thread to operate on (unlocked and need not be valid).
* @param call The GO call that we are servicing.
*/
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;
}
 
/** Stop a thread (i.e. take its GO away)
*
* Generates a STOP event as soon as the thread becomes stoppable (i.e.
* can be considered stopped).
*
* @param t The thread to operate on (unlocked and need not be valid).
* @param call The GO call that we are servicing.
*/
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;
}
 
/** Read the list of userspace threads in the current task.
*
* The list takes the form of a sequence of thread hashes (i.e. the pointers
* to thread structures). A buffer of size @a buf_size is allocated and
* a pointer to it written to @a buffer. The sequence of hashes is written
* into this buffer.
*
* If the sequence is longer than @a buf_size bytes, only as much hashes
* as can fit are copied. The number of thread hashes copied is stored
* in @a n.
*
* The rationale for having @a buf_size is that this function is only
* used for servicing the THREAD_READ message, which always specifies
* a maximum size for the userspace buffer.
*
* @param buffer The buffer for storing thread hashes.
* @param buf_size Buffer size in bytes.
* @param n The actual number of hashes copied will be stored here.
*/
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;
}
 
/** Read the arguments of a system call.
*
* The arguments of the system call being being executed are copied
* to an allocated buffer and a pointer to it is written to @a buffer.
* The size of the buffer is exactly such that it can hold the maximum number
* of system-call arguments.
*
* Unless the thread is currently blocked in a SYSCALL_B or SYSCALL_E event,
* this function will fail with an EINVAL error code.
*
* @param buffer The buffer for storing thread hashes.
*/
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;
}
 
/** Read the memory of the debugged task.
*
* Reads @a n bytes from the address space of the debugged task, starting
* from @a uspace_addr. The bytes are copied into an allocated buffer
* and a pointer to it is written into @a buffer.
*
* @param uspace_addr Address from where to start reading.
* @param n Number of bytes to read.
* @param buffer For storing a pointer to the allocated buffer.
*/
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/sparc/kernel/Makefile
44,7 → 44,7
 
GCC_CFLAGS = -I$(INCLUDES) -O$(OPTIMIZATION) \
-fno-builtin -Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -Werror \
-nostdlib -nostdinc
-nostdlib -nostdinc -pipe
 
ICC_CFLAGS = -I$(INCLUDES) -O$(OPTIMIZATION) \
-fno-builtin -Wall -Wmissing-prototypes -Werror \
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
#
 
350,7 → 365,7
ln -sfn ../../genarch/include/ generic/include/genarch
 
depend: archlinks
-makedepend $(DEFS) $(CFLAGS) -f - $(ARCH_SOURCES) $(GENARCH_SOURCES) $(GENERIC_SOURCES) > Makefile.depend 2> /dev/null
-makedepend -f - -- $(DEFS) $(CFLAGS) -- $(ARCH_SOURCES) $(GENARCH_SOURCES) $(GENERIC_SOURCES) > Makefile.depend 2> /dev/null
 
arch/$(ARCH)/_link.ld: arch/$(ARCH)/_link.ld.in
$(GCC) $(DEFS) $(GCC_CFLAGS) -D__ASM__ -D__LINKER__ -E -x c $< | grep -v "^\#" > $@
/branches/sparc/kernel/arch/sparc64/include/trap/syscall.h
31,26 → 31,14
*/
/**
* @file
* @brief This file contains the trap_instruction handler.
*
* The trap_instruction trap is used to implement syscalls.
* @brief
*/
 
#ifndef KERN_sparc64_SYSCALL_TRAP_H_
#define KERN_sparc64_SYSCALL_TRAP_H_
 
#define TT_TRAP_INSTRUCTION(n) (0x100 + (n))
#define TT_TRAP_INSTRUCTION_LAST TT_TRAP_INSTRUCTION(127)
#define TT_TRAP_INSTRUCTION_0 0x100
 
#ifdef __ASM__
 
.macro TRAP_INSTRUCTION n
ba trap_instruction_handler
mov TT_TRAP_INSTRUCTION(\n) - TT_TRAP_INSTRUCTION(0), %g2
.endm
 
#endif /* __ASM__ */
 
#endif
 
/** @}
/branches/sparc/kernel/arch/sparc64/include/trap/regwin.h
39,6 → 39,7
 
#include <arch/stack.h>
#include <arch/arch.h>
#include <align.h>
 
#define TT_CLEAN_WINDOW 0x24
#define TT_SPILL_0_NORMAL 0x80 /* kernel spills */
72,6 → 73,11
#define I6_OFFSET 112
#define I7_OFFSET 120
 
/* Uspace Window Buffer constants. */
#define UWB_SIZE ((NWINDOWS - 1) * STACK_WINDOW_SAVE_AREA_SIZE)
#define UWB_ALIGNMENT 1024
#define UWB_ASIZE ALIGN_UP(UWB_SIZE, UWB_ALIGNMENT)
 
#ifdef __ASM__
 
/*
/branches/sparc/kernel/arch/sparc64/src/proc/thread.c
34,9 → 34,8
 
#include <proc/thread.h>
#include <arch/proc/thread.h>
#include <mm/frame.h>
#include <mm/page.h>
#include <arch/mm/page.h>
#include <mm/slab.h>
#include <arch/trap/regwin.h>
#include <align.h>
 
void thr_constructor_arch(thread_t *t)
50,12 → 49,12
void thr_destructor_arch(thread_t *t)
{
if (t->arch.uspace_window_buffer) {
uintptr_t uw_buf = (uintptr_t) t->arch.uspace_window_buffer;
/*
* Mind the possible alignment of the userspace window buffer
* belonging to a killed thread.
*/
frame_free(KA2PA(ALIGN_DOWN((uintptr_t)
t->arch.uspace_window_buffer, PAGE_SIZE)));
free((uint8_t *) ALIGN_DOWN(uw_buf, UWB_ALIGNMENT));
}
}
 
67,7 → 66,7
* The thread needs userspace window buffer and the object
* returned from the slab allocator doesn't have any.
*/
t->arch.uspace_window_buffer = frame_alloc(ONE_FRAME, FRAME_KA);
t->arch.uspace_window_buffer = malloc(UWB_ASIZE, 0);
} else {
uintptr_t uw_buf = (uintptr_t) t->arch.uspace_window_buffer;
 
76,7 → 75,7
* belonging to a killed thread.
*/
t->arch.uspace_window_buffer = (uint8_t *) ALIGN_DOWN(uw_buf,
PAGE_SIZE);
UWB_ASIZE);
}
}
 
/branches/sparc/kernel/arch/sparc64/src/trap/trap_table.S
329,198 → 329,22
fill_1_normal_tl0:
FILL_NORMAL_HANDLER_USERSPACE
 
/* TT = 0x100, TL = 0, trap_instruction_0 */
.org trap_table + TT_TRAP_INSTRUCTION(0)*ENTRY_SIZE
.global trap_instruction_0_tl0
trap_instruction_0_tl0:
TRAP_INSTRUCTION 0
/* TT = 0x100 - 0x17f, TL = 0, trap_instruction_0 - trap_instruction_7f */
.irp cur, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,\
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,\
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,\
58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,\
77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,\
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,\
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,\
127
.org trap_table + (TT_TRAP_INSTRUCTION_0+\cur)*ENTRY_SIZE
.global trap_instruction_\cur\()_tl0
trap_instruction_\cur\()_tl0:
ba trap_instruction_handler
mov \cur, %g2
.endr
 
/* TT = 0x101, TL = 0, trap_instruction_1 */
.org trap_table + TT_TRAP_INSTRUCTION(1)*ENTRY_SIZE
.global trap_instruction_1_tl0
trap_instruction_1_tl0:
TRAP_INSTRUCTION 1
 
/* TT = 0x102, TL = 0, trap_instruction_2 */
.org trap_table + TT_TRAP_INSTRUCTION(2)*ENTRY_SIZE
.global trap_instruction_2_tl0
trap_instruction_2_tl0:
TRAP_INSTRUCTION 2
 
/* TT = 0x103, TL = 0, trap_instruction_3 */
.org trap_table + TT_TRAP_INSTRUCTION(3)*ENTRY_SIZE
.global trap_instruction_3_tl0
trap_instruction_3_tl0:
TRAP_INSTRUCTION 3
 
/* TT = 0x104, TL = 0, trap_instruction_4 */
.org trap_table + TT_TRAP_INSTRUCTION(4)*ENTRY_SIZE
.global trap_instruction_4_tl0
trap_instruction_4_tl0:
TRAP_INSTRUCTION 4
 
/* TT = 0x105, TL = 0, trap_instruction_5 */
.org trap_table + TT_TRAP_INSTRUCTION(5)*ENTRY_SIZE
.global trap_instruction_5_tl0
trap_instruction_5_tl0:
TRAP_INSTRUCTION 5
 
/* TT = 0x106, TL = 0, trap_instruction_6 */
.org trap_table + TT_TRAP_INSTRUCTION(6)*ENTRY_SIZE
.global trap_instruction_6_tl0
trap_instruction_6_tl0:
TRAP_INSTRUCTION 6
 
/* TT = 0x107, TL = 0, trap_instruction_7 */
.org trap_table + TT_TRAP_INSTRUCTION(7)*ENTRY_SIZE
.global trap_instruction_7_tl0
trap_instruction_7_tl0:
TRAP_INSTRUCTION 7
 
/* TT = 0x108, TL = 0, trap_instruction_8 */
.org trap_table + TT_TRAP_INSTRUCTION(8)*ENTRY_SIZE
.global trap_instruction_8_tl0
trap_instruction_8_tl0:
TRAP_INSTRUCTION 8
 
/* TT = 0x109, TL = 0, trap_instruction_9 */
.org trap_table + TT_TRAP_INSTRUCTION(9)*ENTRY_SIZE
.global trap_instruction_9_tl0
trap_instruction_9_tl0:
TRAP_INSTRUCTION 9
 
/* TT = 0x10a, TL = 0, trap_instruction_10 */
.org trap_table + TT_TRAP_INSTRUCTION(10)*ENTRY_SIZE
.global trap_instruction_10_tl0
trap_instruction_10_tl0:
TRAP_INSTRUCTION 10
 
/* TT = 0x10b, TL = 0, trap_instruction_11 */
.org trap_table + TT_TRAP_INSTRUCTION(11)*ENTRY_SIZE
.global trap_instruction_11_tl0
trap_instruction_11_tl0:
TRAP_INSTRUCTION 11
 
/* TT = 0x10c, TL = 0, trap_instruction_12 */
.org trap_table + TT_TRAP_INSTRUCTION(12)*ENTRY_SIZE
.global trap_instruction_12_tl0
trap_instruction_12_tl0:
TRAP_INSTRUCTION 12
 
/* TT = 0x10d, TL = 0, trap_instruction_13 */
.org trap_table + TT_TRAP_INSTRUCTION(13)*ENTRY_SIZE
.global trap_instruction_13_tl0
trap_instruction_13_tl0:
TRAP_INSTRUCTION 13
 
/* TT = 0x10e, TL = 0, trap_instruction_14 */
.org trap_table + TT_TRAP_INSTRUCTION(14)*ENTRY_SIZE
.global trap_instruction_14_tl0
trap_instruction_14_tl0:
TRAP_INSTRUCTION 14
 
/* TT = 0x10f, TL = 0, trap_instruction_15 */
.org trap_table + TT_TRAP_INSTRUCTION(15)*ENTRY_SIZE
.global trap_instruction_15_tl0
trap_instruction_15_tl0:
TRAP_INSTRUCTION 15
 
/* TT = 0x110, TL = 0, trap_instruction_16 */
.org trap_table + TT_TRAP_INSTRUCTION(16)*ENTRY_SIZE
.global trap_instruction_16_tl0
trap_instruction_16_tl0:
TRAP_INSTRUCTION 16
 
/* TT = 0x111, TL = 0, trap_instruction_17 */
.org trap_table + TT_TRAP_INSTRUCTION(17)*ENTRY_SIZE
.global trap_instruction_17_tl0
trap_instruction_17_tl0:
TRAP_INSTRUCTION 17
 
/* TT = 0x112, TL = 0, trap_instruction_18 */
.org trap_table + TT_TRAP_INSTRUCTION(18)*ENTRY_SIZE
.global trap_instruction_18_tl0
trap_instruction_18_tl0:
TRAP_INSTRUCTION 18
 
/* TT = 0x113, TL = 0, trap_instruction_19 */
.org trap_table + TT_TRAP_INSTRUCTION(19)*ENTRY_SIZE
.global trap_instruction_19_tl0
trap_instruction_19_tl0:
TRAP_INSTRUCTION 19
 
/* TT = 0x114, TL = 0, trap_instruction_20 */
.org trap_table + TT_TRAP_INSTRUCTION(20)*ENTRY_SIZE
.global trap_instruction_20_tl0
trap_instruction_20_tl0:
TRAP_INSTRUCTION 20
 
/* TT = 0x115, TL = 0, trap_instruction_21 */
.org trap_table + TT_TRAP_INSTRUCTION(21)*ENTRY_SIZE
.global trap_instruction_21_tl0
trap_instruction_21_tl0:
TRAP_INSTRUCTION 21
 
/* TT = 0x116, TL = 0, trap_instruction_22 */
.org trap_table + TT_TRAP_INSTRUCTION(22)*ENTRY_SIZE
.global trap_instruction_22_tl0
trap_instruction_22_tl0:
TRAP_INSTRUCTION 22
 
/* TT = 0x117, TL = 0, trap_instruction_23 */
.org trap_table + TT_TRAP_INSTRUCTION(23)*ENTRY_SIZE
.global trap_instruction_23_tl0
trap_instruction_23_tl0:
TRAP_INSTRUCTION 23
 
/* TT = 0x118, TL = 0, trap_instruction_24 */
.org trap_table + TT_TRAP_INSTRUCTION(24)*ENTRY_SIZE
.global trap_instruction_24_tl0
trap_instruction_24_tl0:
TRAP_INSTRUCTION 24
 
/* TT = 0x119, TL = 0, trap_instruction_25 */
.org trap_table + TT_TRAP_INSTRUCTION(25)*ENTRY_SIZE
.global trap_instruction_25_tl0
trap_instruction_25_tl0:
TRAP_INSTRUCTION 25
 
/* TT = 0x11a, TL = 0, trap_instruction_26 */
.org trap_table + TT_TRAP_INSTRUCTION(26)*ENTRY_SIZE
.global trap_instruction_26_tl0
trap_instruction_26_tl0:
TRAP_INSTRUCTION 26
 
/* TT = 0x11b, TL = 0, trap_instruction_27 */
.org trap_table + TT_TRAP_INSTRUCTION(27)*ENTRY_SIZE
.global trap_instruction_27_tl0
trap_instruction_27_tl0:
TRAP_INSTRUCTION 27
 
/* TT = 0x11c, TL = 0, trap_instruction_28 */
.org trap_table + TT_TRAP_INSTRUCTION(28)*ENTRY_SIZE
.global trap_instruction_28_tl0
trap_instruction_28_tl0:
TRAP_INSTRUCTION 28
 
/* TT = 0x11d, TL = 0, trap_instruction_29 */
.org trap_table + TT_TRAP_INSTRUCTION(29)*ENTRY_SIZE
.global trap_instruction_29_tl0
trap_instruction_29_tl0:
TRAP_INSTRUCTION 29
 
/* TT = 0x11e, TL = 0, trap_instruction_30 */
.org trap_table + TT_TRAP_INSTRUCTION(30)*ENTRY_SIZE
.global trap_instruction_30_tl0
trap_instruction_30_tl0:
TRAP_INSTRUCTION 30
 
/* TT = 0x11f, TL = 0, trap_instruction_31 */
.org trap_table + TT_TRAP_INSTRUCTION(31)*ENTRY_SIZE
.global trap_instruction_31_tl0
trap_instruction_31_tl0:
TRAP_INSTRUCTION 31
 
/*
* Handlers for TL>0.
*/
924,9 → 748,8
* Fill all windows stored in the buffer.
*/
clr %g4
set PAGE_SIZE - 1, %g5
0: andcc %g7, %g5, %g0 ! PAGE_SIZE alignment check
bz 0f ! %g7 is page-aligned, no more windows to refill
0: andcc %g7, UWB_ALIGNMENT - 1, %g0 ! alignment check
bz 0f ! %g7 is UWB_ALIGNMENT-aligned, no more windows to refill
nop
 
add %g7, -STACK_WINDOW_SAVE_AREA_SIZE, %g7
/branches/sparc/kernel/arch/ia64/include/asm.h
44,21 → 44,50
 
static inline void outb(uint64_t port,uint8_t v)
{
*((char *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 )))) = v;
*((uint8_t *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 )))) = v;
 
asm volatile ("mf\n" ::: "memory");
}
 
static inline void outw(uint64_t port,uint16_t v)
{
*((uint16_t *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 )))) = v;
 
asm volatile ("mf\n" ::: "memory");
}
 
static inline void outl(uint64_t port,uint32_t v)
{
*((uint32_t *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 )))) = v;
 
asm volatile ("mf\n" ::: "memory");
}
 
 
 
static inline uint8_t inb(uint64_t port)
{
asm volatile ("mf\n" ::: "memory");
 
return *((char *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 ))));
return *((uint8_t *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 ))));
}
 
static inline uint16_t inw(uint64_t port)
{
asm volatile ("mf\n" ::: "memory");
 
return *((uint16_t *)(IA64_IOSPACE_ADDRESS + ( (port & 0xffE) | ( (port >> 2) << 12 ))));
}
 
static inline uint32_t inl(uint64_t port)
{
asm volatile ("mf\n" ::: "memory");
 
return *((uint32_t *)(IA64_IOSPACE_ADDRESS + ( (port & 0xfff) | ( (port >> 2) << 12 ))));
}
 
 
 
/** Return base address of current stack
*
* Return the base address of the current stack.
/branches/sparc/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
/branches/sparc/kernel/arch/ppc32/include/asm/regname.h
214,8 → 214,8
#define hid0 1008
 
/* MSR bits */
#define msr_ir (1 << 4)
#define msr_dr (1 << 5)
#define msr_dr (1 << 4)
#define msr_ir (1 << 5)
#define msr_pr (1 << 14)
#define msr_ee (1 << 15)
 
/branches/sparc/kernel/arch/ppc32/include/exception.h
36,6 → 36,7
#define KERN_ppc32_EXCEPTION_H_
 
#include <arch/types.h>
#include <arch/regutils.h>
 
typedef struct {
uint32_t r0;
84,11 → 85,10
}
 
/** Return true if exception happened while in userspace */
#include <panic.h>
static inline int istate_from_uspace(istate_t *istate)
{
panic("istate_from_uspace not yet implemented");
return 0;
/* true if privilege level PR (copied from MSR) == 1 */
return (istate->srr1 & MSR_PR) != 0;
}
 
static inline unative_t istate_get_pc(istate_t *istate)
/branches/sparc/kernel/arch/ppc32/include/regutils.h
0,0 → 1,45
/*
* 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 ppc32
* @{
*/
/**
* @file
* @brief Utilities for convenient manipulation with ppc32 registers.
*/
 
#ifndef KERN_ppc32_REGUTILS_H_
#define KERN_ppc32_REGUTILS_H_
 
#define MSR_PR (1 << 14)
 
#endif
 
/** @}
*/
/branches/sparc/kernel/arch/amd64/include/syscall.h
35,8 → 35,6
#ifndef KERN_amd64_SYSCALL_H_
#define KERN_amd64_SYSCALL_H_
 
#include <arch/types.h>
 
extern void syscall_setup_cpu(void);
 
#endif
/branches/sparc/kernel/arch/ia32/include/syscall.h
0,0 → 1,0
link ../../amd64/include/syscall.h
Property changes:
Added: svn:special
+*
\ No newline at end of property
/branches/sparc/kernel/arch/ia32/include/asm.h
247,6 → 247,22
return v;
}
 
/** Write to MSR */
static inline void write_msr(uint32_t msr, uint64_t value)
{
asm volatile ("wrmsr" : : "c" (msr), "a" ((uint32_t)(value)),
"d" ((uint32_t)(value >> 32)));
}
 
static inline uint64_t read_msr(uint32_t msr)
{
uint32_t ax, dx;
 
asm volatile ("rdmsr" : "=a"(ax), "=d"(dx) : "c" (msr));
return ((uint64_t)dx << 32) | ax;
}
 
 
/** Return base address of current stack
*
* Return the base address of the current stack.
/branches/sparc/kernel/arch/ia32/include/cpu.h
35,12 → 35,21
#ifndef KERN_ia32_CPU_H_
#define KERN_ia32_CPU_H_
 
#define EFLAGS_IF (1 << 9)
#define EFLAGS_RF (1 << 16)
 
#define CR4_OSFXSR_MASK (1<<9)
 
/* Support for SYSENTER and SYSEXIT */
#define IA32_MSR_SYSENTER_CS 0x174
#define IA32_MSR_SYSENTER_ESP 0x175
#define IA32_MSR_SYSENTER_EIP 0x176
 
#ifndef __ASM__
 
#include <arch/pm.h>
#include <arch/asm.h>
 
#define EFLAGS_IF (1 << 9)
#define EFLAGS_RF (1 << 16)
 
typedef struct {
int vendor;
int family;
51,9 → 60,8
count_t iomapver_copy; /** Copy of TASK's I/O Permission bitmap generation count. */
} cpu_arch_t;
 
#endif
 
#define CR4_OSFXSR_MASK (1<<9)
 
#endif
 
/** @}
/branches/sparc/kernel/arch/ia32/Makefile.inc
160,4 → 160,5
arch/$(ARCH)/src/boot/boot.S \
arch/$(ARCH)/src/boot/memmap.c \
arch/$(ARCH)/src/fpu_context.c \
arch/$(ARCH)/src/debugger.c
arch/$(ARCH)/src/debugger.c \
arch/$(ARCH)/src/syscall.c
/branches/sparc/kernel/arch/ia32/src/asm.S
147,6 → 147,45
popfl
.endm
 
/*
* The SYSENTER syscall mechanism can be used for syscalls with
* four or fewer arguments. To pass these four arguments, we
* use four registers: EDX, ECX, EBX, ESI. The syscall number
* is passed in EAX. We use EDI to remember the return address
* and EBP to remember the stack. The INT-based syscall mechanism
* can actually handle six arguments plus the syscall number
* entirely in registers.
*/
.global sysenter_handler
sysenter_handler:
pushl %ebp # remember user stack
pushl %edi # remember return user address
 
pushl %gs # remember TLS
 
pushl %eax # syscall number
subl $8, %esp # unused sixth and fifth argument
pushl %esi # fourth argument
pushl %ebx # third argument
pushl %ecx # second argument
pushl %edx # first argument
 
movw $16, %ax
movw %ax, %ds
movw %ax, %es
 
cld
call syscall_handler
addl $28, %esp # remove arguments from stack
 
pop %gs # restore TLS
 
pop %edx # prepare return EIP for SYSEXIT
pop %ecx # prepare userspace ESP for SYSEXIT
 
sysexit # return to userspace
 
 
## Declare interrupt handlers
#
# Declare interrupt handlers for n interrupt
/branches/sparc/kernel/arch/ia32/src/cpu/cpu.c
42,6 → 42,7
#include <fpu_context.h>
 
#include <arch/smp/apic.h>
#include <arch/syscall.h>
 
/*
* Identification of CPUs.
123,6 → 124,9
: "i" (CR4_OSFXSR_MASK|(1<<10))
);
}
/* Setup fast SYSENTER/SYSEXIT syscalls */
syscall_setup_cpu();
}
 
void cpu_identify(void)
/branches/sparc/kernel/arch/ia32/src/syscall.c
0,0 → 1,53
/*
* Copyright (c) 2008 Jakub Jermar
* 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 ia32
* @{
*/
/** @file
*/
 
#include <arch/syscall.h>
#include <arch/cpu.h>
#include <arch/asm.h>
#include <arch/types.h>
#include <arch/pm.h>
 
/** Enable & setup support for SYSENTER/SYSEXIT */
void syscall_setup_cpu(void)
{
extern void sysenter_handler(void);
 
/* set kernel mode CS selector */
write_msr(IA32_MSR_SYSENTER_CS, selector(KTEXT_DES));
/* set kernel mode entry point */
write_msr(IA32_MSR_SYSENTER_EIP, (uint32_t) sysenter_handler);
}
 
/** @}
*/
/branches/sparc/kernel/arch/ia32/src/proc/scheduler.c
58,8 → 58,14
*/
void before_thread_runs_arch(void)
{
CPU->arch.tss->esp0 = (uintptr_t) &THREAD->kstack[THREAD_STACK_SIZE -
uintptr_t kstk = (uintptr_t) &THREAD->kstack[THREAD_STACK_SIZE -
SP_DELTA];
 
/* Set kernel stack for CP3 -> CPL0 switch via SYSENTER */
write_msr(IA32_MSR_SYSENTER_ESP, kstk);
 
/* Set kernel stack for CPL3 -> CPL0 switch via interrupt */
CPU->arch.tss->esp0 = kstk;
CPU->arch.tss->ss0 = selector(KDATA_DES);
 
/* Set up TLS in GS register */