Subversion Repositories HelenOS

Compare Revisions

No changes between revisions

Ignore whitespace Rev 4686 → Rev 4687

/tags/0.4.1/kernel/generic/include/string.h
0,0 → 1,99
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_STRING_H_
#define KERN_STRING_H_
 
#include <typedefs.h>
 
/**< Common Unicode characters */
#define U_SPECIAL '?'
 
#define U_LEFT_ARROW 0x2190
#define U_UP_ARROW 0x2191
#define U_RIGHT_ARROW 0x2192
#define U_DOWN_ARROW 0x2193
 
#define U_PAGE_UP 0x21de
#define U_PAGE_DOWN 0x21df
 
#define U_HOME_ARROW 0x21f1
#define U_END_ARROW 0x21f2
 
#define U_NULL 0x2400
#define U_ESCAPE 0x241b
#define U_DELETE 0x2421
 
#define U_CURSOR 0x2588
 
/**< No size limit constant */
#define STR_NO_LIMIT ((size_t) -1)
 
/**< Maximum size of a string containing cnt characters */
#define STR_BOUNDS(cnt) (cnt << 2)
 
extern wchar_t str_decode(const char *str, size_t *offset, size_t sz);
extern int chr_encode(wchar_t ch, char *str, size_t *offset, size_t sz);
 
extern size_t str_size(const char *str);
extern size_t wstr_size(const wchar_t *str);
 
extern size_t str_lsize(const char *str, size_t max_len);
extern size_t wstr_lsize(const wchar_t *str, size_t max_len);
 
extern size_t str_length(const char *str);
extern size_t wstr_length(const wchar_t *wstr);
 
extern size_t str_nlength(const char *str, size_t size);
extern size_t wstr_nlength(const wchar_t *str, size_t size);
 
extern bool ascii_check(wchar_t ch);
extern bool chr_check(wchar_t ch);
 
extern int str_cmp(const char *s1, const char *s2);
extern int str_lcmp(const char *s1, const char *s2, size_t max_len);
 
extern void str_cpy(char *dest, size_t size, const char *src);
extern void str_ncpy(char *dest, size_t size, const char *src, size_t n);
extern void wstr_nstr(char *dst, const wchar_t *src, size_t size);
 
extern char *str_chr(const char *str, wchar_t ch);
 
extern bool wstr_linsert(wchar_t *str, wchar_t ch, size_t pos, size_t max_pos);
extern bool wstr_remove(wchar_t *str, size_t pos);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/context.h
0,0 → 1,89
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_CONTEXT_H_
#define KERN_CONTEXT_H_
 
#include <arch/types.h>
#include <arch/context.h>
 
 
#ifndef context_set
#define context_set(c, _pc, stack, size) \
(c)->pc = (uintptr_t) (_pc); \
(c)->sp = ((uintptr_t) (stack)) + (size) - SP_DELTA;
#endif /* context_set */
 
extern int context_save_arch(context_t *c) __attribute__ ((returns_twice));
extern void context_restore_arch(context_t *c) __attribute__ ((noreturn));
 
/** Save register context.
*
* Save current register context (including stack pointers)
* to context structure.
*
* Note that call to context_restore() will return at the same
* address as the corresponding call to context_save().
*
* This MUST be a macro, gcc -O0 does not inline functions even
* if they are marked inline and context_save_arch must be called
* from level <= that when context_restore is called.
*
* @param c Context structure.
*
* @return context_save() returns 1, context_restore() returns 0.
*/
#define context_save(c) context_save_arch(c)
 
/** Restore register context.
*
* Restore previously saved register context (including stack pointers)
* from context structure.
*
* Note that this function does not normally return.
* Instead, it returns at the same address as the
* corresponding call to context_save(), the only
* difference being return value.
*
* @param c Context structure.
*/
static inline void context_restore(context_t *c)
{
context_restore_arch(c);
}
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/byteorder.h
0,0 → 1,111
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_BYTEORDER_H_
#define KERN_BYTEORDER_H_
 
#include <arch/types.h>
 
#if !(defined(__BE__) ^ defined(__LE__))
#error The architecture must be either big-endian or little-endian.
#endif
 
#ifdef __BE__
 
#define uint16_t_le2host(n) (uint16_t_byteorder_swap(n))
#define uint32_t_le2host(n) (uint32_t_byteorder_swap(n))
#define uint64_t_le2host(n) (uint64_t_byteorder_swap(n))
 
#define uint16_t_be2host(n) (n)
#define uint32_t_be2host(n) (n)
#define uint64_t_be2host(n) (n)
 
#define host2uint16_t_le(n) (uint16_t_byteorder_swap(n))
#define host2uint32_t_le(n) (uint32_t_byteorder_swap(n))
#define host2uint64_t_le(n) (uint64_t_byteorder_swap(n))
 
#define host2uint16_t_be(n) (n)
#define host2uint32_t_be(n) (n)
#define host2uint64_t_be(n) (n)
 
#else
 
#define uint16_t_le2host(n) (n)
#define uint32_t_le2host(n) (n)
#define uint64_t_le2host(n) (n)
 
#define uint16_t_be2host(n) (uint16_t_byteorder_swap(n))
#define uint32_t_be2host(n) (uint32_t_byteorder_swap(n))
#define uint64_t_be2host(n) (uint64_t_byteorder_swap(n))
 
#define host2uint16_t_le(n) (n)
#define host2uint32_t_le(n) (n)
#define host2uint64_t_le(n) (n)
 
#define host2uint16_t_be(n) (uint16_t_byteorder_swap(n))
#define host2uint32_t_be(n) (uint32_t_byteorder_swap(n))
#define host2uint64_t_be(n) (uint64_t_byteorder_swap(n))
 
#endif
 
static inline uint64_t uint64_t_byteorder_swap(uint64_t n)
{
return ((n & 0xff) << 56) |
((n & 0xff00) << 40) |
((n & 0xff0000) << 24) |
((n & 0xff000000LL) << 8) |
((n & 0xff00000000LL) >> 8) |
((n & 0xff0000000000LL) >> 24) |
((n & 0xff000000000000LL) >> 40) |
((n & 0xff00000000000000LL) >> 56);
}
 
static inline uint32_t uint32_t_byteorder_swap(uint32_t n)
{
return ((n & 0xff) << 24) |
((n & 0xff00) << 8) |
((n & 0xff0000) >> 8) |
((n & 0xff000000) >> 24);
}
 
static inline uint16_t uint16_t_byteorder_swap(uint16_t n)
{
return ((n & 0xff) << 8) |
((n & 0xff00) >> 8);
}
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/symtab.h
0,0 → 1,63
/*
* Copyright (c) 2005 Ondrej Palkovsky
* 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_SYMTAB_H_
#define KERN_SYMTAB_H_
 
#include <arch/types.h>
 
#define MAX_SYMBOL_NAME 64
 
struct symtab_entry {
uint64_t address_le;
char symbol_name[MAX_SYMBOL_NAME];
};
 
extern int symtab_name_lookup(unative_t addr, char **name);
extern char *symtab_fmt_name_lookup(unative_t addr);
extern int symtab_addr_lookup(const char *name, uintptr_t *addr);
extern void symtab_print_search(const char *name);
extern int symtab_compl(char *input, size_t size);
 
#ifdef CONFIG_SYMTAB
 
/* Symtable linked together by build process */
extern struct symtab_entry symbol_table[];
 
#endif
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/config.h
0,0 → 1,85
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_CONFIG_H_
#define KERN_CONFIG_H_
 
#include <arch/types.h>
#include <arch/mm/page.h>
 
#define STACK_SIZE PAGE_SIZE
 
#define CONFIG_INIT_TASKS 32
#define CONFIG_TASK_NAME_BUFLEN 32
 
typedef struct {
uintptr_t addr;
size_t size;
char name[CONFIG_TASK_NAME_BUFLEN];
} init_task_t;
 
typedef struct {
size_t cnt;
init_task_t tasks[CONFIG_INIT_TASKS];
} init_t;
 
/** Boot allocations.
*
* Allocatations made by the boot that are meant to be used by the kernel
* are all recorded in the ballocs_t type.
*/
typedef struct {
uintptr_t base;
size_t size;
} ballocs_t;
 
typedef struct {
size_t cpu_count; /**< Number of processors detected. */
volatile size_t cpu_active; /**< Number of processors that are up and running. */
uintptr_t base;
size_t kernel_size; /**< Size of memory in bytes taken by kernel and stack */
uintptr_t stack_base; /**< Base adddress of initial stack */
size_t stack_size; /**< Size of initial stack */
} config_t;
 
extern config_t config;
extern init_t init;
extern ballocs_t ballocs;
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/proc/scheduler.h
0,0 → 1,72
/*
* Copyright (c) 2001-2004 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 genericproc
* @{
*/
/** @file
*/
 
#ifndef KERN_SCHEDULER_H_
#define KERN_SCHEDULER_H_
 
#include <synch/spinlock.h>
#include <time/clock.h> /* HZ */
#include <atomic.h>
#include <adt/list.h>
 
#define RQ_COUNT 16
#define NEEDS_RELINK_MAX (HZ)
 
/** Scheduler run queue structure. */
typedef struct {
SPINLOCK_DECLARE(lock);
link_t rq_head; /**< List of ready threads. */
size_t n; /**< Number of threads in rq_ready. */
} runq_t;
 
extern atomic_t nrdy;
extern void scheduler_init(void);
 
extern void scheduler_fpu_lazy_request(void);
extern void scheduler(void);
extern void kcpulb(void *arg);
 
extern void sched_print_list(void);
 
/*
* To be defined by architectures:
*/
extern void before_task_runs_arch(void);
extern void before_thread_runs_arch(void);
extern void after_thread_ran_arch(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/proc/task.h
0,0 → 1,152
/*
* Copyright (c) 2001-2004 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 genericproc
* @{
*/
/** @file
*/
 
#ifndef KERN_TASK_H_
#define KERN_TASK_H_
 
#include <cpu.h>
#include <ipc/ipc.h>
#include <synch/spinlock.h>
#include <synch/mutex.h>
#include <synch/rwlock.h>
#include <synch/futex.h>
#include <adt/avl.h>
#include <adt/btree.h>
#include <adt/list.h>
#include <security/cap.h>
#include <arch/proc/task.h>
#include <arch/proc/thread.h>
#include <arch/context.h>
#include <arch/fpu_context.h>
#include <arch/cpu.h>
#include <mm/tlb.h>
#include <proc/scheduler.h>
#include <udebug/udebug.h>
#include <ipc/kbox.h>
 
#define TASK_NAME_BUFLEN 20
 
struct thread;
 
/** Task structure. */
typedef struct task {
/** Task's linkage for the tasks_tree AVL tree. */
avltree_node_t tasks_tree_node;
/** Task lock.
*
* Must be acquired before threads_lock and thread lock of any of its
* threads.
*/
SPINLOCK_DECLARE(lock);
 
char name[TASK_NAME_BUFLEN];
/** List of threads contained in this task. */
link_t th_head;
/** Address space. */
as_t *as;
/** Unique identity of task. */
task_id_t taskid;
/** Task security context. */
context_id_t context;
 
/** Number of references (i.e. threads). */
atomic_t refcount;
/** Number of threads that haven't exited yet. */
atomic_t lifecount;
 
/** Task capabilities. */
cap_t capabilities;
 
/* IPC stuff */
answerbox_t answerbox; /**< Communication endpoint */
phone_t phones[IPC_MAX_PHONES];
/**
* Active asynchronous messages. It is used for limiting uspace to
* certain extent.
*/
atomic_t active_calls;
 
#ifdef CONFIG_UDEBUG
/** Debugging stuff. */
udebug_task_t udebug;
 
/** Kernel answerbox. */
kbox_t kb;
#endif
 
/** Architecture specific task data. */
task_arch_t arch;
/**
* Serializes access to the B+tree of task's futexes. This mutex is
* independent on the task spinlock.
*/
mutex_t futexes_lock;
/** B+tree of futexes referenced by this task. */
btree_t futexes;
/** Accumulated accounting. */
uint64_t cycles;
} task_t;
 
SPINLOCK_EXTERN(tasks_lock);
extern avltree_t tasks_tree;
 
extern void task_init(void);
extern void task_done(void);
extern task_t *task_create(as_t *as, char *name);
extern void task_destroy(task_t *t);
extern task_t *task_find_by_id(task_id_t id);
extern int task_kill(task_id_t id);
extern uint64_t task_get_accounting(task_t *t);
 
extern void cap_set(task_t *t, cap_t caps);
extern cap_t cap_get(task_t *t);
 
#ifndef task_create_arch
extern void task_create_arch(task_t *t);
#endif
 
#ifndef task_destroy_arch
extern void task_destroy_arch(task_t *t);
#endif
 
extern unative_t sys_task_get_id(task_id_t *uspace_task_id);
extern unative_t sys_task_set_name(const char *uspace_name, size_t name_len);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/proc/program.h
0,0 → 1,67
/*
* 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 genericproc
* @{
*/
/** @file
*/
 
#ifndef KERN_PROGRAM_H_
#define KERN_PROGRAM_H_
 
#include <arch/types.h>
 
struct task;
struct thread;
 
/** Program info structure.
*
* A program is an abstraction of a freshly created (not yet running)
* userspace task containing a main thread along with its userspace stack.
*/
typedef struct program {
struct task *task; /**< Program task */
struct thread *main_thread; /**< Program main thread */
} program_t;
 
extern void *program_loader;
 
extern void program_create(as_t *as, uintptr_t entry_addr, char *name,
program_t *p);
extern int program_create_from_image(void *image_addr, char *name,
program_t *p);
extern int program_create_loader(program_t *p, char *name);
extern void program_ready(program_t *p);
 
extern unative_t sys_program_spawn_loader(char *uspace_name, size_t name_len);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/proc/thread.h
0,0 → 1,270
/*
* Copyright (c) 2001-2007 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 genericproc
* @{
*/
/** @file
*/
 
#ifndef KERN_THREAD_H_
#define KERN_THREAD_H_
 
#include <synch/waitq.h>
#include <proc/task.h>
#include <time/timeout.h>
#include <cpu.h>
#include <synch/rwlock.h>
#include <synch/spinlock.h>
#include <adt/avl.h>
#include <mm/slab.h>
#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
 
extern char *thread_states[];
 
/* Thread flags */
 
/** Thread cannot be migrated to another CPU.
*
* When using this flag, the caller must set cpu in the thread_t
* structure manually before calling thread_ready (even on uniprocessor).
*/
#define THREAD_FLAG_WIRED (1 << 0)
/** Thread was migrated to another CPU and has not run yet. */
#define THREAD_FLAG_STOLEN (1 << 1)
/** Thread executes in userspace. */
#define THREAD_FLAG_USPACE (1 << 2)
/** Thread will be attached by the caller. */
#define THREAD_FLAG_NOATTACH (1 << 3)
 
/** Thread states. */
typedef enum {
/** It is an error, if thread is found in this state. */
Invalid,
/** State of a thread that is currently executing on some CPU. */
Running,
/** Thread in this state is waiting for an event. */
Sleeping,
/** State of threads in a run queue. */
Ready,
/** Threads are in this state before they are first readied. */
Entering,
/** After a thread calls thread_exit(), it is put into Exiting state. */
Exiting,
/** Threads that were not detached but exited are Lingering. */
Lingering
} state_t;
 
/** Thread structure. There is one per thread. */
typedef struct thread {
link_t rq_link; /**< Run queue link. */
link_t wq_link; /**< Wait queue link. */
link_t th_link; /**< Links to threads within containing task. */
 
/** Threads linkage to the threads_tree. */
avltree_node_t threads_tree_node;
/** Lock protecting thread structure.
*
* Protects the whole thread structure except list links above.
*/
SPINLOCK_DECLARE(lock);
 
char name[THREAD_NAME_BUFLEN];
 
/** Function implementing the thread. */
void (* thread_code)(void *);
/** Argument passed to thread_code() function. */
void *thread_arg;
 
/**
* From here, the stored context is restored when the thread is
* scheduled.
*/
context_t saved_context;
/**
* From here, the stored timeout context is restored when sleep times
* out.
*/
context_t sleep_timeout_context;
/**
* From here, the stored interruption context is restored when sleep is
* interrupted.
*/
context_t sleep_interruption_context;
 
/** If true, the thread can be interrupted from sleep. */
bool sleep_interruptible;
/** Wait queue in which this thread sleeps. */
waitq_t *sleep_queue;
/** Timeout used for timeoutable sleeping. */
timeout_t sleep_timeout;
/** Flag signalling sleep timeout in progress. */
volatile int timeout_pending;
 
/**
* True if this thread is executing copy_from_uspace().
* False otherwise.
*/
bool in_copy_from_uspace;
/**
* True if this thread is executing copy_to_uspace().
* False otherwise.
*/
bool in_copy_to_uspace;
/**
* If true, the thread will not go to sleep at all and will call
* thread_exit() before returning to userspace.
*/
bool interrupted;
/** If true, thread_join_timeout() cannot be used on this thread. */
bool detached;
/** Waitq for thread_join_timeout(). */
waitq_t join_wq;
/** Link used in the joiner_head list. */
link_t joiner_link;
 
fpu_context_t *saved_fpu_context;
int fpu_context_exists;
 
/*
* Defined only if thread doesn't run.
* It means that fpu context is in CPU that last time executes this
* thread. This disables migration.
*/
int fpu_context_engaged;
 
rwlock_type_t rwlock_holder_type;
 
/** Callback fired in scheduler before the thread is put asleep. */
void (* call_me)(void *);
/** Argument passed to call_me(). */
void *call_me_with;
 
/** Thread's state. */
state_t state;
/** Thread's flags. */
int flags;
/** Thread's CPU. */
cpu_t *cpu;
/** Containing task. */
task_t *task;
 
/** Ticks before preemption. */
uint64_t ticks;
/** Thread accounting. */
uint64_t cycles;
/** Last sampled cycle. */
uint64_t last_cycle;
/** Thread doesn't affect accumulated accounting. */
bool uncounted;
 
/** Thread's priority. Implemented as index to CPU->rq */
int priority;
/** Thread ID. */
thread_id_t tid;
/** Architecture-specific data. */
thread_arch_t arch;
 
/** Thread's kernel stack. */
uint8_t *kstack;
 
#ifdef CONFIG_UDEBUG
/** Debugging stuff */
udebug_thread_t udebug;
#endif
 
} thread_t;
 
/** Thread list lock.
*
* This lock protects the threads_tree.
* Must be acquired before T.lock for each T of type thread_t.
*
*/
SPINLOCK_EXTERN(threads_lock);
 
/** AVL tree containing all threads. */
extern avltree_t threads_tree;
 
extern void thread_init(void);
extern thread_t *thread_create(void (* func)(void *), void *arg, task_t *task,
int flags, char *name, bool uncounted);
extern void thread_attach(thread_t *t, task_t *task);
extern void thread_ready(thread_t *t);
extern void thread_exit(void) __attribute__((noreturn));
 
#ifndef thread_create_arch
extern void thread_create_arch(thread_t *t);
#endif
#ifndef thr_constructor_arch
extern void thr_constructor_arch(thread_t *t);
#endif
#ifndef thr_destructor_arch
extern void thr_destructor_arch(thread_t *t);
#endif
 
extern void thread_sleep(uint32_t sec);
extern void thread_usleep(uint32_t usec);
 
#define thread_join(t) \
thread_join_timeout((t), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
extern int thread_join_timeout(thread_t *t, uint32_t usec, int flags);
extern void thread_detach(thread_t *t);
 
extern void thread_register_call_me(void (* call_me)(void *),
void *call_me_with);
extern void thread_print_list(void);
extern void thread_destroy(thread_t *t);
extern void thread_update_accounting(void);
extern bool thread_exists(thread_t *t);
 
/** Fpu context slab cache. */
extern slab_cache_t *fpu_context_slab;
 
/* Thread syscall prototypes. */
extern unative_t sys_thread_create(uspace_arg_t *uspace_uarg,
char *uspace_name, size_t name_len, thread_id_t *uspace_thread_id);
extern unative_t sys_thread_exit(int uspace_status);
extern unative_t sys_thread_get_id(thread_id_t *uspace_thread_id);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/proc/tasklet.h
0,0 → 1,73
/*
* Copyright (c) 2007 Jan Hudecek
* Copyright (c) 2008 Martin Decky
* 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 genericproc
* @{
*/
/** @file tasklet.h
* @brief Tasklets declarations
*/
 
#ifndef KERN_TASKLET_H_
#define KERN_TASKLET_H_
 
#include <adt/list.h>
 
/** Tasklet callback type */
typedef void (* tasklet_callback_t)(void *arg);
 
/** Tasklet state */
typedef enum {
NotActive,
Scheduled,
InProgress,
Disabled
} tasklet_state_t;
 
/** Structure describing a tasklet */
typedef struct tasklet_descriptor {
link_t link;
/** Callback to call */
tasklet_callback_t callback;
/** Argument passed to the callback */
void *arg;
/** State of the tasklet */
tasklet_state_t state;
} tasklet_descriptor_t;
 
 
extern void tasklet_init(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/proc/uarg.h
0,0 → 1,52
/*
* Copyright (c) 2006 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 genericproc
* @{
*/
/** @file
*/
 
#ifndef KERN_UARG_H_
#define KERN_UARG_H_
 
/** Structure passed to uinit kernel thread as argument. */
typedef struct uspace_arg {
void *uspace_entry;
void *uspace_stack;
 
void (* uspace_thread_function)();
void *uspace_thread_arg;
struct uspace_arg *uspace_uarg;
} uspace_arg_t;
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/cpu.h
0,0 → 1,103
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_CPU_H_
#define KERN_CPU_H_
 
#include <mm/tlb.h>
#include <synch/spinlock.h>
#include <proc/scheduler.h>
#include <arch/cpu.h>
#include <arch/context.h>
 
#define CPU_STACK_SIZE STACK_SIZE
 
/** CPU structure.
*
* There is one structure like this for every processor.
*/
typedef struct {
SPINLOCK_DECLARE(lock);
 
tlb_shootdown_msg_t tlb_messages[TLB_MESSAGE_QUEUE_LEN];
size_t tlb_messages_count;
context_t saved_context;
 
atomic_t nrdy;
runq_t rq[RQ_COUNT];
volatile size_t needs_relink;
 
SPINLOCK_DECLARE(timeoutlock);
link_t timeout_active_head;
 
size_t missed_clock_ticks; /**< When system clock loses a tick, it is recorded here
so that clock() can react. This variable is
CPU-local and can be only accessed when interrupts
are disabled. */
 
/**
* Processor ID assigned by kernel.
*/
unsigned int id;
int active;
int tlb_active;
 
uint16_t frequency_mhz;
uint32_t delay_loop_const;
 
cpu_arch_t arch;
 
struct thread *fpu_owner;
/**
* Stack used by scheduler when there is no running thread.
*/
uint8_t *stack;
} cpu_t;
 
extern cpu_t *cpus;
 
extern void cpu_init(void);
extern void cpu_list(void);
 
extern void cpu_arch_init(void);
extern void cpu_identify(void);
extern void cpu_print_report(cpu_t *m);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/futex.h
0,0 → 1,65
/*
* Copyright (c) 2006 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_FUTEX_H_
#define KERN_FUTEX_H_
 
#include <arch/types.h>
#include <synch/waitq.h>
#include <genarch/mm/page_ht.h>
#include <genarch/mm/page_pt.h>
 
/** Kernel-side futex structure. */
typedef struct {
/** Physical address of the status variable. */
uintptr_t paddr;
/** Wait queue for threads waiting for futex availability. */
waitq_t wq;
/** Futex hash table link. */
link_t ht_link;
/** Number of tasks that reference this futex. */
size_t refcount;
} futex_t;
 
extern void futex_init(void);
extern unative_t sys_futex_sleep_timeout(uintptr_t uaddr, uint32_t usec,
int flags);
extern unative_t sys_futex_wakeup(uintptr_t uaddr);
 
extern void futex_cleanup(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/rwlock.h
0,0 → 1,83
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_RWLOCK_H_
#define KERN_RWLOCK_H_
 
#include <arch/types.h>
#include <synch/mutex.h>
#include <synch/synch.h>
#include <synch/spinlock.h>
 
typedef enum {
RWLOCK_NONE,
RWLOCK_READER,
RWLOCK_WRITER
} rwlock_type_t;
 
typedef struct {
SPINLOCK_DECLARE(lock);
/**
* Mutex for writers, readers can bypass it if readers_in is positive.
*/
mutex_t exclusive;
/** Number of readers in critical section. */
size_t readers_in;
} rwlock_t;
 
#define rwlock_write_lock(rwl) \
_rwlock_write_lock_timeout((rwl), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
#define rwlock_read_lock(rwl) \
_rwlock_read_lock_timeout((rwl), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
#define rwlock_write_trylock(rwl) \
_rwlock_write_lock_timeout((rwl), SYNCH_NO_TIMEOUT, \
SYNCH_FLAGS_NON_BLOCKING)
#define rwlock_read_trylock(rwl) \
_rwlock_read_lock_timeout((rwl), SYNCH_NO_TIMEOUT, \
SYNCH_FLAGS_NON_BLOCKING)
#define rwlock_write_lock_timeout(rwl, usec) \
_rwlock_write_lock_timeout((rwl), (usec), SYNCH_FLAGS_NONE)
#define rwlock_read_lock_timeout(rwl, usec) \
_rwlock_read_lock_timeout((rwl), (usec), SYNCH_FLAGS_NONE)
 
extern void rwlock_initialize(rwlock_t *rwl);
extern void rwlock_read_unlock(rwlock_t *rwl);
extern void rwlock_write_unlock(rwlock_t *rwl);
extern int _rwlock_read_lock_timeout(rwlock_t *rwl, uint32_t usec, int flags);
extern int _rwlock_write_lock_timeout(rwlock_t *rwl, uint32_t usec, int flags);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/spinlock.h
0,0 → 1,143
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_SPINLOCK_H_
#define KERN_SPINLOCK_H_
 
#include <arch/types.h>
#include <arch/barrier.h>
#include <preemption.h>
#include <atomic.h>
#include <debug.h>
 
#ifdef CONFIG_SMP
typedef struct {
#ifdef CONFIG_DEBUG_SPINLOCK
char *name;
#endif
atomic_t val;
} spinlock_t;
 
/*
* SPINLOCK_DECLARE is to be used for dynamically allocated spinlocks,
* where the lock gets initialized in run time.
*/
#define SPINLOCK_DECLARE(slname) spinlock_t slname
#define SPINLOCK_EXTERN(slname) extern spinlock_t slname
 
/*
* SPINLOCK_INITIALIZE is to be used for statically allocated spinlocks.
* It declares and initializes the lock.
*/
#ifdef CONFIG_DEBUG_SPINLOCK
#define SPINLOCK_INITIALIZE(slname) \
spinlock_t slname = { \
.name = #slname, \
.val = { 0 } \
}
#else
#define SPINLOCK_INITIALIZE(slname) \
spinlock_t slname = { \
.val = { 0 } \
}
#endif
 
extern void spinlock_initialize(spinlock_t *sl, char *name);
extern int spinlock_trylock(spinlock_t *sl);
extern void spinlock_lock_debug(spinlock_t *sl);
 
#ifdef CONFIG_DEBUG_SPINLOCK
# define spinlock_lock(x) spinlock_lock_debug(x)
#else
# define spinlock_lock(x) atomic_lock_arch(&(x)->val)
#endif
 
/** Unlock spinlock
*
* Unlock spinlock.
*
* @param sl Pointer to spinlock_t structure.
*/
static inline void spinlock_unlock(spinlock_t *sl)
{
ASSERT(atomic_get(&sl->val) != 0);
 
/*
* Prevent critical section code from bleeding out this way down.
*/
CS_LEAVE_BARRIER();
atomic_set(&sl->val, 0);
preemption_enable();
}
 
#ifdef CONFIG_DEBUG_SPINLOCK
 
extern int printf(const char *, ...);
 
#define DEADLOCK_THRESHOLD 100000000
#define DEADLOCK_PROBE_INIT(pname) size_t pname = 0
#define DEADLOCK_PROBE(pname, value) \
if ((pname)++ > (value)) { \
(pname) = 0; \
printf("Deadlock probe %s: exceeded threshold %u\n", \
"cpu%u: function=%s, line=%u\n", \
#pname, (value), CPU->id, __func__, __LINE__); \
}
#else
#define DEADLOCK_PROBE_INIT(pname)
#define DEADLOCK_PROBE(pname, value)
#endif
 
#else
 
/* On UP systems, spinlocks are effectively left out. */
#define SPINLOCK_DECLARE(name)
#define SPINLOCK_EXTERN(name)
#define SPINLOCK_INITIALIZE(name)
 
#define spinlock_initialize(x, name)
#define spinlock_lock(x) preemption_disable()
#define spinlock_trylock(x) (preemption_disable(), 1)
#define spinlock_unlock(x) preemption_enable()
 
#define DEADLOCK_PROBE_INIT(pname)
#define DEADLOCK_PROBE(pname, value)
 
#endif
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/smc.h
0,0 → 1,43
/*
* 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_SMC_H_
#define KERN_SMC_H_
 
extern unative_t sys_smc_coherence(uintptr_t va, size_t size);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/mutex.h
0,0 → 1,66
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_MUTEX_H_
#define KERN_MUTEX_H_
 
#include <arch/types.h>
#include <synch/semaphore.h>
#include <synch/synch.h>
 
typedef enum {
MUTEX_PASSIVE,
MUTEX_ACTIVE
} mutex_type_t;
 
typedef struct {
mutex_type_t type;
semaphore_t sem;
} mutex_t;
 
#define mutex_lock(mtx) \
_mutex_lock_timeout((mtx), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
#define mutex_trylock(mtx) \
_mutex_lock_timeout((mtx), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING)
#define mutex_lock_timeout(mtx, usec) \
_mutex_lock_timeout((mtx), (usec), SYNCH_FLAGS_NON_BLOCKING)
 
extern void mutex_initialize(mutex_t *, mutex_type_t);
extern int _mutex_lock_timeout(mutex_t *, uint32_t, int);
extern void mutex_unlock(mutex_t *);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/waitq.h
0,0 → 1,82
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_WAITQ_H_
#define KERN_WAITQ_H_
 
#include <arch/types.h>
#include <synch/spinlock.h>
#include <synch/synch.h>
#include <adt/list.h>
 
typedef enum {
WAKEUP_FIRST = 0,
WAKEUP_ALL
} wakeup_mode_t;
 
/** Wait queue structure. */
typedef struct {
 
/** Lock protecting wait queue structure.
*
* Must be acquired before T.lock for each T of type thread_t.
*/
SPINLOCK_DECLARE(lock);
 
/**
* Number of waitq_wakeup() calls that didn't find a thread to wake up.
*/
int missed_wakeups;
/** List of sleeping threads for wich there was no missed_wakeup. */
link_t head;
} waitq_t;
 
#define waitq_sleep(wq) \
waitq_sleep_timeout((wq), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
 
struct thread;
 
extern void waitq_initialize(waitq_t *wq);
extern int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, int flags);
extern ipl_t waitq_sleep_prepare(waitq_t *wq);
extern int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags);
extern void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl);
extern void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode);
extern void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode);
extern void waitq_interrupt_sleep(struct thread *t);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/condvar.h
0,0 → 1,61
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_CONDVAR_H_
#define KERN_CONDVAR_H_
 
#include <arch/types.h>
#include <synch/waitq.h>
#include <synch/mutex.h>
#include <synch/synch.h>
 
typedef struct {
waitq_t wq;
} condvar_t;
 
#define condvar_wait(cv, mtx) \
_condvar_wait_timeout((cv), (mtx), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
#define condvar_wait_timeout(cv, mtx, usec) \
_condvar_wait_timeout((cv), (mtx), (usec), SYNCH_FLAGS_NONE)
 
extern void condvar_initialize(condvar_t *cv);
extern void condvar_signal(condvar_t *cv);
extern void condvar_broadcast(condvar_t *cv);
extern int _condvar_wait_timeout(condvar_t *cv, mutex_t *mtx, uint32_t usec,
int flags);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/semaphore.h
0,0 → 1,60
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_SEMAPHORE_H_
#define KERN_SEMAPHORE_H_
 
#include <arch/types.h>
#include <synch/waitq.h>
#include <synch/synch.h>
 
typedef struct {
waitq_t wq;
} semaphore_t;
 
#define semaphore_down(s) \
_semaphore_down_timeout((s), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE)
#define semaphore_trydown(s) \
_semaphore_down_timeout((s), SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NON_BLOCKING)
#define semaphore_down_timeout(s, usec) \
_semaphore_down_timeout((s), (usec), SYNCH_FLAGS_NONE)
 
extern void semaphore_initialize(semaphore_t *s, int val);
extern int _semaphore_down_timeout(semaphore_t *s, uint32_t usec, int flags);
extern void semaphore_up(semaphore_t *s);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/synch/synch.h
0,0 → 1,67
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
/** @file
*/
 
#ifndef KERN_SYNCH_H_
#define KERN_SYNCH_H_
 
/** Request with no timeout. */
#define SYNCH_NO_TIMEOUT 0
 
/** No flags specified. */
#define SYNCH_FLAGS_NONE 0
/** Non-blocking operation request. */
#define SYNCH_FLAGS_NON_BLOCKING (1 << 0)
/** Interruptible operation. */
#define SYNCH_FLAGS_INTERRUPTIBLE (1 << 1)
 
/** Could not satisfy the request without going to sleep. */
#define ESYNCH_WOULD_BLOCK 1
/** Timeout occurred. */
#define ESYNCH_TIMEOUT 2
/** Sleep was interrupted. */
#define ESYNCH_INTERRUPTED 4
/** Operation succeeded without sleeping. */
#define ESYNCH_OK_ATOMIC 8
/** Operation succeeded and did sleep. */
#define ESYNCH_OK_BLOCKED 16
 
#define SYNCH_FAILED(rc) \
((rc) & (ESYNCH_WOULD_BLOCK | ESYNCH_TIMEOUT | ESYNCH_INTERRUPTED))
#define SYNCH_OK(rc) \
((rc) & (ESYNCH_OK_ATOMIC | ESYNCH_OK_BLOCKED))
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ddi/irq.h
0,0 → 1,175
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
/** @file
*/
 
#ifndef KERN_IRQ_H_
#define KERN_IRQ_H_
 
typedef enum {
CMD_PIO_READ_8 = 1,
CMD_PIO_READ_16,
CMD_PIO_READ_32,
CMD_PIO_WRITE_8,
CMD_PIO_WRITE_16,
CMD_PIO_WRITE_32,
CMD_BTEST,
CMD_PREDICATE,
CMD_ACCEPT,
CMD_DECLINE,
CMD_LAST
} irq_cmd_type;
 
typedef struct {
irq_cmd_type cmd;
void *addr;
unsigned long long value;
unsigned int srcarg;
unsigned int dstarg;
} irq_cmd_t;
 
typedef struct {
unsigned int cmdcount;
irq_cmd_t *cmds;
} irq_code_t;
 
#ifdef KERNEL
 
#include <arch/types.h>
#include <adt/list.h>
#include <adt/hash_table.h>
#include <synch/spinlock.h>
#include <proc/task.h>
#include <ipc/ipc.h>
 
typedef enum {
IRQ_DECLINE, /**< Decline to service. */
IRQ_ACCEPT /**< Accept to service. */
} irq_ownership_t;
 
typedef enum {
IRQ_TRIGGER_LEVEL = 1,
IRQ_TRIGGER_EDGE
} irq_trigger_t;
 
struct irq;
typedef void (* irq_handler_t)(struct irq *);
 
/** Type for function used to clear the interrupt. */
typedef void (* cir_t)(void *, inr_t);
 
/** IPC notification config structure.
*
* Primarily, this structure is encapsulated in the irq_t structure.
* It is protected by irq_t::lock.
*/
typedef struct {
/** When false, notifications are not sent. */
bool notify;
/** Answerbox for notifications. */
answerbox_t *answerbox;
/** Method to be used for the notification. */
unative_t method;
/** Arguments that will be sent if the IRQ is claimed. */
unative_t scratch[IPC_CALL_LEN];
/** Top-half pseudocode. */
irq_code_t *code;
/** Counter. */
size_t counter;
/**
* Link between IRQs that are notifying the same answerbox. The list is
* protected by the answerbox irq_lock.
*/
link_t link;
} ipc_notif_cfg_t;
 
/** Structure representing one device IRQ.
*
* If one device has multiple interrupts, there will be multiple irq_t
* instantions with the same devno.
*/
typedef struct irq {
/** Hash table link. */
link_t link;
 
/** Lock protecting everything in this structure
* except the link member. When both the IRQ
* hash table lock and this lock are to be acquired,
* this lock must not be taken first.
*/
SPINLOCK_DECLARE(lock);
/** Send EOI before processing the interrupt.
* This is essential for timer interrupt which
* has to be acknowledged before doing preemption
* to make sure another timer interrupt will
* be eventually generated.
*/
bool preack;
 
/** Unique device number. -1 if not yet assigned. */
devno_t devno;
 
/** Actual IRQ number. -1 if not yet assigned. */
inr_t inr;
/** Trigger level of the IRQ. */
irq_trigger_t trigger;
/** Claim ownership of the IRQ. */
irq_ownership_t (* claim)(struct irq *);
/** Handler for this IRQ and device. */
irq_handler_t handler;
/** Instance argument for the handler and the claim function. */
void *instance;
 
/** Clear interrupt routine. */
cir_t cir;
/** First argument to the clear interrupt routine. */
void *cir_arg;
 
/** Notification configuration structure. */
ipc_notif_cfg_t notif_cfg;
} irq_t;
 
SPINLOCK_EXTERN(irq_uspace_hash_table_lock);
extern hash_table_t irq_uspace_hash_table;
 
extern void irq_init(size_t, size_t);
extern void irq_initialize(irq_t *);
extern void irq_register(irq_t *);
extern irq_t *irq_dispatch_and_lock(inr_t);
 
#endif
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ddi/device.h
0,0 → 1,47
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
/** @file
*/
 
#ifndef KERN_DEVICE_H_
#define KERN_DEVICE_H_
 
#include <arch/types.h>
#include <typedefs.h>
 
extern devno_t device_assign_devno(void);
extern unative_t sys_device_assign_devno(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ddi/ddi.h
0,0 → 1,67
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
/** @file
*/
 
#ifndef KERN_DDI_H_
#define KERN_DDI_H_
 
#include <ddi/ddi_arg.h>
#include <arch/types.h>
#include <proc/task.h>
#include <adt/list.h>
 
/** Structure representing contiguous physical memory area. */
typedef struct {
uintptr_t pbase; /**< Physical base of the area. */
pfn_t frames; /**< Number of frames in the area. */
link_t link; /**< Linked list link */
} parea_t;
 
extern void ddi_init(void);
extern void ddi_parea_register(parea_t *parea);
 
extern unative_t sys_physmem_map(unative_t phys_base, unative_t virt_base,
unative_t pages, unative_t flags);
extern unative_t sys_iospace_enable(ddi_ioarg_t *uspace_io_arg);
extern unative_t sys_preempt_control(int enable);
 
/*
* Interface to be implemented by all architectures.
*/
extern int ddi_iospace_enable_arch(task_t *task, uintptr_t ioaddr, size_t size);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ddi/ddi_arg.h
0,0 → 1,62
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
/** @file
*/
 
#ifndef KERN_DDI_ARG_H_
#define KERN_DDI_ARG_H_
 
/** Structure encapsulating arguments for SYS_PHYSMEM_MAP syscall. */
typedef struct {
/** ID of the destination task. */
unsigned long long task_id;
/** Physical address of starting frame. */
void *phys_base;
/** Virtual address of starting page. */
void *virt_base;
/** Number of pages to map. */
unsigned long pages;
/** Address space area flags for the mapping. */
int flags;
} ddi_memarg_t;
 
/** Structure encapsulating arguments for SYS_ENABLE_IOSPACE syscall. */
typedef struct {
unsigned long long task_id; /**< ID of the destination task. */
void *ioaddr; /**< Starting I/O space address. */
unsigned long size; /**< Number of bytes. */
} ddi_ioarg_t;
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/console/chardev.h
0,0 → 1,102
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
/** @file
*/
 
#ifndef KERN_CHARDEV_H_
#define KERN_CHARDEV_H_
 
#include <arch/types.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
 
#define INDEV_BUFLEN 512
 
struct indev;
 
/* Input character device operations interface. */
typedef struct {
/** Read character directly from device, assume interrupts disabled. */
wchar_t (* poll)(struct indev *);
} indev_operations_t;
 
/** Character input device. */
typedef struct indev {
char *name;
waitq_t wq;
/** Protects everything below. */
SPINLOCK_DECLARE(lock);
wchar_t buffer[INDEV_BUFLEN];
size_t counter;
/** Implementation of indev operations. */
indev_operations_t *op;
size_t index;
void *data;
} indev_t;
 
 
struct outdev;
 
/* Output character device operations interface. */
typedef struct {
/** Write character to output. */
void (* write)(struct outdev *, wchar_t, bool);
} outdev_operations_t;
 
/** Character input device. */
typedef struct outdev {
char *name;
/** Protects everything below. */
SPINLOCK_DECLARE(lock);
/** Implementation of outdev operations. */
outdev_operations_t *op;
void *data;
} outdev_t;
 
extern void indev_initialize(char *name, indev_t *indev,
indev_operations_t *op);
extern void indev_push_character(indev_t *indev, wchar_t ch);
extern wchar_t indev_pop_character(indev_t *indev);
 
extern void outdev_initialize(char *name, outdev_t *outdev,
outdev_operations_t *op);
 
extern bool check_poll(indev_t *indev);
 
#endif /* KERN_CHARDEV_H_ */
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/console/kconsole.h
0,0 → 1,104
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
/** @file
*/
 
#ifndef KERN_KCONSOLE_H_
#define KERN_KCONSOLE_H_
 
#include <adt/list.h>
#include <synch/spinlock.h>
#include <ipc/irq.h>
 
#define MAX_CMDLINE 256
#define KCONSOLE_HISTORY 10
 
typedef enum {
ARG_TYPE_INVALID = 0,
ARG_TYPE_INT,
ARG_TYPE_STRING,
/** Variable type - either symbol or string. */
ARG_TYPE_VAR
} cmd_arg_type_t;
 
/** Structure representing one argument of kconsole command line. */
typedef struct {
/** Type descriptor. */
cmd_arg_type_t type;
/** Buffer where to store data. */
void *buffer;
/** Size of the buffer. */
size_t len;
/** Integer value. */
unative_t intval;
/** Resulting type of variable arg */
cmd_arg_type_t vartype;
} cmd_arg_t;
 
/** Structure representing one kconsole command. */
typedef struct {
/** Command list link. */
link_t link;
/** This lock protects everything below. */
SPINLOCK_DECLARE(lock);
/** Command name. */
const char *name;
/** Textual description. */
const char *description;
/** Function implementing the command. */
int (* func)(cmd_arg_t *);
/** Number of arguments. */
size_t argc;
/** Argument vector. */
cmd_arg_t *argv;
/** Function for printing detailed help. */
void (* help)(void);
} cmd_info_t;
 
extern bool kconsole_notify;
extern irq_t kconsole_irq;
 
SPINLOCK_EXTERN(cmd_lock);
extern link_t cmd_head;
 
extern void kconsole_init(void);
extern void kconsole_notify_init(void);
extern bool kconsole_check_poll(void);
extern void kconsole(char *prompt, char *msg, bool kcon);
extern void kconsole_thread(void *data);
 
extern bool cmd_register(cmd_info_t *cmd);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/console/console.h
0,0 → 1,67
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
/** @file
*/
 
#ifndef KERN_CONSOLE_H_
#define KERN_CONSOLE_H_
 
#include <arch/types.h>
#include <console/chardev.h>
 
extern indev_t *stdin;
extern outdev_t *stdout;
extern bool silent;
 
extern indev_t *stdin_wire(void);
extern void console_init(void);
 
extern void klog_init(void);
extern void klog_update(void);
 
extern wchar_t getc(indev_t *indev);
extern size_t gets(indev_t *indev, char *buf, size_t buflen);
extern unative_t sys_klog(int fd, const void *buf, size_t size);
 
extern void grab_console(void);
extern void release_console(void);
 
extern unative_t sys_debug_enable_console(void);
extern unative_t sys_debug_disable_console(void);
 
extern void arch_grab_console(void);
extern void arch_release_console(void);
 
#endif /* KERN_CONSOLE_H_ */
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/console/cmd.h
0,0 → 1,46
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
/** @file
*/
 
#ifndef KERN_CMD_H_
#define KERN_CMD_H_
 
#include <console/kconsole.h>
 
extern void cmd_initialize(cmd_info_t *cmd);
extern void cmd_init(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/arch.h
0,0 → 1,86
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_ARCH_H_
#define KERN_ARCH_H_
 
#include <arch/arch.h>
#include <proc/thread.h>
#include <proc/task.h>
 
#define DEFAULT_CONTEXT 0
 
#define CPU THE->cpu
#define THREAD THE->thread
#define TASK THE->task
#define AS THE->as
#define CONTEXT (THE->task ? THE->task->context : DEFAULT_CONTEXT)
#define PREEMPTION_DISABLED THE->preemption_disabled
 
#define context_check(ctx1, ctx2) ((ctx1) == (ctx2))
 
/**
* For each possible kernel stack, structure
* of the following type will be placed at
* the base address of the stack.
*/
typedef struct {
size_t preemption_disabled; /**< Preemption disabled counter. */
thread_t *thread; /**< Current thread. */
task_t *task; /**< Current task. */
cpu_t *cpu; /**< Executing cpu. */
as_t *as; /**< Current address space. */
} the_t;
 
#define THE ((the_t * )(get_stack_base()))
 
extern void the_initialize(the_t *the);
extern void the_copy(the_t *src, the_t *dst);
 
extern void arch_pre_mm_init(void);
extern void arch_post_mm_init(void);
extern void arch_post_cpu_init(void);
extern void arch_pre_smp_init(void);
extern void arch_post_smp_init(void);
 
extern void calibrate_delay_loop(void);
 
extern void reboot(void);
extern void arch_reboot(void);
extern void *arch_construct_function(fncptr_t *fptr, void *addr, void *caller);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/adt/hash_table.h
0,0 → 1,88
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
/** @file
*/
 
#ifndef KERN_HASH_TABLE_H_
#define KERN_HASH_TABLE_H_
 
#include <adt/list.h>
#include <arch/types.h>
 
/** Set of operations for hash table. */
typedef struct {
/** Hash function.
*
* @param key Array of keys needed to compute hash index. All keys must
* be passed.
*
* @return Index into hash table.
*/
size_t (* hash)(unative_t key[]);
/** Hash table item comparison function.
*
* @param key Array of keys that will be compared with item. It is not
* necessary to pass all keys.
*
* @return true if the keys match, false otherwise.
*/
bool (*compare)(unative_t key[], size_t keys, link_t *item);
 
/** Hash table item removal callback.
*
* @param item Item that was removed from the hash table.
*/
void (*remove_callback)(link_t *item);
} hash_table_operations_t;
 
/** Hash table structure. */
typedef struct {
link_t *entry;
size_t entries;
size_t max_keys;
hash_table_operations_t *op;
} hash_table_t;
 
#define hash_table_get_instance(item, type, member) \
list_get_instance((item), type, member)
 
extern void hash_table_create(hash_table_t *h, size_t m, size_t max_keys,
hash_table_operations_t *op);
extern void hash_table_insert(hash_table_t *h, unative_t key[], link_t *item);
extern link_t *hash_table_find(hash_table_t *h, unative_t key[]);
extern void hash_table_remove(hash_table_t *h, unative_t key[], size_t keys);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/adt/bitmap.h
0,0 → 1,64
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
/** @file
*/
 
#ifndef KERN_BITMAP_H_
#define KERN_BITMAP_H_
 
#include <arch/types.h>
 
#define BITS2BYTES(bits) (bits ? ((((bits)-1)>>3)+1) : 0)
 
typedef struct {
uint8_t *map;
size_t bits;
} bitmap_t;
 
extern void bitmap_initialize(bitmap_t *bitmap, uint8_t *map, size_t bits);
extern void bitmap_set_range(bitmap_t *bitmap, size_t start, size_t bits);
extern void bitmap_clear_range(bitmap_t *bitmap, size_t start, size_t bits);
extern void bitmap_copy(bitmap_t *dst, bitmap_t *src, size_t bits);
 
static inline int bitmap_get(bitmap_t *bitmap, size_t bit)
{
if(bit >= bitmap->bits)
return 0;
return !! ((bitmap->map)[bit/8] & (1 << (bit & 7)));
}
 
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/adt/btree.h
0,0 → 1,113
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
/** @file
*/
 
#ifndef KERN_BTREE_H_
#define KERN_BTREE_H_
 
#include <arch/types.h>
#include <adt/list.h>
 
#define BTREE_M 5
#define BTREE_MAX_KEYS (BTREE_M - 1)
 
typedef uint64_t btree_key_t;
 
/** B-tree node structure. */
typedef struct btree_node {
/** Number of keys. */
size_t keys;
 
/**
* Keys. We currently support only single keys. Additional room for one
* extra key is provided.
*/
btree_key_t key[BTREE_MAX_KEYS + 1];
 
/**
* Pointers to values. Sorted according to the key array. Defined only in
* leaf-level. There is room for storing value for the extra key.
*/
void *value[BTREE_MAX_KEYS + 1];
/**
* Pointers to descendants of this node sorted according to the key
* array.
*
* subtree[0] points to subtree with keys lesser than to key[0].
* subtree[1] points to subtree with keys greater than or equal to
* key[0] and lesser than key[1].
* ...
* There is room for storing a subtree pointer for the extra key.
*/
struct btree_node *subtree[BTREE_M + 1];
 
/** Pointer to parent node. Root node has NULL parent. */
struct btree_node *parent;
 
/**
* Link connecting leaf-level nodes. Defined only when this node is a
* leaf. */
link_t leaf_link;
 
/* Variables needed by btree_print(). */
link_t bfs_link;
int depth;
} btree_node_t;
 
/** B-tree structure. */
typedef struct {
btree_node_t *root; /**< B-tree root node pointer. */
link_t leaf_head; /**< Leaf-level list head. */
} btree_t;
 
extern void btree_init(void);
 
extern void btree_create(btree_t *t);
extern void btree_destroy(btree_t *t);
 
extern void btree_insert(btree_t *t, btree_key_t key, void *value,
btree_node_t *leaf_node);
extern void btree_remove(btree_t *t, btree_key_t key, btree_node_t *leaf_node);
extern void *btree_search(btree_t *t, btree_key_t key, btree_node_t **leaf_node);
 
extern btree_node_t *btree_leaf_node_left_neighbour(btree_t *t,
btree_node_t *node);
extern btree_node_t *btree_leaf_node_right_neighbour(btree_t *t,
btree_node_t *node);
 
extern void btree_print(btree_t *t);
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/adt/fifo.h
0,0 → 1,122
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
/** @file
*/
 
/*
* This implementation of FIFO stores values in an array
* (static or dynamic). As such, these FIFOs have upper bound
* on number of values they can store. Push and pop operations
* are done via accessing the array through head and tail indices.
* Because of better operation ordering in fifo_pop(), the access
* policy for these two indices is to 'increment (mod size of FIFO)
* and use'.
*/
 
#ifndef KERN_FIFO_H_
#define KERN_FIFO_H_
 
#include <mm/slab.h>
 
/** Create and initialize static FIFO.
*
* FIFO is allocated statically.
* This macro is suitable for creating smaller FIFOs.
*
* @param name Name of FIFO.
* @param t Type of values stored in FIFO.
* @param itms Number of items that can be stored in FIFO.
*/
#define FIFO_INITIALIZE_STATIC(name, t, itms) \
struct { \
t fifo[(itms)]; \
size_t items; \
size_t head; \
size_t tail; \
} name = { \
.items = (itms), \
.head = 0, \
.tail = 0 \
}
 
/** Create and prepare dynamic FIFO.
*
* FIFO is allocated dynamically.
* This macro is suitable for creating larger FIFOs.
*
* @param name Name of FIFO.
* @param t Type of values stored in FIFO.
* @param itms Number of items that can be stored in FIFO.
*/
#define FIFO_INITIALIZE_DYNAMIC(name, t, itms) \
struct { \
t *fifo; \
size_t items; \
size_t head; \
size_t tail; \
} name = { \
.fifo = NULL, \
.items = (itms), \
.head = 0, \
.tail = 0 \
}
 
/** Pop value from head of FIFO.
*
* @param name FIFO name.
*
* @return Leading value in FIFO.
*/
#define fifo_pop(name) \
name.fifo[name.head = (name.head + 1) < name.items ? (name.head + 1) : 0]
 
/** Push value to tail of FIFO.
*
* @param name FIFO name.
* @param value Value to be appended to FIFO.
*
*/
#define fifo_push(name, value) \
name.fifo[name.tail = \
(name.tail + 1) < name.items ? (name.tail + 1) : 0] = (value)
 
/** Allocate memory for dynamic FIFO.
*
* @param name FIFO name.
*/
#define fifo_create(name) \
name.fifo = malloc(sizeof(*name.fifo) * name.items, 0)
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/adt/list.h
0,0 → 1,193
/*
* Copyright (c) 2001-2004 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 genericadt
* @{
*/
/** @file
*/
 
#ifndef KERN_LIST_H_
#define KERN_LIST_H_
 
#include <arch/types.h>
#include <typedefs.h>
 
/** Doubly linked list head and link type. */
typedef struct link {
struct link *prev; /**< Pointer to the previous item in the list. */
struct link *next; /**< Pointer to the next item in the list. */
} link_t;
 
/** Declare and initialize statically allocated list.
*
* @param name Name of the new statically allocated list.
*/
#define LIST_INITIALIZE(name) \
link_t name = { .prev = &name, .next = &name }
 
/** Initialize doubly-linked circular list link
*
* Initialize doubly-linked list link.
*
* @param link Pointer to link_t structure to be initialized.
*/
static inline void link_initialize(link_t *link)
{
link->prev = NULL;
link->next = NULL;
}
 
/** Initialize doubly-linked circular list
*
* Initialize doubly-linked circular list.
*
* @param head Pointer to link_t structure representing head of the list.
*/
static inline void list_initialize(link_t *head)
{
head->prev = head;
head->next = head;
}
 
/** Add item to the beginning of doubly-linked circular list
*
* Add item to the beginning of doubly-linked circular list.
*
* @param link Pointer to link_t structure to be added.
* @param head Pointer to link_t structure representing head of the list.
*/
static inline void list_prepend(link_t *link, link_t *head)
{
link->next = head->next;
link->prev = head;
head->next->prev = link;
head->next = link;
}
 
/** Add item to the end of doubly-linked circular list
*
* Add item to the end of doubly-linked circular list.
*
* @param link Pointer to link_t structure to be added.
* @param head Pointer to link_t structure representing head of the list.
*/
static inline void list_append(link_t *link, link_t *head)
{
link->prev = head->prev;
link->next = head;
head->prev->next = link;
head->prev = link;
}
 
/** Remove item from doubly-linked circular list
*
* Remove item from doubly-linked circular list.
*
* @param link Pointer to link_t structure to be removed from the list it is
* contained in.
*/
static inline void list_remove(link_t *link)
{
link->next->prev = link->prev;
link->prev->next = link->next;
link_initialize(link);
}
 
/** Query emptiness of doubly-linked circular list
*
* Query emptiness of doubly-linked circular list.
*
* @param head Pointer to link_t structure representing head of the list.
*/
static inline bool list_empty(link_t *head)
{
return head->next == head ? true : false;
}
 
 
/** Split or concatenate headless doubly-linked circular list
*
* Split or concatenate headless doubly-linked circular list.
*
* Note that the algorithm works both directions:
* concatenates splitted lists and splits concatenated lists.
*
* @param part1 Pointer to link_t structure leading the first (half of the
* headless) list.
* @param part2 Pointer to link_t structure leading the second (half of the
* headless) list.
*/
static inline void headless_list_split_or_concat(link_t *part1, link_t *part2)
{
link_t *hlp;
 
part1->prev->next = part2;
part2->prev->next = part1;
hlp = part1->prev;
part1->prev = part2->prev;
part2->prev = hlp;
}
 
 
/** Split headless doubly-linked circular list
*
* Split headless doubly-linked circular list.
*
* @param part1 Pointer to link_t structure leading the first half of the
* headless list.
* @param part2 Pointer to link_t structure leading the second half of the
* headless list.
*/
static inline void headless_list_split(link_t *part1, link_t *part2)
{
headless_list_split_or_concat(part1, part2);
}
 
/** Concatenate two headless doubly-linked circular lists
*
* Concatenate two headless doubly-linked circular lists.
*
* @param part1 Pointer to link_t structure leading the first headless list.
* @param part2 Pointer to link_t structure leading the second headless list.
*/
static inline void headless_list_concat(link_t *part1, link_t *part2)
{
headless_list_split_or_concat(part1, part2);
}
 
#define list_get_instance(link, type, member) \
((type *)(((uint8_t *)(link)) - ((uint8_t *)&(((type *)NULL)->member))))
 
extern bool list_member(const link_t *link, const link_t *head);
extern void list_concat(link_t *head1, link_t *head2);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/adt/avl.h
0,0 → 1,142
/*
* Copyright (C) 2007 Vojtech Mencl
* 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 genericadt
* @{
*/
/** @file
*/
 
#ifndef KERN_AVLTREE_H_
#define KERN_AVLTREE_H_
 
#include <arch/types.h>
#include <typedefs.h>
 
/**
* Macro for getting a pointer to the structure which contains the avltree
* structure.
*
* @param link Pointer to the avltree structure.
* @param type Name of the outer structure.
* @param member Name of avltree attribute in the outer structure.
*/
#define avltree_get_instance(node, type, member) \
((type *)(((uint8_t *)(node)) - ((uint8_t *) &(((type *) NULL)->member))))
 
typedef struct avltree_node avltree_node_t;
typedef struct avltree avltree_t;
 
typedef uint64_t avltree_key_t;
 
typedef bool (* avltree_walker_t)(avltree_node_t *, void *);
 
/** AVL tree node structure. */
struct avltree_node
{
/**
* Pointer to the left descendant of this node.
*
* All keys of nodes in the left subtree are less than the key of this
* node.
*/
struct avltree_node *lft;
/**
* Pointer to the right descendant of this node.
*
* All keys of nodes in the right subtree are greater than the key of
* this node.
*/
struct avltree_node *rgt;
/** Pointer to the parent node. Root node has NULL parent. */
struct avltree_node *par;
/** Node's key. */
avltree_key_t key;
/**
* Difference between the heights of the left and the right subtree of
* this node.
*/
int8_t balance;
};
 
/** AVL tree structure. */
struct avltree
{
/** AVL root node pointer */
struct avltree_node *root;
 
/**
* Base of the tree is a value that is smaller or equal than every value
* in the tree (valid for positive keys otherwise ignore this atribute).
*
* The base is added to the current key when a new node is inserted into
* the tree. The base is changed to the key of the node which is deleted
* with avltree_delete_min().
*/
avltree_key_t base;
};
 
 
/** Create empty AVL tree.
*
* @param t AVL tree.
*/
static inline void avltree_create(avltree_t *t)
{
t->root = NULL;
t->base = 0;
}
 
/** Initialize node.
*
* @param node Node which is initialized.
*/
static inline void avltree_node_initialize(avltree_node_t *node)
{
node->key = 0;
node->lft = NULL;
node->rgt = NULL;
node->par = NULL;
node->balance = 0;
}
 
extern avltree_node_t *avltree_find_min(avltree_t *t);
extern avltree_node_t *avltree_search(avltree_t *t, avltree_key_t key);
extern void avltree_insert(avltree_t *t, avltree_node_t *newnode);
extern void avltree_delete(avltree_t *t, avltree_node_t *node);
extern bool avltree_delete_min(avltree_t *t);
extern void avltree_walk(avltree_t *t, avltree_walker_t walker, void *arg);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/frame.h
0,0 → 1,182
/*
* Copyright (c) 2005 Jakub Jermar
* Copyright (c) 2005 Sergey Bondari
* 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_FRAME_H_
#define KERN_FRAME_H_
 
#include <arch/types.h>
#include <adt/list.h>
#include <mm/buddy.h>
#include <synch/spinlock.h>
#include <arch/mm/page.h>
#include <arch/mm/frame.h>
 
#define ONE_FRAME 0
#define TWO_FRAMES 1
#define FOUR_FRAMES 2
 
 
#ifdef ARCH_STACK_FRAMES
#define STACK_FRAMES ARCH_STACK_FRAMES
#else
#define STACK_FRAMES ONE_FRAME
#endif
 
/** Maximum number of zones in the system. */
#define ZONES_MAX 32
 
typedef uint8_t frame_flags_t;
 
/** Convert the frame address to kernel VA. */
#define FRAME_KA 0x01
/** Do not panic and do not sleep on failure. */
#define FRAME_ATOMIC 0x02
/** Do not start reclaiming when no free memory. */
#define FRAME_NO_RECLAIM 0x04
 
typedef uint8_t zone_flags_t;
 
/** Available zone (free for allocation) */
#define ZONE_AVAILABLE 0x00
/** Zone is reserved (not available for allocation) */
#define ZONE_RESERVED 0x08
/** Zone is used by firmware (not available for allocation) */
#define ZONE_FIRMWARE 0x10
 
/** Currently there is no equivalent zone flags
for frame flags */
#define FRAME_TO_ZONE_FLAGS(frame_flags) 0
 
typedef struct {
size_t refcount; /**< Tracking of shared frames */
uint8_t buddy_order; /**< Buddy system block order */
link_t buddy_link; /**< Link to the next free block inside
one order */
void *parent; /**< If allocated by slab, this points there */
} frame_t;
 
typedef struct {
pfn_t base; /**< Frame_no of the first frame
in the frames array */
size_t count; /**< Size of zone */
size_t free_count; /**< Number of free frame_t
structures */
size_t busy_count; /**< Number of busy frame_t
structures */
zone_flags_t flags; /**< Type of the zone */
frame_t *frames; /**< Array of frame_t structures
in this zone */
buddy_system_t *buddy_system; /**< Buddy system for the zone */
} zone_t;
 
/*
* The zoneinfo.lock must be locked when accessing zoneinfo structure.
* Some of the attributes in zone_t structures are 'read-only'
*/
typedef struct {
SPINLOCK_DECLARE(lock);
size_t count;
zone_t info[ZONES_MAX];
} zones_t;
 
extern zones_t zones;
 
static inline uintptr_t PFN2ADDR(pfn_t frame)
{
return (uintptr_t) (frame << FRAME_WIDTH);
}
 
static inline pfn_t ADDR2PFN(uintptr_t addr)
{
return (pfn_t) (addr >> FRAME_WIDTH);
}
 
static inline size_t SIZE2FRAMES(size_t size)
{
if (!size)
return 0;
return (size_t) ((size - 1) >> FRAME_WIDTH) + 1;
}
 
static inline size_t FRAMES2SIZE(size_t frames)
{
return (size_t) (frames << FRAME_WIDTH);
}
 
static inline bool zone_flags_available(zone_flags_t flags)
{
return ((flags & (ZONE_RESERVED | ZONE_FIRMWARE)) == 0);
}
 
#define IS_BUDDY_ORDER_OK(index, order) \
((~(((unative_t) -1) << (order)) & (index)) == 0)
#define IS_BUDDY_LEFT_BLOCK(zone, frame) \
(((frame_index((zone), (frame)) >> (frame)->buddy_order) & 0x01) == 0)
#define IS_BUDDY_RIGHT_BLOCK(zone, frame) \
(((frame_index((zone), (frame)) >> (frame)->buddy_order) & 0x01) == 1)
#define IS_BUDDY_LEFT_BLOCK_ABS(zone, frame) \
(((frame_index_abs((zone), (frame)) >> (frame)->buddy_order) & 0x01) == 0)
#define IS_BUDDY_RIGHT_BLOCK_ABS(zone, frame) \
(((frame_index_abs((zone), (frame)) >> (frame)->buddy_order) & 0x01) == 1)
 
#define frame_alloc(order, flags) \
frame_alloc_generic(order, flags, NULL)
 
extern void frame_init(void);
extern void *frame_alloc_generic(uint8_t, frame_flags_t, size_t *);
extern void frame_free(uintptr_t);
extern void frame_reference_add(pfn_t);
 
extern size_t find_zone(pfn_t frame, size_t count, size_t hint);
extern size_t zone_create(pfn_t, size_t, pfn_t, zone_flags_t);
extern void *frame_get_parent(pfn_t, size_t);
extern void frame_set_parent(pfn_t, void *, size_t);
extern void frame_mark_unavailable(pfn_t, size_t);
extern uintptr_t zone_conf_size(size_t);
extern bool zone_merge(size_t, size_t);
extern void zone_merge_all(void);
extern uint64_t zone_total_size(void);
 
/*
* Console functions
*/
extern void zone_print_list(void);
extern void zone_print_one(size_t);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/slab.h
0,0 → 1,148
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_SLAB_H_
#define KERN_SLAB_H_
 
#include <adt/list.h>
#include <synch/spinlock.h>
#include <atomic.h>
#include <mm/frame.h>
 
/** Minimum size to be allocated by malloc */
#define SLAB_MIN_MALLOC_W 4
 
/** Maximum size to be allocated by malloc */
#define SLAB_MAX_MALLOC_W 22
 
/** Initial Magazine size (TODO: dynamically growing magazines) */
#define SLAB_MAG_SIZE 4
 
/** If object size is less, store control structure inside SLAB */
#define SLAB_INSIDE_SIZE (PAGE_SIZE >> 3)
 
/** Maximum wasted space we allow for cache */
#define SLAB_MAX_BADNESS(cache) \
(((unsigned int) PAGE_SIZE << (cache)->order) >> 2)
 
/* slab_reclaim constants */
 
/** Reclaim all possible memory, because we are in memory stress */
#define SLAB_RECLAIM_ALL 0x01
 
/* cache_create flags */
 
/** Do not use per-cpu cache */
#define SLAB_CACHE_NOMAGAZINE 0x01
/** Have control structure inside SLAB */
#define SLAB_CACHE_SLINSIDE 0x02
/** We add magazine cache later, if we have this flag */
#define SLAB_CACHE_MAGDEFERRED (0x04 | SLAB_CACHE_NOMAGAZINE)
 
typedef struct {
link_t link;
size_t busy; /**< Count of full slots in magazine */
size_t size; /**< Number of slots in magazine */
void *objs[]; /**< Slots in magazine */
} slab_magazine_t;
 
typedef struct {
slab_magazine_t *current;
slab_magazine_t *last;
SPINLOCK_DECLARE(lock);
} slab_mag_cache_t;
 
 
typedef struct {
char *name;
link_t link;
/* Configuration */
/** Size of slab position - align_up(sizeof(obj)) */
size_t size;
int (*constructor)(void *obj, int kmflag);
int (*destructor)(void *obj);
/** Flags changing behaviour of cache */
int flags;
/* Computed values */
uint8_t order; /**< Order of frames to be allocated */
unsigned int objects; /**< Number of objects that fit in */
/* Statistics */
atomic_t allocated_slabs;
atomic_t allocated_objs;
atomic_t cached_objs;
/** How many magazines in magazines list */
atomic_t magazine_counter;
/* Slabs */
link_t full_slabs; /**< List of full slabs */
link_t partial_slabs; /**< List of partial slabs */
SPINLOCK_DECLARE(slablock);
/* Magazines */
link_t magazines; /**< List o full magazines */
SPINLOCK_DECLARE(maglock);
/** CPU cache */
slab_mag_cache_t *mag_cache;
} slab_cache_t;
 
extern slab_cache_t *slab_cache_create(char *, size_t, size_t,
int (*)(void *, int), int (*)(void *), int);
extern void slab_cache_destroy(slab_cache_t *);
 
extern void * slab_alloc(slab_cache_t *, int);
extern void slab_free(slab_cache_t *, void *);
extern size_t slab_reclaim(int);
 
/* slab subsytem initialization */
extern void slab_cache_init(void);
extern void slab_enable_cpucache(void);
 
/* kconsole debug */
extern void slab_print_list(void);
 
/* malloc support */
extern void *malloc(unsigned int, int);
extern void *realloc(void *, unsigned int, int);
extern void free(void *);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/tlb.h
0,0 → 1,91
/*
* Copyright (c) 2001-2004 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_TLB_H_
#define KERN_TLB_H_
 
#include <arch/mm/asid.h>
#include <arch/types.h>
 
/**
* Number of TLB shootdown messages that can be queued in processor tlb_messages
* queue.
*/
#define TLB_MESSAGE_QUEUE_LEN 10
 
/** Type of TLB shootdown message. */
typedef enum {
/** Invalid type. */
TLB_INVL_INVALID = 0,
/** Invalidate all entries in TLB. */
TLB_INVL_ALL,
/** Invalidate all entries belonging to one address space. */
TLB_INVL_ASID,
/** Invalidate specified page range belonging to one address space. */
TLB_INVL_PAGES
} tlb_invalidate_type_t;
 
/** TLB shootdown message. */
typedef struct {
tlb_invalidate_type_t type; /**< Message type. */
asid_t asid; /**< Address space identifier. */
uintptr_t page; /**< Page address. */
size_t count; /**< Number of pages to invalidate. */
} tlb_shootdown_msg_t;
 
extern void tlb_init(void);
 
#ifdef CONFIG_SMP
extern void tlb_shootdown_start(tlb_invalidate_type_t type, asid_t asid,
uintptr_t page, size_t count);
extern void tlb_shootdown_finalize(void);
extern void tlb_shootdown_ipi_recv(void);
#else
#define tlb_shootdown_start(w, x, y, z)
#define tlb_shootdown_finalize()
#define tlb_shootdown_ipi_recv()
#endif /* CONFIG_SMP */
 
/* Export TLB interface that each architecture must implement. */
extern void tlb_arch_init(void);
extern void tlb_print(void);
extern void tlb_shootdown_ipi_send(void);
 
extern void tlb_invalidate_all(void);
extern void tlb_invalidate_asid(asid_t asid);
extern void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt);
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/as.h
0,0 → 1,277
/*
* Copyright (c) 2001-2004 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_AS_H_
#define KERN_AS_H_
 
/** Address space area flags. */
#define AS_AREA_READ 1
#define AS_AREA_WRITE 2
#define AS_AREA_EXEC 4
#define AS_AREA_CACHEABLE 8
 
#ifdef KERNEL
 
#include <arch/mm/page.h>
#include <arch/mm/as.h>
#include <arch/mm/asid.h>
#include <arch/types.h>
#include <synch/spinlock.h>
#include <synch/mutex.h>
#include <adt/list.h>
#include <adt/btree.h>
#include <lib/elf.h>
 
/**
* Defined to be true if user address space and kernel address space shadow each
* other.
*/
#define KERNEL_ADDRESS_SPACE_SHADOWED KERNEL_ADDRESS_SPACE_SHADOWED_ARCH
 
#define KERNEL_ADDRESS_SPACE_START KERNEL_ADDRESS_SPACE_START_ARCH
#define KERNEL_ADDRESS_SPACE_END KERNEL_ADDRESS_SPACE_END_ARCH
#define USER_ADDRESS_SPACE_START USER_ADDRESS_SPACE_START_ARCH
#define USER_ADDRESS_SPACE_END USER_ADDRESS_SPACE_END_ARCH
 
#define USTACK_ADDRESS USTACK_ADDRESS_ARCH
 
/** Kernel address space. */
#define FLAG_AS_KERNEL (1 << 0)
 
/* Address space area attributes. */
#define AS_AREA_ATTR_NONE 0
#define AS_AREA_ATTR_PARTIAL 1 /**< Not fully initialized area. */
 
/** The page fault was not resolved by as_page_fault(). */
#define AS_PF_FAULT 0
/** The page fault was resolved by as_page_fault(). */
#define AS_PF_OK 1
/** The page fault was caused by memcpy_from_uspace() or memcpy_to_uspace(). */
#define AS_PF_DEFER 2
 
/** Address space structure.
*
* as_t contains the list of as_areas of userspace accessible
* pages for one or more tasks. Ranges of kernel memory pages are not
* supposed to figure in the list as they are shared by all tasks and
* set up during system initialization.
*/
typedef struct as {
/** Protected by asidlock. */
link_t inactive_as_with_asid_link;
/**
* Number of processors on wich is this address space active.
* Protected by asidlock.
*/
size_t cpu_refcount;
/**
* Address space identifier.
* Constant on architectures that do not support ASIDs.
* Protected by asidlock.
*/
asid_t asid;
 
/** Number of references (i.e tasks that reference this as). */
atomic_t refcount;
 
mutex_t lock;
 
/** B+tree of address space areas. */
btree_t as_area_btree;
/** Non-generic content. */
as_genarch_t genarch;
 
/** Architecture specific content. */
as_arch_t arch;
} as_t;
 
typedef struct {
pte_t *(* page_table_create)(int flags);
void (* page_table_destroy)(pte_t *page_table);
void (* page_table_lock)(as_t *as, bool lock);
void (* page_table_unlock)(as_t *as, bool unlock);
} as_operations_t;
 
/**
* This structure contains information associated with the shared address space
* area.
*/
typedef struct {
/** This lock must be acquired only when the as_area lock is held. */
mutex_t lock;
/** This structure can be deallocated if refcount drops to 0. */
size_t refcount;
/**
* B+tree containing complete map of anonymous pages of the shared area.
*/
btree_t pagemap;
} share_info_t;
 
/** Page fault access type. */
typedef enum {
PF_ACCESS_READ,
PF_ACCESS_WRITE,
PF_ACCESS_EXEC
} pf_access_t;
 
struct mem_backend;
 
/** Backend data stored in address space area. */
typedef union mem_backend_data {
struct { /**< elf_backend members */
elf_header_t *elf;
elf_segment_header_t *segment;
};
struct { /**< phys_backend members */
uintptr_t base;
size_t frames;
};
} mem_backend_data_t;
 
/** Address space area structure.
*
* Each as_area_t structure describes one contiguous area of virtual memory.
*/
typedef struct {
mutex_t lock;
/** Containing address space. */
as_t *as;
/**
* Flags related to the memory represented by the address space area.
*/
int flags;
/** Attributes related to the address space area itself. */
int attributes;
/** Size of this area in multiples of PAGE_SIZE. */
size_t pages;
/** Base address of this area. */
uintptr_t base;
/** Map of used space. */
btree_t used_space;
 
/**
* If the address space area has been shared, this pointer will
* reference the share info structure.
*/
share_info_t *sh_info;
 
/** Memory backend backing this address space area. */
struct mem_backend *backend;
 
/** Data to be used by the backend. */
mem_backend_data_t backend_data;
} as_area_t;
 
/** Address space area backend structure. */
typedef struct mem_backend {
int (* page_fault)(as_area_t *area, uintptr_t addr, pf_access_t access);
void (* frame_free)(as_area_t *area, uintptr_t page, uintptr_t frame);
void (* share)(as_area_t *area);
} mem_backend_t;
 
extern as_t *AS_KERNEL;
 
extern as_operations_t *as_operations;
extern link_t inactive_as_with_asid_head;
 
extern void as_init(void);
 
extern as_t *as_create(int flags);
extern void as_destroy(as_t *as);
extern void as_switch(as_t *old_as, as_t *new_as);
extern int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate);
 
extern as_area_t *as_area_create(as_t *as, int flags, size_t size,
uintptr_t base, int attrs, mem_backend_t *backend,
mem_backend_data_t *backend_data);
extern int as_area_destroy(as_t *as, uintptr_t address);
extern int as_area_resize(as_t *as, uintptr_t address, size_t size, int flags);
int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
as_t *dst_as, uintptr_t dst_base, int dst_flags_mask);
extern int as_area_change_flags(as_t *as, int flags, uintptr_t address);
 
extern int as_area_get_flags(as_area_t *area);
extern bool as_area_check_access(as_area_t *area, pf_access_t access);
extern size_t as_area_get_size(uintptr_t base);
extern int used_space_insert(as_area_t *a, uintptr_t page, size_t count);
extern int used_space_remove(as_area_t *a, uintptr_t page, size_t count);
 
 
/* Interface to be implemented by architectures. */
#ifndef as_constructor_arch
extern int as_constructor_arch(as_t *as, int flags);
#endif /* !def as_constructor_arch */
#ifndef as_destructor_arch
extern int as_destructor_arch(as_t *as);
#endif /* !def as_destructor_arch */
#ifndef as_create_arch
extern int as_create_arch(as_t *as, int flags);
#endif /* !def as_create_arch */
#ifndef as_install_arch
extern void as_install_arch(as_t *as);
#endif /* !def as_install_arch */
#ifndef as_deinstall_arch
extern void as_deinstall_arch(as_t *as);
#endif /* !def as_deinstall_arch */
 
/* Backend declarations and functions. */
extern mem_backend_t anon_backend;
extern mem_backend_t elf_backend;
extern mem_backend_t phys_backend;
 
/**
* This flags is passed when running the loader, otherwise elf_load()
* would return with a EE_LOADER error code.
*/
#define ELD_F_NONE 0
#define ELD_F_LOADER 1
 
extern unsigned int elf_load(elf_header_t *header, as_t *as, int flags);
 
/* Address space area related syscalls. */
extern unative_t sys_as_area_create(uintptr_t address, size_t size, int flags);
extern unative_t sys_as_area_resize(uintptr_t address, size_t size, int flags);
extern unative_t sys_as_area_change_flags(uintptr_t address, int flags);
extern unative_t sys_as_area_destroy(uintptr_t address);
 
/* Introspection functions. */
extern void as_print(as_t *as);
 
#endif /* KERNEL */
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/page.h
0,0 → 1,68
/*
* Copyright (c) 2001-2004 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_PAGE_H_
#define KERN_PAGE_H_
 
#include <arch/types.h>
#include <mm/as.h>
#include <memstr.h>
 
/** Operations to manipulate page mappings. */
typedef struct {
void (* mapping_insert)(as_t *as, uintptr_t page, uintptr_t frame,
int flags);
void (* mapping_remove)(as_t *as, uintptr_t page);
pte_t *(* mapping_find)(as_t *as, uintptr_t page);
} page_mapping_operations_t;
 
extern page_mapping_operations_t *page_mapping_operations;
 
extern void page_init(void);
extern void page_table_lock(as_t *as, bool lock);
extern void page_table_unlock(as_t *as, bool unlock);
extern void page_mapping_insert(as_t *as, uintptr_t page, uintptr_t frame,
int flags);
extern void page_mapping_remove(as_t *as, uintptr_t page);
extern pte_t *page_mapping_find(as_t *as, uintptr_t page);
extern pte_t *page_table_create(int flags);
extern void page_table_destroy(pte_t *page_table);
extern void map_structure(uintptr_t s, size_t size);
 
extern uintptr_t hw_map(uintptr_t physaddr, size_t size);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/buddy.h
0,0 → 1,91
/*
* Copyright (c) 2005 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_BUDDY_H_
#define KERN_BUDDY_H_
 
#include <arch/types.h>
#include <adt/list.h>
 
#define BUDDY_SYSTEM_INNER_BLOCK 0xff
 
struct buddy_system;
 
/** Buddy system operations to be implemented by each implementation. */
typedef struct {
/**
* Return pointer to left-side or right-side buddy for block passed as
* argument.
*/
link_t *(* find_buddy)(struct buddy_system *, link_t *);
/**
* Bisect the block passed as argument and return pointer to the new
* right-side buddy.
*/
link_t *(* bisect)(struct buddy_system *, link_t *);
/** Coalesce two buddies into a bigger block. */
link_t *(* coalesce)(struct buddy_system *, link_t *, link_t *);
/** Set order of block passed as argument. */
void (*set_order)(struct buddy_system *, link_t *, uint8_t);
/** Return order of block passed as argument. */
uint8_t (*get_order)(struct buddy_system *, link_t *);
/** Mark block as busy. */
void (*mark_busy)(struct buddy_system *, link_t *);
/** Mark block as available. */
void (*mark_available)(struct buddy_system *, link_t *);
/** Find parent of block that has given order */
link_t *(* find_block)(struct buddy_system *, link_t *, uint8_t);
} buddy_system_operations_t;
 
typedef struct buddy_system {
/** Maximal order of block which can be stored by buddy system. */
uint8_t max_order;
link_t *order;
buddy_system_operations_t *op;
/** Pointer to be used by the implementation. */
void *data;
} buddy_system_t;
 
extern void buddy_system_create(buddy_system_t *, uint8_t,
buddy_system_operations_t *, void *);
extern link_t *buddy_system_alloc(buddy_system_t *, uint8_t);
extern bool buddy_system_can_alloc(buddy_system_t *, uint8_t);
extern void buddy_system_free(buddy_system_t *, link_t *);
extern size_t buddy_conf_size(size_t);
extern link_t *buddy_system_alloc_block(buddy_system_t *, link_t *);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/mm.h
0,0 → 1,67
/*
* Copyright (c) 2007 Martin Decky
* 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_MM_H_
#define KERN_MM_H_
 
#define PAGE_CACHEABLE_SHIFT 0
#define PAGE_NOT_CACHEABLE_SHIFT PAGE_CACHEABLE_SHIFT
#define PAGE_PRESENT_SHIFT 1
#define PAGE_NOT_PRESENT_SHIFT PAGE_PRESENT_SHIFT
#define PAGE_USER_SHIFT 2
#define PAGE_KERNEL_SHIFT PAGE_USER_SHIFT
#define PAGE_READ_SHIFT 3
#define PAGE_WRITE_SHIFT 4
#define PAGE_EXEC_SHIFT 5
#define PAGE_GLOBAL_SHIFT 6
 
#define PAGE_NOT_CACHEABLE (0 << PAGE_CACHEABLE_SHIFT)
#define PAGE_CACHEABLE (1 << PAGE_CACHEABLE_SHIFT)
 
#define PAGE_PRESENT (0 << PAGE_PRESENT_SHIFT)
#define PAGE_NOT_PRESENT (1 << PAGE_PRESENT_SHIFT)
 
#define PAGE_USER (1 << PAGE_USER_SHIFT)
#define PAGE_KERNEL (0 << PAGE_USER_SHIFT)
 
#define PAGE_READ (1 << PAGE_READ_SHIFT)
#define PAGE_WRITE (1 << PAGE_WRITE_SHIFT)
#define PAGE_EXEC (1 << PAGE_EXEC_SHIFT)
 
#define PAGE_GLOBAL (1 << PAGE_GLOBAL_SHIFT)
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/mm/asid.h
0,0 → 1,89
/*
* Copyright (c) 2006 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 genericmm
* @{
*/
/** @file
*/
 
/*
* This is generic interface for managing
* Address Space IDentifiers (ASIDs).
*/
 
#ifndef KERN_ASID_H_
#define KERN_ASID_H_
 
#ifndef __ASM__
 
#include <arch/mm/asid.h>
#include <synch/spinlock.h>
#include <adt/list.h>
#include <mm/as.h>
 
#endif
 
#define ASID_KERNEL 0
#define ASID_INVALID 1
#define ASID_START 2
#define ASID_MAX ASID_MAX_ARCH
 
#ifndef __ASM__
 
#define ASIDS_ALLOCABLE ((ASID_MAX + 1) - ASID_START)
 
SPINLOCK_EXTERN(asidlock);
extern link_t as_with_asid_head;
 
#ifndef asid_get
extern asid_t asid_get(void);
#endif /* !def asid_get */
 
#ifndef asid_put
extern void asid_put(asid_t asid);
#endif /* !def asid_put */
 
#ifndef asid_install
extern void asid_install(as_t *as);
#endif /* !def asid_install */
 
#ifndef asid_find_free
extern asid_t asid_find_free(void);
#endif /* !def asid_find_free */
 
#ifndef asid_put_arch
extern void asid_put_arch(asid_t asid);
#endif /* !def asid_put_arch */
 
#endif
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ipc/event.h
0,0 → 1,79
/*
* Copyright (c) 2009 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_EVENT_H_
#define KERN_EVENT_H_
 
#include <ipc/event_types.h>
#include <arch/types.h>
#include <synch/spinlock.h>
#include <ipc/ipc.h>
 
/** Event notification structure. */
typedef struct {
SPINLOCK_DECLARE(lock);
/** Answerbox for notifications. */
answerbox_t *answerbox;
/** Method to be used for the notification. */
unative_t method;
/** Counter. */
size_t counter;
} event_t;
 
extern void event_init(void);
extern unative_t sys_event_subscribe(unative_t, unative_t);
extern bool event_is_subscribed(event_type_t);
extern void event_cleanup_answerbox(answerbox_t *);
 
#define event_notify_0(e) \
event_notify((e), 0, 0, 0, 0, 0)
#define event_notify_1(e, a1) \
event_notify((e), (a1), 0, 0, 0, 0)
#define event_notify_2(e, a1, a2) \
event_notify((e), (a1), (a2), 0, 0, 0)
#define event_notify_3(e, a1, a2, a3) \
event_notify((e), (a1), (a2), (a3), 0, 0)
#define event_notify_4(e, a1, a2, a3, a4) \
event_notify((e), (a1), (a2), (a3), (a4), 0)
#define event_notify_5(e, a1, a2, a3, a4, a5) \
event_notify((e), (a1), (a2), (a3), (a4), (a5))
 
extern void event_notify(event_type_t, unative_t, unative_t, unative_t,
unative_t, unative_t);
 
#endif
 
/** @}
*/
Property changes:
Added: svn:mergeinfo
/tags/0.4.1/kernel/generic/include/ipc/event_types.h
0,0 → 1,53
/*
* Copyright (c) 2009 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_EVENT_TYPES_H_
#define KERN_EVENT_TYPES_H_
 
typedef enum event_type {
EVENT_KLOG = 0,
EVENT_KCONSOLE,
EVENT_WAIT,
EVENT_END
} event_type_t;
 
typedef enum wait_type {
TASK_CREATE = 0,
TASK_DESTROY
} wait_type_t;
 
#endif
 
/** @}
*/
Property changes:
Added: svn:mergeinfo
/tags/0.4.1/kernel/generic/include/ipc/ipc.h
0,0 → 1,345
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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_H_
#define KERN_IPC_H_
 
/* Length of data being transfered with IPC call */
/* - the uspace may not be able to utilize full length */
#define IPC_CALL_LEN 6
 
/** Maximum active async calls per thread */
#ifdef CONFIG_DEBUG
#define IPC_MAX_ASYNC_CALLS 4
#else
#define IPC_MAX_ASYNC_CALLS 4000
#endif
 
/* Flags for calls */
 
/** This is answer to a call */
#define IPC_CALL_ANSWERED (1 << 0)
/** This call will not be freed on error */
#define IPC_CALL_STATIC_ALLOC (1 << 1)
/** Answer will not be passed to userspace, will be discarded */
#define IPC_CALL_DISCARD_ANSWER (1 << 2)
/** Call was forwarded */
#define IPC_CALL_FORWARDED (1 << 3)
/** Identify connect_me_to answer */
#define IPC_CALL_CONN_ME_TO (1 << 4)
/** Interrupt notification */
#define IPC_CALL_NOTIF (1 << 5)
 
/*
* Bits used in call hashes.
* The addresses are aligned at least to 4 that is why we can use the 2 least
* significant bits of the call address.
*/
/** Type of this call is 'answer' */
#define IPC_CALLID_ANSWERED 1
/** Type of this call is 'notification' */
#define IPC_CALLID_NOTIFICATION 2
 
/* Return values from sys_ipc_call_async(). */
#define IPC_CALLRET_FATAL -1
#define IPC_CALLRET_TEMPORARY -2
 
 
/* Macros for manipulating calling data */
#define IPC_SET_RETVAL(data, retval) ((data).args[0] = (retval))
#define IPC_SET_METHOD(data, val) ((data).args[0] = (val))
#define IPC_SET_ARG1(data, val) ((data).args[1] = (val))
#define IPC_SET_ARG2(data, val) ((data).args[2] = (val))
#define IPC_SET_ARG3(data, val) ((data).args[3] = (val))
#define IPC_SET_ARG4(data, val) ((data).args[4] = (val))
#define IPC_SET_ARG5(data, val) ((data).args[5] = (val))
 
#define IPC_GET_METHOD(data) ((data).args[0])
#define IPC_GET_RETVAL(data) ((data).args[0])
 
#define IPC_GET_ARG1(data) ((data).args[1])
#define IPC_GET_ARG2(data) ((data).args[2])
#define IPC_GET_ARG3(data) ((data).args[3])
#define IPC_GET_ARG4(data) ((data).args[4])
#define IPC_GET_ARG5(data) ((data).args[5])
 
/* Well known phone descriptors */
#define PHONE_NS 0
 
/* Forwarding flags. */
#define IPC_FF_NONE 0
/**
* The call will be routed as though it was initially sent via the phone used to
* forward it. This feature is intended to support the situation in which the
* forwarded call needs to be handled by the same connection fibril as any other
* calls that were initially sent by the forwarder to the same destination. This
* flag has no imapct on routing replies.
*/
#define IPC_FF_ROUTE_FROM_ME (1 << 0)
 
/* System-specific methods - only through special syscalls
* These methods have special behaviour
*/
/** Clone connection.
*
* The calling task clones one of its phones for the callee.
*
* - ARG1 - The caller sets ARG1 to the phone of the cloned connection.
* - The callee gets the new phone from ARG1.
* - on answer, the callee acknowledges the new connection by sending EOK back
* or the kernel closes it
*/
#define IPC_M_CONNECTION_CLONE 1
/** Protocol for CONNECT - ME
*
* Through this call, the recipient learns about the new cloned connection.
*
* - ARG5 - the kernel sets ARG5 to contain the hash of the used phone
* - on asnwer, the callee acknowledges the new connection by sending EOK back
* or the kernel closes it
*/
#define IPC_M_CONNECT_ME 2
/** Protocol for CONNECT - TO - ME
*
* Calling process asks the callee to create a callback connection,
* so that it can start initiating new messages.
*
* The protocol for negotiating is:
* - sys_connect_to_me - sends a message IPC_M_CONNECT_TO_ME
* - recipient - upon receipt tries to allocate new phone
* - if it fails, responds with ELIMIT
* - passes call to userspace. If userspace
* responds with error, phone is deallocated and
* error is sent back to caller. Otherwise
* the call is accepted and the response is sent back.
* - the allocated phoneid is passed to userspace
* (on the receiving side) as ARG5 of the call.
*/
#define IPC_M_CONNECT_TO_ME 3
/** Protocol for CONNECT - ME - TO
*
* Calling process asks the callee to create for him a new connection.
* E.g. the caller wants a name server to connect him to print server.
*
* The protocol for negotiating is:
* - sys_connect_me_to - send a synchronous message to name server
* indicating that it wants to be connected to some
* service
* - arg1/2/3 are user specified, arg5 contains
* address of the phone that should be connected
* (TODO: it leaks to userspace)
* - recipient - if ipc_answer == 0, then accept connection
* - otherwise connection refused
* - recepient may forward message.
*
*/
#define IPC_M_CONNECT_ME_TO 4
/** This message is sent to answerbox when the phone
* is hung up
*/
#define IPC_M_PHONE_HUNGUP 5
 
/** Send as_area over IPC.
* - ARG1 - source as_area base address
* - ARG2 - size of source as_area (filled automatically by kernel)
* - ARG3 - flags of the as_area being sent
*
* on answer, the recipient must set:
* - ARG1 - dst as_area base adress
*/
#define IPC_M_SHARE_OUT 6
 
/** Receive as_area over IPC.
* - ARG1 - destination as_area base address
* - ARG2 - destination as_area size
* - ARG3 - user defined argument
*
* on answer, the recipient must set:
*
* - ARG1 - source as_area base address
* - ARG2 - flags that will be used for sharing
*/
#define IPC_M_SHARE_IN 7
 
/** Send data to another address space over IPC.
* - ARG1 - source address space virtual address
* - ARG2 - size of data to be copied, may be overriden by the recipient
*
* on answer, the recipient must set:
*
* - ARG1 - final destination address space virtual address
* - ARG2 - final size of data to be copied
*/
#define IPC_M_DATA_WRITE 8
 
/** Receive data from another address space over IPC.
* - ARG1 - destination virtual address in the source address space
* - ARG2 - size of data to be received, may be cropped by the recipient
*
* on answer, the recipient must set:
*
* - ARG1 - source virtual address in the destination address space
* - ARG2 - final size of data to be copied
*/
#define IPC_M_DATA_READ 9
 
/** 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 10
 
/* Well-known methods */
#define IPC_M_LAST_SYSTEM 511
#define IPC_M_PING 512
/* User methods */
#define IPC_FIRST_USER_METHOD 1024
 
#ifdef KERNEL
 
#define IPC_MAX_PHONES 16
 
#include <synch/spinlock.h>
#include <synch/mutex.h>
#include <synch/waitq.h>
 
struct answerbox;
struct task;
 
typedef enum {
/** Phone is free and can be allocated */
IPC_PHONE_FREE = 0,
/** Phone is connecting somewhere */
IPC_PHONE_CONNECTING,
/** Phone is connected */
IPC_PHONE_CONNECTED,
/** Phone is hung up, waiting for answers to come */
IPC_PHONE_HUNGUP,
/** Phone was hungup from server */
IPC_PHONE_SLAMMED
} ipc_phone_state_t;
 
/** Structure identifying phone (in TASK structure) */
typedef struct {
mutex_t lock;
link_t link;
struct answerbox *callee;
ipc_phone_state_t state;
atomic_t active_calls;
} phone_t;
 
typedef struct answerbox {
SPINLOCK_DECLARE(lock);
 
struct task *task;
 
waitq_t wq;
 
/** Phones connected to this answerbox. */
link_t connected_phones;
/** Received calls. */
link_t calls;
link_t dispatched_calls; /* Should be hash table in the future */
 
/** Answered calls. */
link_t answers;
 
SPINLOCK_DECLARE(irq_lock);
/** Notifications from IRQ handlers. */
link_t irq_notifs;
/** IRQs with notifications to this answerbox. */
link_t irq_head;
} answerbox_t;
 
typedef struct {
unative_t args[IPC_CALL_LEN];
phone_t *phone;
} ipc_data_t;
 
typedef struct {
link_t link;
 
int flags;
 
/** Identification of the caller. */
struct task *sender;
/** The caller box is different from sender->answerbox for synchronous
* calls. */
answerbox_t *callerbox;
 
/** Private data to internal IPC. */
unative_t priv;
 
/** Data passed from/to userspace. */
ipc_data_t data;
 
/** Buffer for IPC_M_DATA_WRITE and IPC_M_DATA_READ. */
uint8_t *buffer;
 
/*
* The forward operation can masquerade the caller phone. For those
* cases, we must keep it aside so that the answer is processed
* correctly.
*/
phone_t *caller_phone;
} call_t;
 
extern void ipc_init(void);
extern call_t * ipc_wait_for_call(answerbox_t *, uint32_t, int);
extern void ipc_answer(answerbox_t *, call_t *);
extern int ipc_call(phone_t *, call_t *);
extern int ipc_call_sync(phone_t *, call_t *);
extern void ipc_phone_init(phone_t *);
extern void ipc_phone_connect(phone_t *, answerbox_t *);
extern void ipc_call_free(call_t *);
extern call_t * ipc_call_alloc(int);
extern void ipc_answerbox_init(answerbox_t *, struct task *);
extern void ipc_call_static_init(call_t *);
extern void task_print_list(void);
extern int ipc_forward(call_t *, phone_t *, answerbox_t *, int);
extern void ipc_cleanup(void);
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;
 
#endif
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ipc/ipcrsc.h
0,0 → 1,49
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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_IPCRSC_H_
#define KERN_IPCRSC_H_
 
#include <proc/task.h>
#include <ipc/ipc.h>
 
extern call_t * get_call(unative_t callid);
extern int phone_alloc(task_t *t);
extern void phone_connect(int phoneid, answerbox_t *box);
extern void phone_dealloc(int phoneid);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ipc/irq.h
0,0 → 1,78
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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_IRQ_H_
#define KERN_IPC_IRQ_H_
 
/** Maximum length of IPC IRQ program */
#define IRQ_MAX_PROG_SIZE 20
 
#include <ipc/ipc.h>
#include <ddi/irq.h>
#include <arch/types.h>
#include <adt/list.h>
 
extern int ipc_irq_register(answerbox_t *, inr_t, devno_t, unative_t,
irq_code_t *);
 
extern irq_ownership_t ipc_irq_top_half_claim(irq_t *);
extern void ipc_irq_top_half_handler(irq_t *);
 
extern int ipc_irq_unregister(answerbox_t *, inr_t, devno_t);
extern void ipc_irq_cleanup(answerbox_t *);
 
/*
* User friendly wrappers for ipc_irq_send_msg(). They are in the form
* ipc_irq_send_msg_m(), where m is the number of payload arguments.
*/
#define ipc_irq_send_msg_0(irq) \
ipc_irq_send_msg((irq), 0, 0, 0, 0, 0)
#define ipc_irq_send_msg_1(irq, a1) \
ipc_irq_send_msg((irq), (a1), 0, 0, 0, 0)
#define ipc_irq_send_msg_2(irq, a1, a2) \
ipc_irq_send_msg((irq), (a1), (a2), 0, 0, 0)
#define ipc_irq_send_msg_3(irq, a1, a2, a3) \
ipc_irq_send_msg((irq), (a1), (a2), (a3), 0, 0)
#define ipc_irq_send_msg_4(irq, a1, a2, a3, a4) \
ipc_irq_send_msg((irq), (a1), (a2), (a3), (a4), 0)
#define ipc_irq_send_msg_5(irq, a1, a2, a3, a4, a5) \
ipc_irq_send_msg((irq), (a1), (a2), (a3), (a4), (a5))
 
extern void ipc_irq_send_msg(irq_t *, unative_t, unative_t, unative_t, unative_t,
unative_t);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ipc/sysipc.h
0,0 → 1,67
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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_SYSIPC_H_
#define KERN_SYSIPC_H_
 
#include <ipc/ipc.h>
#include <ipc/irq.h>
#include <arch/types.h>
 
unative_t sys_ipc_call_sync_fast(unative_t phoneid, unative_t method,
unative_t arg1, unative_t arg2, unative_t arg3, ipc_data_t *data);
unative_t sys_ipc_call_sync_slow(unative_t phoneid, ipc_data_t *question,
ipc_data_t *reply);
unative_t sys_ipc_call_async_fast(unative_t phoneid, unative_t method,
unative_t arg1, unative_t arg2, unative_t arg3, unative_t arg4);
unative_t sys_ipc_call_async_slow(unative_t phoneid, ipc_data_t *data);
unative_t sys_ipc_answer_fast(unative_t callid, unative_t retval,
unative_t arg1, unative_t arg2, unative_t arg3, unative_t arg4);
unative_t sys_ipc_answer_slow(unative_t callid, ipc_data_t *data);
unative_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec,
int nonblocking);
unative_t sys_ipc_forward_fast(unative_t callid, unative_t phoneid,
unative_t method, unative_t arg1, unative_t arg2, int mode);
unative_t sys_ipc_forward_slow(unative_t callid, unative_t phoneid,
ipc_data_t *data, int mode);
unative_t sys_ipc_hangup(int phoneid);
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
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/ipc/kbox.h
0,0 → 1,58
/*
* 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>
 
/** Kernel answerbox structure. */
typedef struct kbox {
/** The answerbox itself. */
answerbox_t box;
/** Thread used to service the answerbox. */
struct thread *thread;
/** Kbox thread creation vs. begin of cleanup mutual exclusion. */
mutex_t cleanup_lock;
/** True if cleanup of kbox has already started. */
bool finished;
} kbox_t;
 
extern int ipc_connect_kbox(task_id_t);
extern void ipc_kbox_cleanup(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/sort.h
0,0 → 1,57
/*
* Copyright (c) 2005 Sergey Bondari
* 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_SORT_H_
#define KERN_SORT_H_
 
#include <arch/types.h>
 
/*
* sorting routines
*/
extern void bubblesort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b));
extern void qsort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b));
 
/*
* default sorting comparators
*/
extern int int_cmp(void * a, void * b);
extern int uint32_t_cmp(void * a, void * b);
extern int uint16_t_cmp(void * a, void * b);
extern int uint8_t_cmp(void * a, void * b);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/macros.h
0,0 → 1,108
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_MACROS_H_
#define KERN_MACROS_H_
 
#ifndef __ASM__
 
#include <arch/types.h>
 
/** Return true if the intervals overlap.
*
* @param s1 Start address of the first interval.
* @param sz1 Size of the first interval.
* @param s2 Start address of the second interval.
* @param sz2 Size of the second interval.
*/
static inline int overlaps(uintptr_t s1, size_t sz1, uintptr_t s2, size_t sz2)
{
uintptr_t e1 = s1 + sz1;
uintptr_t e2 = s2 + sz2;
return ((s1 < e2) && (s2 < e1));
}
 
#endif /* __ASM__ */
 
#define isdigit(d) (((d) >= '0') && ((d) <= '9'))
#define islower(c) (((c) >= 'a') && ((c) <= 'z'))
#define isupper(c) (((c) >= 'A') && ((c) <= 'Z'))
#define isalpha(c) (is_lower((c)) || is_upper((c)))
#define isalphanum(c) (is_alpha((c)) || is_digit((c)))
#define isspace(c) \
(((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
 
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
 
#define min3(a, b, c) ((a) < (b) ? (min(a, c)) : (min(b, c)))
#define max3(a, b, c) ((a) > (b) ? (max(a, c)) : (max(b, c)))
 
/* Compute overlapping of physical addresses */
#define PA_overlaps(x, szx, y, szy) \
overlaps(KA2PA((x)), (szx), KA2PA((y)), (szy))
 
#define SIZE2KB(size) ((size) >> 10)
#define SIZE2MB(size) ((size) >> 20)
 
#define KB2SIZE(kb) ((kb) << 10)
#define MB2SIZE(mb) ((mb) << 20)
 
#define STRING(arg) STRING_ARG(arg)
#define STRING_ARG(arg) #arg
 
#define LOWER32(arg) ((arg) & 0xffffffff)
#define UPPER32(arg) (((arg) >> 32) & 0xffffffff)
 
#define MERGE_LOUP32(lo, up) \
((((uint64_t) (lo)) & 0xffffffff) \
| ((((uint64_t) (up)) & 0xffffffff) << 32))
 
/** Pseudorandom generator
*
* A pretty standard linear congruential pseudorandom
* number generator (m = 2^32 or 2^64 depending on architecture).
*
*/
#define RANDI(seed) \
({ \
(seed) = 1103515245 * (seed) + 12345; \
(seed); \
})
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/sysinfo/sysinfo.h
0,0 → 1,92
/*
* Copyright (c) 2006 Jakub Vana
* 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_SYSINFO_H_
#define KERN_SYSINFO_H_
 
#include <arch/types.h>
#include <string.h>
 
typedef union sysinfo_item_val {
unative_t val;
void *fn;
} sysinfo_item_val_t;
 
typedef struct sysinfo_item {
char *name;
union {
unative_t val;
void *fn;
} val;
 
union {
struct sysinfo_item *table;
void *fn;
} subinfo;
 
struct sysinfo_item *next;
int val_type;
int subinfo_type;
} sysinfo_item_t;
 
#define SYSINFO_VAL_VAL 0
#define SYSINFO_VAL_FUNCTION 1
#define SYSINFO_VAL_UNDEFINED U_SPECIAL
 
#define SYSINFO_SUBINFO_NONE 0
#define SYSINFO_SUBINFO_TABLE 1
#define SYSINFO_SUBINFO_FUNCTION 2
 
typedef unative_t (*sysinfo_val_fn_t)(sysinfo_item_t *root);
typedef unative_t (*sysinfo_subinfo_fn_t)(const char *subname);
 
typedef struct sysinfo_rettype {
unative_t val;
unative_t valid;
} sysinfo_rettype_t;
 
void sysinfo_set_item_val(const char *name, sysinfo_item_t **root, unative_t val);
void sysinfo_dump(sysinfo_item_t **root, int depth);
void sysinfo_set_item_function(const char *name, sysinfo_item_t **root, sysinfo_val_fn_t fn);
void sysinfo_set_item_undefined(const char *name, sysinfo_item_t **root);
 
sysinfo_rettype_t sysinfo_get_val(const char *name, sysinfo_item_t **root);
 
unative_t sys_sysinfo_valid(unative_t ptr, unative_t len);
unative_t sys_sysinfo_value(unative_t ptr, unative_t len);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/printf/printf_core.h
0,0 → 1,58
/*
* Copyright (c) 2006 Josef Cejka
* 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_PRINTF_CORE_H_
#define KERN_PRINTF_CORE_H_
 
#include <typedefs.h>
#include <arch/arg.h>
 
/** Structure for specifying output methods for different printf clones. */
typedef struct {
/* String output function, returns number of printed characters or EOF */
int (*str_write)(const char *, size_t, void *);
/* Wide string output function, returns number of printed characters or EOF */
int (*wstr_write)(const wchar_t *, size_t, void *);
/* User data - output stream specification, state, locks, etc. */
void *data;
} printf_spec_t;
 
int printf_core(const char *fmt, printf_spec_t *ps, va_list ap);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/print.h
0,0 → 1,57
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_PRINT_H_
#define KERN_PRINT_H_
 
#include <arch/types.h>
#include <synch/spinlock.h>
#include <arch/arg.h>
 
/* We need this address in spinlock to avoid deadlock in deadlock detection */
SPINLOCK_EXTERN(printf_lock);
 
#define EOF (-1)
 
extern int puts(const char *s);
extern int printf(const char *fmt, ...);
extern int snprintf(char *str, size_t size, const char *fmt, ...);
 
extern int vprintf(const char *fmt, va_list ap);
extern int vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/putchar.h
0,0 → 1,43
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_PUTCHAR_H_
#define KERN_PUTCHAR_H_
 
extern void putchar(const wchar_t ch);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/syscall/syscall.h
0,0 → 1,108
/*
* Copyright (c) 2005 Martin Decky
* 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_SYSCALL_H_
#define KERN_SYSCALL_H_
 
typedef enum {
SYS_KLOG = 0,
SYS_TLS_SET = 1, /* Hardcoded in AMD64, IA32 uspace - fibril.S */
SYS_THREAD_CREATE,
SYS_THREAD_EXIT,
SYS_THREAD_GET_ID,
SYS_TASK_GET_ID,
SYS_TASK_SET_NAME,
SYS_PROGRAM_SPAWN_LOADER,
SYS_FUTEX_SLEEP,
SYS_FUTEX_WAKEUP,
SYS_SMC_COHERENCE,
SYS_AS_AREA_CREATE,
SYS_AS_AREA_RESIZE,
SYS_AS_AREA_CHANGE_FLAGS,
SYS_AS_AREA_DESTROY,
SYS_IPC_CALL_SYNC_FAST,
SYS_IPC_CALL_SYNC_SLOW,
SYS_IPC_CALL_ASYNC_FAST,
SYS_IPC_CALL_ASYNC_SLOW,
SYS_IPC_ANSWER_FAST,
SYS_IPC_ANSWER_SLOW,
SYS_IPC_FORWARD_FAST,
SYS_IPC_FORWARD_SLOW,
SYS_IPC_WAIT,
SYS_IPC_HANGUP,
SYS_IPC_REGISTER_IRQ,
SYS_IPC_UNREGISTER_IRQ,
 
SYS_EVENT_SUBSCRIBE,
SYS_CAP_GRANT,
SYS_CAP_REVOKE,
SYS_DEVICE_ASSIGN_DEVNO,
SYS_PHYSMEM_MAP,
SYS_IOSPACE_ENABLE,
SYS_PREEMPT_CONTROL,
SYS_SYSINFO_VALID,
SYS_SYSINFO_VALUE,
SYS_DEBUG_ENABLE_CONSOLE,
SYS_DEBUG_DISABLE_CONSOLE,
SYS_IPC_CONNECT_KBOX,
SYSCALL_END
} syscall_t;
 
#ifdef KERNEL
 
#include <arch/types.h>
 
typedef unative_t (*syshandler_t)(unative_t, unative_t, unative_t, unative_t,
unative_t, unative_t);
 
extern syshandler_t syscall_table[SYSCALL_END];
extern unative_t syscall_handler(unative_t, unative_t, unative_t, unative_t,
unative_t, unative_t, unative_t);
extern unative_t sys_tls_set(unative_t);
 
#endif
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/syscall/copy.h
0,0 → 1,58
/*
* Copyright (c) 2006 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_COPY_H_
#define KERN_COPY_H_
 
#include <arch/types.h>
 
/** Label within memcpy_from_uspace() that contains return -1. */
extern char memcpy_from_uspace_failover_address;
 
/** Label within memcpy_to_uspace() that contains return -1. */
extern char memcpy_to_uspace_failover_address;
 
extern int copy_from_uspace(void *dst, const void *uspace_src, size_t size);
extern int copy_to_uspace(void *dst_uspace, const void *src, size_t size);
 
/*
* This interface must be implemented by each architecture.
*/
extern int memcpy_from_uspace(void *dst, const void *uspace_src, size_t size);
extern int memcpy_to_uspace(void *uspace_dst, const void *src, size_t size);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/syscall/sysarg64.h
0,0 → 1,48
/*
* Copyright (c) 2006 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 generic
* @{
*/
 
/**
* @file
* @brief Wrapper for explicit 64-bit arguments passed to syscalls.
*/
 
#ifndef KERN_SYSARG64_H_
#define KERN_SYSARG64_H_
 
typedef struct {
unsigned long long value;
} sysarg64_t;
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/typedefs.h
0,0 → 1,63
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_TYPEDEFS_H_
#define KERN_TYPEDEFS_H_
 
#include <arch/types.h>
 
#define NULL 0
#define false 0
#define true 1
 
typedef void (* function)();
 
typedef uint8_t bool;
typedef uint64_t thread_id_t;
typedef uint64_t task_id_t;
typedef uint32_t context_id_t;
 
typedef int32_t inr_t;
typedef int32_t devno_t;
 
typedef int32_t wchar_t;
 
typedef volatile uint8_t ioport8_t;
typedef volatile uint16_t ioport16_t;
typedef volatile uint32_t ioport32_t;
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/errno.h
0,0 → 1,67
/*
* Copyright (c) 2005 Martin Decky
* 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_ERRNO_H_
#define KERN_ERRNO_H_
 
/* 1-255 are kernel error codes, 256-512 are user error codes */
 
#define EOK 0 /* No error */
#define ENOENT -1 /* No such entry */
#define ENOMEM -2 /* Not enough memory */
#define ELIMIT -3 /* Limit exceeded */
#define EREFUSED -4 /* Connection refused */
#define EFORWARD -5 /* Forward error */
#define EPERM -6 /* Permission denied */
#define EHANGUP -7 /* Answerbox closed connection, call
* sys_ipc_hangup() to close the connection.
* Used by answerbox to close the connection.
*/
#define EPARTY -8 /* The other party encountered an error when
* receiving the call.
*/
#define EEXISTS -9 /* Entry already exists */
#define EBADMEM -10 /* Bad memory pointer */
#define ENOTSUP -11 /* Not supported */
#define EADDRNOTAVAIL -12 /* Address not available. */
#define ETIMEOUT -13 /* Timeout expired */
#define EINVAL -14 /* Invalid value */
#define EBUSY -15 /* Resource is busy */
#define EOVERFLOW -16 /* The result does not fit its size. */
#define EINTR -17 /* Operation was interrupted. */
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/func.h
0,0 → 1,50
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_FUNC_H_
#define KERN_FUNC_H_
 
#include <arch/types.h>
#include <atomic.h>
 
extern atomic_t haltstate;
 
extern void halt(void);
extern unative_t atoi(const char *text);
extern void order(const uint64_t val, uint64_t *rv, char *suffix);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/memstr.h
0,0 → 1,52
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_MEMSTR_H_
#define KERN_MEMSTR_H_
 
#include <arch/types.h>
#include <arch/memstr.h>
 
/*
* Architecture independent variants.
*/
extern void *_memcpy(void *dst, const void *src, size_t cnt);
extern void _memsetb(void *dst, size_t cnt, uint8_t x);
extern void _memsetw(void *dst, size_t cnt, uint16_t x);
extern void *memmove(void *dst, const void *src, size_t cnt);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/main/main.h
0,0 → 1,48
/*
* Copyright (c) 2006 Martin Decky
* 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 main
* @{
*/
/** @file
*/
#ifndef KERN_MAIN_H_
#define KERN_MAIN_H_
 
#include <arch/types.h>
 
extern uintptr_t stack_safe;
 
extern void main_bsp(void);
extern void main_ap(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/main/kinit.h
0,0 → 1,43
/*
* Copyright (c) 2001-2004 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 main
* @{
*/
/** @file
*/
 
#ifndef KERN_KINIT_H_
#define KERN_KINIT_H_
 
extern void kinit(void *arg);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/main/version.h
0,0 → 1,44
/*
* Copyright (c) 2006 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 main
* @{
*/
/** @file
*/
 
#ifndef KERN_VERSION_H_
#define KERN_VERSION_H_
 
extern void version_print(void);
 
#endif
 
/** @}
*/
 
/tags/0.4.1/kernel/generic/include/main/uinit.h
0,0 → 1,45
/*
* Copyright (c) 2005 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 main
* @{
*/
/** @file
*/
 
#ifndef KERN_UINIT_H_
#define KERN_UINIT_H_
 
#include <arch/types.h>
 
extern void uinit(void *arg);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/align.h
0,0 → 1,59
/*
* Copyright (c) 2005 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 generic
* @ingroup others
* @{
*/
/**
* @file
* @brief Macros for making values and addresses aligned.
*/
 
#ifndef KERN_ALIGN_H_
#define KERN_ALIGN_H_
 
/** Align to the nearest lower address.
*
* @param s Address or size to be aligned.
* @param a Size of alignment, must be power of 2.
*/
#define ALIGN_DOWN(s, a) ((s) & ~((a) - 1))
 
 
/** Align to the nearest higher address.
*
* @param s Address or size to be aligned.
* @param a Size of alignment, must be power of 2.
*/
#define ALIGN_UP(s, a) (((s) + ((a) - 1)) & ~((a) - 1))
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/fpu_context.h
0,0 → 1,49
/*
* Copyright (c) 2005 Jakub Vana
* 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_FPU_CONTEXT_H_
#define KERN_FPU_CONTEXT_H_
 
#include <arch/fpu_context.h>
 
extern void fpu_context_save(fpu_context_t *);
extern void fpu_context_restore(fpu_context_t *);
extern void fpu_init(void);
extern void fpu_enable(void);
extern void fpu_disable(void);
 
#endif /* KERN_FPU_CONTEXT_H_ */
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/interrupt.h
0,0 → 1,65
/*
* Copyright (c) 2005 Ondrej Palkovsky
* 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 genericinterrupt
* @{
*/
/** @file
*/
 
#ifndef KERN_INTERRUPT_H_
#define KERN_INTERRUPT_H_
 
#include <arch/interrupt.h>
#include <arch/types.h>
#include <proc/task.h>
#include <proc/thread.h>
#include <arch.h>
#include <ddi/irq.h>
 
typedef void (* iroutine)(int n, istate_t *istate);
 
#define fault_if_from_uspace(istate, fmt, ...) \
{ \
if (istate_from_uspace(istate)) { \
task_t *task = TASK; \
printf("Task %s (%" PRIu64 ") killed due to an exception at %p: ", task->name, task->taskid, istate_get_pc(istate)); \
printf(fmt "\n", ##__VA_ARGS__); \
task_kill(task->taskid); \
thread_exit(); \
} \
}
 
extern iroutine exc_register(int n, const char *name, iroutine f);
extern void exc_dispatch(int n, istate_t *t);
void exc_init(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/debug.h
0,0 → 1,105
/*
* Copyright (c) 2005 Martin Decky
* 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 genericdebug
* @{
*/
/** @file
*/
 
#ifndef KERN_DEBUG_H_
#define KERN_DEBUG_H_
 
#include <panic.h>
#include <arch/debug.h>
 
#define CALLER ((uintptr_t) __builtin_return_address(0))
 
#ifndef HERE
/** Current Instruction Pointer address */
# define HERE ((uintptr_t *) 0)
#endif
 
/** Debugging ASSERT macro
*
* If CONFIG_DEBUG is set, the ASSERT() macro
* evaluates expr and if it is false raises
* kernel panic.
*
* @param expr Expression which is expected to be true.
*
*/
#ifdef CONFIG_DEBUG
# define ASSERT(expr) \
if (!(expr)) { \
panic("Assertion failed (%s), caller=%p.", #expr, CALLER); \
}
#else
# define ASSERT(expr)
#endif
 
/** Extensive logging output macro
*
* If CONFIG_LOG is set, the LOG() macro
* will print whatever message is indicated plus
* an information about the location.
*
*/
 
#ifdef CONFIG_LOG
# define LOG(format, ...) \
printf("%s() at %s:%u: " format "\n", __func__, __FILE__, \
__LINE__, ##__VA_ARGS__);
#else
# define LOG(format, ...)
#endif
 
/** Extensive logging execute macro
*
* If CONFIG_LOG is set, the LOG_EXEC() macro
* will print an information about calling a given
* function and call it.
*
*/
 
#ifdef CONFIG_LOG
# define LOG_EXEC(fnc) \
{ \
printf("%s() at %s:%u: " #fnc "\n", __func__, __FILE__, \
__LINE__); \
fnc; \
}
#else
# define LOG_EXEC(fnc) fnc
#endif
 
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/panic.h
0,0 → 1,52
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_PANIC_H_
#define KERN_PANIC_H_
 
#ifdef CONFIG_DEBUG
# define panic(format, ...) \
panic_printf("Kernel panic in %s() at %s:%u: " format "\n", \
__func__, __FILE__, __LINE__, ##__VA_ARGS__);
#else
# define panic(format, ...) \
panic_printf("Kernel panic: " format "\n", ##__VA_ARGS__);
#endif
 
extern void panic_printf(char *fmt, ...) __attribute__((noreturn));
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/udebug/udebug.h
0,0 → 1,210
/*
* 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
} 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 {
/** 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];
istate_t *uspace_state;
 
/** What type of event are we stopped in or 0 if none. */
udebug_event_t cur_event;
bool go; /**< thread is GO */
bool stoppable; /**< thread is stoppable */
bool active; /**< thread is 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_attach(struct thread *t, struct task *ta);
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
 
/** @}
*/
/tags/0.4.1/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
 
/** @}
*/
/tags/0.4.1/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
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/stdarg.h
0,0 → 1,53
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
/*
* Variable argument list manipulation macros
* for all architectures with compiler support for __builtin_va_*.
*/
#ifndef KERN_STDARG_H_
#define KERN_STDARG_H_
 
typedef __builtin_va_list va_list;
 
#define va_start(ap, last) __builtin_va_start(ap, last)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
#define va_end(ap) __builtin_va_end(ap)
#define va_copy(dst, src) __builtin_va_copy(dst, src)
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/lib/elf.h
0,0 → 1,349
/*
* Copyright (c) 2006 Sergey Bondari
* 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_ELF_H_
#define KERN_ELF_H_
 
#include <arch/elf.h>
#include <arch/types.h>
 
/**
* current ELF version
*/
#define EV_CURRENT 1
 
/**
* ELF types
*/
#define ET_NONE 0 /* No type */
#define ET_REL 1 /* Relocatable file */
#define ET_EXEC 2 /* Executable */
#define ET_DYN 3 /* Shared object */
#define ET_CORE 4 /* Core */
#define ET_LOPROC 0xff00 /* Processor specific */
#define ET_HIPROC 0xffff /* Processor specific */
 
/**
* ELF machine types
*/
#define EM_NO 0 /* No machine */
#define EM_SPARC 2 /* SPARC */
#define EM_386 3 /* i386 */
#define EM_MIPS 8 /* MIPS RS3000 */
#define EM_MIPS_RS3_LE 10 /* MIPS RS3000 LE */
#define EM_PPC 20 /* PPC32 */
#define EM_PPC64 21 /* PPC64 */
#define EM_ARM 40 /* ARM */
#define EM_SPARCV9 43 /* SPARC64 */
#define EM_IA_64 50 /* IA-64 */
#define EM_X86_64 62 /* AMD64/EMT64 */
 
/**
* ELF identification indexes
*/
#define EI_MAG0 0
#define EI_MAG1 1
#define EI_MAG2 2
#define EI_MAG3 3
#define EI_CLASS 4 /* File class */
#define EI_DATA 5 /* Data encoding */
#define EI_VERSION 6 /* File version */
#define EI_OSABI 7
#define EI_ABIVERSION 8
#define EI_PAD 9 /* Start of padding bytes */
#define EI_NIDENT 16 /* ELF identification table size */
 
/**
* ELF magic number
*/
#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
 
/**
* ELF file classes
*/
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
 
/**
* ELF data encoding types
*/
#define ELFDATANONE 0
#define ELFDATA2LSB 1 /* Least significant byte first (little endian) */
#define ELFDATA2MSB 2 /* Most signigicant byte first (big endian) */
 
/**
* ELF error return codes
*/
#define EE_OK 0 /* No error */
#define EE_INVALID 1 /* Invalid ELF image */
#define EE_MEMORY 2 /* Cannot allocate address space */
#define EE_INCOMPATIBLE 3 /* ELF image is not compatible with current architecture */
#define EE_UNSUPPORTED 4 /* Non-supported ELF (e.g. dynamic ELFs) */
#define EE_LOADER 5 /* The image is actually a program loader */
#define EE_IRRECOVERABLE 6
 
/**
* ELF section types
*/
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_LOOS 0x60000000
#define SHT_HIOS 0x6fffffff
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xffffffff
 
/**
* ELF section flags
*/
#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
#define SHF_TLS 0x400
#define SHF_MASKPROC 0xf0000000
 
/**
* Symbol binding
*/
#define STB_LOCAL 0
#define STB_GLOBAL 1
#define STB_WEAK 2
#define STB_LOPROC 13
#define STB_HIPROC 15
 
/**
* Symbol types
*/
#define STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define STT_LOPROC 13
#define STT_HIPROC 15
 
/**
* Program segment types
*/
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
 
/**
* Program segment attributes.
*/
#define PF_X 1
#define PF_W 2
#define PF_R 4
 
/**
* ELF data types
*
* These types are found to be identical in both 32-bit and 64-bit
* ELF object file specifications. They are the only types used
* in ELF header.
*/
typedef uint64_t elf_xword;
typedef int64_t elf_sxword;
typedef uint32_t elf_word;
typedef int32_t elf_sword;
typedef uint16_t elf_half;
 
/**
* 32-bit ELF data types.
*
* These types are specific for 32-bit format.
*/
typedef uint32_t elf32_addr;
typedef uint32_t elf32_off;
 
/**
* 64-bit ELF data types.
*
* These types are specific for 64-bit format.
*/
typedef uint64_t elf64_addr;
typedef uint64_t elf64_off;
 
/** ELF header */
struct elf32_header {
uint8_t e_ident[EI_NIDENT];
elf_half e_type;
elf_half e_machine;
elf_word e_version;
elf32_addr e_entry;
elf32_off e_phoff;
elf32_off e_shoff;
elf_word e_flags;
elf_half e_ehsize;
elf_half e_phentsize;
elf_half e_phnum;
elf_half e_shentsize;
elf_half e_shnum;
elf_half e_shstrndx;
};
struct elf64_header {
uint8_t e_ident[EI_NIDENT];
elf_half e_type;
elf_half e_machine;
elf_word e_version;
elf64_addr e_entry;
elf64_off e_phoff;
elf64_off e_shoff;
elf_word e_flags;
elf_half e_ehsize;
elf_half e_phentsize;
elf_half e_phnum;
elf_half e_shentsize;
elf_half e_shnum;
elf_half e_shstrndx;
};
 
/*
* ELF segment header.
* Segments headers are also known as program headers.
*/
struct elf32_segment_header {
elf_word p_type;
elf32_off p_offset;
elf32_addr p_vaddr;
elf32_addr p_paddr;
elf_word p_filesz;
elf_word p_memsz;
elf_word p_flags;
elf_word p_align;
};
struct elf64_segment_header {
elf_word p_type;
elf_word p_flags;
elf64_off p_offset;
elf64_addr p_vaddr;
elf64_addr p_paddr;
elf_xword p_filesz;
elf_xword p_memsz;
elf_xword p_align;
};
 
/*
* ELF section header
*/
struct elf32_section_header {
elf_word sh_name;
elf_word sh_type;
elf_word sh_flags;
elf32_addr sh_addr;
elf32_off sh_offset;
elf_word sh_size;
elf_word sh_link;
elf_word sh_info;
elf_word sh_addralign;
elf_word sh_entsize;
};
struct elf64_section_header {
elf_word sh_name;
elf_word sh_type;
elf_xword sh_flags;
elf64_addr sh_addr;
elf64_off sh_offset;
elf_xword sh_size;
elf_word sh_link;
elf_word sh_info;
elf_xword sh_addralign;
elf_xword sh_entsize;
};
 
/*
* ELF symbol table entry
*/
struct elf32_symbol {
elf_word st_name;
elf32_addr st_value;
elf_word st_size;
uint8_t st_info;
uint8_t st_other;
elf_half st_shndx;
};
struct elf64_symbol {
elf_word st_name;
uint8_t st_info;
uint8_t st_other;
elf_half st_shndx;
elf64_addr st_value;
elf_xword st_size;
};
 
#ifdef __32_BITS__
typedef struct elf32_header elf_header_t;
typedef struct elf32_segment_header elf_segment_header_t;
typedef struct elf32_section_header elf_section_header_t;
typedef struct elf32_symbol elf_symbol_t;
#endif
#ifdef __64_BITS__
typedef struct elf64_header elf_header_t;
typedef struct elf64_segment_header elf_segment_header_t;
typedef struct elf64_section_header elf_section_header_t;
typedef struct elf64_symbol elf_symbol_t;
#endif
 
extern char *elf_error(unsigned int rc);
 
/* Interpreter string used to recognize the program loader */
#define ELF_INTERP_ZSTR "kernel"
#define ELF_INTERP_ZLEN sizeof(ELF_INTERP_ZSTR)
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/lib/rd.h
0,0 → 1,84
/*
* Copyright (c) 2006 Martin Decky
* 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 genericmm
* @{
*/
/** @file
*/
 
#ifndef KERN_RD_H_
#define KERN_RD_H_
 
#include <arch/types.h>
 
/**
* RAM disk version
*/
#define RD_VERSION 1
 
/**
* RAM disk magic number
*/
#define RD_MAGIC_SIZE 4
#define RD_MAG0 'H'
#define RD_MAG1 'O'
#define RD_MAG2 'R'
#define RD_MAG3 'D'
 
/**
* RAM disk data encoding types
*/
#define RD_DATA_NONE 0
#define RD_DATA_LSB 1 /* Least significant byte first (little endian) */
#define RD_DATA_MSB 2 /* Most signigicant byte first (big endian) */
 
/**
* RAM disk error return codes
*/
#define RE_OK 0 /* No error */
#define RE_INVALID 1 /* Invalid RAM disk image */
#define RE_UNSUPPORTED 2 /* Non-supported image (e.g. wrong version) */
 
/** RAM disk header */
struct rd_header {
uint8_t magic[RD_MAGIC_SIZE];
uint8_t version;
uint8_t data_type;
uint32_t header_size;
uint64_t data_size;
} __attribute__ ((packed));
 
typedef struct rd_header rd_header_t;
 
extern int init_rd(rd_header_t *addr, size_t size);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/stackarg.h
0,0 → 1,64
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
/*
* Variable argument list manipulation macros
* for architectures using stack to pass arguments.
*/
#ifndef KERN_STACKARG_H_
#define KERN_STACKARG_H_
 
#include <arch/types.h>
 
typedef struct va_list {
int pos;
uint8_t *last;
} va_list;
 
#define va_start(ap, lst) \
(ap).pos = sizeof(lst); \
(ap).last = (uint8_t *) &(lst)
 
#define va_arg(ap, type) \
(*((type *)((ap).last + ((ap).pos += sizeof(type)) - sizeof(type))))
 
#define va_copy(dst, src) dst = src
#define va_end(ap)
 
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/time/clock.h
0,0 → 1,57
/*
* Copyright (c) 2001-2004 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 time
* @{
*/
/** @file
*/
 
#ifndef KERN_CLOCK_H_
#define KERN_CLOCK_H_
 
#include <arch/types.h>
 
#define HZ 100
 
/** Uptime structure */
typedef struct {
unative_t seconds1;
unative_t useconds;
unative_t seconds2;
} uptime_t;
 
extern uptime_t *uptime;
 
extern void clock(void);
extern void clock_counter_init(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/time/timeout.h
0,0 → 1,71
/*
* Copyright (c) 2001-2004 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 time
* @{
*/
/** @file
*/
 
#ifndef KERN_TIMEOUT_H_
#define KERN_TIMEOUT_H_
 
#include <arch/types.h>
#include <adt/list.h>
#include <cpu.h>
 
typedef void (* timeout_handler_t)(void *arg);
 
typedef struct {
SPINLOCK_DECLARE(lock);
 
/** Link to the list of active timeouts on THE->cpu */
link_t link;
/** Timeout will be activated in this amount of clock() ticks. */
uint64_t ticks;
/** Function that will be called on timeout activation. */
timeout_handler_t handler;
/** Argument to be passed to handler() function. */
void *arg;
/** On which processor is this timeout registered. */
cpu_t *cpu;
} timeout_t;
 
#define us2ticks(us) ((uint64_t) (((uint32_t) (us) / (1000000 / HZ))))
 
extern void timeout_init(void);
extern void timeout_initialize(timeout_t *t);
extern void timeout_reinitialize(timeout_t *t);
extern void timeout_register(timeout_t *t, uint64_t usec, timeout_handler_t f,
void *arg);
extern bool timeout_unregister(timeout_t *t);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/time/delay.h
0,0 → 1,45
/*
* Copyright (c) 2001-2004 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 time
* @{
*/
/** @file
*/
 
#ifndef KERN_DELAY_H_
#define KERN_DELAY_H_
 
#include <arch/types.h>
 
extern void delay(uint32_t microseconds);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/bitops.h
0,0 → 1,92
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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_BITOPS_H_
#define KERN_BITOPS_H_
 
 
/** Return position of first non-zero bit from left (i.e. [log_2(arg)]).
*
* If number is zero, it returns 0
*/
static inline int fnzb32(uint32_t arg)
{
int n = 0;
 
if (arg >> 16) {
arg >>= 16;
n += 16;
}
if (arg >> 8) {
arg >>= 8;
n += 8;
}
if (arg >> 4) {
arg >>= 4;
n += 4;
}
if (arg >> 2) {
arg >>= 2;
n += 2;
}
if (arg >> 1) {
arg >>= 1;
n += 1;
}
return n;
}
 
static inline int fnzb64(uint64_t arg)
{
int n = 0;
 
if (arg >> 32) {
arg >>= 32;
n += 32;
}
return n + fnzb32((uint32_t) arg);
}
 
#define fnzb(x) fnzb32(x)
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/security/cap.h
0,0 → 1,89
/*
* Copyright (c) 2006 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 generic
* @{
*/
/** @file
*/
 
/**
* @file
* @brief Capabilities definitions.
*
* Capabilities represent virtual rights that entitle their
* holder to perform certain security sensitive tasks.
*
* Each task can have arbitrary combination of the capabilities
* defined in this file. Therefore, they are required to be powers
* of two.
*/
 
#ifndef __CAP_H__
#define __CAP_H__
 
#include <syscall/sysarg64.h>
#include <arch/types.h>
 
/**
* CAP_CAP allows its holder to grant/revoke arbitrary
* privilege to/from other tasks.
*/
#define CAP_CAP (1<<0)
 
/**
* CAP_MEM_MANAGER allows its holder to map physical memory
* to other tasks.
*/
#define CAP_MEM_MANAGER (1<<1)
 
/**
* CAP_IO_MANAGER allows its holder to access I/O space
* to other tasks.
*/
#define CAP_IO_MANAGER (1<<2)
 
/**
* CAP_PREEMPT_CONTROL allows its holder to disable/enable preemption.
*/
#define CAP_PREEMPT_CONTROL (1<<3)
 
/**
* CAP_IRQ_REG entitles its holder to register IRQ handlers.
*/
#define CAP_IRQ_REG (1<<4)
 
typedef uint32_t cap_t;
 
extern unative_t sys_cap_grant(sysarg64_t *uspace_taskid_arg, cap_t caps);
extern unative_t sys_cap_revoke(sysarg64_t *uspace_taskid_arg, cap_t caps);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/userspace.h
0,0 → 1,47
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_USERSPACE_H_
#define KERN_USERSPACE_H_
 
#include <proc/thread.h>
#include <arch/types.h>
 
/** Switch to user-space (CPU user priviledge level) */
extern void userspace(uspace_arg_t *uarg) __attribute__ ((noreturn));
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/smp/smp.h
0,0 → 1,52
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_SMP_H_
#define KERN_SMP_H_
 
#include <synch/waitq.h>
 
extern waitq_t ap_completion_wq;
 
#ifdef CONFIG_SMP
extern void smp_init(void);
extern void kmp(void *arg);
#else
#define smp_init()
#endif /* CONFIG_SMP */
 
#endif /* __SMP_H__ */
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/smp/ipi.h
0,0 → 1,48
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_IPI_H_
#define KERN_IPI_H_
 
#ifdef CONFIG_SMP
extern void ipi_broadcast(int ipi);
extern void ipi_broadcast_arch(int ipi);
#else
#define ipi_broadcast(x) ;
#endif /* CONFIG_SMP */
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/atomic.h
0,0 → 1,57
/*
* Copyright (c) 2006 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_ATOMIC_H_
#define KERN_ATOMIC_H_
 
typedef struct atomic {
volatile long count;
} atomic_t;
 
#include <arch/atomic.h>
 
static inline void atomic_set(atomic_t *val, long i)
{
val->count = i;
}
 
static inline long atomic_get(atomic_t *val)
{
return val->count;
}
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/include/preemption.h
0,0 → 1,44
/*
* Copyright (c) 2005 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 generic
* @{
*/
/** @file
*/
 
#ifndef KERN_PREEMPTION_H_
#define KERN_PREEMPTION_H_
 
extern void preemption_disable(void);
extern void preemption_enable(void);
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/lib/string.c
0,0 → 1,712
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
 
/**
* @file
* @brief String functions.
*
* Strings and characters use the Universal Character Set (UCS). The standard
* strings, called just strings are encoded in UTF-8. Wide strings (encoded
* in UTF-32) are supported to a limited degree. A single character is
* represented as wchar_t.@n
*
* Overview of the terminology:@n
*
* Term Meaning
* -------------------- ----------------------------------------------------
* byte 8 bits stored in uint8_t (unsigned 8 bit integer)
*
* character UTF-32 encoded Unicode character, stored in wchar_t
* (signed 32 bit integer), code points 0 .. 1114111
* are valid
*
* ASCII character 7 bit encoded ASCII character, stored in char
* (usually signed 8 bit integer), code points 0 .. 127
* are valid
*
* string UTF-8 encoded NULL-terminated Unicode string, char *
*
* wide string UTF-32 encoded NULL-terminated Unicode string,
* wchar_t *
*
* [wide] string size number of BYTES in a [wide] string (excluding
* the NULL-terminator), size_t
*
* [wide] string length number of CHARACTERS in a [wide] string (excluding
* the NULL-terminator), size_t
*
* [wide] string width number of display cells on a monospace display taken
* by a [wide] string, size_t
*
*
* Overview of string metrics:@n
*
* Metric Abbrev. Type Meaning
* ------ ------ ------ -------------------------------------------------
* size n size_t number of BYTES in a string (excluding the
* NULL-terminator)
*
* length l size_t number of CHARACTERS in a string (excluding the
* null terminator)
*
* width w size_t number of display cells on a monospace display
* taken by a string
*
*
* Function naming prefixes:@n
*
* chr_ operate on characters
* ascii_ operate on ASCII characters
* str_ operate on strings
* wstr_ operate on wide strings
*
* [w]str_[n|l|w] operate on a prefix limited by size, length
* or width
*
*
* A specific character inside a [wide] string can be referred to by:@n
*
* pointer (char *, wchar_t *)
* byte offset (size_t)
* character index (size_t)
*
*/
 
#include <string.h>
#include <print.h>
#include <cpu.h>
#include <arch/asm.h>
#include <arch.h>
#include <errno.h>
#include <align.h>
#include <debug.h>
 
/** Byte mask consisting of lowest @n bits (out of 8) */
#define LO_MASK_8(n) ((uint8_t) ((1 << (n)) - 1))
 
/** Byte mask consisting of lowest @n bits (out of 32) */
#define LO_MASK_32(n) ((uint32_t) ((1 << (n)) - 1))
 
/** Byte mask consisting of highest @n bits (out of 8) */
#define HI_MASK_8(n) (~LO_MASK_8(8 - (n)))
 
/** Number of data bits in a UTF-8 continuation byte */
#define CONT_BITS 6
 
/** Decode a single character from a string.
*
* Decode a single character from a string of size @a size. Decoding starts
* at @a offset and this offset is moved to the beginning of the next
* character. In case of decoding error, offset generally advances at least
* by one. However, offset is never moved beyond size.
*
* @param str String (not necessarily NULL-terminated).
* @param offset Byte offset in string where to start decoding.
* @param size Size of the string (in bytes).
*
* @return Value of decoded character, U_SPECIAL on decoding error or
* NULL if attempt to decode beyond @a size.
*
*/
wchar_t str_decode(const char *str, size_t *offset, size_t size)
{
if (*offset + 1 > size)
return 0;
/* First byte read from string */
uint8_t b0 = (uint8_t) str[(*offset)++];
/* Determine code length */
unsigned int b0_bits; /* Data bits in first byte */
unsigned int cbytes; /* Number of continuation bytes */
if ((b0 & 0x80) == 0) {
/* 0xxxxxxx (Plain ASCII) */
b0_bits = 7;
cbytes = 0;
} else if ((b0 & 0xe0) == 0xc0) {
/* 110xxxxx 10xxxxxx */
b0_bits = 5;
cbytes = 1;
} else if ((b0 & 0xf0) == 0xe0) {
/* 1110xxxx 10xxxxxx 10xxxxxx */
b0_bits = 4;
cbytes = 2;
} else if ((b0 & 0xf8) == 0xf0) {
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
b0_bits = 3;
cbytes = 3;
} else {
/* 10xxxxxx -- unexpected continuation byte */
return U_SPECIAL;
}
if (*offset + cbytes > size)
return U_SPECIAL;
wchar_t ch = b0 & LO_MASK_8(b0_bits);
/* Decode continuation bytes */
while (cbytes > 0) {
uint8_t b = (uint8_t) str[(*offset)++];
/* Must be 10xxxxxx */
if ((b & 0xc0) != 0x80)
return U_SPECIAL;
/* Shift data bits to ch */
ch = (ch << CONT_BITS) | (wchar_t) (b & LO_MASK_8(CONT_BITS));
cbytes--;
}
return ch;
}
 
/** Encode a single character to string representation.
*
* Encode a single character to string representation (i.e. UTF-8) and store
* it into a buffer at @a offset. Encoding starts at @a offset and this offset
* is moved to the position where the next character can be written to.
*
* @param ch Input character.
* @param str Output buffer.
* @param offset Byte offset where to start writing.
* @param size Size of the output buffer (in bytes).
*
* @return EOK if the character was encoded successfully, EOVERFLOW if there
* was not enough space in the output buffer or EINVAL if the character
* code was invalid.
*/
int chr_encode(wchar_t ch, char *str, size_t *offset, size_t size)
{
if (*offset >= size)
return EOVERFLOW;
if (!chr_check(ch))
return EINVAL;
/* Unsigned version of ch (bit operations should only be done
on unsigned types). */
uint32_t cc = (uint32_t) ch;
/* Determine how many continuation bytes are needed */
unsigned int b0_bits; /* Data bits in first byte */
unsigned int cbytes; /* Number of continuation bytes */
if ((cc & ~LO_MASK_32(7)) == 0) {
b0_bits = 7;
cbytes = 0;
} else if ((cc & ~LO_MASK_32(11)) == 0) {
b0_bits = 5;
cbytes = 1;
} else if ((cc & ~LO_MASK_32(16)) == 0) {
b0_bits = 4;
cbytes = 2;
} else if ((cc & ~LO_MASK_32(21)) == 0) {
b0_bits = 3;
cbytes = 3;
} else {
/* Codes longer than 21 bits are not supported */
return EINVAL;
}
/* Check for available space in buffer */
if (*offset + cbytes >= size)
return EOVERFLOW;
/* Encode continuation bytes */
unsigned int i;
for (i = cbytes; i > 0; i--) {
str[*offset + i] = 0x80 | (cc & LO_MASK_32(CONT_BITS));
cc = cc >> CONT_BITS;
}
/* Encode first byte */
str[*offset] = (cc & LO_MASK_32(b0_bits)) | HI_MASK_8(8 - b0_bits - 1);
/* Advance offset */
*offset += cbytes + 1;
return EOK;
}
 
/** Get size of string.
*
* Get the number of bytes which are used by the string @a str (excluding the
* NULL-terminator).
*
* @param str String to consider.
*
* @return Number of bytes used by the string
*
*/
size_t str_size(const char *str)
{
size_t size = 0;
while (*str++ != 0)
size++;
return size;
}
 
/** Get size of wide string.
*
* Get the number of bytes which are used by the wide string @a str (excluding the
* NULL-terminator).
*
* @param str Wide string to consider.
*
* @return Number of bytes used by the wide string
*
*/
size_t wstr_size(const wchar_t *str)
{
return (wstr_length(str) * sizeof(wchar_t));
}
 
/** Get size of string with length limit.
*
* Get the number of bytes which are used by up to @a max_len first
* characters in the string @a str. If @a max_len is greater than
* the length of @a str, the entire string is measured (excluding the
* NULL-terminator).
*
* @param str String to consider.
* @param max_len Maximum number of characters to measure.
*
* @return Number of bytes used by the characters.
*
*/
size_t str_lsize(const char *str, size_t max_len)
{
size_t len = 0;
size_t offset = 0;
while (len < max_len) {
if (str_decode(str, &offset, STR_NO_LIMIT) == 0)
break;
len++;
}
return offset;
}
 
/** Get size of wide string with length limit.
*
* Get the number of bytes which are used by up to @a max_len first
* wide characters in the wide string @a str. If @a max_len is greater than
* the length of @a str, the entire wide string is measured (excluding the
* NULL-terminator).
*
* @param str Wide string to consider.
* @param max_len Maximum number of wide characters to measure.
*
* @return Number of bytes used by the wide characters.
*
*/
size_t wstr_lsize(const wchar_t *str, size_t max_len)
{
return (wstr_nlength(str, max_len * sizeof(wchar_t)) * sizeof(wchar_t));
}
 
/** Get number of characters in a string.
*
* @param str NULL-terminated string.
*
* @return Number of characters in string.
*
*/
size_t str_length(const char *str)
{
size_t len = 0;
size_t offset = 0;
while (str_decode(str, &offset, STR_NO_LIMIT) != 0)
len++;
return len;
}
 
/** Get number of characters in a wide string.
*
* @param str NULL-terminated wide string.
*
* @return Number of characters in @a str.
*
*/
size_t wstr_length(const wchar_t *wstr)
{
size_t len = 0;
while (*wstr++ != 0)
len++;
return len;
}
 
/** Get number of characters in a string with size limit.
*
* @param str NULL-terminated string.
* @param size Maximum number of bytes to consider.
*
* @return Number of characters in string.
*
*/
size_t str_nlength(const char *str, size_t size)
{
size_t len = 0;
size_t offset = 0;
while (str_decode(str, &offset, size) != 0)
len++;
return len;
}
 
/** Get number of characters in a string with size limit.
*
* @param str NULL-terminated string.
* @param size Maximum number of bytes to consider.
*
* @return Number of characters in string.
*
*/
size_t wstr_nlength(const wchar_t *str, size_t size)
{
size_t len = 0;
size_t limit = ALIGN_DOWN(size, sizeof(wchar_t));
size_t offset = 0;
while ((offset < limit) && (*str++ != 0)) {
len++;
offset += sizeof(wchar_t);
}
return len;
}
 
/** Check whether character is plain ASCII.
*
* @return True if character is plain ASCII.
*
*/
bool ascii_check(wchar_t ch)
{
if ((ch >= 0) && (ch <= 127))
return true;
return false;
}
 
/** Check whether character is valid
*
* @return True if character is a valid Unicode code point.
*
*/
bool chr_check(wchar_t ch)
{
if ((ch >= 0) && (ch <= 1114111))
return true;
return false;
}
 
/** Compare two NULL terminated strings.
*
* Do a char-by-char comparison of two NULL-terminated strings.
* The strings are considered equal iff they consist of the same
* characters on the minimum of their lengths.
*
* @param s1 First string to compare.
* @param s2 Second string to compare.
*
* @return 0 if the strings are equal, -1 if first is smaller,
* 1 if second smaller.
*
*/
int str_cmp(const char *s1, const char *s2)
{
wchar_t c1 = 0;
wchar_t c2 = 0;
size_t off1 = 0;
size_t off2 = 0;
 
while (true) {
c1 = str_decode(s1, &off1, STR_NO_LIMIT);
c2 = str_decode(s2, &off2, STR_NO_LIMIT);
 
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
 
if (c1 == 0 || c2 == 0)
break;
}
 
return 0;
}
 
/** Compare two NULL terminated strings with length limit.
*
* Do a char-by-char comparison of two NULL-terminated strings.
* The strings are considered equal iff they consist of the same
* characters on the minimum of their lengths and the length limit.
*
* @param s1 First string to compare.
* @param s2 Second string to compare.
* @param max_len Maximum number of characters to consider.
*
* @return 0 if the strings are equal, -1 if first is smaller,
* 1 if second smaller.
*
*/
int str_lcmp(const char *s1, const char *s2, size_t max_len)
{
wchar_t c1 = 0;
wchar_t c2 = 0;
size_t off1 = 0;
size_t off2 = 0;
size_t len = 0;
 
while (true) {
if (len >= max_len)
break;
 
c1 = str_decode(s1, &off1, STR_NO_LIMIT);
c2 = str_decode(s2, &off2, STR_NO_LIMIT);
 
if (c1 < c2)
return -1;
 
if (c1 > c2)
return 1;
 
if (c1 == 0 || c2 == 0)
break;
 
++len;
}
 
return 0;
 
}
 
/** Copy string.
*
* Copy source string @a src to destination buffer @a dest.
* No more than @a size bytes are written. If the size of the output buffer
* is at least one byte, the output string will always be well-formed, i.e.
* null-terminated and containing only complete characters.
*
* @param dst Destination buffer.
* @param count Size of the destination buffer (must be > 0).
* @param src Source string.
*/
void str_cpy(char *dest, size_t size, const char *src)
{
wchar_t ch;
size_t src_off;
size_t dest_off;
 
/* There must be space for a null terminator in the buffer. */
ASSERT(size > 0);
src_off = 0;
dest_off = 0;
 
while ((ch = str_decode(src, &src_off, STR_NO_LIMIT)) != 0) {
if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
break;
}
 
dest[dest_off] = '\0';
}
 
/** Copy size-limited substring.
*
* Copy prefix of string @a src of max. size @a size to destination buffer
* @a dest. No more than @a size bytes are written. The output string will
* always be well-formed, i.e. null-terminated and containing only complete
* characters.
*
* No more than @a n bytes are read from the input string, so it does not
* have to be null-terminated.
*
* @param dst Destination buffer.
* @param count Size of the destination buffer (must be > 0).
* @param src Source string.
* @param n Maximum number of bytes to read from @a src.
*/
void str_ncpy(char *dest, size_t size, const char *src, size_t n)
{
wchar_t ch;
size_t src_off;
size_t dest_off;
 
/* There must be space for a null terminator in the buffer. */
ASSERT(size > 0);
src_off = 0;
dest_off = 0;
 
while ((ch = str_decode(src, &src_off, n)) != 0) {
if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
break;
}
 
dest[dest_off] = '\0';
}
 
/** Copy NULL-terminated wide string to string
*
* Copy source wide string @a src to destination buffer @a dst.
* No more than @a size bytes are written. NULL-terminator is always
* written after the last succesfully copied character (i.e. if the
* destination buffer is has at least 1 byte, it will be always
* NULL-terminated).
*
* @param src Source wide string.
* @param dst Destination buffer.
* @param count Size of the destination buffer.
*
*/
void wstr_nstr(char *dst, const wchar_t *src, size_t size)
{
/* No space for the NULL-terminator in the buffer */
if (size == 0)
return;
wchar_t ch;
size_t src_idx = 0;
size_t dst_off = 0;
while ((ch = src[src_idx++]) != 0) {
if (chr_encode(ch, dst, &dst_off, size) != EOK)
break;
}
if (dst_off >= size)
dst[size - 1] = 0;
else
dst[dst_off] = 0;
}
 
/** Find first occurence of character in string.
*
* @param str String to search.
* @param ch Character to look for.
*
* @return Pointer to character in @a str or NULL if not found.
*
*/
char *str_chr(const char *str, wchar_t ch)
{
wchar_t acc;
size_t off = 0;
size_t last = 0;
while ((acc = str_decode(str, &off, STR_NO_LIMIT)) != 0) {
if (acc == ch)
return (char *) (str + last);
last = off;
}
return NULL;
}
 
/** Insert a wide character into a wide string.
*
* Insert a wide character into a wide string at position
* @a pos. The characters after the position are shifted.
*
* @param str String to insert to.
* @param ch Character to insert to.
* @param pos Character index where to insert.
@ @param max_pos Characters in the buffer.
*
* @return True if the insertion was sucessful, false if the position
* is out of bounds.
*
*/
bool wstr_linsert(wchar_t *str, wchar_t ch, size_t pos, size_t max_pos)
{
size_t len = wstr_length(str);
if ((pos > len) || (pos + 1 > max_pos))
return false;
size_t i;
for (i = len; i + 1 > pos; i--)
str[i + 1] = str[i];
str[pos] = ch;
return true;
}
 
/** Remove a wide character from a wide string.
*
* Remove a wide character from a wide string at position
* @a pos. The characters after the position are shifted.
*
* @param str String to remove from.
* @param pos Character index to remove.
*
* @return True if the removal was sucessful, false if the position
* is out of bounds.
*
*/
bool wstr_remove(wchar_t *str, size_t pos)
{
size_t len = wstr_length(str);
if (pos >= len)
return false;
size_t i;
for (i = pos + 1; i <= len; i++)
str[i - 1] = str[i];
return true;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/lib/sort.c
0,0 → 1,206
/*
* Copyright (c) 2005 Sergey Bondari
* 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 Sorting functions.
*
* This files contains functions implementing several sorting
* algorithms (e.g. quick sort and bubble sort).
*/
#include <mm/slab.h>
#include <memstr.h>
#include <sort.h>
#include <panic.h>
 
#define EBUFSIZE 32
 
void _qsort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b), void *tmp, void *pivot);
void _bubblesort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b), void *slot);
 
/** Quicksort wrapper
*
* This is only a wrapper that takes care of memory allocations for storing
* the pivot and temporary elements for generic quicksort algorithm.
*
* This function _can_ sleep
*
* @param data Pointer to data to be sorted.
* @param n Number of elements to be sorted.
* @param e_size Size of one element.
* @param cmp Comparator function.
*
*/
void qsort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b))
{
uint8_t buf_tmp[EBUFSIZE];
uint8_t buf_pivot[EBUFSIZE];
void * tmp = buf_tmp;
void * pivot = buf_pivot;
 
if (e_size > EBUFSIZE) {
pivot = (void *) malloc(e_size, 0);
tmp = (void *) malloc(e_size, 0);
}
 
_qsort(data, n, e_size, cmp, tmp, pivot);
if (e_size > EBUFSIZE) {
free(tmp);
free(pivot);
}
}
 
/** Quicksort
*
* Apply generic quicksort algorithm on supplied data, using pre-allocated buffers.
*
* @param data Pointer to data to be sorted.
* @param n Number of elements to be sorted.
* @param e_size Size of one element.
* @param cmp Comparator function.
* @param tmp Pointer to scratch memory buffer e_size bytes long.
* @param pivot Pointer to scratch memory buffer e_size bytes long.
*
*/
void _qsort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b), void *tmp, void *pivot)
{
if (n > 4) {
unsigned int i = 0, j = n - 1;
 
memcpy(pivot, data, e_size);
 
while (1) {
while ((cmp(data + i * e_size, pivot) < 0) && (i < n))
i++;
while ((cmp(data + j * e_size, pivot) >= 0) && (j > 0))
j--;
if (i < j) {
memcpy(tmp, data + i * e_size, e_size);
memcpy(data + i * e_size, data + j * e_size, e_size);
memcpy(data + j * e_size, tmp, e_size);
} else {
break;
}
}
 
_qsort(data, j + 1, e_size, cmp, tmp, pivot);
_qsort(data + (j + 1) * e_size, n - j - 1, e_size, cmp, tmp, pivot);
} else {
_bubblesort(data, n, e_size, cmp, tmp);
}
}
 
/** Bubblesort wrapper
*
* This is only a wrapper that takes care of memory allocation for storing
* the slot element for generic bubblesort algorithm.
*
* @param data Pointer to data to be sorted.
* @param n Number of elements to be sorted.
* @param e_size Size of one element.
* @param cmp Comparator function.
*
*/
void bubblesort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b))
{
uint8_t buf_slot[EBUFSIZE];
void * slot = buf_slot;
if (e_size > EBUFSIZE) {
slot = (void *) malloc(e_size, 0);
}
 
_bubblesort(data, n, e_size, cmp, slot);
if (e_size > EBUFSIZE) {
free(slot);
}
}
 
/** Bubblesort
*
* Apply generic bubblesort algorithm on supplied data, using pre-allocated buffer.
*
* @param data Pointer to data to be sorted.
* @param n Number of elements to be sorted.
* @param e_size Size of one element.
* @param cmp Comparator function.
* @param slot Pointer to scratch memory buffer e_size bytes long.
*
*/
void _bubblesort(void * data, size_t n, size_t e_size, int (* cmp) (void * a, void * b), void *slot)
{
bool done = false;
void * p;
 
while (!done) {
done = true;
for (p = data; p < data + e_size * (n - 1); p = p + e_size) {
if (cmp(p, p + e_size) == 1) {
memcpy(slot, p, e_size);
memcpy(p, p + e_size, e_size);
memcpy(p + e_size, slot, e_size);
done = false;
}
}
}
 
}
 
/*
* Comparator returns 1 if a > b, 0 if a == b, -1 if a < b
*/
int int_cmp(void * a, void * b)
{
return (* (int *) a > * (int*)b) ? 1 : (*(int *)a < * (int *)b) ? -1 : 0;
}
 
int uint8_t_cmp(void * a, void * b)
{
return (* (uint8_t *) a > * (uint8_t *)b) ? 1 : (*(uint8_t *)a < * (uint8_t *)b) ? -1 : 0;
}
 
int uint16_t_cmp(void * a, void * b)
{
return (* (uint16_t *) a > * (uint16_t *)b) ? 1 : (*(uint16_t *)a < * (uint16_t *)b) ? -1 : 0;
}
 
int uint32_t_cmp(void * a, void * b)
{
return (* (uint32_t *) a > * (uint32_t *)b) ? 1 : (*(uint32_t *)a < * (uint32_t *)b) ? -1 : 0;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/lib/func.c
0,0 → 1,153
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
 
/**
* @file
* @brief Miscellaneous functions.
*/
 
#include <func.h>
#include <print.h>
#include <cpu.h>
#include <arch/asm.h>
#include <arch.h>
#include <console/kconsole.h>
 
atomic_t haltstate = {0}; /**< Halt flag */
 
 
/** Halt wrapper
*
* Set halt flag and halt the CPU.
*
*/
void halt()
{
#ifdef CONFIG_DEBUG
bool rundebugger = false;
if (!atomic_get(&haltstate)) {
atomic_set(&haltstate, 1);
rundebugger = true;
}
#else
atomic_set(&haltstate, 1);
#endif
interrupts_disable();
#if (defined(CONFIG_DEBUG)) && (defined(CONFIG_KCONSOLE))
if ((rundebugger) && (kconsole_check_poll()))
kconsole("panic", "\nLast resort kernel console ready.\n", false);
#endif
if (CPU)
printf("cpu%u: halted\n", CPU->id);
else
printf("cpu: halted\n");
cpu_halt();
}
 
/** Convert ascii representation to unative_t
*
* Supports 0x for hexa & 0 for octal notation.
* Does not check for overflows, does not support negative numbers
*
* @param text Textual representation of number
* @return Converted number or 0 if no valid number ofund
*/
unative_t atoi(const char *text)
{
int base = 10;
unative_t result = 0;
 
if (text[0] == '0' && text[1] == 'x') {
base = 16;
text += 2;
} else if (text[0] == '0')
base = 8;
 
while (*text) {
if (base != 16 && \
((*text >= 'A' && *text <= 'F' )
|| (*text >='a' && *text <='f')))
break;
if (base == 8 && *text >='8')
break;
 
if (*text >= '0' && *text <= '9') {
result *= base;
result += *text - '0';
} else if (*text >= 'A' && *text <= 'F') {
result *= base;
result += *text - 'A' + 10;
} else if (*text >= 'a' && *text <= 'f') {
result *= base;
result += *text - 'a' + 10;
} else
break;
text++;
}
 
return result;
}
 
 
void order(const uint64_t val, uint64_t *rv, char *suffix)
{
if (val > 10000000000000000000ULL) {
*rv = val / 1000000000000000000ULL;
*suffix = 'Z';
} else if (val > 1000000000000000000ULL) {
*rv = val / 1000000000000000ULL;
*suffix = 'E';
} else if (val > 1000000000000000ULL) {
*rv = val / 1000000000000ULL;
*suffix = 'T';
} else if (val > 1000000000000ULL) {
*rv = val / 1000000000ULL;
*suffix = 'G';
} else if (val > 1000000000ULL) {
*rv = val / 1000000ULL;
*suffix = 'M';
} else if (val > 1000000ULL) {
*rv = val / 1000ULL;
*suffix = 'k';
} else {
*rv = val;
*suffix = ' ';
}
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/lib/memstr.c
0,0 → 1,164
/*
* Copyright (c) 2001-2004 Jakub Jermar
* 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 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.
*/
 
#include <memstr.h>
#include <arch/types.h>
#include <align.h>
 
/** 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.
*
* @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)
{
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) {
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];
}
return (char *) dst;
}
 
/** Move memory block with possible overlapping.
*
* Copy cnt bytes from src address to dst address. The source and destination
* memory areas may overlap.
*
* @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 *memmove(void *dst, const void *src, size_t n)
{
const uint8_t *sp;
uint8_t *dp;
 
/* Nothing to do? */
if (src == dst)
return dst;
 
/* Non-overlapping? */
if (dst >= src + n || src >= dst + n) {
return memcpy(dst, src, n);
}
 
/* Which direction? */
if (src > dst) {
/* Forwards. */
sp = src;
dp = dst;
 
while (n-- != 0)
*dp++ = *sp++;
} else {
/* Backwards. */
sp = src + (n - 1);
dp = dst + (n - 1);
 
while (n-- != 0)
*dp-- = *sp--;
}
 
return dst;
}
 
/** Fill block of memory
*
* Fill cnt bytes at dst address with the value x. The filling is done
* byte-by-byte.
*
* @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)
{
unsigned int i;
uint8_t *p = (uint8_t *) dst;
for (i = 0; i < cnt; i++)
p[i] = x;
}
 
/** Fill block of memory.
*
* Fill cnt words at dst address with the value x. The filling is done
* word-by-word.
*
* @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)
{
unsigned int i;
uint16_t *p = (uint16_t *) dst;
for (i = 0; i < cnt; i++)
p[i] = x;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/lib/rd.c
0,0 → 1,104
/*
* Copyright (c) 2006 Martin Decky
* 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 genericmm
* @{
*/
 
/**
* @file
* @brief RAM disk support.
*
* Support for RAM disk images.
*/
 
#include <lib/rd.h>
#include <byteorder.h>
#include <mm/frame.h>
#include <sysinfo/sysinfo.h>
#include <ddi/ddi.h>
#include <align.h>
 
static parea_t rd_parea; /**< Physical memory area for rd. */
 
/**
* RAM disk initialization routine. At this point, the RAM disk memory is shared
* and information about the share is provided as sysinfo values to the
* userspace tasks.
*/
int init_rd(rd_header_t *header, size_t size)
{
/* Identify RAM disk */
if ((header->magic[0] != RD_MAG0) || (header->magic[1] != RD_MAG1) ||
(header->magic[2] != RD_MAG2) || (header->magic[3] != RD_MAG3))
return RE_INVALID;
/* Identify version */
if (header->version != RD_VERSION)
return RE_UNSUPPORTED;
uint32_t hsize;
uint64_t dsize;
switch (header->data_type) {
case RD_DATA_LSB:
hsize = uint32_t_le2host(header->header_size);
dsize = uint64_t_le2host(header->data_size);
break;
case RD_DATA_MSB:
hsize = uint32_t_be2host(header->header_size);
dsize = uint64_t_be2host(header->data_size);
break;
default:
return RE_UNSUPPORTED;
}
if ((hsize % FRAME_SIZE) || (dsize % FRAME_SIZE))
return RE_UNSUPPORTED;
if (hsize > size)
return RE_INVALID;
if ((uint64_t) hsize + dsize > size)
dsize = size - hsize;
rd_parea.pbase = ALIGN_DOWN((uintptr_t) KA2PA((void *) header + hsize),
FRAME_SIZE);
rd_parea.frames = SIZE2FRAMES(dsize);
ddi_parea_register(&rd_parea);
 
sysinfo_set_item_val("rd", NULL, true);
sysinfo_set_item_val("rd.header_size", NULL, hsize);
sysinfo_set_item_val("rd.size", NULL, dsize);
sysinfo_set_item_val("rd.address.physical", NULL,
(unative_t) KA2PA((void *) header + hsize));
 
return RE_OK;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/lib/elf.c
0,0 → 1,272
/*
* Copyright (c) 2006 Sergey Bondari
* Copyright (c) 2006 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 generic
* @{
*/
 
/**
* @file
* @brief Kernel ELF loader.
*/
 
#include <lib/elf.h>
#include <debug.h>
#include <arch/types.h>
#include <mm/as.h>
#include <mm/frame.h>
#include <mm/slab.h>
#include <align.h>
#include <memstr.h>
#include <macros.h>
#include <arch.h>
 
static char *error_codes[] = {
"no error",
"invalid image",
"address space error",
"incompatible image",
"unsupported image type",
"irrecoverable error"
};
 
static int segment_header(elf_segment_header_t *entry, elf_header_t *elf,
as_t *as, int flags);
static int section_header(elf_section_header_t *entry, elf_header_t *elf,
as_t *as);
static int load_segment(elf_segment_header_t *entry, elf_header_t *elf,
as_t *as);
 
/** ELF loader
*
* @param header Pointer to ELF header in memory
* @param as Created and properly mapped address space
* @param flags A combination of ELD_F_*
* @return EE_OK on success
*/
unsigned int elf_load(elf_header_t *header, as_t * as, int flags)
{
int i, rc;
 
/* Identify ELF */
if (header->e_ident[EI_MAG0] != ELFMAG0 ||
header->e_ident[EI_MAG1] != ELFMAG1 ||
header->e_ident[EI_MAG2] != ELFMAG2 ||
header->e_ident[EI_MAG3] != ELFMAG3) {
return EE_INVALID;
}
/* Identify ELF compatibility */
if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
header->e_machine != ELF_MACHINE ||
header->e_ident[EI_VERSION] != EV_CURRENT ||
header->e_version != EV_CURRENT ||
header->e_ident[EI_CLASS] != ELF_CLASS) {
return EE_INCOMPATIBLE;
}
 
if (header->e_phentsize != sizeof(elf_segment_header_t))
return EE_INCOMPATIBLE;
 
if (header->e_shentsize != sizeof(elf_section_header_t))
return EE_INCOMPATIBLE;
 
/* Check if the object type is supported. */
if (header->e_type != ET_EXEC)
return EE_UNSUPPORTED;
 
/* Check if the ELF image starts on a page boundary */
if (ALIGN_UP((uintptr_t)header, PAGE_SIZE) != (uintptr_t)header)
return EE_UNSUPPORTED;
 
/* Walk through all segment headers and process them. */
for (i = 0; i < header->e_phnum; i++) {
elf_segment_header_t *seghdr;
 
seghdr = &((elf_segment_header_t *)(((uint8_t *) header) +
header->e_phoff))[i];
rc = segment_header(seghdr, header, as, flags);
if (rc != EE_OK)
return rc;
}
 
/* Inspect all section headers and proccess them. */
for (i = 0; i < header->e_shnum; i++) {
elf_section_header_t *sechdr;
 
sechdr = &((elf_section_header_t *)(((uint8_t *) header) +
header->e_shoff))[i];
rc = section_header(sechdr, header, as);
if (rc != EE_OK)
return rc;
}
 
return EE_OK;
}
 
/** Print error message according to error code.
*
* @param rc Return code returned by elf_load().
*
* @return NULL terminated description of error.
*/
char *elf_error(unsigned int rc)
{
ASSERT(rc < sizeof(error_codes) / sizeof(char *));
 
return error_codes[rc];
}
 
/** Process segment header.
*
* @param entry Segment header.
* @param elf ELF header.
* @param as Address space into wich the ELF is being loaded.
*
* @return EE_OK on success, error code otherwise.
*/
static int segment_header(elf_segment_header_t *entry, elf_header_t *elf,
as_t *as, int flags)
{
char *interp;
 
switch (entry->p_type) {
case PT_NULL:
case PT_PHDR:
break;
case PT_LOAD:
return load_segment(entry, elf, as);
break;
case PT_DYNAMIC:
case PT_INTERP:
interp = (char *)elf + entry->p_offset;
/* FIXME */
/*if (memcmp((uintptr_t)interp, (uintptr_t)ELF_INTERP_ZSTR,
ELF_INTERP_ZLEN) != 0) {
return EE_UNSUPPORTED;
}*/
if ((flags & ELD_F_LOADER) == 0) {
return EE_LOADER;
}
break;
case PT_SHLIB:
case PT_NOTE:
case PT_LOPROC:
case PT_HIPROC:
default:
return EE_UNSUPPORTED;
break;
}
return EE_OK;
}
 
/** Load segment described by program header entry.
*
* @param entry Program header entry describing segment to be loaded.
* @param elf ELF header.
* @param as Address space into wich the ELF is being loaded.
*
* @return EE_OK on success, error code otherwise.
*/
int load_segment(elf_segment_header_t *entry, elf_header_t *elf, as_t *as)
{
as_area_t *a;
int flags = 0;
mem_backend_data_t backend_data;
uintptr_t base;
size_t mem_sz;
backend_data.elf = elf;
backend_data.segment = entry;
 
if (entry->p_align > 1) {
if ((entry->p_offset % entry->p_align) !=
(entry->p_vaddr % entry->p_align)) {
return EE_INVALID;
}
}
 
if (entry->p_flags & PF_X)
flags |= AS_AREA_EXEC;
if (entry->p_flags & PF_W)
flags |= AS_AREA_WRITE;
if (entry->p_flags & PF_R)
flags |= AS_AREA_READ;
flags |= AS_AREA_CACHEABLE;
 
/*
* Align vaddr down, inserting a little "gap" at the beginning.
* Adjust area size, so that its end remains in place.
*/
base = ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE);
mem_sz = entry->p_memsz + (entry->p_vaddr - base);
 
a = as_area_create(as, flags, mem_sz, base,
AS_AREA_ATTR_NONE, &elf_backend, &backend_data);
if (!a)
return EE_MEMORY;
/*
* The segment will be mapped on demand by elf_page_fault().
*/
 
return EE_OK;
}
 
/** Process section header.
*
* @param entry Segment header.
* @param elf ELF header.
* @param as Address space into wich the ELF is being loaded.
*
* @return EE_OK on success, error code otherwise.
*/
static int section_header(elf_section_header_t *entry, elf_header_t *elf,
as_t *as)
{
switch (entry->sh_type) {
case SHT_PROGBITS:
if (entry->sh_flags & SHF_TLS) {
/* .tdata */
}
break;
case SHT_NOBITS:
if (entry->sh_flags & SHF_TLS) {
/* .tbss */
}
break;
default:
break;
}
return EE_OK;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ipc/event.c
0,0 → 1,156
/*
* Copyright (c) 2009 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 generic
* @{
*/
/**
* @file
* @brief Kernel event notifications.
*/
 
#include <ipc/event.h>
#include <ipc/event_types.h>
#include <mm/slab.h>
#include <arch/types.h>
#include <synch/spinlock.h>
#include <console/console.h>
#include <memstr.h>
#include <errno.h>
#include <arch.h>
 
/**
* The events array.
* Arranging the events in this two-dimensional array should decrease the
* likelyhood of cacheline ping-pong.
*/
static event_t events[EVENT_END];
 
/** Initialize kernel events. */
void event_init(void)
{
unsigned int i;
for (i = 0; i < EVENT_END; i++) {
spinlock_initialize(&events[i].lock, "event.lock");
events[i].answerbox = NULL;
events[i].counter = 0;
events[i].method = 0;
}
}
 
static int event_subscribe(event_type_t evno, unative_t method,
answerbox_t *answerbox)
{
if (evno >= EVENT_END)
return ELIMIT;
spinlock_lock(&events[evno].lock);
int res;
if (events[evno].answerbox == NULL) {
events[evno].answerbox = answerbox;
events[evno].method = method;
events[evno].counter = 0;
res = EOK;
} else
res = EEXISTS;
spinlock_unlock(&events[evno].lock);
return res;
}
 
unative_t sys_event_subscribe(unative_t evno, unative_t method)
{
return (unative_t) event_subscribe((event_type_t) evno, (unative_t)
method, &TASK->answerbox);
}
 
bool event_is_subscribed(event_type_t evno)
{
bool res;
ASSERT(evno < EVENT_END);
spinlock_lock(&events[evno].lock);
res = events[evno].answerbox != NULL;
spinlock_unlock(&events[evno].lock);
return res;
}
 
 
void event_cleanup_answerbox(answerbox_t *answerbox)
{
unsigned int i;
for (i = 0; i < EVENT_END; i++) {
spinlock_lock(&events[i].lock);
if (events[i].answerbox == answerbox) {
events[i].answerbox = NULL;
events[i].counter = 0;
events[i].method = 0;
}
spinlock_unlock(&events[i].lock);
}
}
 
void event_notify(event_type_t evno, unative_t a1, unative_t a2, unative_t a3,
unative_t a4, unative_t a5)
{
ASSERT(evno < EVENT_END);
spinlock_lock(&events[evno].lock);
if (events[evno].answerbox != NULL) {
call_t *call = ipc_call_alloc(FRAME_ATOMIC);
if (call) {
call->flags |= IPC_CALL_NOTIF;
call->priv = ++events[evno].counter;
IPC_SET_METHOD(call->data, events[evno].method);
IPC_SET_ARG1(call->data, a1);
IPC_SET_ARG2(call->data, a2);
IPC_SET_ARG3(call->data, a3);
IPC_SET_ARG4(call->data, a4);
IPC_SET_ARG5(call->data, a5);
ipl_t ipl = interrupts_disable();
spinlock_lock(&events[evno].answerbox->irq_lock);
list_append(&call->link, &events[evno].answerbox->irq_notifs);
spinlock_unlock(&events[evno].answerbox->irq_lock);
interrupts_restore(ipl);
waitq_wakeup(&events[evno].answerbox->wq, WAKEUP_FIRST);
}
}
spinlock_unlock(&events[evno].lock);
}
 
/** @}
*/
Property changes:
Added: svn:mergeinfo
/tags/0.4.1/kernel/generic/src/ipc/sysipc.c
0,0 → 1,1114
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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 <arch.h>
#include <proc/task.h>
#include <proc/thread.h>
#include <errno.h>
#include <memstr.h>
#include <debug.h>
#include <ipc/ipc.h>
#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 <syscall/copy.h>
#include <security/cap.h>
#include <mm/as.h>
#include <print.h>
 
/**
* Maximum buffer size allowed for IPC_M_DATA_WRITE and IPC_M_DATA_READ
* requests.
*/
#define DATA_XFER_LIMIT (64 * 1024)
 
#define GET_CHECK_PHONE(phone, phoneid, err) \
{ \
if (phoneid > IPC_MAX_PHONES) { \
err; \
} \
phone = &TASK->phones[phoneid]; \
}
 
#define STRUCT_TO_USPACE(dst, src) copy_to_uspace(dst, src, sizeof(*(src)))
 
/** Decide if the method is a system method.
*
* @param method Method to be decided.
*
* @return Return 1 if the method is a system method.
* Otherwise return 0.
*/
static inline int method_is_system(unative_t method)
{
if (method <= IPC_M_LAST_SYSTEM)
return 1;
return 0;
}
 
/** Decide if the message with this method is forwardable.
*
* - some system messages may be forwarded, for some of them
* it is useless
*
* @param method Method to be decided.
*
* @return Return 1 if the method is forwardable.
* Otherwise return 0.
*/
static inline int method_is_forwardable(unative_t method)
{
switch (method) {
case IPC_M_CONNECTION_CLONE:
case IPC_M_CONNECT_ME:
case IPC_M_PHONE_HUNGUP:
/* This message is meant only for the original recipient. */
return 0;
default:
return 1;
}
}
 
/** Decide if the message with this method is immutable on forward.
*
* - some system messages may be forwarded but their content cannot be altered
*
* @param method Method to be decided.
*
* @return Return 1 if the method is immutable on forward.
* Otherwise return 0.
*/
static inline int method_is_immutable(unative_t method)
{
switch (method) {
case IPC_M_SHARE_OUT:
case IPC_M_SHARE_IN:
case IPC_M_DATA_WRITE:
case IPC_M_DATA_READ:
return 1;
break;
default:
return 0;
}
}
 
 
/***********************************************************************
* Functions that preprocess answer before sending it to the recepient.
***********************************************************************/
 
/** Decide if the caller (e.g. ipc_answer()) should save the old call contents
* for answer_preprocess().
*
* @param call Call structure to be decided.
*
* @return Return 1 if the old call contents should be saved.
* Return 0 otherwise.
*/
static inline int answer_need_old(call_t *call)
{
switch (IPC_GET_METHOD(call->data)) {
case IPC_M_CONNECTION_CLONE:
case IPC_M_CONNECT_ME:
case IPC_M_CONNECT_TO_ME:
case IPC_M_CONNECT_ME_TO:
case IPC_M_SHARE_OUT:
case IPC_M_SHARE_IN:
case IPC_M_DATA_WRITE:
case IPC_M_DATA_READ:
return 1;
default:
return 0;
}
}
 
/** Interpret process answer as control information.
*
* This function is called directly after sys_ipc_answer().
*
* @param answer Call structure with the answer.
* @param olddata Saved data of the request.
*
* @return Return 0 on success or an error code.
*/
static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
{
int phoneid;
 
if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
/* In case of forward, hangup the forwared phone,
* not the originator
*/
mutex_lock(&answer->data.phone->lock);
spinlock_lock(&TASK->answerbox.lock);
if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
list_remove(&answer->data.phone->link);
answer->data.phone->state = IPC_PHONE_SLAMMED;
}
spinlock_unlock(&TASK->answerbox.lock);
mutex_unlock(&answer->data.phone->lock);
}
 
if (!olddata)
return 0;
 
if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTION_CLONE) {
phoneid = IPC_GET_ARG1(*olddata);
phone_t *phone = &TASK->phones[phoneid];
if (IPC_GET_RETVAL(answer->data) != EOK) {
/*
* The recipient of the cloned phone rejected the offer.
* In this case, the connection was established at the
* request time and therefore we need to slam the phone.
* We don't merely hangup as that would result in
* sending IPC_M_HUNGUP to the third party on the
* other side of the cloned phone.
*/
mutex_lock(&phone->lock);
if (phone->state == IPC_PHONE_CONNECTED) {
spinlock_lock(&phone->callee->lock);
list_remove(&phone->link);
phone->state = IPC_PHONE_SLAMMED;
spinlock_unlock(&phone->callee->lock);
}
mutex_unlock(&phone->lock);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME) {
phone_t *phone = (phone_t *)IPC_GET_ARG5(*olddata);
if (IPC_GET_RETVAL(answer->data) != EOK) {
/*
* The other party on the cloned phoned rejected our
* request for connection on the protocol level.
* We need to break the connection without sending
* IPC_M_HUNGUP back.
*/
mutex_lock(&phone->lock);
if (phone->state == IPC_PHONE_CONNECTED) {
spinlock_lock(&phone->callee->lock);
list_remove(&phone->link);
phone->state = IPC_PHONE_SLAMMED;
spinlock_unlock(&phone->callee->lock);
}
mutex_unlock(&phone->lock);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
phoneid = IPC_GET_ARG5(*olddata);
if (IPC_GET_RETVAL(answer->data) != EOK) {
/* The connection was not accepted */
phone_dealloc(phoneid);
} else {
/* The connection was accepted */
phone_connect(phoneid, &answer->sender->answerbox);
/* Set 'phone hash' as arg5 of response */
IPC_SET_ARG5(answer->data,
(unative_t) &TASK->phones[phoneid]);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
/* If the users accepted call, connect */
if (IPC_GET_RETVAL(answer->data) == EOK) {
ipc_phone_connect((phone_t *) IPC_GET_ARG5(*olddata),
&TASK->answerbox);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_SHARE_OUT) {
if (!IPC_GET_RETVAL(answer->data)) {
/* Accepted, handle as_area receipt */
ipl_t ipl;
int rc;
as_t *as;
ipl = interrupts_disable();
spinlock_lock(&answer->sender->lock);
as = answer->sender->as;
spinlock_unlock(&answer->sender->lock);
interrupts_restore(ipl);
rc = as_area_share(as, IPC_GET_ARG1(*olddata),
IPC_GET_ARG2(*olddata), AS,
IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
IPC_SET_RETVAL(answer->data, rc);
return rc;
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_SHARE_IN) {
if (!IPC_GET_RETVAL(answer->data)) {
ipl_t ipl;
as_t *as;
int rc;
ipl = interrupts_disable();
spinlock_lock(&answer->sender->lock);
as = answer->sender->as;
spinlock_unlock(&answer->sender->lock);
interrupts_restore(ipl);
rc = as_area_share(AS, IPC_GET_ARG1(answer->data),
IPC_GET_ARG2(*olddata), as, IPC_GET_ARG1(*olddata),
IPC_GET_ARG2(answer->data));
IPC_SET_RETVAL(answer->data, rc);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_DATA_READ) {
ASSERT(!answer->buffer);
if (!IPC_GET_RETVAL(answer->data)) {
/* The recipient agreed to send data. */
uintptr_t src = IPC_GET_ARG1(answer->data);
uintptr_t dst = IPC_GET_ARG1(*olddata);
size_t max_size = IPC_GET_ARG2(*olddata);
size_t size = IPC_GET_ARG2(answer->data);
if (size && size <= max_size) {
/*
* Copy the destination VA so that this piece of
* information is not lost.
*/
IPC_SET_ARG1(answer->data, dst);
 
answer->buffer = malloc(size, 0);
int rc = copy_from_uspace(answer->buffer,
(void *) src, size);
if (rc) {
IPC_SET_RETVAL(answer->data, rc);
free(answer->buffer);
answer->buffer = NULL;
}
} else if (!size) {
IPC_SET_RETVAL(answer->data, EOK);
} else {
IPC_SET_RETVAL(answer->data, ELIMIT);
}
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_DATA_WRITE) {
ASSERT(answer->buffer);
if (!IPC_GET_RETVAL(answer->data)) {
/* The recipient agreed to receive data. */
int rc;
uintptr_t dst;
size_t size;
size_t max_size;
 
dst = (uintptr_t)IPC_GET_ARG1(answer->data);
size = (size_t)IPC_GET_ARG2(answer->data);
max_size = (size_t)IPC_GET_ARG2(*olddata);
 
if (size <= max_size) {
rc = copy_to_uspace((void *) dst,
answer->buffer, size);
if (rc)
IPC_SET_RETVAL(answer->data, rc);
} else {
IPC_SET_RETVAL(answer->data, ELIMIT);
}
}
free(answer->buffer);
answer->buffer = NULL;
}
return 0;
}
 
static void phones_lock(phone_t *p1, phone_t *p2)
{
if (p1 < p2) {
mutex_lock(&p1->lock);
mutex_lock(&p2->lock);
} else if (p1 > p2) {
mutex_lock(&p2->lock);
mutex_lock(&p1->lock);
} else {
mutex_lock(&p1->lock);
}
}
 
static void phones_unlock(phone_t *p1, phone_t *p2)
{
mutex_unlock(&p1->lock);
if (p1 != p2)
mutex_unlock(&p2->lock);
}
 
/** 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, phone_t *phone)
{
int newphid;
size_t size;
uintptr_t src;
int rc;
 
switch (IPC_GET_METHOD(call->data)) {
case IPC_M_CONNECTION_CLONE: {
phone_t *cloned_phone;
GET_CHECK_PHONE(cloned_phone, IPC_GET_ARG1(call->data),
return ENOENT);
phones_lock(cloned_phone, phone);
if ((cloned_phone->state != IPC_PHONE_CONNECTED) ||
phone->state != IPC_PHONE_CONNECTED) {
phones_unlock(cloned_phone, phone);
return EINVAL;
}
/*
* We can be pretty sure now that both tasks exist and we are
* connected to them. As we continue to hold the phone locks,
* we are effectively preventing them from finishing their
* potential cleanup.
*/
newphid = phone_alloc(phone->callee->task);
if (newphid < 0) {
phones_unlock(cloned_phone, phone);
return ELIMIT;
}
ipc_phone_connect(&phone->callee->task->phones[newphid],
cloned_phone->callee);
phones_unlock(cloned_phone, phone);
/* Set the new phone for the callee. */
IPC_SET_ARG1(call->data, newphid);
break;
}
case IPC_M_CONNECT_ME:
IPC_SET_ARG5(call->data, (unative_t) phone);
break;
case IPC_M_CONNECT_ME_TO:
newphid = phone_alloc(TASK);
if (newphid < 0)
return ELIMIT;
/* Set arg5 for server */
IPC_SET_ARG5(call->data, (unative_t) &TASK->phones[newphid]);
call->flags |= IPC_CALL_CONN_ME_TO;
call->priv = newphid;
break;
case IPC_M_SHARE_OUT:
size = as_area_get_size(IPC_GET_ARG1(call->data));
if (!size)
return EPERM;
IPC_SET_ARG2(call->data, size);
break;
case IPC_M_DATA_READ:
size = IPC_GET_ARG2(call->data);
if ((size <= 0 || (size > DATA_XFER_LIMIT)))
return ELIMIT;
break;
case IPC_M_DATA_WRITE:
src = IPC_GET_ARG1(call->data);
size = IPC_GET_ARG2(call->data);
if (size > DATA_XFER_LIMIT)
return ELIMIT;
call->buffer = (uint8_t *) malloc(size, 0);
rc = copy_from_uspace(call->buffer, (void *) src, size);
if (rc != 0) {
free(call->buffer);
return rc;
}
break;
#ifdef CONFIG_UDEBUG
case IPC_M_DEBUG_ALL:
return udebug_request_preprocess(call, phone);
#endif
default:
break;
}
return 0;
}
 
/*******************************************************************************
* Functions called to process received call/answer before passing it to uspace.
*******************************************************************************/
 
/** Do basic kernel processing of received call answer.
*
* @param call Call structure with the answer.
*/
static void process_answer(call_t *call)
{
if (((native_t) IPC_GET_RETVAL(call->data) == EHANGUP) &&
(call->flags & IPC_CALL_FORWARDED))
IPC_SET_RETVAL(call->data, EFORWARD);
 
if (call->flags & IPC_CALL_CONN_ME_TO) {
if (IPC_GET_RETVAL(call->data))
phone_dealloc(call->priv);
else
IPC_SET_ARG5(call->data, call->priv);
}
 
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);
if (rc)
IPC_SET_RETVAL(call->data, rc);
free(call->buffer);
call->buffer = NULL;
}
}
 
/** Do basic kernel processing of received call request.
*
* @param box Destination answerbox structure.
* @param call Call structure with the request.
*
* @return Return 0 if the call should be passed to userspace.
* Return -1 if the call should be ignored.
*/
static int process_request(answerbox_t *box, call_t *call)
{
int phoneid;
 
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
phoneid = phone_alloc(TASK);
if (phoneid < 0) { /* Failed to allocate phone */
IPC_SET_RETVAL(call->data, ELIMIT);
ipc_answer(box, call);
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;
}
 
/** Make a fast call over IPC, wait for reply and return to user.
*
* This function can handle only three arguments of payload, but is faster than
* the generic function (i.e. sys_ipc_call_sync_slow()).
*
* @param phoneid Phone handle for the call.
* @param method Method of the call.
* @param arg1 Service-defined payload argument.
* @param arg2 Service-defined payload argument.
* @param arg3 Service-defined payload argument.
* @param data Address of userspace structure where the reply call will
* be stored.
*
* @return Returns 0 on success.
* Return ENOENT if there is no such phone handle.
*/
unative_t sys_ipc_call_sync_fast(unative_t phoneid, unative_t method,
unative_t arg1, unative_t arg2, unative_t arg3, ipc_data_t *data)
{
call_t call;
phone_t *phone;
int res;
int rc;
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
ipc_call_static_init(&call);
IPC_SET_METHOD(call.data, method);
IPC_SET_ARG1(call.data, arg1);
IPC_SET_ARG2(call.data, arg2);
IPC_SET_ARG3(call.data, arg3);
/*
* To achieve deterministic behavior, zero out arguments that are beyond
* the limits of the fast version.
*/
IPC_SET_ARG4(call.data, 0);
IPC_SET_ARG5(call.data, 0);
 
if (!(res = request_preprocess(&call, phone))) {
#ifdef CONFIG_UDEBUG
udebug_stoppable_begin();
#endif
rc = ipc_call_sync(phone, &call);
#ifdef CONFIG_UDEBUG
udebug_stoppable_end();
#endif
if (rc != EOK)
return rc;
process_answer(&call);
 
} else {
IPC_SET_RETVAL(call.data, res);
}
rc = STRUCT_TO_USPACE(&data->args, &call.data.args);
if (rc != 0)
return rc;
 
return 0;
}
 
/** Make a synchronous IPC call allowing to transmit the entire payload.
*
* @param phoneid Phone handle for the call.
* @param question Userspace address of call data with the request.
* @param reply Userspace address of call data where to store the
* answer.
*
* @return Zero on success or an error code.
*/
unative_t sys_ipc_call_sync_slow(unative_t phoneid, ipc_data_t *question,
ipc_data_t *reply)
{
call_t call;
phone_t *phone;
int res;
int rc;
 
ipc_call_static_init(&call);
rc = copy_from_uspace(&call.data.args, &question->args,
sizeof(call.data.args));
if (rc != 0)
return (unative_t) rc;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
if (!(res = request_preprocess(&call, phone))) {
#ifdef CONFIG_UDEBUG
udebug_stoppable_begin();
#endif
rc = ipc_call_sync(phone, &call);
#ifdef CONFIG_UDEBUG
udebug_stoppable_end();
#endif
if (rc != EOK)
return rc;
process_answer(&call);
} else
IPC_SET_RETVAL(call.data, res);
 
rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
if (rc != 0)
return rc;
 
return 0;
}
 
/** Check that the task did not exceed the allowed limit of asynchronous calls.
*
* @return Return 0 if limit not reached or -1 if limit exceeded.
*/
static int check_call_limit(void)
{
if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
atomic_dec(&TASK->active_calls);
return -1;
}
return 0;
}
 
/** Make a fast asynchronous call over IPC.
*
* This function can only handle four arguments of payload, but is faster than
* the generic function sys_ipc_call_async_slow().
*
* @param phoneid Phone handle for the call.
* @param method Method of the call.
* @param arg1 Service-defined payload argument.
* @param arg2 Service-defined payload argument.
* @param arg3 Service-defined payload argument.
* @param arg4 Service-defined payload argument.
*
* @return Return call hash on success.
* Return IPC_CALLRET_FATAL in case of a fatal error and
* IPC_CALLRET_TEMPORARY if there are too many pending
* asynchronous requests; answers should be handled first.
*/
unative_t sys_ipc_call_async_fast(unative_t phoneid, unative_t method,
unative_t arg1, unative_t arg2, unative_t arg3, unative_t arg4)
{
call_t *call;
phone_t *phone;
int res;
 
if (check_call_limit())
return IPC_CALLRET_TEMPORARY;
 
GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
 
call = ipc_call_alloc(0);
IPC_SET_METHOD(call->data, method);
IPC_SET_ARG1(call->data, arg1);
IPC_SET_ARG2(call->data, arg2);
IPC_SET_ARG3(call->data, arg3);
IPC_SET_ARG4(call->data, arg4);
/*
* To achieve deterministic behavior, zero out arguments that are beyond
* the limits of the fast version.
*/
IPC_SET_ARG5(call->data, 0);
 
if (!(res = request_preprocess(call, phone)))
ipc_call(phone, call);
else
ipc_backsend_err(phone, call, res);
 
return (unative_t) call;
}
 
/** Make an asynchronous IPC call allowing to transmit the entire payload.
*
* @param phoneid Phone handle for the call.
* @param data Userspace address of call data with the request.
*
* @return See sys_ipc_call_async_fast().
*/
unative_t sys_ipc_call_async_slow(unative_t phoneid, ipc_data_t *data)
{
call_t *call;
phone_t *phone;
int res;
int rc;
 
if (check_call_limit())
return IPC_CALLRET_TEMPORARY;
 
GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
 
call = ipc_call_alloc(0);
rc = copy_from_uspace(&call->data.args, &data->args,
sizeof(call->data.args));
if (rc != 0) {
ipc_call_free(call);
return (unative_t) rc;
}
if (!(res = request_preprocess(call, phone)))
ipc_call(phone, call);
else
ipc_backsend_err(phone, call, res);
 
return (unative_t) call;
}
 
/** Forward a received call to another destination - common code for both the
* fast and the slow version.
*
* @param callid Hash of the call to forward.
* @param phoneid Phone handle to use for forwarding.
* @param method New method to use for the forwarded call.
* @param arg1 New value of the first argument for the forwarded call.
* @param arg2 New value of the second argument for the forwarded call.
* @param arg3 New value of the third argument for the forwarded call.
* @param arg4 New value of the fourth argument for the forwarded call.
* @param arg5 New value of the fifth argument for the forwarded call.
* @param mode Flags that specify mode of the forward operation.
* @param slow If true, arg3, arg4 and arg5 are considered. Otherwise
* the function considers only the fast version arguments:
* i.e. arg1 and arg2.
*
* @return Return 0 on succes, otherwise return an error code.
*
* Warning: Make sure that ARG5 is not rewritten for certain system IPC
*/
static unative_t sys_ipc_forward_common(unative_t callid, unative_t phoneid,
unative_t method, unative_t arg1, unative_t arg2, unative_t arg3,
unative_t arg4, unative_t arg5, int mode, bool slow)
{
call_t *call;
phone_t *phone;
 
call = get_call(callid);
if (!call)
return ENOENT;
call->flags |= IPC_CALL_FORWARDED;
 
GET_CHECK_PHONE(phone, phoneid, {
IPC_SET_RETVAL(call->data, EFORWARD);
ipc_answer(&TASK->answerbox, call);
return ENOENT;
});
 
if (!method_is_forwardable(IPC_GET_METHOD(call->data))) {
IPC_SET_RETVAL(call->data, EFORWARD);
ipc_answer(&TASK->answerbox, call);
return EPERM;
}
 
/*
* Userspace is not allowed to change method of system methods on
* forward, allow changing ARG1, ARG2, ARG3 and ARG4 by means of method,
* arg1, arg2 and arg3.
* If the method is immutable, don't change anything.
*/
if (!method_is_immutable(IPC_GET_METHOD(call->data))) {
if (method_is_system(IPC_GET_METHOD(call->data))) {
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
phone_dealloc(IPC_GET_ARG5(call->data));
 
IPC_SET_ARG1(call->data, method);
IPC_SET_ARG2(call->data, arg1);
IPC_SET_ARG3(call->data, arg2);
if (slow) {
IPC_SET_ARG4(call->data, arg3);
/*
* For system methods we deliberately don't
* overwrite ARG5.
*/
}
} else {
IPC_SET_METHOD(call->data, method);
IPC_SET_ARG1(call->data, arg1);
IPC_SET_ARG2(call->data, arg2);
if (slow) {
IPC_SET_ARG3(call->data, arg3);
IPC_SET_ARG4(call->data, arg4);
IPC_SET_ARG5(call->data, arg5);
}
}
}
 
return ipc_forward(call, phone, &TASK->answerbox, mode);
}
 
/** Forward a received call to another destination - fast version.
*
* @param callid Hash of the call to forward.
* @param phoneid Phone handle to use for forwarding.
* @param method New method to use for the forwarded call.
* @param arg1 New value of the first argument for the forwarded call.
* @param arg2 New value of the second argument for the forwarded call.
* @param mode Flags that specify mode of the forward operation.
*
* @return Return 0 on succes, otherwise return an error code.
*
* In case the original method is a system method, ARG1, ARG2 and ARG3 are
* overwritten in the forwarded message with the new method and the new
* arg1 and arg2, respectively. Otherwise the METHOD, ARG1 and ARG2 are
* rewritten with the new method, arg1 and arg2, respectively. Also note there
* is a set of immutable methods, for which the new method and arguments are not
* set and these values are ignored.
*/
unative_t sys_ipc_forward_fast(unative_t callid, unative_t phoneid,
unative_t method, unative_t arg1, unative_t arg2, int mode)
{
return sys_ipc_forward_common(callid, phoneid, method, arg1, arg2, 0, 0,
0, mode, false);
}
 
/** Forward a received call to another destination - slow version.
*
* @param callid Hash of the call to forward.
* @param phoneid Phone handle to use for forwarding.
* @param data Userspace address of the new IPC data.
* @param mode Flags that specify mode of the forward operation.
*
* @return Return 0 on succes, otherwise return an error code.
*
* This function is the slow verision of the sys_ipc_forward_fast interface.
* It can copy all five new arguments and the new method from the userspace.
* It naturally extends the functionality of the fast version. For system
* methods, it additionally stores the new value of arg3 to ARG4. For non-system
* methods, it additionally stores the new value of arg3, arg4 and arg5,
* respectively, to ARG3, ARG4 and ARG5, respectively.
*/
unative_t sys_ipc_forward_slow(unative_t callid, unative_t phoneid,
ipc_data_t *data, int mode)
{
ipc_data_t newdata;
int rc;
 
rc = copy_from_uspace(&newdata.args, &data->args,
sizeof(newdata.args));
if (rc != 0)
return (unative_t) rc;
 
return sys_ipc_forward_common(callid, phoneid,
IPC_GET_METHOD(newdata), IPC_GET_ARG1(newdata),
IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true);
}
 
/** Answer an IPC call - fast version.
*
* This function can handle only two return arguments of payload, but is faster
* than the generic sys_ipc_answer().
*
* @param callid Hash of the call to be answered.
* @param retval Return value of the answer.
* @param arg1 Service-defined return value.
* @param arg2 Service-defined return value.
* @param arg3 Service-defined return value.
* @param arg4 Service-defined return value.
*
* @return Return 0 on success, otherwise return an error code.
*/
unative_t sys_ipc_answer_fast(unative_t callid, unative_t retval,
unative_t arg1, unative_t arg2, unative_t arg3, unative_t arg4)
{
call_t *call;
ipc_data_t saved_data;
int saveddata = 0;
int rc;
 
/* Do not answer notification callids */
if (callid & IPC_CALLID_NOTIFICATION)
return 0;
 
call = get_call(callid);
if (!call)
return ENOENT;
 
if (answer_need_old(call)) {
memcpy(&saved_data, &call->data, sizeof(call->data));
saveddata = 1;
}
 
IPC_SET_RETVAL(call->data, retval);
IPC_SET_ARG1(call->data, arg1);
IPC_SET_ARG2(call->data, arg2);
IPC_SET_ARG3(call->data, arg3);
IPC_SET_ARG4(call->data, arg4);
/*
* To achieve deterministic behavior, zero out arguments that are beyond
* the limits of the fast version.
*/
IPC_SET_ARG5(call->data, 0);
rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
 
ipc_answer(&TASK->answerbox, call);
return rc;
}
 
/** Answer an IPC call.
*
* @param callid Hash of the call to be answered.
* @param data Userspace address of call data with the answer.
*
* @return Return 0 on success, otherwise return an error code.
*/
unative_t sys_ipc_answer_slow(unative_t callid, ipc_data_t *data)
{
call_t *call;
ipc_data_t saved_data;
int saveddata = 0;
int rc;
 
/* Do not answer notification callids */
if (callid & IPC_CALLID_NOTIFICATION)
return 0;
 
call = get_call(callid);
if (!call)
return ENOENT;
 
if (answer_need_old(call)) {
memcpy(&saved_data, &call->data, sizeof(call->data));
saveddata = 1;
}
rc = copy_from_uspace(&call->data.args, &data->args,
sizeof(call->data.args));
if (rc != 0)
return rc;
 
rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
ipc_answer(&TASK->answerbox, call);
 
return rc;
}
 
/** Hang up a phone.
*
* @param Phone handle of the phone to be hung up.
*
* @return Return 0 on success or an error code.
*/
unative_t sys_ipc_hangup(int phoneid)
{
phone_t *phone;
 
GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
if (ipc_phone_hangup(phone))
return -1;
 
return 0;
}
 
/** Wait for an incoming IPC call or an answer.
*
* @param calldata Pointer to buffer where the call/answer data is stored.
* @param usec Timeout. See waitq_sleep_timeout() for explanation.
* @param flags Select mode of sleep operation. See waitq_sleep_timeout()
* for explanation.
*
* @return Hash of the call.
* If IPC_CALLID_NOTIFICATION bit is set in the hash, the
* call is a notification. IPC_CALLID_ANSWERED denotes an
* answer.
*/
unative_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec, int flags)
{
call_t *call;
 
restart:
 
#ifdef CONFIG_UDEBUG
udebug_stoppable_begin();
#endif
call = ipc_wait_for_call(&TASK->answerbox, usec,
flags | SYNCH_FLAGS_INTERRUPTIBLE);
 
#ifdef CONFIG_UDEBUG
udebug_stoppable_end();
#endif
if (!call)
return 0;
 
if (call->flags & IPC_CALL_NOTIF) {
ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
/* Set in_phone_hash to the interrupt counter */
call->data.phone = (void *) call->priv;
STRUCT_TO_USPACE(calldata, &call->data);
 
ipc_call_free(call);
return ((unative_t) call) | IPC_CALLID_NOTIFICATION;
}
 
if (call->flags & IPC_CALL_ANSWERED) {
process_answer(call);
 
ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
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);
ipc_call_free(call);
 
return ((unative_t) call) | IPC_CALLID_ANSWERED;
}
 
if (process_request(&TASK->answerbox, call))
goto restart;
 
/* Include phone address('id') of the caller in the request,
* copy whole call->data, not only call->data.args */
if (STRUCT_TO_USPACE(calldata, &call->data)) {
/*
* The callee will not receive this call and no one else has
* a chance to answer it. Reply with the EPARTY error code.
*/
ipc_data_t saved_data;
int saveddata = 0;
 
if (answer_need_old(call)) {
memcpy(&saved_data, &call->data, sizeof(call->data));
saveddata = 1;
}
IPC_SET_RETVAL(call->data, EPARTY);
(void) answer_preprocess(call, saveddata ? &saved_data : NULL);
ipc_answer(&TASK->answerbox, call);
return 0;
}
return (unative_t)call;
}
 
/** Connect an IRQ handler to a task.
*
* @param inr IRQ number.
* @param devno Device number.
* @param method Method to be associated with the notification.
* @param ucode Uspace pointer to the top-half pseudocode.
*
* @return EPERM or a return code returned by ipc_irq_register().
*/
unative_t sys_ipc_register_irq(inr_t inr, devno_t devno, unative_t method,
irq_code_t *ucode)
{
if (!(cap_get(TASK) & CAP_IRQ_REG))
return EPERM;
 
return ipc_irq_register(&TASK->answerbox, inr, devno, method, ucode);
}
 
/** Disconnect an IRQ handler from a task.
*
* @param inr IRQ number.
* @param devno Device number.
*
* @return Zero on success or EPERM on error..
*/
unative_t sys_ipc_unregister_irq(inr_t inr, devno_t devno)
{
if (!(cap_get(TASK) & CAP_IRQ_REG))
return EPERM;
 
ipc_irq_unregister(&TASK->answerbox, inr, devno);
 
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
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ipc/ipcrsc.c
0,0 → 1,234
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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
*/
 
/* IPC resources management
*
* The goal of this source code is to properly manage IPC resources and allow
* straight and clean clean-up procedure upon task termination.
*
* The pattern of usage of the resources is:
* - allocate empty phone slot, connect | deallocate slot
* - disconnect connected phone (some messages might be on the fly)
* - find phone in slot and send a message using phone
* - answer message to phone
* - hangup phone (the caller has hung up)
* - hangup phone (the answerbox is exiting)
*
* Locking strategy
*
* - To use a phone, disconnect a phone etc., the phone must be first locked and
* then checked that it is connected
* - To connect an allocated phone it need not be locked (assigning pointer is
* atomic on all platforms)
*
* - To find an empty phone slot, the TASK must be locked
* - To answer a message, the answerbox must be locked
* - The locking of phone and answerbox is done at the ipc_ level.
* It is perfectly correct to pass unconnected phone to these functions and
* proper reply will be generated.
*
* Locking order
*
* - first phone, then answerbox
* + Easy locking on calls
* - Very hard traversing list of phones when disconnecting because the phones
* may disconnect during traversal of list of connected phones. The only
* possibility is try_lock with restart of list traversal.
*
* Destroying is less frequent, this approach is taken.
*
* Phone call
*
* *** Connect_me_to ***
* The caller sends IPC_M_CONNECT_ME_TO to an answerbox. The server receives
* 'phoneid' of the connecting phone as an ARG5. If it answers with RETVAL=0,
* the phonecall is accepted, otherwise it is refused.
*
* *** Connect_to_me ***
* The caller sends IPC_M_CONNECT_TO_ME.
* The server receives an automatically opened phoneid. If it accepts
* (RETVAL=0), it can use the phoneid immediately.
* Possible race condition can arise, when the client receives messages from new
* connection before getting response for connect_to_me message. Userspace
* should implement handshake protocol that would control it.
*
* Phone hangup
*
* *** The caller hangs up (sys_ipc_hangup) ***
* - The phone is disconnected (no more messages can be sent over this phone),
* all in-progress messages are correctly handled. The answerbox receives
* IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async
* calls are answered, the phone is deallocated.
*
* *** The answerbox hangs up (ipc_answer(EHANGUP))
* - The phone is disconnected. EHANGUP response code is sent
* to the calling task. All new calls through this phone
* get a EHUNGUP error code, the task is expected to
* send an sys_ipc_hangup after cleaning up its internal structures.
*
* Call forwarding
*
* The call can be forwarded, so that the answer to call is passed directly
* to the original sender. However, this poses special problems regarding
* routing of hangup messages.
*
* sys_ipc_hangup -> IPC_M_PHONE_HUNGUP
* - this message CANNOT be forwarded
*
* EHANGUP during forward
* - The *forwarding* phone will be closed, EFORWARD is sent to receiver.
*
* EHANGUP, ENOENT during forward
* - EFORWARD is sent to the receiver, ipc_forward returns error code EFORWARD
*
* Cleanup strategy
*
* 1) Disconnect all our phones ('ipc_phone_hangup').
*
* 2) Disconnect all phones connected to answerbox.
*
* 3) Answer all messages in 'calls' and 'dispatched_calls' queues with
* appropriate error code (EHANGUP, EFORWARD).
*
* 4) Wait for all async answers to arrive and dispose of them.
*
*/
 
#include <synch/spinlock.h>
#include <ipc/ipc.h>
#include <arch.h>
#include <proc/task.h>
#include <ipc/ipcrsc.h>
#include <debug.h>
 
/** Find call_t * in call table according to callid.
*
* @todo Some speedup (hash table?)
*
* @param callid Userspace hash of the call. Currently it is the call
* structure kernel address.
*
* @return NULL on not found, otherwise pointer to the call
* structure.
*/
call_t *get_call(unative_t callid)
{
link_t *lst;
call_t *call, *result = NULL;
 
spinlock_lock(&TASK->answerbox.lock);
for (lst = TASK->answerbox.dispatched_calls.next;
lst != &TASK->answerbox.dispatched_calls; lst = lst->next) {
call = list_get_instance(lst, call_t, link);
if ((unative_t) call == callid) {
result = call;
break;
}
}
spinlock_unlock(&TASK->answerbox.lock);
return result;
}
 
/** Allocate new phone slot in the specified task.
*
* @param t Task for which to allocate a new phone.
*
* @return New phone handle or -1 if the phone handle limit is
* exceeded.
*/
int phone_alloc(task_t *t)
{
int i;
 
spinlock_lock(&t->lock);
for (i = 0; i < IPC_MAX_PHONES; i++) {
if (t->phones[i].state == IPC_PHONE_HUNGUP &&
atomic_get(&t->phones[i].active_calls) == 0)
t->phones[i].state = IPC_PHONE_FREE;
 
if (t->phones[i].state == IPC_PHONE_FREE) {
t->phones[i].state = IPC_PHONE_CONNECTING;
break;
}
}
spinlock_unlock(&t->lock);
 
if (i == IPC_MAX_PHONES)
return -1;
 
return i;
}
 
/** Mark a phone structure free.
*
* @param phone Phone structure to be marked free.
*/
static void phone_deallocp(phone_t *phone)
{
ASSERT(phone->state == IPC_PHONE_CONNECTING);
/* atomic operation */
phone->state = IPC_PHONE_FREE;
}
 
/** Free slot from a disconnected phone.
*
* All already sent messages will be correctly processed.
*
* @param phoneid Phone handle of the phone to be freed.
*/
void phone_dealloc(int phoneid)
{
phone_deallocp(&TASK->phones[phoneid]);
}
 
/** Connect phone to a given answerbox.
*
* @param phoneid Phone handle to be connected.
* @param box Answerbox to which to connect the phone handle.
*
* The procedure _enforces_ that the user first marks the phone
* busy (e.g. via phone_alloc) and then connects the phone, otherwise
* race condition may appear.
*/
void phone_connect(int phoneid, answerbox_t *box)
{
phone_t *phone = &TASK->phones[phoneid];
ASSERT(phone->state == IPC_PHONE_CONNECTING);
ipc_phone_connect(phone, box);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ipc/kbox.c
0,0 → 1,282
/*
* 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>
#include <print.h>
 
void ipc_kbox_cleanup(void)
{
ipl_t ipl;
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->kb.box, have_kb_thread);
 
/*
* If the task was being debugged, clean up debugging session.
* This is necessarry as slamming the phones won't force
* kbox thread to clean it up since sender != debugger.
*/
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
udebug_task_cleanup(TASK);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
if (have_kb_thread) {
LOG("Join kb.thread.");
thread_join(TASK->kb.thread);
thread_detach(TASK->kb.thread);
LOG("...join done.");
TASK->kb.thread = NULL;
}
 
/* Answer all messages in 'calls' and 'dispatched_calls' queues. */
spinlock_lock(&TASK->kb.box.lock);
ipc_cleanup_call_list(&TASK->kb.box.dispatched_calls);
ipc_cleanup_call_list(&TASK->kb.box.calls);
spinlock_unlock(&TASK->kb.box.lock);
}
 
/** Handle hangup message in kbox.
*
* @param call The IPC_M_PHONE_HUNGUP call structure.
* @param last Output, the function stores @c true here if
* this was the last phone, @c false otherwise.
**/
static void kbox_proc_phone_hungup(call_t *call, bool *last)
{
ipl_t ipl;
 
/* Was it our debugger, who hung up? */
if (call->sender == TASK->udebug.debugger) {
/* Terminate debugging session (if any). */
LOG("Terminate debugging session.");
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
udebug_task_cleanup(TASK);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
} else {
LOG("Was not debugger.");
}
 
LOG("Continue with hangup message.");
IPC_SET_RETVAL(call->data, 0);
ipc_answer(&TASK->kb.box, call);
 
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
spinlock_lock(&TASK->kb.box.lock);
if (list_empty(&TASK->kb.box.connected_phones)) {
/*
* Last phone has been disconnected. Detach this thread so it
* gets freed and signal to the caller.
*/
 
/* Only detach kbox thread unless already terminating. */
mutex_lock(&TASK->kb.cleanup_lock);
if (&TASK->kb.finished == false) {
/* Detach kbox thread so it gets freed from memory. */
thread_detach(TASK->kb.thread);
TASK->kb.thread = NULL;
}
mutex_unlock(&TASK->kb.cleanup_lock);
 
LOG("Phone list is empty.");
*last = true;
} else {
*last = false;
}
 
spinlock_unlock(&TASK->kb.box.lock);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
}
 
/** Implementing function for the kbox thread.
*
* This function listens for debug requests. It terminates
* when all phones are disconnected from the kbox.
*
* @param arg Ignored.
*/
static void kbox_thread_proc(void *arg)
{
call_t *call;
bool done;
 
(void)arg;
LOG("Starting.");
done = false;
 
while (!done) {
call = ipc_wait_for_call(&TASK->kb.box, SYNCH_NO_TIMEOUT,
SYNCH_FLAGS_NONE);
 
if (call == NULL)
continue; /* Try again. */
 
switch (IPC_GET_METHOD(call->data)) {
 
case IPC_M_DEBUG_ALL:
/* Handle debug call. */
udebug_call_receive(call);
break;
 
case IPC_M_PHONE_HUNGUP:
/*
* Process the hangup call. If this was the last
* phone, done will be set to true and the
* while loop will terminate.
*/
kbox_proc_phone_hungup(call, &done);
break;
 
default:
/* Ignore */
break;
}
}
 
LOG("Exiting.");
}
 
 
/**
* 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(TASK);
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->kb.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;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ipc/ipc.c
0,0 → 1,701
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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
*/
 
/* Lock ordering
*
* First the answerbox, then the phone.
*/
 
#include <synch/synch.h>
#include <synch/spinlock.h>
#include <synch/mutex.h>
#include <synch/waitq.h>
#include <synch/synch.h>
#include <ipc/ipc.h>
#include <ipc/kbox.h>
#include <ipc/event.h>
#include <errno.h>
#include <mm/slab.h>
#include <arch.h>
#include <proc/task.h>
#include <memstr.h>
#include <debug.h>
#include <print.h>
#include <console/console.h>
#include <proc/thread.h>
#include <arch/interrupt.h>
#include <ipc/irq.h>
 
/** Open channel that is assigned automatically to new tasks */
answerbox_t *ipc_phone_0 = NULL;
 
static slab_cache_t *ipc_call_slab;
 
/** Initialize a call structure.
*
* @param call Call structure to be initialized.
*/
static void _ipc_call_init(call_t *call)
{
memsetb(call, sizeof(*call), 0);
call->callerbox = &TASK->answerbox;
call->sender = TASK;
call->buffer = NULL;
}
 
/** Allocate and initialize a call structure.
*
* The call is initialized, so that the reply will be directed to
* TASK->answerbox.
*
* @param flags Parameters for slab_alloc (e.g FRAME_ATOMIC).
*
* @return If flags permit it, return NULL, or initialized kernel
* call structure.
*/
call_t *ipc_call_alloc(int flags)
{
call_t *call;
 
call = slab_alloc(ipc_call_slab, flags);
if (call)
_ipc_call_init(call);
 
return call;
}
 
/** Initialize a statically allocated call structure.
*
* @param call Statically allocated kernel call structure to be
* initialized.
*/
void ipc_call_static_init(call_t *call)
{
_ipc_call_init(call);
call->flags |= IPC_CALL_STATIC_ALLOC;
}
 
/** Deallocate a call structure.
*
* @param call Call structure to be freed.
*/
void ipc_call_free(call_t *call)
{
ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
/* Check to see if we have data in the IPC_M_DATA_SEND buffer. */
if (call->buffer)
free(call->buffer);
slab_free(ipc_call_slab, call);
}
 
/** Initialize an answerbox structure.
*
* @param box Answerbox structure to be initialized.
* @param task Task to which the answerbox belongs.
*/
void ipc_answerbox_init(answerbox_t *box, task_t *task)
{
spinlock_initialize(&box->lock, "ipc_box_lock");
spinlock_initialize(&box->irq_lock, "ipc_box_irqlock");
waitq_initialize(&box->wq);
list_initialize(&box->connected_phones);
list_initialize(&box->calls);
list_initialize(&box->dispatched_calls);
list_initialize(&box->answers);
list_initialize(&box->irq_notifs);
list_initialize(&box->irq_head);
box->task = task;
}
 
/** Connect a phone to an answerbox.
*
* @param phone Initialized phone structure.
* @param box Initialized answerbox structure.
*/
void ipc_phone_connect(phone_t *phone, answerbox_t *box)
{
mutex_lock(&phone->lock);
 
phone->state = IPC_PHONE_CONNECTED;
phone->callee = box;
 
spinlock_lock(&box->lock);
list_append(&phone->link, &box->connected_phones);
spinlock_unlock(&box->lock);
 
mutex_unlock(&phone->lock);
}
 
/** Initialize a phone structure.
*
* @param phone Phone structure to be initialized.
*/
void ipc_phone_init(phone_t *phone)
{
mutex_initialize(&phone->lock, MUTEX_PASSIVE);
phone->callee = NULL;
phone->state = IPC_PHONE_FREE;
atomic_set(&phone->active_calls, 0);
}
 
/** Helper function to facilitate synchronous calls.
*
* @param phone Destination kernel phone structure.
* @param request Call structure with request.
*
* @return EOK on success or EINTR if the sleep was interrupted.
*/
int ipc_call_sync(phone_t *phone, call_t *request)
{
answerbox_t sync_box;
 
ipc_answerbox_init(&sync_box, TASK);
 
/* We will receive data in a special box. */
request->callerbox = &sync_box;
 
ipc_call(phone, request);
if (!ipc_wait_for_call(&sync_box, SYNCH_NO_TIMEOUT,
SYNCH_FLAGS_INTERRUPTIBLE))
return EINTR;
return EOK;
}
 
/** Answer a message which was not dispatched and is not listed in any queue.
*
* @param call Call structure to be answered.
*/
static void _ipc_answer_free_call(call_t *call)
{
answerbox_t *callerbox = call->callerbox;
 
call->flags |= IPC_CALL_ANSWERED;
 
if (call->flags & IPC_CALL_FORWARDED) {
if (call->caller_phone) {
/* Demasquerade the caller phone. */
call->data.phone = call->caller_phone;
}
}
 
spinlock_lock(&callerbox->lock);
list_append(&call->link, &callerbox->answers);
spinlock_unlock(&callerbox->lock);
waitq_wakeup(&callerbox->wq, WAKEUP_FIRST);
}
 
/** Answer a message which is in a callee queue.
*
* @param box Answerbox that is answering the message.
* @param call Modified request that is being sent back.
*/
void ipc_answer(answerbox_t *box, call_t *call)
{
/* Remove from active box */
spinlock_lock(&box->lock);
list_remove(&call->link);
spinlock_unlock(&box->lock);
/* Send back answer */
_ipc_answer_free_call(call);
}
 
/** Simulate sending back a message.
*
* Most errors are better handled by forming a normal backward
* message and sending it as a normal answer.
*
* @param phone Phone structure the call should appear to come from.
* @param call Call structure to be answered.
* @param err Return value to be used for the answer.
*/
void ipc_backsend_err(phone_t *phone, call_t *call, unative_t err)
{
call->data.phone = phone;
atomic_inc(&phone->active_calls);
IPC_SET_RETVAL(call->data, err);
_ipc_answer_free_call(call);
}
 
/** Unsafe unchecking version of ipc_call.
*
* @param phone Phone structure the call comes from.
* @param box Destination answerbox structure.
* @param call Call structure with request.
*/
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call)
{
if (!(call->flags & IPC_CALL_FORWARDED)) {
atomic_inc(&phone->active_calls);
call->data.phone = phone;
}
 
spinlock_lock(&box->lock);
list_append(&call->link, &box->calls);
spinlock_unlock(&box->lock);
waitq_wakeup(&box->wq, WAKEUP_FIRST);
}
 
/** Send an asynchronous request using a phone to an answerbox.
*
* @param phone Phone structure the call comes from and which is
* connected to the destination answerbox.
* @param call Call structure with request.
*
* @return Return 0 on success, ENOENT on error.
*/
int ipc_call(phone_t *phone, call_t *call)
{
answerbox_t *box;
 
mutex_lock(&phone->lock);
if (phone->state != IPC_PHONE_CONNECTED) {
mutex_unlock(&phone->lock);
if (call->flags & IPC_CALL_FORWARDED) {
IPC_SET_RETVAL(call->data, EFORWARD);
_ipc_answer_free_call(call);
} else {
if (phone->state == IPC_PHONE_HUNGUP)
ipc_backsend_err(phone, call, EHANGUP);
else
ipc_backsend_err(phone, call, ENOENT);
}
return ENOENT;
}
box = phone->callee;
_ipc_call(phone, box, call);
mutex_unlock(&phone->lock);
return 0;
}
 
/** Disconnect phone from answerbox.
*
* This call leaves the phone in the HUNGUP state. The change to 'free' is done
* lazily later.
*
* @param phone Phone structure to be hung up.
*
* @return Return 0 if the phone is disconnected.
* Return -1 if the phone was already disconnected.
*/
int ipc_phone_hangup(phone_t *phone)
{
answerbox_t *box;
call_t *call;
mutex_lock(&phone->lock);
if (phone->state == IPC_PHONE_FREE ||
phone->state == IPC_PHONE_HUNGUP ||
phone->state == IPC_PHONE_CONNECTING) {
mutex_unlock(&phone->lock);
return -1;
}
box = phone->callee;
if (phone->state != IPC_PHONE_SLAMMED) {
/* Remove myself from answerbox */
spinlock_lock(&box->lock);
list_remove(&phone->link);
spinlock_unlock(&box->lock);
 
call = ipc_call_alloc(0);
IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
call->flags |= IPC_CALL_DISCARD_ANSWER;
_ipc_call(phone, box, call);
}
 
phone->state = IPC_PHONE_HUNGUP;
mutex_unlock(&phone->lock);
 
return 0;
}
 
/** Forwards call from one answerbox to another one.
*
* @param call Call structure to be redirected.
* @param newphone Phone structure to target answerbox.
* @param oldbox Old answerbox structure.
* @param mode Flags that specify mode of the forward operation.
*
* @return Return 0 if forwarding succeeded or an error code if
* there was error.
*
* The return value serves only as an information for the forwarder,
* the original caller is notified automatically with EFORWARD.
*/
int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox, int mode)
{
spinlock_lock(&oldbox->lock);
list_remove(&call->link);
spinlock_unlock(&oldbox->lock);
 
if (mode & IPC_FF_ROUTE_FROM_ME) {
if (!call->caller_phone)
call->caller_phone = call->data.phone;
call->data.phone = newphone;
}
 
return ipc_call(newphone, call);
}
 
 
/** Wait for a phone call.
*
* @param box Answerbox expecting the call.
* @param usec Timeout in microseconds. See documentation for
* waitq_sleep_timeout() for decription of its special
* meaning.
* @param flags Select mode of sleep operation. See documentation for
* waitq_sleep_timeout() for description of its special
* meaning.
* @return Recived call structure or NULL.
*
* To distinguish between a call and an answer, have a look at call->flags.
*/
call_t *ipc_wait_for_call(answerbox_t *box, uint32_t usec, int flags)
{
call_t *request;
ipl_t ipl;
int rc;
 
restart:
rc = waitq_sleep_timeout(&box->wq, usec, flags);
if (SYNCH_FAILED(rc))
return NULL;
spinlock_lock(&box->lock);
if (!list_empty(&box->irq_notifs)) {
ipl = interrupts_disable();
spinlock_lock(&box->irq_lock);
 
request = list_get_instance(box->irq_notifs.next, call_t, link);
list_remove(&request->link);
 
spinlock_unlock(&box->irq_lock);
interrupts_restore(ipl);
} else if (!list_empty(&box->answers)) {
/* Handle asynchronous answers */
request = list_get_instance(box->answers.next, call_t, link);
list_remove(&request->link);
atomic_dec(&request->data.phone->active_calls);
} else if (!list_empty(&box->calls)) {
/* Handle requests */
request = list_get_instance(box->calls.next, call_t, link);
list_remove(&request->link);
/* Append request to dispatch queue */
list_append(&request->link, &box->dispatched_calls);
} else {
/* This can happen regularly after ipc_cleanup */
spinlock_unlock(&box->lock);
goto restart;
}
spinlock_unlock(&box->lock);
return request;
}
 
/** Answer all calls from list with EHANGUP answer.
*
* @param lst Head of the list to be cleaned up.
*/
void ipc_cleanup_call_list(link_t *lst)
{
call_t *call;
 
while (!list_empty(lst)) {
call = list_get_instance(lst->next, call_t, link);
if (call->buffer)
free(call->buffer);
list_remove(&call->link);
 
IPC_SET_RETVAL(call->data, EHANGUP);
_ipc_answer_free_call(call);
}
}
 
/** Disconnects all phones connected to an answerbox.
*
* @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_answerbox_slam_phones(answerbox_t *box, bool notify_box)
{
phone_t *phone;
DEADLOCK_PROBE_INIT(p_phonelck);
ipl_t ipl;
call_t *call;
 
call = notify_box ? ipc_call_alloc(0) : NULL;
 
/* Disconnect all phones connected to our answerbox */
restart_phones:
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(&box->lock);
interrupts_restore(ipl);
DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
goto restart_phones;
}
/* Disconnect phone */
ASSERT(phone->state == IPC_PHONE_CONNECTED);
 
list_remove(&phone->link);
phone->state = IPC_PHONE_SLAMMED;
 
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]);
 
/* Unsubscribe from any event notifications. */
event_cleanup_answerbox(&TASK->answerbox);
 
/* 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);
/* Wait for all async answers to arrive */
while (1) {
/* Go through all phones, until all are FREE... */
/* Locking not needed, no one else should modify
* it, when we are in cleanup */
for (i = 0; i < IPC_MAX_PHONES; i++) {
if (TASK->phones[i].state == IPC_PHONE_HUNGUP &&
atomic_get(&TASK->phones[i].active_calls) == 0)
TASK->phones[i].state = IPC_PHONE_FREE;
/* Just for sure, we might have had some
* IPC_PHONE_CONNECTING phones */
if (TASK->phones[i].state == IPC_PHONE_CONNECTED)
ipc_phone_hangup(&TASK->phones[i]);
/* If the hangup succeeded, it has sent a HANGUP
* message, the IPC is now in HUNGUP state, we
* wait for the reply to come */
if (TASK->phones[i].state != IPC_PHONE_FREE)
break;
}
/* Voila, got into cleanup */
if (i == IPC_MAX_PHONES)
break;
call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT,
SYNCH_FLAGS_NONE);
ASSERT((call->flags & IPC_CALL_ANSWERED) ||
(call->flags & IPC_CALL_NOTIF));
ASSERT(!(call->flags & IPC_CALL_STATIC_ALLOC));
/*
* 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);
}
}
 
 
/** Initilize IPC subsystem */
void ipc_init(void)
{
ipc_call_slab = slab_cache_create("ipc_call", sizeof(call_t), 0, NULL,
NULL, 0);
}
 
 
/** List answerbox contents.
*
* @param taskid Task ID.
*/
void ipc_print_task(task_id_t taskid)
{
task_t *task;
int i;
call_t *call;
link_t *tmp;
spinlock_lock(&tasks_lock);
task = task_find_by_id(taskid);
if (task)
spinlock_lock(&task->lock);
spinlock_unlock(&tasks_lock);
if (!task)
return;
 
/* Print opened phones & details */
printf("PHONE:\n");
for (i = 0; i < IPC_MAX_PHONES; i++) {
if (SYNCH_FAILED(mutex_trylock(&task->phones[i].lock))) {
printf("%d: mutex busy\n", i);
continue;
}
if (task->phones[i].state != IPC_PHONE_FREE) {
printf("%d: ", i);
switch (task->phones[i].state) {
case IPC_PHONE_CONNECTING:
printf("connecting ");
break;
case IPC_PHONE_CONNECTED:
printf("connected to: %p ",
task->phones[i].callee);
break;
case IPC_PHONE_SLAMMED:
printf("slammed by: %p ",
task->phones[i].callee);
break;
case IPC_PHONE_HUNGUP:
printf("hung up - was: %p ",
task->phones[i].callee);
break;
default:
break;
}
printf("active: %ld\n",
atomic_get(&task->phones[i].active_calls));
}
mutex_unlock(&task->phones[i].lock);
}
 
 
/* Print answerbox - calls */
spinlock_lock(&task->answerbox.lock);
printf("ABOX - CALLS:\n");
for (tmp = task->answerbox.calls.next; tmp != &task->answerbox.calls;
tmp = tmp->next) {
call = list_get_instance(tmp, call_t, link);
printf("Callid: %p Srctask:%" PRIu64 " M:%" PRIun
" A1:%" PRIun " A2:%" PRIun " A3:%" PRIun
" A4:%" PRIun " A5:%" PRIun " Flags:%x\n", call,
call->sender->taskid,
IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
call->flags);
}
/* Print answerbox - calls */
printf("ABOX - DISPATCHED CALLS:\n");
for (tmp = task->answerbox.dispatched_calls.next;
tmp != &task->answerbox.dispatched_calls;
tmp = tmp->next) {
call = list_get_instance(tmp, call_t, link);
printf("Callid: %p Srctask:%" PRIu64 " M:%" PRIun
" A1:%" PRIun " A2:%" PRIun " A3:%" PRIun
" A4:%" PRIun " A5:%" PRIun " Flags:%x\n", call,
call->sender->taskid,
IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
call->flags);
}
/* Print answerbox - calls */
printf("ABOX - ANSWERS:\n");
for (tmp = task->answerbox.answers.next;
tmp != &task->answerbox.answers;
tmp = tmp->next) {
call = list_get_instance(tmp, call_t, link);
printf("Callid:%p M:%" PRIun " A1:%" PRIun " A2:%" PRIun
" A3:%" PRIun " A4:%" PRIun " A5:%" PRIun " Flags:%x\n",
call, IPC_GET_METHOD(call->data), IPC_GET_ARG1(call->data),
IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data),
IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data),
call->flags);
}
 
spinlock_unlock(&task->answerbox.lock);
spinlock_unlock(&task->lock);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ipc/irq.c
0,0 → 1,503
/*
* Copyright (c) 2006 Ondrej Palkovsky
* Copyright (c) 2006 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 genericipc
* @{
*/
/**
* @file
* @brief IRQ notification framework.
*
* This framework allows applications to register to receive a notification
* when interrupt is detected. The application may provide a simple 'top-half'
* handler as part of its registration, which can perform simple operations
* (read/write port/memory, add information to notification ipc message).
*
* The structure of a notification message is as follows:
* - METHOD: method as registered by the SYS_IPC_REGISTER_IRQ syscall
* - ARG1: payload modified by a 'top-half' handler
* - ARG2: payload modified by a 'top-half' handler
* - ARG3: payload modified by a 'top-half' handler
* - ARG4: payload modified by a 'top-half' handler
* - ARG5: payload modified by a 'top-half' handler
* - in_phone_hash: interrupt counter (may be needed to assure correct order
* in multithreaded drivers)
*
* Note on synchronization for ipc_irq_register(), ipc_irq_unregister(),
* ipc_irq_cleanup() and IRQ handlers:
*
* By always taking all of the uspace IRQ hash table lock, IRQ structure lock
* and answerbox lock, we can rule out race conditions between the
* registration functions and also the cleanup function. Thus the observer can
* either see the IRQ structure present in both the hash table and the
* answerbox list or absent in both. Views in which the IRQ structure would be
* linked in the hash table but not in the answerbox list, or vice versa, are
* not possible.
*
* By always taking the hash table lock and the IRQ structure lock, we can
* rule out a scenario in which we would free up an IRQ structure, which is
* still referenced by, for example, an IRQ handler. The locking scheme forces
* us to lock the IRQ structure only after any progressing IRQs on that
* structure are finished. Because we hold the hash table lock, we prevent new
* IRQs from taking new references to the IRQ structure.
*/
 
#include <arch.h>
#include <mm/slab.h>
#include <errno.h>
#include <ddi/irq.h>
#include <ipc/ipc.h>
#include <ipc/irq.h>
#include <syscall/copy.h>
#include <console/console.h>
#include <print.h>
 
/** Free the top-half pseudocode.
*
* @param code Pointer to the top-half pseudocode.
*/
static void code_free(irq_code_t *code)
{
if (code) {
free(code->cmds);
free(code);
}
}
 
/** Copy the top-half pseudocode from userspace into the kernel.
*
* @param ucode Userspace address of the top-half pseudocode.
*
* @return Kernel address of the copied pseudocode.
*/
static irq_code_t *code_from_uspace(irq_code_t *ucode)
{
irq_code_t *code;
irq_cmd_t *ucmds;
int rc;
 
code = malloc(sizeof(*code), 0);
rc = copy_from_uspace(code, ucode, sizeof(*code));
if (rc != 0) {
free(code);
return NULL;
}
if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
free(code);
return NULL;
}
ucmds = code->cmds;
code->cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
rc = copy_from_uspace(code->cmds, ucmds,
sizeof(code->cmds[0]) * code->cmdcount);
if (rc != 0) {
free(code->cmds);
free(code);
return NULL;
}
 
return code;
}
 
/** Register an answerbox as a receiving end for IRQ notifications.
*
* @param box Receiving answerbox.
* @param inr IRQ number.
* @param devno Device number.
* @param method Method to be associated with the notification.
* @param ucode Uspace pointer to top-half pseudocode.
*
* @return EBADMEM, ENOENT or EEXISTS on failure or 0 on success.
*
*/
int ipc_irq_register(answerbox_t *box, inr_t inr, devno_t devno,
unative_t method, irq_code_t *ucode)
{
ipl_t ipl;
irq_code_t *code;
irq_t *irq;
link_t *hlp;
unative_t key[] = {
(unative_t) inr,
(unative_t) devno
};
if (ucode) {
code = code_from_uspace(ucode);
if (!code)
return EBADMEM;
} else {
code = NULL;
}
/*
* Allocate and populate the IRQ structure.
*/
irq = malloc(sizeof(irq_t), 0);
irq_initialize(irq);
irq->devno = devno;
irq->inr = inr;
irq->claim = ipc_irq_top_half_claim;
irq->handler = ipc_irq_top_half_handler;
irq->notif_cfg.notify = true;
irq->notif_cfg.answerbox = box;
irq->notif_cfg.method = method;
irq->notif_cfg.code = code;
irq->notif_cfg.counter = 0;
/*
* Enlist the IRQ structure in the uspace IRQ hash table and the
* answerbox's list.
*/
ipl = interrupts_disable();
spinlock_lock(&irq_uspace_hash_table_lock);
hlp = hash_table_find(&irq_uspace_hash_table, key);
if (hlp) {
irq_t *hirq __attribute__((unused))
= hash_table_get_instance(hlp, irq_t, link);
/* hirq is locked */
spinlock_unlock(&hirq->lock);
code_free(code);
spinlock_unlock(&irq_uspace_hash_table_lock);
free(irq);
interrupts_restore(ipl);
return EEXISTS;
}
spinlock_lock(&irq->lock); /* Not really necessary, but paranoid */
spinlock_lock(&box->irq_lock);
hash_table_insert(&irq_uspace_hash_table, key, &irq->link);
list_append(&irq->notif_cfg.link, &box->irq_head);
spinlock_unlock(&box->irq_lock);
spinlock_unlock(&irq->lock);
spinlock_unlock(&irq_uspace_hash_table_lock);
interrupts_restore(ipl);
return EOK;
}
 
/** Unregister task from IRQ notification.
*
* @param box Answerbox associated with the notification.
* @param inr IRQ number.
* @param devno Device number.
*/
int ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
{
ipl_t ipl;
unative_t key[] = {
(unative_t) inr,
(unative_t) devno
};
link_t *lnk;
irq_t *irq;
 
ipl = interrupts_disable();
spinlock_lock(&irq_uspace_hash_table_lock);
lnk = hash_table_find(&irq_uspace_hash_table, key);
if (!lnk) {
spinlock_unlock(&irq_uspace_hash_table_lock);
interrupts_restore(ipl);
return ENOENT;
}
irq = hash_table_get_instance(lnk, irq_t, link);
/* irq is locked */
spinlock_lock(&box->irq_lock);
ASSERT(irq->notif_cfg.answerbox == box);
/* Free up the pseudo code and associated structures. */
code_free(irq->notif_cfg.code);
 
/* Remove the IRQ from the answerbox's list. */
list_remove(&irq->notif_cfg.link);
 
/*
* We need to drop the IRQ lock now because hash_table_remove() will try
* to reacquire it. That basically violates the natural locking order,
* but a deadlock in hash_table_remove() is prevented by the fact that
* we already held the IRQ lock and didn't drop the hash table lock in
* the meantime.
*/
spinlock_unlock(&irq->lock);
 
/* Remove the IRQ from the uspace IRQ hash table. */
hash_table_remove(&irq_uspace_hash_table, key, 2);
spinlock_unlock(&irq_uspace_hash_table_lock);
spinlock_unlock(&box->irq_lock);
/* Free up the IRQ structure. */
free(irq);
interrupts_restore(ipl);
return EOK;
}
 
 
/** Disconnect all IRQ notifications from an answerbox.
*
* This function is effective because the answerbox contains
* list of all irq_t structures that are registered to
* send notifications to it.
*
* @param box Answerbox for which we want to carry out the cleanup.
*/
void ipc_irq_cleanup(answerbox_t *box)
{
ipl_t ipl;
loop:
ipl = interrupts_disable();
spinlock_lock(&irq_uspace_hash_table_lock);
spinlock_lock(&box->irq_lock);
while (box->irq_head.next != &box->irq_head) {
link_t *cur = box->irq_head.next;
irq_t *irq;
DEADLOCK_PROBE_INIT(p_irqlock);
unative_t key[2];
irq = list_get_instance(cur, irq_t, notif_cfg.link);
if (!spinlock_trylock(&irq->lock)) {
/*
* Avoid deadlock by trying again.
*/
spinlock_unlock(&box->irq_lock);
spinlock_unlock(&irq_uspace_hash_table_lock);
interrupts_restore(ipl);
DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
goto loop;
}
key[0] = irq->inr;
key[1] = irq->devno;
ASSERT(irq->notif_cfg.answerbox == box);
/* Unlist from the answerbox. */
list_remove(&irq->notif_cfg.link);
/* Free up the pseudo code and associated structures. */
code_free(irq->notif_cfg.code);
/*
* We need to drop the IRQ lock now because hash_table_remove()
* will try to reacquire it. That basically violates the natural
* locking order, but a deadlock in hash_table_remove() is
* prevented by the fact that we already held the IRQ lock and
* didn't drop the hash table lock in the meantime.
*/
spinlock_unlock(&irq->lock);
/* Remove from the hash table. */
hash_table_remove(&irq_uspace_hash_table, key, 2);
free(irq);
}
spinlock_unlock(&box->irq_lock);
spinlock_unlock(&irq_uspace_hash_table_lock);
interrupts_restore(ipl);
}
 
/** Add a call to the proper answerbox queue.
*
* Assume irq->lock is locked.
*
* @param irq IRQ structure referencing the target answerbox.
* @param call IRQ notification call.
*/
static void send_call(irq_t *irq, call_t *call)
{
spinlock_lock(&irq->notif_cfg.answerbox->irq_lock);
list_append(&call->link, &irq->notif_cfg.answerbox->irq_notifs);
spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock);
waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
}
 
/** Apply the top-half pseudo code to find out whether to accept the IRQ or not.
*
* @param irq IRQ structure.
*
* @return IRQ_ACCEPT if the interrupt is accepted by the
* pseudocode. IRQ_DECLINE otherwise.
*/
irq_ownership_t ipc_irq_top_half_claim(irq_t *irq)
{
unsigned int i;
unative_t dstval;
irq_code_t *code = irq->notif_cfg.code;
unative_t *scratch = irq->notif_cfg.scratch;
 
if (!irq->notif_cfg.notify)
return IRQ_DECLINE;
if (!code)
return IRQ_DECLINE;
for (i = 0; i < code->cmdcount; i++) {
unsigned int srcarg = code->cmds[i].srcarg;
unsigned int dstarg = code->cmds[i].dstarg;
if (srcarg >= IPC_CALL_LEN)
break;
if (dstarg >= IPC_CALL_LEN)
break;
switch (code->cmds[i].cmd) {
case CMD_PIO_READ_8:
dstval = pio_read_8((ioport8_t *) code->cmds[i].addr);
if (dstarg)
scratch[dstarg] = dstval;
break;
case CMD_PIO_READ_16:
dstval = pio_read_16((ioport16_t *) code->cmds[i].addr);
if (dstarg)
scratch[dstarg] = dstval;
break;
case CMD_PIO_READ_32:
dstval = pio_read_32((ioport32_t *) code->cmds[i].addr);
if (dstarg)
scratch[dstarg] = dstval;
break;
case CMD_PIO_WRITE_8:
pio_write_8((ioport8_t *) code->cmds[i].addr,
(uint8_t) code->cmds[i].value);
break;
case CMD_PIO_WRITE_16:
pio_write_16((ioport16_t *) code->cmds[i].addr,
(uint16_t) code->cmds[i].value);
break;
case CMD_PIO_WRITE_32:
pio_write_32((ioport32_t *) code->cmds[i].addr,
(uint32_t) code->cmds[i].value);
break;
case CMD_BTEST:
if (srcarg && dstarg) {
dstval = scratch[srcarg] & code->cmds[i].value;
scratch[dstarg] = dstval;
}
break;
case CMD_PREDICATE:
if (srcarg && !scratch[srcarg]) {
i += code->cmds[i].value;
continue;
}
break;
case CMD_ACCEPT:
return IRQ_ACCEPT;
break;
case CMD_DECLINE:
default:
return IRQ_DECLINE;
}
}
return IRQ_DECLINE;
}
 
 
/* IRQ top-half handler.
*
* We expect interrupts to be disabled and the irq->lock already held.
*
* @param irq IRQ structure.
*/
void ipc_irq_top_half_handler(irq_t *irq)
{
ASSERT(irq);
 
if (irq->notif_cfg.answerbox) {
call_t *call;
 
call = ipc_call_alloc(FRAME_ATOMIC);
if (!call)
return;
call->flags |= IPC_CALL_NOTIF;
/* Put a counter to the message */
call->priv = ++irq->notif_cfg.counter;
 
/* Set up args */
IPC_SET_METHOD(call->data, irq->notif_cfg.method);
IPC_SET_ARG1(call->data, irq->notif_cfg.scratch[1]);
IPC_SET_ARG2(call->data, irq->notif_cfg.scratch[2]);
IPC_SET_ARG3(call->data, irq->notif_cfg.scratch[3]);
IPC_SET_ARG4(call->data, irq->notif_cfg.scratch[4]);
IPC_SET_ARG5(call->data, irq->notif_cfg.scratch[5]);
 
send_call(irq, call);
}
}
 
/** Send notification message.
*
* @param irq IRQ structure.
* @param a1 Driver-specific payload argument.
* @param a2 Driver-specific payload argument.
* @param a3 Driver-specific payload argument.
* @param a4 Driver-specific payload argument.
* @param a5 Driver-specific payload argument.
*/
void ipc_irq_send_msg(irq_t *irq, unative_t a1, unative_t a2, unative_t a3,
unative_t a4, unative_t a5)
{
call_t *call;
 
spinlock_lock(&irq->lock);
 
if (irq->notif_cfg.answerbox) {
call = ipc_call_alloc(FRAME_ATOMIC);
if (!call) {
spinlock_unlock(&irq->lock);
return;
}
call->flags |= IPC_CALL_NOTIF;
/* Put a counter to the message */
call->priv = ++irq->notif_cfg.counter;
 
IPC_SET_METHOD(call->data, irq->notif_cfg.method);
IPC_SET_ARG1(call->data, a1);
IPC_SET_ARG2(call->data, a2);
IPC_SET_ARG3(call->data, a3);
IPC_SET_ARG4(call->data, a4);
IPC_SET_ARG5(call->data, a5);
send_call(irq, call);
}
spinlock_unlock(&irq->lock);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/main/uinit.c
0,0 → 1,94
/*
* Copyright (c) 2005 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 main
* @{
*/
 
/**
* @file
* @brief Userspace bootstrap thread.
*
* This file contains uinit kernel thread wich is used to start every
* userspace thread including threads created by SYS_THREAD_CREATE syscall.
*
* @see SYS_THREAD_CREATE
*/
#include <main/uinit.h>
#include <arch/types.h>
#include <proc/thread.h>
#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
* addresses.
*/
void uinit(void *arg)
{
uspace_arg_t uarg;
 
/*
* So far, we don't have a use for joining userspace threads so we
* immediately detach each uinit thread. If joining of userspace threads
* is required, some userspace API based on the kernel mechanism will
* have to be implemented. Moreover, garbage collecting of threads that
* didn't detach themselves and nobody else joined them will have to be
* 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;
uarg.uspace_uarg = ((uspace_arg_t *) arg)->uspace_uarg;
uarg.uspace_thread_function = NULL;
uarg.uspace_thread_arg = NULL;
 
free((uspace_arg_t *) arg);
/*
* Disable interrupts so that the execution of userspace() is not
* disturbed by any interrupts as some of the userspace()
* implementations will switch to the userspace stack before switching
* the mode.
*/
(void) interrupts_disable();
userspace(&uarg);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/main/kinit.c
0,0 → 1,244
/*
* Copyright (c) 2001-2004 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 main
* @{
*/
 
/**
* @file
* @brief Kernel initialization thread.
*
* This file contains kinit kernel thread which carries out
* high level system initialization.
*
* This file is responsible for finishing SMP configuration
* and creation of userspace init tasks.
*/
 
#include <main/kinit.h>
#include <config.h>
#include <arch.h>
#include <proc/scheduler.h>
#include <proc/task.h>
#include <proc/thread.h>
#include <proc/program.h>
#include <panic.h>
#include <func.h>
#include <cpu.h>
#include <arch/asm.h>
#include <mm/page.h>
#include <arch/mm/page.h>
#include <mm/as.h>
#include <mm/frame.h>
#include <print.h>
#include <memstr.h>
#include <console/console.h>
#include <interrupt.h>
#include <console/kconsole.h>
#include <security/cap.h>
#include <lib/rd.h>
#include <ipc/ipc.h>
#include <debug.h>
#include <string.h>
 
#ifdef CONFIG_SMP
#include <smp/smp.h>
#endif /* CONFIG_SMP */
 
#include <synch/waitq.h>
#include <synch/spinlock.h>
 
#define ALIVE_CHARS 4
 
#ifdef CONFIG_KCONSOLE
static char alive[ALIVE_CHARS] = "-\\|/";
#endif
 
#define INIT_PREFIX "init:"
#define INIT_PREFIX_LEN 5
 
/** Kernel initialization thread.
*
* kinit takes care of higher level kernel
* initialization (i.e. thread creation,
* userspace initialization etc.).
*
* @param arg Not used.
*/
void kinit(void *arg)
{
 
#if defined(CONFIG_SMP) || defined(CONFIG_KCONSOLE)
thread_t *thread;
#endif
/*
* Detach kinit as nobody will call thread_join_timeout() on it.
*/
thread_detach(THREAD);
interrupts_disable();
#ifdef CONFIG_SMP
if (config.cpu_count > 1) {
waitq_initialize(&ap_completion_wq);
/*
* Create the kmp thread and wait for its completion.
* cpu1 through cpuN-1 will come up consecutively and
* not mess together with kcpulb threads.
* Just a beautification.
*/
thread = thread_create(kmp, NULL, TASK, THREAD_FLAG_WIRED, "kmp", true);
if (thread != NULL) {
spinlock_lock(&thread->lock);
thread->cpu = &cpus[0];
spinlock_unlock(&thread->lock);
thread_ready(thread);
} else
panic("Unable to create kmp thread.");
thread_join(thread);
thread_detach(thread);
}
if (config.cpu_count > 1) {
size_t i;
/*
* For each CPU, create its load balancing thread.
*/
for (i = 0; i < config.cpu_count; i++) {
thread = thread_create(kcpulb, NULL, TASK, THREAD_FLAG_WIRED, "kcpulb", true);
if (thread != NULL) {
spinlock_lock(&thread->lock);
thread->cpu = &cpus[i];
spinlock_unlock(&thread->lock);
thread_ready(thread);
} else
printf("Unable to create kcpulb thread for cpu" PRIs "\n", i);
}
}
#endif /* CONFIG_SMP */
/*
* At this point SMP, if present, is configured.
*/
arch_post_smp_init();
#ifdef CONFIG_KCONSOLE
if (stdin) {
/*
* Create kernel console.
*/
thread = thread_create(kconsole_thread, NULL, TASK, 0, "kconsole", false);
if (thread != NULL)
thread_ready(thread);
else
printf("Unable to create kconsole thread\n");
}
#endif /* CONFIG_KCONSOLE */
interrupts_enable();
/*
* Create user tasks, load RAM disk images.
*/
size_t i;
program_t programs[CONFIG_INIT_TASKS];
for (i = 0; i < init.cnt; i++) {
if (init.tasks[i].addr % FRAME_SIZE) {
printf("init[%" PRIs "].addr is not frame aligned\n", i);
continue;
}
/*
* Construct task name from the 'init:' prefix and the
* name stored in the init structure (if any).
*/
char namebuf[TASK_NAME_BUFLEN];
char *name;
name = init.tasks[i].name;
if (name[0] == 0)
name = "<unknown>";
ASSERT(TASK_NAME_BUFLEN >= INIT_PREFIX_LEN);
str_cpy(namebuf, TASK_NAME_BUFLEN, INIT_PREFIX);
str_cpy(namebuf + INIT_PREFIX_LEN,
TASK_NAME_BUFLEN - INIT_PREFIX_LEN, name);
 
int rc = program_create_from_image((void *) init.tasks[i].addr,
namebuf, &programs[i]);
if ((rc == 0) && (programs[i].task != NULL)) {
/*
* Set capabilities to init userspace tasks.
*/
cap_set(programs[i].task, CAP_CAP | CAP_MEM_MANAGER |
CAP_IO_MANAGER | CAP_PREEMPT_CONTROL | CAP_IRQ_REG);
if (!ipc_phone_0)
ipc_phone_0 = &programs[i].task->answerbox;
} else if (rc == 0) {
/* It was the program loader and was registered */
} else {
/* RAM disk image */
int rd = init_rd((rd_header_t *) init.tasks[i].addr, init.tasks[i].size);
if (rd != RE_OK)
printf("Init binary %" PRIs " not used (error %d)\n", i, rd);
}
}
/*
* Run user tasks.
*/
for (i = 0; i < init.cnt; i++) {
if (programs[i].task != NULL)
program_ready(&programs[i]);
}
#ifdef CONFIG_KCONSOLE
if (!stdin) {
thread_sleep(10);
printf("kinit: No stdin\nKernel alive: .");
unsigned int i = 0;
while (true) {
printf("\b%c", alive[i % ALIVE_CHARS]);
thread_sleep(1);
i++;
}
}
#endif /* CONFIG_KCONSOLE */
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/main/main.c
0,0 → 1,356
/*
* Copyright (c) 2001-2004 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 main
* @{
*/
 
/**
* @file
* @brief Main initialization kernel function for all processors.
*
* During kernel boot, all processors, after architecture dependent
* initialization, start executing code found in this file. After
* bringing up all subsystems, control is passed to scheduler().
*
* The bootstrap processor starts executing main_bsp() while
* the application processors start executing main_ap().
*
* @see scheduler()
* @see main_bsp()
* @see main_ap()
*/
 
#include <arch/asm.h>
#include <context.h>
#include <print.h>
#include <panic.h>
#include <debug.h>
#include <config.h>
#include <time/clock.h>
#include <time/timeout.h>
#include <proc/scheduler.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <proc/tasklet.h>
#include <main/kinit.h>
#include <main/version.h>
#include <console/kconsole.h>
#include <console/console.h>
#include <cpu.h>
#include <align.h>
#include <interrupt.h>
#include <mm/frame.h>
#include <mm/page.h>
#include <genarch/mm/page_pt.h>
#include <mm/tlb.h>
#include <mm/as.h>
#include <mm/slab.h>
#include <synch/waitq.h>
#include <synch/futex.h>
#include <arch/arch.h>
#include <arch.h>
#include <arch/faddr.h>
#include <ipc/ipc.h>
#include <macros.h>
#include <adt/btree.h>
#include <smp/smp.h>
#include <ddi/ddi.h>
#include <main/main.h>
#include <ipc/event.h>
 
/** Global configuration structure. */
config_t config;
 
/** Initial user-space tasks */
init_t init = {
.cnt = 0
};
 
/** Boot allocations. */
ballocs_t ballocs = {
.base = NULL,
.size = 0
};
 
context_t ctx;
 
/*
* These 'hardcoded' variables will be intialized by
* the linker or the low level assembler code with
* appropriate sizes and addresses.
*/
 
/** Virtual address of where the kernel is loaded. */
uintptr_t hardcoded_load_address = 0;
/** Size of the kernel code in bytes. */
size_t hardcoded_ktext_size = 0;
/** Size of the kernel data in bytes. */
size_t hardcoded_kdata_size = 0;
/** Lowest safe stack virtual address. */
uintptr_t stack_safe = 0;
 
/*
* These two functions prevent stack from underflowing during the
* kernel boot phase when SP is set to the very top of the reserved
* space. The stack could get corrupted by a fooled compiler-generated
* pop sequence otherwise.
*/
static void main_bsp_separated_stack(void);
#ifdef CONFIG_SMP
static void main_ap_separated_stack(void);
#endif
 
#define CONFIG_STACK_SIZE ((1 << STACK_FRAMES) * STACK_SIZE)
 
/** Main kernel routine for bootstrap CPU.
*
* The code here still runs on the boot stack, which knows nothing about
* preemption counts. Because of that, this function cannot directly call
* functions that disable or enable preemption (e.g. spinlock_lock()). The
* primary task of this function is to calculate address of a new stack and
* switch to it.
*
* Assuming interrupts_disable().
*
*/
void main_bsp(void)
{
config.cpu_count = 1;
config.cpu_active = 1;
config.base = hardcoded_load_address;
config.kernel_size = ALIGN_UP(hardcoded_ktext_size +
hardcoded_kdata_size, PAGE_SIZE);
config.stack_size = CONFIG_STACK_SIZE;
/* Initialy the stack is placed just after the kernel */
config.stack_base = config.base + config.kernel_size;
/* Avoid placing stack on top of init */
size_t i;
for (i = 0; i < init.cnt; i++) {
if (PA_overlaps(config.stack_base, config.stack_size,
init.tasks[i].addr, init.tasks[i].size))
config.stack_base = ALIGN_UP(init.tasks[i].addr +
init.tasks[i].size, config.stack_size);
}
 
/* Avoid placing stack on top of boot allocations. */
if (ballocs.size) {
if (PA_overlaps(config.stack_base, config.stack_size,
ballocs.base, ballocs.size))
config.stack_base = ALIGN_UP(ballocs.base +
ballocs.size, PAGE_SIZE);
}
if (config.stack_base < stack_safe)
config.stack_base = ALIGN_UP(stack_safe, PAGE_SIZE);
context_save(&ctx);
context_set(&ctx, FADDR(main_bsp_separated_stack), config.stack_base,
THREAD_STACK_SIZE);
context_restore(&ctx);
/* not reached */
}
 
 
/** Main kernel routine for bootstrap CPU using new stack.
*
* Second part of main_bsp().
*
*/
void main_bsp_separated_stack(void)
{
/* Keep this the first thing. */
the_initialize(THE);
version_print();
LOG("\nconfig.base=%#" PRIp " config.kernel_size=%" PRIs
"\nconfig.stack_base=%#" PRIp " config.stack_size=%" PRIs,
config.base, config.kernel_size, config.stack_base,
config.stack_size);
#ifdef CONFIG_KCONSOLE
/*
* kconsole data structures must be initialized very early
* because other subsystems will register their respective
* commands.
*/
LOG_EXEC(kconsole_init());
#endif
/*
* Exception handler initialization, before architecture
* starts adding its own handlers
*/
LOG_EXEC(exc_init());
/*
* Memory management subsystems initialization.
*/
LOG_EXEC(arch_pre_mm_init());
LOG_EXEC(frame_init());
/* Initialize at least 1 memory segment big enough for slab to work. */
LOG_EXEC(slab_cache_init());
LOG_EXEC(btree_init());
LOG_EXEC(as_init());
LOG_EXEC(page_init());
LOG_EXEC(tlb_init());
LOG_EXEC(ddi_init());
LOG_EXEC(tasklet_init());
LOG_EXEC(arch_post_mm_init());
LOG_EXEC(arch_pre_smp_init());
LOG_EXEC(smp_init());
/* Slab must be initialized after we know the number of processors. */
LOG_EXEC(slab_enable_cpucache());
printf("Detected %" PRIs " CPU(s), %" PRIu64" MiB free memory\n",
config.cpu_count, SIZE2MB(zone_total_size()));
LOG_EXEC(cpu_init());
LOG_EXEC(calibrate_delay_loop());
LOG_EXEC(clock_counter_init());
LOG_EXEC(timeout_init());
LOG_EXEC(scheduler_init());
LOG_EXEC(task_init());
LOG_EXEC(thread_init());
LOG_EXEC(futex_init());
if (init.cnt > 0) {
size_t i;
for (i = 0; i < init.cnt; i++)
LOG("init[%" PRIs "].addr=%#" PRIp ", init[%" PRIs
"].size=%#" PRIs, i, init.tasks[i].addr, i,
init.tasks[i].size);
} else
printf("No init binaries found.\n");
LOG_EXEC(ipc_init());
LOG_EXEC(event_init());
LOG_EXEC(klog_init());
/*
* Create kernel task.
*/
task_t *kernel = task_create(AS_KERNEL, "kernel");
if (!kernel)
panic("Cannot create kernel task.");
/*
* Create the first thread.
*/
thread_t *kinit_thread
= thread_create(kinit, NULL, kernel, 0, "kinit", true);
if (!kinit_thread)
panic("Cannot create kinit thread.");
LOG_EXEC(thread_ready(kinit_thread));
/*
* This call to scheduler() will return to kinit,
* starting the thread of kernel threads.
*/
scheduler();
/* not reached */
}
 
 
#ifdef CONFIG_SMP
/** Main kernel routine for application CPUs.
*
* Executed by application processors, temporary stack
* is at ctx.sp which was set during BSP boot.
* This function passes control directly to
* main_ap_separated_stack().
*
* Assuming interrupts_disable()'d.
*
*/
void main_ap(void)
{
/*
* Incrementing the active CPU counter will guarantee that the
* *_init() functions can find out that they need to
* do initialization for AP only.
*/
config.cpu_active++;
 
/*
* The THE structure is well defined because ctx.sp is used as stack.
*/
the_initialize(THE);
arch_pre_mm_init();
frame_init();
page_init();
tlb_init();
arch_post_mm_init();
cpu_init();
calibrate_delay_loop();
arch_post_cpu_init();
 
the_copy(THE, (the_t *) CPU->stack);
 
/*
* If we woke kmp up before we left the kernel stack, we could
* collide with another CPU coming up. To prevent this, we
* switch to this cpu's private stack prior to waking kmp up.
*/
context_save(&CPU->saved_context);
context_set(&CPU->saved_context, FADDR(main_ap_separated_stack),
(uintptr_t) CPU->stack, CPU_STACK_SIZE);
context_restore(&CPU->saved_context);
/* not reached */
}
 
 
/** Main kernel routine for application CPUs using new stack.
*
* Second part of main_ap().
*
*/
void main_ap_separated_stack(void)
{
/*
* Configure timeouts for this cpu.
*/
timeout_init();
 
waitq_wakeup(&ap_completion_wq, WAKEUP_FIRST);
scheduler();
/* not reached */
}
#endif /* CONFIG_SMP */
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/main/shutdown.c
0,0 → 1,55
/*
* Copyright (c) 2007 Martin Decky
* 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 main
* @{
*/
 
/**
* @file
* @brief Shutdown procedures.
*/
 
#include <arch.h>
#include <func.h>
#include <print.h>
 
void reboot(void)
{
task_done();
#ifdef CONFIG_DEBUG
printf("Rebooting the system\n");
#endif
arch_reboot();
halt();
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/main/version.c
0,0 → 1,65
/*
* Copyright (c) 2006 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 main
* @{
*/
/** @file
*/
 
#include <main/version.h>
#include <print.h>
#include <macros.h>
 
char *project = "SPARTAN kernel";
char *copyright = "Copyright (c) 2001-2009 HelenOS project";
char *release = STRING(RELEASE);
char *name = STRING(NAME);
char *arch = STRING(KARCH);
 
#ifdef REVISION
char *revision = ", revision " STRING(REVISION);
#else
char *revision = "";
#endif
 
#ifdef TIMESTAMP
char *timestamp = " on " STRING(TIMESTAMP);
#else
char *timestamp = "";
#endif
 
/** Print version information. */
void version_print(void)
{
printf("%s, release %s (%s)%s\nBuilt%s for %s\n%s\n",
project, release, name, revision, timestamp, arch, copyright);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/printf/printf_core.c
0,0 → 1,892
/*
* Copyright (c) 2001-2004 Jakub Jermar
* Copyright (c) 2006 Josef Cejka
* Copyright (c) 2009 Martin Decky
* 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 Printing functions.
*/
 
#include <printf/printf_core.h>
#include <print.h>
#include <arch/arg.h>
#include <macros.h>
#include <string.h>
#include <arch.h>
 
/** show prefixes 0x or 0 */
#define __PRINTF_FLAG_PREFIX 0x00000001
/** signed / unsigned number */
#define __PRINTF_FLAG_SIGNED 0x00000002
/** print leading zeroes */
#define __PRINTF_FLAG_ZEROPADDED 0x00000004
/** align to left */
#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
/** always show + sign */
#define __PRINTF_FLAG_SHOWPLUS 0x00000020
/** print space instead of plus */
#define __PRINTF_FLAG_SPACESIGN 0x00000040
/** show big characters */
#define __PRINTF_FLAG_BIGCHARS 0x00000080
/** number has - sign */
#define __PRINTF_FLAG_NEGATIVE 0x00000100
 
/**
* Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
* to terminate string... (last one is only for better testing end of buffer by
* zero-filling subroutine)
*/
#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
 
/** Enumeration of possible arguments types.
*/
typedef enum {
PrintfQualifierByte = 0,
PrintfQualifierShort,
PrintfQualifierInt,
PrintfQualifierLong,
PrintfQualifierLongLong,
PrintfQualifierPointer
} qualifier_t;
 
static char nullstr[] = "(NULL)";
static char digits_small[] = "0123456789abcdef";
static char digits_big[] = "0123456789ABCDEF";
static char invalch = U_SPECIAL;
 
/** Print one or more characters without adding newline.
*
* @param buf Buffer holding characters with size of
* at least size bytes. NULL is not allowed!
* @param size Size of the buffer in bytes.
* @param ps Output method and its data.
*
* @return Number of characters printed.
*
*/
static int printf_putnchars(const char *buf, size_t size,
printf_spec_t *ps)
{
return ps->str_write((void *) buf, size, ps->data);
}
 
/** Print one or more wide characters without adding newline.
*
* @param buf Buffer holding wide characters with size of
* at least size bytes. NULL is not allowed!
* @param size Size of the buffer in bytes.
* @param ps Output method and its data.
*
* @return Number of wide characters printed.
*
*/
static int printf_wputnchars(const wchar_t *buf, size_t size,
printf_spec_t *ps)
{
return ps->wstr_write((void *) buf, size, ps->data);
}
 
/** Print string without adding a newline.
*
* @param str String to print.
* @param ps Write function specification and support data.
*
* @return Number of characters printed.
*
*/
static int printf_putstr(const char *str, printf_spec_t *ps)
{
if (str == NULL)
return printf_putnchars(nullstr, str_size(nullstr), ps);
return ps->str_write((void *) str, str_size(str), ps->data);
}
 
/** Print one ASCII character.
*
* @param c ASCII character to be printed.
* @param ps Output method.
*
* @return Number of characters printed.
*
*/
static int printf_putchar(const char ch, printf_spec_t *ps)
{
if (!ascii_check(ch))
return ps->str_write((void *) &invalch, 1, ps->data);
return ps->str_write(&ch, 1, ps->data);
}
 
/** Print one wide character.
*
* @param c Wide character to be printed.
* @param ps Output method.
*
* @return Number of characters printed.
*
*/
static int printf_putwchar(const wchar_t ch, printf_spec_t *ps)
{
if (!chr_check(ch))
return ps->str_write((void *) &invalch, 1, ps->data);
return ps->wstr_write(&ch, sizeof(wchar_t), ps->data);
}
 
/** Print one formatted ASCII character.
*
* @param ch Character to print.
* @param width Width modifier.
* @param flags Flags that change the way the character is printed.
*
* @return Number of characters printed, negative value on failure.
*
*/
static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
{
size_t counter = 0;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (--width > 0) {
/*
* One space is consumed by the character itself, hence
* the predecrement.
*/
if (printf_putchar(' ', ps) > 0)
counter++;
}
}
if (printf_putchar(ch, ps) > 0)
counter++;
while (--width > 0) {
/*
* One space is consumed by the character itself, hence
* the predecrement.
*/
if (printf_putchar(' ', ps) > 0)
counter++;
}
return (int) (counter + 1);
}
 
/** Print one formatted wide character.
*
* @param ch Character to print.
* @param width Width modifier.
* @param flags Flags that change the way the character is printed.
*
* @return Number of characters printed, negative value on failure.
*
*/
static int print_wchar(const wchar_t ch, int width, uint32_t flags, printf_spec_t *ps)
{
size_t counter = 0;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (--width > 0) {
/*
* One space is consumed by the character itself, hence
* the predecrement.
*/
if (printf_putchar(' ', ps) > 0)
counter++;
}
}
if (printf_putwchar(ch, ps) > 0)
counter++;
while (--width > 0) {
/*
* One space is consumed by the character itself, hence
* the predecrement.
*/
if (printf_putchar(' ', ps) > 0)
counter++;
}
return (int) (counter + 1);
}
 
/** Print string.
*
* @param str String to be printed.
* @param width Width modifier.
* @param precision Precision modifier.
* @param flags Flags that modify the way the string is printed.
*
* @return Number of characters printed, negative value on failure.
*/
static int print_str(char *str, int width, unsigned int precision,
uint32_t flags, printf_spec_t *ps)
{
if (str == NULL)
return printf_putstr(nullstr, ps);
 
/* Print leading spaces. */
size_t strw = str_length(str);
if (precision == 0)
precision = strw;
 
/* Left padding */
size_t counter = 0;
width -= precision;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
}
 
/* Part of @a str fitting into the alloted space. */
int retval;
size_t size = str_lsize(str, precision);
if ((retval = printf_putnchars(str, size, ps)) < 0)
return -counter;
 
counter += retval;
 
/* Right padding */
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
 
return ((int) counter);
 
}
 
/** Print wide string.
*
* @param str Wide string to be printed.
* @param width Width modifier.
* @param precision Precision modifier.
* @param flags Flags that modify the way the string is printed.
*
* @return Number of wide characters printed, negative value on failure.
*/
static int print_wstr(wchar_t *str, int width, unsigned int precision,
uint32_t flags, printf_spec_t *ps)
{
if (str == NULL)
return printf_putstr(nullstr, ps);
/* Print leading spaces. */
size_t strw = wstr_length(str);
if (precision == 0)
precision = strw;
/* Left padding */
size_t counter = 0;
width -= precision;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
}
/* Part of @a wstr fitting into the alloted space. */
int retval;
size_t size = wstr_lsize(str, precision);
if ((retval = printf_wputnchars(str, size, ps)) < 0)
return -counter;
counter += retval;
/* Right padding */
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
 
return ((int) counter);
}
 
/** Print a number in a given base.
*
* Print significant digits of a number in given base.
*
* @param num Number to print.
* @param width Width modifier.
* @param precision Precision modifier.
* @param base Base to print the number in (must be between 2 and 16).
* @param flags Flags that modify the way the number is printed.
*
* @return Number of characters printed.
*
*/
static int print_number(uint64_t num, int width, int precision, int base,
uint32_t flags, printf_spec_t *ps)
{
char *digits;
if (flags & __PRINTF_FLAG_BIGCHARS)
digits = digits_big;
else
digits = digits_small;
char data[PRINT_NUMBER_BUFFER_SIZE];
char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
/* Size of number with all prefixes and signs */
int size = 0;
/* Put zero at end of string */
*ptr-- = 0;
if (num == 0) {
*ptr-- = '0';
size++;
} else {
do {
*ptr-- = digits[num % base];
size++;
} while (num /= base);
}
/* Size of plain number */
int number_size = size;
/*
* Collect the sum of all prefixes/signs/etc. to calculate padding and
* leading zeroes.
*/
if (flags & __PRINTF_FLAG_PREFIX) {
switch(base) {
case 2:
/* Binary formating is not standard, but usefull */
size += 2;
break;
case 8:
size++;
break;
case 16:
size += 2;
break;
}
}
char sgn = 0;
if (flags & __PRINTF_FLAG_SIGNED) {
if (flags & __PRINTF_FLAG_NEGATIVE) {
sgn = '-';
size++;
} else if (flags & __PRINTF_FLAG_SHOWPLUS) {
sgn = '+';
size++;
} else if (flags & __PRINTF_FLAG_SPACESIGN) {
sgn = ' ';
size++;
}
}
if (flags & __PRINTF_FLAG_LEFTALIGNED)
flags &= ~__PRINTF_FLAG_ZEROPADDED;
/*
* If the number is left-aligned or precision is specified then
* padding with zeros is ignored.
*/
if (flags & __PRINTF_FLAG_ZEROPADDED) {
if ((precision == 0) && (width > size))
precision = width - size + number_size;
}
/* Print leading spaces */
if (number_size > precision) {
/* Print the whole number, not only a part */
precision = number_size;
}
width -= precision + size - number_size;
size_t counter = 0;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
}
/* Print sign */
if (sgn) {
if (printf_putchar(sgn, ps) == 1)
counter++;
}
/* Print prefix */
if (flags & __PRINTF_FLAG_PREFIX) {
switch(base) {
case 2:
/* Binary formating is not standard, but usefull */
if (printf_putchar('0', ps) == 1)
counter++;
if (flags & __PRINTF_FLAG_BIGCHARS) {
if (printf_putchar('B', ps) == 1)
counter++;
} else {
if (printf_putchar('b', ps) == 1)
counter++;
}
break;
case 8:
if (printf_putchar('o', ps) == 1)
counter++;
break;
case 16:
if (printf_putchar('0', ps) == 1)
counter++;
if (flags & __PRINTF_FLAG_BIGCHARS) {
if (printf_putchar('X', ps) == 1)
counter++;
} else {
if (printf_putchar('x', ps) == 1)
counter++;
}
break;
}
}
/* Print leading zeroes */
precision -= number_size;
while (precision-- > 0) {
if (printf_putchar('0', ps) == 1)
counter++;
}
/* Print the number itself */
int retval;
if ((retval = printf_putstr(++ptr, ps)) > 0)
counter += retval;
/* Print tailing spaces */
while (width-- > 0) {
if (printf_putchar(' ', ps) == 1)
counter++;
}
return ((int) counter);
}
 
/** Print formatted string.
*
* Print string formatted according to the fmt parameter and variadic arguments.
* Each formatting directive must have the following form:
*
* \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
*
* FLAGS:@n
* - "#" Force to print prefix. For \%o conversion, the prefix is 0, for
* \%x and \%X prefixes are 0x and 0X and for conversion \%b the
* prefix is 0b.
*
* - "-" Align to left.
*
* - "+" Print positive sign just as negative.
*
* - " " If the printed number is positive and "+" flag is not set,
* print space in place of sign.
*
* - "0" Print 0 as padding instead of spaces. Zeroes are placed between
* sign and the rest of the number. This flag is ignored if "-"
* flag is specified.
*
* WIDTH:@n
* - Specify the minimal width of a printed argument. If it is bigger,
* width is ignored. If width is specified with a "*" character instead of
* number, width is taken from parameter list. And integer parameter is
* expected before parameter for processed conversion specification. If
* this value is negative its absolute value is taken and the "-" flag is
* set.
*
* PRECISION:@n
* - Value precision. For numbers it specifies minimum valid numbers.
* Smaller numbers are printed with leading zeroes. Bigger numbers are not
* affected. Strings with more than precision characters are cut off. Just
* as with width, an "*" can be used used instead of a number. An integer
* value is then expected in parameters. When both width and precision are
* specified using "*", the first parameter is used for width and the
* second one for precision.
*
* TYPE:@n
* - "hh" Signed or unsigned char.@n
* - "h" Signed or unsigned short.@n
* - "" Signed or unsigned int (default value).@n
* - "l" Signed or unsigned long int.@n
* If conversion is "c", the character is wchar_t (wide character).@n
* If conversion is "s", the string is wchar_t * (wide string).@n
* - "ll" Signed or unsigned long long int.@n
*
* CONVERSION:@n
* - % Print percentile character itself.
*
* - c Print single character. The character is expected to be plain
* ASCII (e.g. only values 0 .. 127 are valid).@n
* If type is "l", then the character is expected to be wide character
* (e.g. values 0 .. 0x10ffff are valid).
*
* - s Print zero terminated string. If a NULL value is passed as
* value, "(NULL)" is printed instead.@n
* If type is "l", then the string is expected to be wide string.
*
* - P, p Print value of a pointer. Void * value is expected and it is
* printed in hexadecimal notation with prefix (as with \%#X / \%#x
* for 32-bit or \%#X / \%#x for 64-bit long pointers).
*
* - b Print value as unsigned binary number. Prefix is not printed by
* default. (Nonstandard extension.)
*
* - o Print value as unsigned octal number. Prefix is not printed by
* default.
*
* - d, i Print signed decimal number. There is no difference between d
* and i conversion.
*
* - u Print unsigned decimal number.
*
* - X, x Print hexadecimal number with upper- or lower-case. Prefix is
* not printed by default.
*
* All other characters from fmt except the formatting directives are printed
* verbatim.
*
* @param fmt Format NULL-terminated string.
*
* @return Number of characters printed, negative value on failure.
*
*/
int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
{
size_t i; /* Index of the currently processed character from fmt */
size_t nxt = 0; /* Index of the next character from fmt */
size_t j = 0; /* Index to the first not printed nonformating character */
size_t counter = 0; /* Number of characters printed */
int retval; /* Return values from nested functions */
while (true) {
i = nxt;
wchar_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 0)
break;
/* Control character */
if (uc == '%') {
/* Print common characters if any processed */
if (i > j) {
if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
/* Error */
counter = -counter;
goto out;
}
counter += retval;
}
j = i;
/* Parse modifiers */
uint32_t flags = 0;
bool end = false;
do {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
switch (uc) {
case '#':
flags |= __PRINTF_FLAG_PREFIX;
break;
case '-':
flags |= __PRINTF_FLAG_LEFTALIGNED;
break;
case '+':
flags |= __PRINTF_FLAG_SHOWPLUS;
break;
case ' ':
flags |= __PRINTF_FLAG_SPACESIGN;
break;
case '0':
flags |= __PRINTF_FLAG_ZEROPADDED;
break;
default:
end = true;
};
} while (!end);
/* Width & '*' operator */
int width = 0;
if (isdigit(uc)) {
while (true) {
width *= 10;
width += uc - '0';
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 0)
break;
if (!isdigit(uc))
break;
}
} else if (uc == '*') {
/* Get width value from argument list */
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
width = (int) va_arg(ap, int);
if (width < 0) {
/* Negative width sets '-' flag */
width *= -1;
flags |= __PRINTF_FLAG_LEFTALIGNED;
}
}
/* Precision and '*' operator */
int precision = 0;
if (uc == '.') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (isdigit(uc)) {
while (true) {
precision *= 10;
precision += uc - '0';
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 0)
break;
if (!isdigit(uc))
break;
}
} else if (uc == '*') {
/* Get precision value from the argument list */
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
precision = (int) va_arg(ap, int);
if (precision < 0) {
/* Ignore negative precision */
precision = 0;
}
}
}
qualifier_t qualifier;
switch (uc) {
/** @todo Unimplemented qualifiers:
* t ptrdiff_t - ISO C 99
*/
case 'h':
/* Char or short */
qualifier = PrintfQualifierShort;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 'h') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
qualifier = PrintfQualifierByte;
}
break;
case 'l':
/* Long or long long */
qualifier = PrintfQualifierLong;
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
if (uc == 'l') {
i = nxt;
uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
qualifier = PrintfQualifierLongLong;
}
break;
default:
/* Default type */
qualifier = PrintfQualifierInt;
}
unsigned int base = 10;
switch (uc) {
/*
* String and character conversions.
*/
case 's':
if (qualifier == PrintfQualifierLong)
retval = print_wstr(va_arg(ap, wchar_t *), width, precision, flags, ps);
else
retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
if (retval < 0) {
counter = -counter;
goto out;
}
counter += retval;
j = nxt;
goto next_char;
case 'c':
if (qualifier == PrintfQualifierLong)
retval = print_wchar(va_arg(ap, wchar_t), width, flags, ps);
else
retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
if (retval < 0) {
counter = -counter;
goto out;
};
counter += retval;
j = nxt;
goto next_char;
/*
* Integer values
*/
case 'P':
/* Pointer */
flags |= __PRINTF_FLAG_BIGCHARS;
case 'p':
flags |= __PRINTF_FLAG_PREFIX;
base = 16;
qualifier = PrintfQualifierPointer;
break;
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
case 'd':
case 'i':
flags |= __PRINTF_FLAG_SIGNED;
case 'u':
break;
case 'X':
flags |= __PRINTF_FLAG_BIGCHARS;
case 'x':
base = 16;
break;
/* Percentile itself */
case '%':
j = i;
goto next_char;
/*
* Bad formatting.
*/
default:
/*
* Unknown format. Now, j is the index of '%'
* so we will print whole bad format sequence.
*/
goto next_char;
}
/* Print integers */
size_t size;
uint64_t number;
switch (qualifier) {
case PrintfQualifierByte:
size = sizeof(unsigned char);
number = (uint64_t) va_arg(ap, unsigned int);
break;
case PrintfQualifierShort:
size = sizeof(unsigned short);
number = (uint64_t) va_arg(ap, unsigned int);
break;
case PrintfQualifierInt:
size = sizeof(unsigned int);
number = (uint64_t) va_arg(ap, unsigned int);
break;
case PrintfQualifierLong:
size = sizeof(unsigned long);
number = (uint64_t) va_arg(ap, unsigned long);
break;
case PrintfQualifierLongLong:
size = sizeof(unsigned long long);
number = (uint64_t) va_arg(ap, unsigned long long);
break;
case PrintfQualifierPointer:
size = sizeof(void *);
number = (uint64_t) (unsigned long) va_arg(ap, void *);
break;
default:
/* Unknown qualifier */
counter = -counter;
goto out;
}
if (flags & __PRINTF_FLAG_SIGNED) {
if (number & (0x1 << (size * 8 - 1))) {
flags |= __PRINTF_FLAG_NEGATIVE;
if (size == sizeof(uint64_t)) {
number = -((int64_t) number);
} else {
number = ~number;
number &=
~(0xFFFFFFFFFFFFFFFFll <<
(size * 8));
number++;
}
}
}
if ((retval = print_number(number, width, precision,
base, flags, ps)) < 0) {
counter = -counter;
goto out;
}
counter += retval;
j = nxt;
}
next_char:
;
}
if (i > j) {
if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
/* Error */
counter = -counter;
goto out;
}
counter += retval;
}
out:
return ((int) counter);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/printf/vprintf.c
0,0 → 1,107
/*
* Copyright (c) 2006 Josef Cejka
* 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
*/
 
#include <print.h>
#include <printf/printf_core.h>
#include <putchar.h>
#include <synch/spinlock.h>
#include <arch/asm.h>
#include <arch/types.h>
#include <typedefs.h>
#include <string.h>
 
SPINLOCK_INITIALIZE(printf_lock); /**< vprintf spinlock */
 
static int vprintf_str_write(const char *str, size_t size, void *data)
{
size_t offset = 0;
size_t chars = 0;
while (offset < size) {
putchar(str_decode(str, &offset, size));
chars++;
}
return chars;
}
 
static int vprintf_wstr_write(const wchar_t *str, size_t size, void *data)
{
size_t offset = 0;
size_t chars = 0;
while (offset < size) {
putchar(str[chars]);
chars++;
offset += sizeof(wchar_t);
}
return chars;
}
 
int puts(const char *str)
{
size_t offset = 0;
size_t chars = 0;
wchar_t uc;
while ((uc = str_decode(str, &offset, STR_NO_LIMIT)) != 0) {
putchar(uc);
chars++;
}
return chars;
}
 
int vprintf(const char *fmt, va_list ap)
{
printf_spec_t ps = {
vprintf_str_write,
vprintf_wstr_write,
NULL
};
ipl_t ipl = interrupts_disable();
spinlock_lock(&printf_lock);
int ret = printf_core(fmt, &ps, ap);
spinlock_unlock(&printf_lock);
interrupts_restore(ipl);
return ret;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/printf/vsnprintf.c
0,0 → 1,186
/*
* Copyright (c) 2006 Josef Cejka
* 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
*/
 
#include <print.h>
#include <printf/printf_core.h>
#include <string.h>
#include <memstr.h>
#include <errno.h>
 
typedef struct {
size_t size; /* Total size of the buffer (in bytes) */
size_t len; /* Number of already used bytes */
char *dst; /* Destination */
} vsnprintf_data_t;
 
/** Write string to given buffer.
*
* Write at most data->size plain characters including trailing zero.
* According to C99, snprintf() has to return number of characters that
* would have been written if enough space had been available. Hence
* the return value is not the number of actually printed characters
* but size of the input string.
*
* @param str Source string to print.
* @param size Number of plain characters in str.
* @param data Structure describing destination string, counter
* of used space and total string size.
*
* @return Number of characters to print (not characters actually
* printed).
*
*/
static int vsnprintf_str_write(const char *str, size_t size, vsnprintf_data_t *data)
{
size_t left = data->size - data->len;
if (left == 0)
return ((int) size);
if (left == 1) {
/* We have only one free byte left in buffer
* -> store trailing zero
*/
data->dst[data->size - 1] = 0;
data->len = data->size;
return ((int) size);
}
if (left <= size) {
/* We do not have enough space for the whole string
* with the trailing zero => print only a part
* of string
*/
size_t index = 0;
while (index < size) {
wchar_t uc = str_decode(str, &index, size);
if (chr_encode(uc, data->dst, &data->len, data->size - 1) != EOK)
break;
}
/* Put trailing zero at end, but not count it
* into data->len so it could be rewritten next time
*/
data->dst[data->len] = 0;
return ((int) size);
}
/* Buffer is big enought to print the whole string */
memcpy((void *)(data->dst + data->len), (void *) str, size);
data->len += size;
/* Put trailing zero at end, but not count it
* into data->len so it could be rewritten next time
*/
data->dst[data->len] = 0;
return ((int) size);
}
 
/** Write wide string to given buffer.
*
* Write at most data->size plain characters including trailing zero.
* According to C99, snprintf() has to return number of characters that
* would have been written if enough space had been available. Hence
* the return value is not the number of actually printed characters
* but size of the input string.
*
* @param str Source wide string to print.
* @param size Number of bytes in str.
* @param data Structure describing destination string, counter
* of used space and total string size.
*
* @return Number of wide characters to print (not characters actually
* printed).
*
*/
static int vsnprintf_wstr_write(const wchar_t *str, size_t size, vsnprintf_data_t *data)
{
size_t index = 0;
while (index < (size / sizeof(wchar_t))) {
size_t left = data->size - data->len;
if (left == 0)
return ((int) size);
if (left == 1) {
/* We have only one free byte left in buffer
* -> store trailing zero
*/
data->dst[data->size - 1] = 0;
data->len = data->size;
return ((int) size);
}
if (chr_encode(str[index], data->dst, &data->len, data->size - 1) != EOK)
break;
index++;
}
/* Put trailing zero at end, but not count it
* into data->len so it could be rewritten next time
*/
data->dst[data->len] = 0;
return ((int) size);
}
 
int vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
{
vsnprintf_data_t data = {
size,
0,
str
};
printf_spec_t ps = {
(int(*) (const char *, size_t, void *)) vsnprintf_str_write,
(int(*) (const wchar_t *, size_t, void *)) vsnprintf_wstr_write,
&data
};
/* Print 0 at end of string - fix the case that nothing will be printed */
if (size > 0)
str[0] = 0;
/* vsnprintf_write ensures that str will be terminated by zero. */
return printf_core(fmt, &ps, ap);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/printf/printf.c
0,0 → 1,52
/*
* Copyright (c) 2006 Josef Cejka
* 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
*/
 
#include <print.h>
 
int printf(const char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt);
ret = vprintf(fmt, args);
va_end(args);
return ret;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/printf/snprintf.c
0,0 → 1,52
/*
* Copyright (c) 2006 Josef Cejka
* 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
*/
 
#include <print.h>
#include <printf/printf_core.h>
 
int snprintf(char *str, size_t size, const char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt);
ret = vsnprintf(str, size, fmt, args);
va_end(args);
return ret;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/spinlock.c
0,0 → 1,158
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
 
/**
* @file
* @brief Spinlocks.
*/
 
#include <synch/spinlock.h>
#include <atomic.h>
#include <arch/barrier.h>
#include <arch.h>
#include <preemption.h>
#include <print.h>
#include <debug.h>
#include <symtab.h>
 
#ifdef CONFIG_FB
#include <genarch/fb/fb.h>
#endif
 
#ifdef CONFIG_SMP
 
/** Initialize spinlock
*
* Initialize spinlock.
*
* @param sl Pointer to spinlock_t structure.
*/
void spinlock_initialize(spinlock_t *sl, char *name)
{
atomic_set(&sl->val, 0);
#ifdef CONFIG_DEBUG_SPINLOCK
sl->name = name;
#endif
}
 
/** Lock spinlock
*
* Lock spinlock.
* This version has limitted ability to report
* possible occurence of deadlock.
*
* @param sl Pointer to spinlock_t structure.
*/
#ifdef CONFIG_DEBUG_SPINLOCK
void spinlock_lock_debug(spinlock_t *sl)
{
size_t i = 0;
bool deadlock_reported = false;
 
preemption_disable();
while (test_and_set(&sl->val)) {
 
/*
* We need to be careful about printf_lock and fb_lock.
* Both of them are used to report deadlocks via
* printf() and fb_putchar().
*
* We trust our code that there is no possible deadlock
* caused by these two locks (except when an exception
* is triggered for instance by printf() or fb_putchar()).
* However, we encountered false positives caused by very
* slow VESA framebuffer interaction (especially when
* run in a simulator) that caused problems with both
* printf_lock and fb_lock.
*
* Possible deadlocks on both printf_lock and fb_lock
* are therefore not reported as they would cause an
* infinite recursion.
*/
if (sl == &printf_lock)
continue;
#ifdef CONFIG_FB
if (sl == &fb_lock)
continue;
#endif
if (i++ > DEADLOCK_THRESHOLD) {
printf("cpu%u: looping on spinlock %" PRIp ":%s, "
"caller=%" PRIp "(%s)\n", CPU->id, sl, sl->name,
CALLER, symtab_fmt_name_lookup(CALLER));
i = 0;
deadlock_reported = true;
}
}
 
if (deadlock_reported)
printf("cpu%u: not deadlocked\n", CPU->id);
 
/*
* Prevent critical section code from bleeding out this way up.
*/
CS_ENTER_BARRIER();
}
#endif
 
/** Lock spinlock conditionally
*
* Lock spinlock conditionally.
* If the spinlock is not available at the moment,
* signal failure.
*
* @param sl Pointer to spinlock_t structure.
*
* @return Zero on failure, non-zero otherwise.
*/
int spinlock_trylock(spinlock_t *sl)
{
int rc;
preemption_disable();
rc = !test_and_set(&sl->val);
 
/*
* Prevent critical section code from bleeding out this way up.
*/
CS_ENTER_BARRIER();
 
if (!rc)
preemption_enable();
return rc;
}
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/waitq.c
0,0 → 1,462
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
 
/**
* @file
* @brief Wait queue.
*
* Wait queue is the basic synchronization primitive upon which all
* other synchronization primitives build.
*
* It allows threads to wait for an event in first-come, first-served
* fashion. Conditional operation as well as timeouts and interruptions
* are supported.
*/
 
#include <synch/waitq.h>
#include <synch/synch.h>
#include <synch/spinlock.h>
#include <proc/thread.h>
#include <proc/scheduler.h>
#include <arch/asm.h>
#include <arch/types.h>
#include <time/timeout.h>
#include <arch.h>
#include <context.h>
#include <adt/list.h>
 
static void waitq_sleep_timed_out(void *data);
 
/** Initialize wait queue
*
* Initialize wait queue.
*
* @param wq Pointer to wait queue to be initialized.
*/
void waitq_initialize(waitq_t *wq)
{
spinlock_initialize(&wq->lock, "waitq_lock");
list_initialize(&wq->head);
wq->missed_wakeups = 0;
}
 
/** Handle timeout during waitq_sleep_timeout() call
*
* This routine is called when waitq_sleep_timeout() times out.
* Interrupts are disabled.
*
* It is supposed to try to remove 'its' thread from the wait queue;
* it can eventually fail to achieve this goal when these two events
* overlap. In that case it behaves just as though there was no
* timeout at all.
*
* @param data Pointer to the thread that called waitq_sleep_timeout().
*/
void waitq_sleep_timed_out(void *data)
{
thread_t *t = (thread_t *) data;
waitq_t *wq;
bool do_wakeup = false;
DEADLOCK_PROBE_INIT(p_wqlock);
 
spinlock_lock(&threads_lock);
if (!thread_exists(t))
goto out;
 
grab_locks:
spinlock_lock(&t->lock);
if ((wq = t->sleep_queue)) { /* assignment */
if (!spinlock_trylock(&wq->lock)) {
spinlock_unlock(&t->lock);
DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
goto grab_locks; /* avoid deadlock */
}
 
list_remove(&t->wq_link);
t->saved_context = t->sleep_timeout_context;
do_wakeup = true;
t->sleep_queue = NULL;
spinlock_unlock(&wq->lock);
}
t->timeout_pending = false;
spinlock_unlock(&t->lock);
if (do_wakeup)
thread_ready(t);
 
out:
spinlock_unlock(&threads_lock);
}
 
/** Interrupt sleeping thread.
*
* This routine attempts to interrupt a thread from its sleep in a waitqueue.
* If the thread is not found sleeping, no action is taken.
*
* @param t Thread to be interrupted.
*/
void waitq_interrupt_sleep(thread_t *t)
{
waitq_t *wq;
bool do_wakeup = false;
ipl_t ipl;
DEADLOCK_PROBE_INIT(p_wqlock);
 
ipl = interrupts_disable();
spinlock_lock(&threads_lock);
if (!thread_exists(t))
goto out;
 
grab_locks:
spinlock_lock(&t->lock);
if ((wq = t->sleep_queue)) { /* assignment */
if (!(t->sleep_interruptible)) {
/*
* The sleep cannot be interrupted.
*/
spinlock_unlock(&t->lock);
goto out;
}
if (!spinlock_trylock(&wq->lock)) {
spinlock_unlock(&t->lock);
DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
goto grab_locks; /* avoid deadlock */
}
 
if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
t->timeout_pending = false;
 
list_remove(&t->wq_link);
t->saved_context = t->sleep_interruption_context;
do_wakeup = true;
t->sleep_queue = NULL;
spinlock_unlock(&wq->lock);
}
spinlock_unlock(&t->lock);
 
if (do_wakeup)
thread_ready(t);
 
out:
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
}
 
/** Sleep until either wakeup, timeout or interruption occurs
*
* This is a sleep implementation which allows itself to time out or to be
* interrupted from the sleep, restoring a failover context.
*
* Sleepers are organised in a FIFO fashion in a structure called wait queue.
*
* This function is really basic in that other functions as waitq_sleep()
* and all the *_timeout() functions use it.
*
* @param wq Pointer to wait queue.
* @param usec Timeout in microseconds.
* @param flags Specify mode of the sleep.
*
* The sleep can be interrupted only if the
* SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
*
* If usec is greater than zero, regardless of the value of the
* SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either
* timeout, interruption or wakeup comes.
*
* If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags,
* the call will not return until wakeup or interruption comes.
*
* If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is set in flags, the
* call will immediately return, reporting either success or failure.
*
* @return Returns one of ESYNCH_WOULD_BLOCK, ESYNCH_TIMEOUT,
* ESYNCH_INTERRUPTED, ESYNCH_OK_ATOMIC and
* ESYNCH_OK_BLOCKED.
*
* @li ESYNCH_WOULD_BLOCK means that the sleep failed because at the time of
* the call there was no pending wakeup.
*
* @li ESYNCH_TIMEOUT means that the sleep timed out.
*
* @li ESYNCH_INTERRUPTED means that somebody interrupted the sleeping thread.
*
* @li ESYNCH_OK_ATOMIC means that the sleep succeeded and that there was
* a pending wakeup at the time of the call. The caller was not put
* asleep at all.
*
* @li ESYNCH_OK_BLOCKED means that the sleep succeeded; the full sleep was
* attempted.
*/
int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, int flags)
{
ipl_t ipl;
int rc;
ipl = waitq_sleep_prepare(wq);
rc = waitq_sleep_timeout_unsafe(wq, usec, flags);
waitq_sleep_finish(wq, rc, ipl);
return rc;
}
 
/** Prepare to sleep in a waitq.
*
* This function will return holding the lock of the wait queue
* and interrupts disabled.
*
* @param wq Wait queue.
*
* @return Interrupt level as it existed on entry to this function.
*/
ipl_t waitq_sleep_prepare(waitq_t *wq)
{
ipl_t ipl;
restart:
ipl = interrupts_disable();
 
if (THREAD) { /* needed during system initiailzation */
/*
* Busy waiting for a delayed timeout.
* This is an important fix for the race condition between
* a delayed timeout and a next call to waitq_sleep_timeout().
* Simply, the thread is not allowed to go to sleep if
* there are timeouts in progress.
*/
spinlock_lock(&THREAD->lock);
if (THREAD->timeout_pending) {
spinlock_unlock(&THREAD->lock);
interrupts_restore(ipl);
goto restart;
}
spinlock_unlock(&THREAD->lock);
}
spinlock_lock(&wq->lock);
return ipl;
}
 
/** Finish waiting in a wait queue.
*
* This function restores interrupts to the state that existed prior
* to the call to waitq_sleep_prepare(). If necessary, the wait queue
* lock is released.
*
* @param wq Wait queue.
* @param rc Return code of waitq_sleep_timeout_unsafe().
* @param ipl Interrupt level returned by waitq_sleep_prepare().
*/
void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl)
{
switch (rc) {
case ESYNCH_WOULD_BLOCK:
case ESYNCH_OK_ATOMIC:
spinlock_unlock(&wq->lock);
break;
default:
break;
}
interrupts_restore(ipl);
}
 
/** Internal implementation of waitq_sleep_timeout().
*
* This function implements logic of sleeping in a wait queue.
* This call must be preceded by a call to waitq_sleep_prepare()
* and followed by a call to waitq_sleep_finish().
*
* @param wq See waitq_sleep_timeout().
* @param usec See waitq_sleep_timeout().
* @param flags See waitq_sleep_timeout().
*
* @return See waitq_sleep_timeout().
*/
int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags)
{
/* checks whether to go to sleep at all */
if (wq->missed_wakeups) {
wq->missed_wakeups--;
return ESYNCH_OK_ATOMIC;
}
else {
if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) {
/* return immediatelly instead of going to sleep */
return ESYNCH_WOULD_BLOCK;
}
}
/*
* Now we are firmly decided to go to sleep.
*/
spinlock_lock(&THREAD->lock);
 
if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
 
/*
* If the thread was already interrupted,
* don't go to sleep at all.
*/
if (THREAD->interrupted) {
spinlock_unlock(&THREAD->lock);
spinlock_unlock(&wq->lock);
return ESYNCH_INTERRUPTED;
}
 
/*
* Set context that will be restored if the sleep
* of this thread is ever interrupted.
*/
THREAD->sleep_interruptible = true;
if (!context_save(&THREAD->sleep_interruption_context)) {
/* Short emulation of scheduler() return code. */
spinlock_unlock(&THREAD->lock);
return ESYNCH_INTERRUPTED;
}
 
} else {
THREAD->sleep_interruptible = false;
}
 
if (usec) {
/* We use the timeout variant. */
if (!context_save(&THREAD->sleep_timeout_context)) {
/* Short emulation of scheduler() return code. */
spinlock_unlock(&THREAD->lock);
return ESYNCH_TIMEOUT;
}
THREAD->timeout_pending = true;
timeout_register(&THREAD->sleep_timeout, (uint64_t) usec,
waitq_sleep_timed_out, THREAD);
}
 
list_append(&THREAD->wq_link, &wq->head);
 
/*
* Suspend execution.
*/
THREAD->state = Sleeping;
THREAD->sleep_queue = wq;
 
spinlock_unlock(&THREAD->lock);
 
/* wq->lock is released in scheduler_separated_stack() */
scheduler();
return ESYNCH_OK_BLOCKED;
}
 
 
/** Wake up first thread sleeping in a wait queue
*
* Wake up first thread sleeping in a wait queue. This is the SMP- and IRQ-safe
* wrapper meant for general use.
*
* Besides its 'normal' wakeup operation, it attempts to unregister possible
* timeout.
*
* @param wq Pointer to wait queue.
* @param mode Wakeup mode.
*/
void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
{
ipl_t ipl;
 
ipl = interrupts_disable();
spinlock_lock(&wq->lock);
 
_waitq_wakeup_unsafe(wq, mode);
 
spinlock_unlock(&wq->lock);
interrupts_restore(ipl);
}
 
/** Internal SMP- and IRQ-unsafe version of waitq_wakeup()
*
* This is the internal SMP- and IRQ-unsafe version of waitq_wakeup(). It
* assumes wq->lock is already locked and interrupts are already disabled.
*
* @param wq Pointer to wait queue.
* @param mode If mode is WAKEUP_FIRST, then the longest waiting
* thread, if any, is woken up. If mode is WAKEUP_ALL, then
* all waiting threads, if any, are woken up. If there are
* no waiting threads to be woken up, the missed wakeup is
* recorded in the wait queue.
*/
void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
{
thread_t *t;
size_t count = 0;
 
loop:
if (list_empty(&wq->head)) {
wq->missed_wakeups++;
if (count && mode == WAKEUP_ALL)
wq->missed_wakeups--;
return;
}
 
count++;
t = list_get_instance(wq->head.next, thread_t, wq_link);
/*
* Lock the thread prior to removing it from the wq.
* This is not necessary because of mutual exclusion
* (the link belongs to the wait queue), but because
* of synchronization with waitq_sleep_timed_out()
* and thread_interrupt_sleep().
*
* In order for these two functions to work, the following
* invariant must hold:
*
* t->sleep_queue != NULL <=> t sleeps in a wait queue
*
* For an observer who locks the thread, the invariant
* holds only when the lock is held prior to removing
* it from the wait queue.
*/
spinlock_lock(&t->lock);
list_remove(&t->wq_link);
if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
t->timeout_pending = false;
t->sleep_queue = NULL;
spinlock_unlock(&t->lock);
 
thread_ready(t);
 
if (mode == WAKEUP_ALL)
goto loop;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/futex.c
0,0 → 1,354
/*
* Copyright (c) 2006 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 sync
* @{
*/
 
/**
* @file
* @brief Kernel backend for futexes.
*/
 
#include <synch/futex.h>
#include <synch/rwlock.h>
#include <synch/spinlock.h>
#include <synch/synch.h>
#include <mm/frame.h>
#include <mm/page.h>
#include <mm/slab.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <genarch/mm/page_pt.h>
#include <genarch/mm/page_ht.h>
#include <adt/hash_table.h>
#include <adt/list.h>
#include <arch.h>
#include <align.h>
#include <panic.h>
#include <errno.h>
#include <print.h>
 
#define FUTEX_HT_SIZE 1024 /* keep it a power of 2 */
 
static void futex_initialize(futex_t *futex);
 
static futex_t *futex_find(uintptr_t paddr);
static size_t futex_ht_hash(unative_t *key);
static bool futex_ht_compare(unative_t *key, size_t keys, link_t *item);
static void futex_ht_remove_callback(link_t *item);
 
/**
* Read-write lock protecting global futex hash table.
* It is also used to serialize access to all futex_t structures.
* Must be acquired before the task futex B+tree lock.
*/
static rwlock_t futex_ht_lock;
 
/** Futex hash table. */
static hash_table_t futex_ht;
 
/** Futex hash table operations. */
static hash_table_operations_t futex_ht_ops = {
.hash = futex_ht_hash,
.compare = futex_ht_compare,
.remove_callback = futex_ht_remove_callback
};
 
/** Initialize futex subsystem. */
void futex_init(void)
{
rwlock_initialize(&futex_ht_lock);
hash_table_create(&futex_ht, FUTEX_HT_SIZE, 1, &futex_ht_ops);
}
 
/** Initialize kernel futex structure.
*
* @param futex Kernel futex structure.
*/
void futex_initialize(futex_t *futex)
{
waitq_initialize(&futex->wq);
link_initialize(&futex->ht_link);
futex->paddr = 0;
futex->refcount = 1;
}
 
/** Sleep in futex wait queue.
*
* @param uaddr Userspace address of the futex counter.
* @param usec If non-zero, number of microseconds this thread is willing to
* sleep.
* @param flags Select mode of operation.
*
* @return One of ESYNCH_TIMEOUT, ESYNCH_OK_ATOMIC and ESYNCH_OK_BLOCKED. See
* synch.h. If there is no physical mapping for uaddr ENOENT is returned.
*/
unative_t sys_futex_sleep_timeout(uintptr_t uaddr, uint32_t usec, int flags)
{
futex_t *futex;
uintptr_t paddr;
pte_t *t;
ipl_t ipl;
int rc;
ipl = interrupts_disable();
 
/*
* Find physical address of futex counter.
*/
page_table_lock(AS, true);
t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE));
if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
page_table_unlock(AS, true);
interrupts_restore(ipl);
return (unative_t) ENOENT;
}
paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
page_table_unlock(AS, true);
interrupts_restore(ipl);
 
futex = futex_find(paddr);
 
#ifdef CONFIG_UDEBUG
udebug_stoppable_begin();
#endif
rc = waitq_sleep_timeout(&futex->wq, usec, flags |
SYNCH_FLAGS_INTERRUPTIBLE);
 
#ifdef CONFIG_UDEBUG
udebug_stoppable_end();
#endif
return (unative_t) rc;
}
 
/** Wakeup one thread waiting in futex wait queue.
*
* @param uaddr Userspace address of the futex counter.
*
* @return ENOENT if there is no physical mapping for uaddr.
*/
unative_t sys_futex_wakeup(uintptr_t uaddr)
{
futex_t *futex;
uintptr_t paddr;
pte_t *t;
ipl_t ipl;
ipl = interrupts_disable();
/*
* Find physical address of futex counter.
*/
page_table_lock(AS, true);
t = page_mapping_find(AS, ALIGN_DOWN(uaddr, PAGE_SIZE));
if (!t || !PTE_VALID(t) || !PTE_PRESENT(t)) {
page_table_unlock(AS, true);
interrupts_restore(ipl);
return (unative_t) ENOENT;
}
paddr = PTE_GET_FRAME(t) + (uaddr - ALIGN_DOWN(uaddr, PAGE_SIZE));
page_table_unlock(AS, true);
interrupts_restore(ipl);
 
futex = futex_find(paddr);
waitq_wakeup(&futex->wq, WAKEUP_FIRST);
return 0;
}
 
/** Find kernel address of the futex structure corresponding to paddr.
*
* If the structure does not exist already, a new one is created.
*
* @param paddr Physical address of the userspace futex counter.
*
* @return Address of the kernel futex structure.
*/
futex_t *futex_find(uintptr_t paddr)
{
link_t *item;
futex_t *futex;
btree_node_t *leaf;
/*
* Find the respective futex structure
* or allocate new one if it does not exist already.
*/
rwlock_read_lock(&futex_ht_lock);
item = hash_table_find(&futex_ht, &paddr);
if (item) {
futex = hash_table_get_instance(item, futex_t, ht_link);
 
/*
* See if the current task knows this futex.
*/
mutex_lock(&TASK->futexes_lock);
if (!btree_search(&TASK->futexes, paddr, &leaf)) {
/*
* The futex is new to the current task.
* However, we only have read access.
* Gain write access and try again.
*/
mutex_unlock(&TASK->futexes_lock);
goto gain_write_access;
}
mutex_unlock(&TASK->futexes_lock);
 
rwlock_read_unlock(&futex_ht_lock);
} else {
gain_write_access:
/*
* Upgrade to writer is not currently supported,
* therefore, it is necessary to release the read lock
* and reacquire it as a writer.
*/
rwlock_read_unlock(&futex_ht_lock);
 
rwlock_write_lock(&futex_ht_lock);
/*
* Avoid possible race condition by searching
* the hash table once again with write access.
*/
item = hash_table_find(&futex_ht, &paddr);
if (item) {
futex = hash_table_get_instance(item, futex_t, ht_link);
/*
* See if this futex is known to the current task.
*/
mutex_lock(&TASK->futexes_lock);
if (!btree_search(&TASK->futexes, paddr, &leaf)) {
/*
* The futex is new to the current task.
* Upgrade its reference count and put it to the
* current task's B+tree of known futexes.
*/
futex->refcount++;
btree_insert(&TASK->futexes, paddr, futex,
leaf);
}
mutex_unlock(&TASK->futexes_lock);
rwlock_write_unlock(&futex_ht_lock);
} else {
futex = (futex_t *) malloc(sizeof(futex_t), 0);
futex_initialize(futex);
futex->paddr = paddr;
hash_table_insert(&futex_ht, &paddr, &futex->ht_link);
/*
* This is the first task referencing the futex.
* It can be directly inserted into its
* B+tree of known futexes.
*/
mutex_lock(&TASK->futexes_lock);
btree_insert(&TASK->futexes, paddr, futex, NULL);
mutex_unlock(&TASK->futexes_lock);
rwlock_write_unlock(&futex_ht_lock);
}
}
return futex;
}
 
/** Compute hash index into futex hash table.
*
* @param key Address where the key (i.e. physical address of futex counter) is
* stored.
*
* @return Index into futex hash table.
*/
size_t futex_ht_hash(unative_t *key)
{
return (*key & (FUTEX_HT_SIZE - 1));
}
 
/** Compare futex hash table item with a key.
*
* @param key Address where the key (i.e. physical address of futex counter) is
* stored.
*
* @return True if the item matches the key. False otherwise.
*/
bool futex_ht_compare(unative_t *key, size_t keys, link_t *item)
{
futex_t *futex;
 
ASSERT(keys == 1);
 
futex = hash_table_get_instance(item, futex_t, ht_link);
return *key == futex->paddr;
}
 
/** Callback for removal items from futex hash table.
*
* @param item Item removed from the hash table.
*/
void futex_ht_remove_callback(link_t *item)
{
futex_t *futex;
 
futex = hash_table_get_instance(item, futex_t, ht_link);
free(futex);
}
 
/** Remove references from futexes known to the current task. */
void futex_cleanup(void)
{
link_t *cur;
rwlock_write_lock(&futex_ht_lock);
mutex_lock(&TASK->futexes_lock);
 
for (cur = TASK->futexes.leaf_head.next;
cur != &TASK->futexes.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
futex_t *ftx;
uintptr_t paddr = node->key[i];
ftx = (futex_t *) node->value[i];
if (--ftx->refcount == 0)
hash_table_remove(&futex_ht, &paddr, 1);
}
}
mutex_unlock(&TASK->futexes_lock);
rwlock_write_unlock(&futex_ht_lock);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/rwlock.c
0,0 → 1,393
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
 
/**
* @file
* @brief Reader/Writer locks.
*
* A reader/writer lock can be held by multiple readers at a time.
* Or it can be exclusively held by a sole writer at a time.
*
* These locks are not recursive.
* Because a technique called direct hand-off is used and because
* waiting takes place in a single wait queue, neither readers
* nor writers will suffer starvation.
*
* If there is a writer followed by a reader waiting for the rwlock
* and the writer times out, all leading readers are automatically woken up
* and allowed in.
*/
 
/*
* NOTE ON rwlock_holder_type
* This field is set on an attempt to acquire the exclusive mutex
* to the respective value depending whether the caller is a reader
* or a writer. The field is examined only if the thread had been
* previously blocked on the exclusive mutex. Thus it is save
* to store the rwlock type in the thread structure, because
* each thread can block on only one rwlock at a time.
*/
#include <synch/rwlock.h>
#include <synch/spinlock.h>
#include <synch/mutex.h>
#include <synch/waitq.h>
#include <synch/synch.h>
#include <adt/list.h>
#include <arch/asm.h>
#include <arch.h>
#include <proc/thread.h>
#include <panic.h>
 
#define ALLOW_ALL 0
#define ALLOW_READERS_ONLY 1
 
static void let_others_in(rwlock_t *rwl, int readers_only);
static void release_spinlock(void *arg);
 
/** Initialize reader/writer lock
*
* Initialize reader/writer lock.
*
* @param rwl Reader/Writer lock.
*/
void rwlock_initialize(rwlock_t *rwl) {
spinlock_initialize(&rwl->lock, "rwlock_t");
mutex_initialize(&rwl->exclusive, MUTEX_PASSIVE);
rwl->readers_in = 0;
}
 
/** Acquire reader/writer lock for reading
*
* Acquire reader/writer lock for reading.
* Timeout and willingness to block may be specified.
*
* @param rwl Reader/Writer lock.
* @param usec Timeout in microseconds.
* @param flags Specify mode of operation.
*
* For exact description of possible combinations of
* usec and flags, see comment for waitq_sleep_timeout().
*
* @return See comment for waitq_sleep_timeout().
*/
int _rwlock_write_lock_timeout(rwlock_t *rwl, uint32_t usec, int flags)
{
ipl_t ipl;
int rc;
ipl = interrupts_disable();
spinlock_lock(&THREAD->lock);
THREAD->rwlock_holder_type = RWLOCK_WRITER;
spinlock_unlock(&THREAD->lock);
interrupts_restore(ipl);
 
/*
* Writers take the easy part.
* They just need to acquire the exclusive mutex.
*/
rc = _mutex_lock_timeout(&rwl->exclusive, usec, flags);
if (SYNCH_FAILED(rc)) {
 
/*
* Lock operation timed out or was interrupted.
* The state of rwl is UNKNOWN at this point.
* No claims about its holder can be made.
*/
ipl = interrupts_disable();
spinlock_lock(&rwl->lock);
/*
* Now when rwl is locked, we can inspect it again.
* If it is held by some readers already, we can let
* readers from the head of the wait queue in.
*/
if (rwl->readers_in)
let_others_in(rwl, ALLOW_READERS_ONLY);
spinlock_unlock(&rwl->lock);
interrupts_restore(ipl);
}
return rc;
}
 
/** Acquire reader/writer lock for writing
*
* Acquire reader/writer lock for writing.
* Timeout and willingness to block may be specified.
*
* @param rwl Reader/Writer lock.
* @param usec Timeout in microseconds.
* @param flags Select mode of operation.
*
* For exact description of possible combinations of
* usec and flags, see comment for waitq_sleep_timeout().
*
* @return See comment for waitq_sleep_timeout().
*/
int _rwlock_read_lock_timeout(rwlock_t *rwl, uint32_t usec, int flags)
{
int rc;
ipl_t ipl;
ipl = interrupts_disable();
spinlock_lock(&THREAD->lock);
THREAD->rwlock_holder_type = RWLOCK_READER;
spinlock_unlock(&THREAD->lock);
 
spinlock_lock(&rwl->lock);
 
/*
* Find out whether we can get what we want without blocking.
*/
rc = mutex_trylock(&rwl->exclusive);
if (SYNCH_FAILED(rc)) {
 
/*
* 'exclusive' mutex is being held by someone else.
* If the holder is a reader and there is no one
* else waiting for it, we can enter the critical
* section.
*/
 
if (rwl->readers_in) {
spinlock_lock(&rwl->exclusive.sem.wq.lock);
if (list_empty(&rwl->exclusive.sem.wq.head)) {
/*
* We can enter.
*/
spinlock_unlock(&rwl->exclusive.sem.wq.lock);
goto shortcut;
}
spinlock_unlock(&rwl->exclusive.sem.wq.lock);
}
 
/*
* In order to prevent a race condition when a reader
* could block another reader at the head of the waitq,
* we register a function to unlock rwl->lock
* after this thread is put asleep.
*/
#ifdef CONFIG_SMP
thread_register_call_me(release_spinlock, &rwl->lock);
#else
thread_register_call_me(release_spinlock, NULL);
#endif
rc = _mutex_lock_timeout(&rwl->exclusive, usec, flags);
switch (rc) {
case ESYNCH_WOULD_BLOCK:
/*
* release_spinlock() wasn't called
*/
thread_register_call_me(NULL, NULL);
spinlock_unlock(&rwl->lock);
case ESYNCH_TIMEOUT:
case ESYNCH_INTERRUPTED:
/*
* The sleep timed out.
* We just restore interrupt priority level.
*/
case ESYNCH_OK_BLOCKED:
/*
* We were woken with rwl->readers_in already
* incremented.
*
* Note that this arrangement avoids race condition
* between two concurrent readers. (Race is avoided if
* 'exclusive' is locked at the same time as
* 'readers_in' is incremented. Same time means both
* events happen atomically when rwl->lock is held.)
*/
interrupts_restore(ipl);
break;
case ESYNCH_OK_ATOMIC:
panic("_mutex_lock_timeout() == ESYNCH_OK_ATOMIC.");
break;
default:
panic("Invalid ESYNCH.");
break;
}
return rc;
}
 
shortcut:
 
/*
* We can increment readers_in only if we didn't go to sleep.
* For sleepers, rwlock_let_others_in() will do the job.
*/
rwl->readers_in++;
spinlock_unlock(&rwl->lock);
interrupts_restore(ipl);
 
return ESYNCH_OK_ATOMIC;
}
 
/** Release reader/writer lock held by writer
*
* Release reader/writer lock held by writer.
* Handoff reader/writer lock ownership directly
* to waiting readers or a writer.
*
* @param rwl Reader/Writer lock.
*/
void rwlock_write_unlock(rwlock_t *rwl)
{
ipl_t ipl;
ipl = interrupts_disable();
spinlock_lock(&rwl->lock);
let_others_in(rwl, ALLOW_ALL);
spinlock_unlock(&rwl->lock);
interrupts_restore(ipl);
}
 
/** Release reader/writer lock held by reader
*
* Release reader/writer lock held by reader.
* Handoff reader/writer lock ownership directly
* to a waiting writer or don't do anything if more
* readers poses the lock.
*
* @param rwl Reader/Writer lock.
*/
void rwlock_read_unlock(rwlock_t *rwl)
{
ipl_t ipl;
 
ipl = interrupts_disable();
spinlock_lock(&rwl->lock);
if (!--rwl->readers_in)
let_others_in(rwl, ALLOW_ALL);
spinlock_unlock(&rwl->lock);
interrupts_restore(ipl);
}
 
 
/** Direct handoff of reader/writer lock ownership.
*
* Direct handoff of reader/writer lock ownership
* to waiting readers or a writer.
*
* Must be called with rwl->lock locked.
* Must be called with interrupts_disable()'d.
*
* @param rwl Reader/Writer lock.
* @param readers_only See the description below.
*
* If readers_only is false: (unlock scenario)
* Let the first sleeper on 'exclusive' mutex in, no matter
* whether it is a reader or a writer. If there are more leading
* readers in line, let each of them in.
*
* Otherwise: (timeout scenario)
* Let all leading readers in.
*/
void let_others_in(rwlock_t *rwl, int readers_only)
{
rwlock_type_t type = RWLOCK_NONE;
thread_t *t = NULL;
bool one_more = true;
spinlock_lock(&rwl->exclusive.sem.wq.lock);
 
if (!list_empty(&rwl->exclusive.sem.wq.head))
t = list_get_instance(rwl->exclusive.sem.wq.head.next, thread_t,
wq_link);
do {
if (t) {
spinlock_lock(&t->lock);
type = t->rwlock_holder_type;
spinlock_unlock(&t->lock);
}
/*
* If readers_only is true, we wake all leading readers
* if and only if rwl is locked by another reader.
* Assumption: readers_only ==> rwl->readers_in
*/
if (readers_only && (type != RWLOCK_READER))
break;
 
 
if (type == RWLOCK_READER) {
/*
* Waking up a reader.
* We are responsible for incrementing rwl->readers_in
* for it.
*/
rwl->readers_in++;
}
 
/*
* Only the last iteration through this loop can increment
* rwl->exclusive.sem.wq.missed_wakeup's. All preceeding
* iterations will wake up a thread.
*/
/* We call the internal version of waitq_wakeup, which
* relies on the fact that the waitq is already locked.
*/
_waitq_wakeup_unsafe(&rwl->exclusive.sem.wq, WAKEUP_FIRST);
t = NULL;
if (!list_empty(&rwl->exclusive.sem.wq.head)) {
t = list_get_instance(rwl->exclusive.sem.wq.head.next,
thread_t, wq_link);
if (t) {
spinlock_lock(&t->lock);
if (t->rwlock_holder_type != RWLOCK_READER)
one_more = false;
spinlock_unlock(&t->lock);
}
}
} while ((type == RWLOCK_READER) && t && one_more);
 
spinlock_unlock(&rwl->exclusive.sem.wq.lock);
}
 
/** Release spinlock callback
*
* This is a callback function invoked from the scheduler.
* The callback is registered in _rwlock_read_lock_timeout().
*
* @param arg Spinlock.
*/
void release_spinlock(void *arg)
{
spinlock_unlock((spinlock_t *) arg);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/smc.c
0,0 → 1,60
/*
* 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 sync
* @{
*/
 
/**
* @file
* @brief Self-modifying code barriers.
*/
 
#include <arch.h>
#include <macros.h>
#include <errno.h>
#include <arch/barrier.h>
#include <synch/smc.h>
 
unative_t sys_smc_coherence(uintptr_t va, size_t size)
{
if (overlaps(va, size, NULL, PAGE_SIZE))
return EINVAL;
 
if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
if (overlaps(va, size, KERNEL_ADDRESS_SPACE_START,
KERNEL_ADDRESS_SPACE_END - KERNEL_ADDRESS_SPACE_START))
return EINVAL;
}
 
smc_coherence_block((void *) va, size);
return 0;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/mutex.c
0,0 → 1,96
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
 
/**
* @file
* @brief Mutexes.
*/
#include <synch/mutex.h>
#include <synch/semaphore.h>
#include <synch/synch.h>
#include <debug.h>
 
/** Initialize mutex.
*
* @param mtx Mutex.
* @param type Type of the mutex.
*/
void mutex_initialize(mutex_t *mtx, mutex_type_t type)
{
mtx->type = type;
semaphore_initialize(&mtx->sem, 1);
}
 
/** Acquire mutex.
*
* Timeout mode and non-blocking mode can be requested.
*
* @param mtx Mutex.
* @param usec Timeout in microseconds.
* @param flags Specify mode of operation.
*
* For exact description of possible combinations of
* usec and flags, see comment for waitq_sleep_timeout().
*
* @return See comment for waitq_sleep_timeout().
*/
int _mutex_lock_timeout(mutex_t *mtx, uint32_t usec, int flags)
{
int rc;
 
if (mtx->type == MUTEX_PASSIVE) {
rc = _semaphore_down_timeout(&mtx->sem, usec, flags);
} else {
ASSERT(mtx->type == MUTEX_ACTIVE);
ASSERT(usec == SYNCH_NO_TIMEOUT);
ASSERT(!(flags & SYNCH_FLAGS_INTERRUPTIBLE));
do {
rc = semaphore_trydown(&mtx->sem);
} while (SYNCH_FAILED(rc) &&
!(flags & SYNCH_FLAGS_NON_BLOCKING));
}
 
return rc;
}
 
/** Release mutex.
*
* @param mtx Mutex.
*/
void mutex_unlock(mutex_t *mtx)
{
semaphore_up(&mtx->sem);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/condvar.c
0,0 → 1,105
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
 
/**
* @file
* @brief Condition variables.
*/
 
#include <synch/condvar.h>
#include <synch/mutex.h>
#include <synch/waitq.h>
#include <synch/synch.h>
#include <arch.h>
 
/** Initialize condition variable.
*
* @param cv Condition variable.
*/
void condvar_initialize(condvar_t *cv)
{
waitq_initialize(&cv->wq);
}
 
/** Signal the condition has become true to the first waiting thread by waking
* it up.
*
* @param cv Condition variable.
*/
void condvar_signal(condvar_t *cv)
{
waitq_wakeup(&cv->wq, WAKEUP_FIRST);
}
 
/** Signal the condition has become true to all waiting threads by waking
* them up.
*
* @param cv Condition variable.
*/
void condvar_broadcast(condvar_t *cv)
{
waitq_wakeup(&cv->wq, WAKEUP_ALL);
}
 
/** Wait for the condition becoming true.
*
* @param cv Condition variable.
* @param mtx Mutex.
* @param usec Timeout value in microseconds.
* @param flags Select mode of operation.
*
* For exact description of meaning of possible combinations of usec and flags,
* see comment for waitq_sleep_timeout(). Note that when
* SYNCH_FLAGS_NON_BLOCKING is specified here, ESYNCH_WOULD_BLOCK is always
* returned.
*
* @return See comment for waitq_sleep_timeout().
*/
int _condvar_wait_timeout(condvar_t *cv, mutex_t *mtx, uint32_t usec, int flags)
{
int rc;
ipl_t ipl;
 
ipl = waitq_sleep_prepare(&cv->wq);
mutex_unlock(mtx);
 
cv->wq.missed_wakeups = 0; /* Enforce blocking. */
rc = waitq_sleep_timeout_unsafe(&cv->wq, usec, flags);
 
mutex_lock(mtx);
waitq_sleep_finish(&cv->wq, rc, ipl);
 
return rc;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/synch/semaphore.c
0,0 → 1,98
/*
* Copyright (c) 2001-2004 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 sync
* @{
*/
 
/**
* @file
* @brief Semaphores.
*/
 
#include <synch/semaphore.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
#include <synch/synch.h>
#include <arch/asm.h>
#include <arch.h>
 
/** Initialize semaphore
*
* Initialize semaphore.
*
* @param s Semaphore.
* @param val Maximal number of threads allowed to enter critical section.
*/
void semaphore_initialize(semaphore_t *s, int val)
{
ipl_t ipl;
waitq_initialize(&s->wq);
ipl = interrupts_disable();
 
spinlock_lock(&s->wq.lock);
s->wq.missed_wakeups = val;
spinlock_unlock(&s->wq.lock);
 
interrupts_restore(ipl);
}
 
/** Semaphore down
*
* Semaphore down.
* Conditional mode and mode with timeout can be requested.
*
* @param s Semaphore.
* @param usec Timeout in microseconds.
* @param flags Select mode of operation.
*
* For exact description of possible combinations of
* usec and flags, see comment for waitq_sleep_timeout().
*
* @return See comment for waitq_sleep_timeout().
*/
int _semaphore_down_timeout(semaphore_t *s, uint32_t usec, int flags)
{
return waitq_sleep_timeout(&s->wq, usec, flags);
}
 
/** Semaphore up
*
* Semaphore up.
*
* @param s Semaphore.
*/
void semaphore_up(semaphore_t *s)
{
waitq_wakeup(&s->wq, WAKEUP_FIRST);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/debug/symtab.c
0,0 → 1,254
/*
* Copyright (c) 2005 Ondrej Palkovsky
* 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 genericdebug
* @{
*/
 
/**
* @file
* @brief Kernel symbol resolver.
*/
 
#include <symtab.h>
#include <byteorder.h>
#include <string.h>
#include <print.h>
#include <arch/types.h>
#include <typedefs.h>
#include <errno.h>
 
/** Get name of a symbol that seems most likely to correspond to address.
*
* @param addr Address.
* @param name Place to store pointer to the symbol name.
*
* @return Zero on success or negative error code, ENOENT if not found,
* ENOTSUP if symbol table not available.
*
*/
int symtab_name_lookup(unative_t addr, char **name)
{
#ifdef CONFIG_SYMTAB
size_t i;
for (i = 1; symbol_table[i].address_le; i++) {
if (addr < uint64_t_le2host(symbol_table[i].address_le))
break;
}
if (addr >= uint64_t_le2host(symbol_table[i - 1].address_le)) {
*name = symbol_table[i - 1].symbol_name;
return EOK;
}
*name = NULL;
return ENOENT;
#else
*name = NULL;
return ENOTSUP;
#endif
}
 
/** Lookup symbol by address and format for display.
*
* Returns name of closest corresponding symbol, "Not found" if none exists
* or "N/A" if no symbol information is available.
*
* @param addr Address.
* @param name Place to store pointer to the symbol name.
*
* @return Pointer to a human-readable string.
*
*/
char *symtab_fmt_name_lookup(unative_t addr)
{
char *name;
int rc = symtab_name_lookup(addr, &name);
switch (rc) {
case EOK:
return name;
case ENOENT:
return "Not found";
default:
return "N/A";
}
}
 
#ifdef CONFIG_SYMTAB
 
/** Find symbols that match the parameter forward and print them.
*
* @param name Search string
* @param startpos Starting position, changes to found position
*
* @return Pointer to the part of string that should be completed or NULL.
*
*/
static const char *symtab_search_one(const char *name, size_t *startpos)
{
size_t namelen = str_length(name);
size_t pos;
for (pos = *startpos; symbol_table[pos].address_le; pos++) {
const char *curname = symbol_table[pos].symbol_name;
/* Find a ':' in curname */
const char *colon = str_chr(curname, ':');
if (colon == NULL)
continue;
if (str_length(curname) < namelen)
continue;
if (str_lcmp(name, curname, namelen) == 0) {
*startpos = pos;
return (curname + str_lsize(curname, namelen));
}
}
return NULL;
}
 
#endif
 
/** Return address that corresponds to the entry.
*
* Search symbol table, and if there is one match, return it
*
* @param name Name of the symbol
* @param addr Place to store symbol address
*
* @return Zero on success, ENOENT - not found, EOVERFLOW - duplicate
* symbol, ENOTSUP - no symbol information available.
*
*/
int symtab_addr_lookup(const char *name, uintptr_t *addr)
{
#ifdef CONFIG_SYMTAB
size_t found = 0;
size_t pos = 0;
const char *hint;
while ((hint = symtab_search_one(name, &pos))) {
if (str_length(hint) == 0) {
*addr = uint64_t_le2host(symbol_table[pos].address_le);
found++;
}
pos++;
}
if (found > 1)
return EOVERFLOW;
if (found < 1)
return ENOENT;
return EOK;
#else
return ENOTSUP;
#endif
}
 
/** Find symbols that match parameter and print them */
void symtab_print_search(const char *name)
{
#ifdef CONFIG_SYMTAB
size_t pos = 0;
while (symtab_search_one(name, &pos)) {
uintptr_t addr = uint64_t_le2host(symbol_table[pos].address_le);
char *realname = symbol_table[pos].symbol_name;
printf("%p: %s\n", addr, realname);
pos++;
}
#else
printf("No symbol information available.\n");
#endif
}
 
/** Symtab completion
*
* @param input Search string, completes to symbol name
* @param size Input buffer size
*
* @return 0 - nothing found, 1 - success, >1 print duplicates
*
*/
int symtab_compl(char *input, size_t size)
{
#ifdef CONFIG_SYMTAB
const char *name = input;
/* Allow completion of pointers */
if ((name[0] == '*') || (name[0] == '&'))
name++;
/* Do not print all symbols */
if (str_length(name) == 0)
return 0;
size_t found = 0;
size_t pos = 0;
const char *hint;
char output[MAX_SYMBOL_NAME];
output[0] = 0;
while ((hint = symtab_search_one(name, &pos))) {
if ((found == 0) || (str_length(output) > str_length(hint)))
str_cpy(output, MAX_SYMBOL_NAME, hint);
pos++;
found++;
}
if ((found > 1) && (str_length(output) != 0)) {
printf("\n");
pos = 0;
while ((hint = symtab_search_one(name, &pos))) {
printf("%s\n", symbol_table[pos].symbol_name);
pos++;
}
}
if (found > 0)
str_cpy(input, size, output);
return found;
#else
return 0;
#endif
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/time/clock.c
0,0 → 1,209
/*
* Copyright (c) 2001-2004 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 time
* @{
*/
 
/**
* @file
* @brief High-level clock interrupt handler.
*
* This file contains the clock() function which is the source
* of preemption. It is also responsible for executing expired
* timeouts.
*/
#include <time/clock.h>
#include <time/timeout.h>
#include <config.h>
#include <synch/spinlock.h>
#include <synch/waitq.h>
#include <func.h>
#include <proc/scheduler.h>
#include <cpu.h>
#include <arch.h>
#include <adt/list.h>
#include <atomic.h>
#include <proc/thread.h>
#include <sysinfo/sysinfo.h>
#include <arch/barrier.h>
#include <mm/frame.h>
#include <ddi/ddi.h>
 
/* Pointer to variable with uptime */
uptime_t *uptime;
 
/** Physical memory area of the real time clock */
static parea_t clock_parea;
 
/* Variable holding fragment of second, so that we would update
* seconds correctly
*/
static unative_t secfrag = 0;
 
/** Initialize realtime clock counter
*
* The applications (and sometimes kernel) need to access accurate
* information about realtime data. We allocate 1 page with these
* data and update it periodically.
*/
void clock_counter_init(void)
{
void *faddr;
 
faddr = frame_alloc(ONE_FRAME, FRAME_ATOMIC);
if (!faddr)
panic("Cannot allocate page for clock.");
uptime = (uptime_t *) PA2KA(faddr);
uptime->seconds1 = 0;
uptime->seconds2 = 0;
uptime->useconds = 0;
 
clock_parea.pbase = (uintptr_t) faddr;
clock_parea.frames = 1;
ddi_parea_register(&clock_parea);
 
/*
* Prepare information for the userspace so that it can successfully
* physmem_map() the clock_parea.
*/
sysinfo_set_item_val("clock.cacheable", NULL, (unative_t) true);
sysinfo_set_item_val("clock.faddr", NULL, (unative_t) faddr);
}
 
 
/** Update public counters
*
* Update it only on first processor
* TODO: Do we really need so many write barriers?
*/
static void clock_update_counters(void)
{
if (CPU->id == 0) {
secfrag += 1000000 / HZ;
if (secfrag >= 1000000) {
secfrag -= 1000000;
uptime->seconds1++;
write_barrier();
uptime->useconds = secfrag;
write_barrier();
uptime->seconds2 = uptime->seconds1;
} else
uptime->useconds += 1000000 / HZ;
}
}
 
/** Clock routine
*
* Clock routine executed from clock interrupt handler
* (assuming interrupts_disable()'d). Runs expired timeouts
* and preemptive scheduling.
*
*/
void clock(void)
{
link_t *l;
timeout_t *h;
timeout_handler_t f;
void *arg;
size_t missed_clock_ticks = CPU->missed_clock_ticks;
unsigned int i;
 
/*
* To avoid lock ordering problems,
* run all expired timeouts as you visit them.
*/
for (i = 0; i <= missed_clock_ticks; i++) {
clock_update_counters();
spinlock_lock(&CPU->timeoutlock);
while ((l = CPU->timeout_active_head.next) != &CPU->timeout_active_head) {
h = list_get_instance(l, timeout_t, link);
spinlock_lock(&h->lock);
if (h->ticks-- != 0) {
spinlock_unlock(&h->lock);
break;
}
list_remove(l);
f = h->handler;
arg = h->arg;
timeout_reinitialize(h);
spinlock_unlock(&h->lock);
spinlock_unlock(&CPU->timeoutlock);
 
f(arg);
 
spinlock_lock(&CPU->timeoutlock);
}
spinlock_unlock(&CPU->timeoutlock);
}
CPU->missed_clock_ticks = 0;
 
/*
* Do CPU usage accounting and find out whether to preempt THREAD.
*/
 
if (THREAD) {
uint64_t ticks;
spinlock_lock(&CPU->lock);
CPU->needs_relink += 1 + missed_clock_ticks;
spinlock_unlock(&CPU->lock);
spinlock_lock(&THREAD->lock);
if ((ticks = THREAD->ticks)) {
if (ticks >= 1 + missed_clock_ticks)
THREAD->ticks -= 1 + missed_clock_ticks;
else
THREAD->ticks = 0;
}
spinlock_unlock(&THREAD->lock);
if (!ticks && !PREEMPTION_DISABLED) {
#ifdef CONFIG_UDEBUG
istate_t *istate;
#endif
scheduler();
#ifdef CONFIG_UDEBUG
/*
* Give udebug chance to stop the thread
* before it begins executing userspace code.
*/
istate = THREAD->udebug.uspace_state;
if (istate && istate_from_uspace(istate))
udebug_before_thread_runs();
#endif
}
}
 
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/time/timeout.c
0,0 → 1,218
/*
* Copyright (c) 2001-2004 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 time
* @{
*/
 
/**
* @file
* @brief Timeout management functions.
*/
 
#include <time/timeout.h>
#include <arch/types.h>
#include <config.h>
#include <panic.h>
#include <synch/spinlock.h>
#include <func.h>
#include <cpu.h>
#include <arch/asm.h>
#include <arch.h>
 
/** Initialize timeouts
*
* Initialize kernel timeouts.
*
*/
void timeout_init(void)
{
spinlock_initialize(&CPU->timeoutlock, "timeout_lock");
list_initialize(&CPU->timeout_active_head);
}
 
 
/** Reinitialize timeout
*
* Initialize all members except the lock.
*
* @param t Timeout to be initialized.
*
*/
void timeout_reinitialize(timeout_t *t)
{
t->cpu = NULL;
t->ticks = 0;
t->handler = NULL;
t->arg = NULL;
link_initialize(&t->link);
}
 
 
/** Initialize timeout
*
* Initialize all members including the lock.
*
* @param t Timeout to be initialized.
*
*/
void timeout_initialize(timeout_t *t)
{
spinlock_initialize(&t->lock, "timeout_t_lock");
timeout_reinitialize(t);
}
 
 
/** Register timeout
*
* Insert timeout handler f (with argument arg)
* to timeout list and make it execute in
* time microseconds (or slightly more).
*
* @param t Timeout structure.
* @param time Number of usec in the future to execute the handler.
* @param f Timeout handler function.
* @param arg Timeout handler argument.
*
*/
void
timeout_register(timeout_t *t, uint64_t time, timeout_handler_t f, void *arg)
{
timeout_t *hlp = NULL;
link_t *l, *m;
ipl_t ipl;
uint64_t sum;
 
ipl = interrupts_disable();
spinlock_lock(&CPU->timeoutlock);
spinlock_lock(&t->lock);
 
if (t->cpu)
panic("Unexpected: t->cpu != 0.");
 
t->cpu = CPU;
t->ticks = us2ticks(time);
t->handler = f;
t->arg = arg;
 
/*
* Insert t into the active timeouts list according to t->ticks.
*/
sum = 0;
l = CPU->timeout_active_head.next;
while (l != &CPU->timeout_active_head) {
hlp = list_get_instance(l, timeout_t, link);
spinlock_lock(&hlp->lock);
if (t->ticks < sum + hlp->ticks) {
spinlock_unlock(&hlp->lock);
break;
}
sum += hlp->ticks;
spinlock_unlock(&hlp->lock);
l = l->next;
}
 
m = l->prev;
list_prepend(&t->link, m); /* avoid using l->prev */
 
/*
* Adjust t->ticks according to ticks accumulated in h's predecessors.
*/
t->ticks -= sum;
 
/*
* Decrease ticks of t's immediate succesor by t->ticks.
*/
if (l != &CPU->timeout_active_head) {
spinlock_lock(&hlp->lock);
hlp->ticks -= t->ticks;
spinlock_unlock(&hlp->lock);
}
 
spinlock_unlock(&t->lock);
spinlock_unlock(&CPU->timeoutlock);
interrupts_restore(ipl);
}
 
 
/** Unregister timeout
*
* Remove timeout from timeout list.
*
* @param t Timeout to unregister.
*
* @return True on success, false on failure.
*/
bool timeout_unregister(timeout_t *t)
{
timeout_t *hlp;
link_t *l;
ipl_t ipl;
DEADLOCK_PROBE_INIT(p_tolock);
 
grab_locks:
ipl = interrupts_disable();
spinlock_lock(&t->lock);
if (!t->cpu) {
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
return false;
}
if (!spinlock_trylock(&t->cpu->timeoutlock)) {
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
DEADLOCK_PROBE(p_tolock, DEADLOCK_THRESHOLD);
goto grab_locks;
}
/*
* Now we know for sure that t hasn't been activated yet
* and is lurking in t->cpu->timeout_active_head queue.
*/
 
l = t->link.next;
if (l != &t->cpu->timeout_active_head) {
hlp = list_get_instance(l, timeout_t, link);
spinlock_lock(&hlp->lock);
hlp->ticks += t->ticks;
spinlock_unlock(&hlp->lock);
}
list_remove(&t->link);
spinlock_unlock(&t->cpu->timeoutlock);
 
timeout_reinitialize(t);
spinlock_unlock(&t->lock);
 
interrupts_restore(ipl);
return true;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/time/delay.c
0,0 → 1,68
/*
* Copyright (c) 2001-2004 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 time
* @{
*/
 
/**
* @file
* @brief Active delay function.
*/
#include <time/delay.h>
#include <arch/types.h>
#include <cpu.h>
#include <arch/asm.h>
#include <arch.h>
 
/** Active delay
*
* Delay the execution for the given number
* of microseconds (or slightly more). The delay
* is implemented as CPU calibrated active loop.
*
* @param usec Number of microseconds to sleep.
*/
void delay(uint32_t usec)
{
ipl_t ipl;
/*
* The delay loop is calibrated for each and every
* CPU in the system. Therefore it is necessary to
* call interrupts_disable() before calling the
* asm_delay_loop().
*/
ipl = interrupts_disable();
asm_delay_loop(usec * CPU->delay_loop_const);
interrupts_restore(ipl);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ddi/ddi.c
0,0 → 1,290
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
 
/**
* @file
* @brief Device Driver Interface functions.
*
* This file contains functions that comprise the Device Driver Interface.
* These are the functions for mapping physical memory and enabling I/O
* space to tasks.
*/
 
#include <ddi/ddi.h>
#include <ddi/ddi_arg.h>
#include <proc/task.h>
#include <security/cap.h>
#include <mm/frame.h>
#include <mm/as.h>
#include <synch/spinlock.h>
#include <syscall/copy.h>
#include <adt/btree.h>
#include <arch.h>
#include <align.h>
#include <errno.h>
 
/** This lock protects the parea_btree. */
SPINLOCK_INITIALIZE(parea_lock);
 
/** B+tree with enabled physical memory areas. */
static btree_t parea_btree;
 
/** Initialize DDI. */
void ddi_init(void)
{
btree_create(&parea_btree);
}
 
/** Enable piece of physical memory for mapping by physmem_map().
*
* @param parea Pointer to physical area structure.
*
*/
void ddi_parea_register(parea_t *parea)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&parea_lock);
/*
* We don't check for overlaps here as the kernel is pretty sane.
*/
btree_insert(&parea_btree, (btree_key_t) parea->pbase, parea, NULL);
spinlock_unlock(&parea_lock);
interrupts_restore(ipl);
}
 
/** Map piece of physical memory into virtual address space of current task.
*
* @param pf Physical address of the starting frame.
* @param vp Virtual address of the starting page.
* @param pages Number of pages to map.
* @param flags Address space area flags for the mapping.
*
* @return 0 on success, EPERM if the caller lacks capabilities to use this
* syscall, EBADMEM if pf or vf is not page aligned, ENOENT if there
* is no task matching the specified ID or the physical address space
* is not enabled for mapping and ENOMEM if there was a problem in
* creating address space area.
*
*/
static int ddi_physmem_map(uintptr_t pf, uintptr_t vp, size_t pages, int flags)
{
ASSERT(TASK);
ASSERT((pf % FRAME_SIZE) == 0);
ASSERT((vp % PAGE_SIZE) == 0);
/*
* Make sure the caller is authorised to make this syscall.
*/
cap_t caps = cap_get(TASK);
if (!(caps & CAP_MEM_MANAGER))
return EPERM;
mem_backend_data_t backend_data;
backend_data.base = pf;
backend_data.frames = pages;
ipl_t ipl = interrupts_disable();
/* Find the zone of the physical memory */
spinlock_lock(&zones.lock);
size_t znum = find_zone(ADDR2PFN(pf), pages, 0);
if (znum == (size_t) -1) {
/* Frames not found in any zones
* -> assume it is hardware device and allow mapping
*/
spinlock_unlock(&zones.lock);
goto map;
}
if (zones.info[znum].flags & ZONE_FIRMWARE) {
/* Frames are part of firmware */
spinlock_unlock(&zones.lock);
goto map;
}
if (zone_flags_available(zones.info[znum].flags)) {
/* Frames are part of physical memory, check if the memory
* region is enabled for mapping.
*/
spinlock_unlock(&zones.lock);
spinlock_lock(&parea_lock);
btree_node_t *nodep;
parea_t *parea = (parea_t *) btree_search(&parea_btree,
(btree_key_t) pf, &nodep);
if ((!parea) || (parea->frames < pages))
goto err;
spinlock_unlock(&parea_lock);
goto map;
}
err:
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return ENOENT;
map:
spinlock_lock(&TASK->lock);
if (!as_area_create(TASK->as, flags, pages * PAGE_SIZE, vp,
AS_AREA_ATTR_NONE, &phys_backend, &backend_data)) {
/*
* The address space area could not have been created.
* We report it using ENOMEM.
*/
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
return ENOMEM;
}
/*
* Mapping is created on-demand during page fault.
*/
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
return 0;
}
 
/** Enable range of I/O space for task.
*
* @param id Task ID of the destination task.
* @param ioaddr Starting I/O address.
* @param size Size of the enabled I/O space..
*
* @return 0 on success, EPERM if the caller lacks capabilities to use this
* syscall, ENOENT if there is no task matching the specified ID.
*
*/
static int ddi_iospace_enable(task_id_t id, uintptr_t ioaddr, size_t size)
{
/*
* Make sure the caller is authorised to make this syscall.
*/
cap_t caps = cap_get(TASK);
if (!(caps & CAP_IO_MANAGER))
return EPERM;
ipl_t ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
task_t *task = task_find_by_id(id);
if ((!task) || (!context_check(CONTEXT, task->context))) {
/*
* There is no task with the specified ID
* or the task belongs to a different security
* context.
*/
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return ENOENT;
}
/* Lock the task and release the lock protecting tasks_btree. */
spinlock_lock(&task->lock);
spinlock_unlock(&tasks_lock);
int rc = ddi_iospace_enable_arch(task, ioaddr, size);
spinlock_unlock(&task->lock);
interrupts_restore(ipl);
return rc;
}
 
/** Wrapper for SYS_PHYSMEM_MAP syscall.
*
* @param phys_base Physical base address to map
* @param virt_base Destination virtual address
* @param pages Number of pages
* @param flags Flags of newly mapped pages
*
* @return 0 on success, otherwise it returns error code found in errno.h
*
*/
unative_t sys_physmem_map(unative_t phys_base, unative_t virt_base,
unative_t pages, unative_t flags)
{
return (unative_t) ddi_physmem_map(ALIGN_DOWN((uintptr_t) phys_base,
FRAME_SIZE), ALIGN_DOWN((uintptr_t) virt_base, PAGE_SIZE),
(size_t) pages, (int) flags);
}
 
/** Wrapper for SYS_ENABLE_IOSPACE syscall.
*
* @param uspace_io_arg User space address of DDI argument structure.
*
* @return 0 on success, otherwise it returns error code found in errno.h
*
*/
unative_t sys_iospace_enable(ddi_ioarg_t *uspace_io_arg)
{
ddi_ioarg_t arg;
int rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
if (rc != 0)
return (unative_t) rc;
return (unative_t) ddi_iospace_enable((task_id_t) arg.task_id,
(uintptr_t) arg.ioaddr, (size_t) arg.size);
}
 
/** Disable or enable preemption.
*
* @param enable If non-zero, the preemption counter will be decremented,
* leading to potential enabling of preemption. Otherwise
* the preemption counter will be incremented, preventing
* preemption from occurring.
*
* @return Zero on success or EPERM if callers capabilities are not sufficient.
*
*/
unative_t sys_preempt_control(int enable)
{
if (!cap_get(TASK) & CAP_PREEMPT_CONTROL)
return EPERM;
if (enable)
preemption_enable();
else
preemption_disable();
return 0;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ddi/irq.c
0,0 → 1,434
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
/**
* @file
* @brief IRQ dispatcher.
*
* This file provides means of connecting IRQs with particular
* devices and logic for dispatching interrupts to IRQ handlers
* defined by those devices.
*
* This code is designed to support:
* - multiple devices sharing single IRQ
* - multiple IRQs per single device
* - multiple instances of the same device
*
*
* Note about architectures.
*
* Some architectures has the term IRQ well defined. Examples
* of such architectures include amd64, ia32 and mips32. Some
* other architectures, such as sparc64, don't use the term
* at all. In those cases, we boldly step forward and define what
* an IRQ is.
*
* The implementation is generic enough and still allows the
* architectures to use the hardware layout effectively.
* For instance, on amd64 and ia32, where there is only 16
* IRQs, the irq_hash_table can be optimized to a one-dimensional
* array. Next, when it is known that the IRQ numbers (aka INR's)
* are unique, the claim functions can always return IRQ_ACCEPT.
*
*
* Note about the irq_hash_table.
*
* The hash table is configured to use two keys: inr and devno.
* However, the hash index is computed only from inr. Moreover,
* if devno is -1, the match is based on the return value of
* the claim() function instead of on devno.
*/
 
#include <ddi/irq.h>
#include <adt/hash_table.h>
#include <mm/slab.h>
#include <arch/types.h>
#include <synch/spinlock.h>
#include <console/console.h>
#include <memstr.h>
#include <arch.h>
 
#define KEY_INR 0
#define KEY_DEVNO 1
 
/**
* Spinlock protecting the kernel IRQ hash table.
* This lock must be taken only when interrupts are disabled.
*/
SPINLOCK_INITIALIZE(irq_kernel_hash_table_lock);
/** The kernel IRQ hash table. */
static hash_table_t irq_kernel_hash_table;
 
/**
* Spinlock protecting the uspace IRQ hash table.
* This lock must be taken only when interrupts are disabled.
*/
SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
/** The uspace IRQ hash table. */
hash_table_t irq_uspace_hash_table;
 
/**
* Hash table operations for cases when we know that
* there will be collisions between different keys.
*/
static size_t irq_ht_hash(unative_t *key);
static bool irq_ht_compare(unative_t *key, size_t keys, link_t *item);
static void irq_ht_remove(link_t *item);
 
static hash_table_operations_t irq_ht_ops = {
.hash = irq_ht_hash,
.compare = irq_ht_compare,
.remove_callback = irq_ht_remove,
};
 
/**
* Hash table operations for cases when we know that
* there will be no collisions between different keys.
* However, there might be still collisions among
* elements with single key (sharing of one IRQ).
*/
static size_t irq_lin_hash(unative_t *key);
static bool irq_lin_compare(unative_t *key, size_t keys, link_t *item);
static void irq_lin_remove(link_t *item);
 
static hash_table_operations_t irq_lin_ops = {
.hash = irq_lin_hash,
.compare = irq_lin_compare,
.remove_callback = irq_lin_remove,
};
 
/** Number of buckets in either of the hash tables. */
static size_t buckets;
 
/** Initialize IRQ subsystem.
*
* @param inrs Numbers of unique IRQ numbers or INRs.
* @param chains Number of chains in the hash table.
*/
void irq_init(size_t inrs, size_t chains)
{
buckets = chains;
/*
* Be smart about the choice of the hash table operations.
* In cases in which inrs equals the requested number of
* chains (i.e. where there is no collision between
* different keys), we can use optimized set of operations.
*/
if (inrs == chains) {
hash_table_create(&irq_uspace_hash_table, chains, 2,
&irq_lin_ops);
hash_table_create(&irq_kernel_hash_table, chains, 2,
&irq_lin_ops);
} else {
hash_table_create(&irq_uspace_hash_table, chains, 2,
&irq_ht_ops);
hash_table_create(&irq_kernel_hash_table, chains, 2,
&irq_ht_ops);
}
}
 
/** Initialize one IRQ structure.
*
* @param irq Pointer to the IRQ structure to be initialized.
*
*/
void irq_initialize(irq_t *irq)
{
memsetb(irq, sizeof(irq_t), 0);
link_initialize(&irq->link);
spinlock_initialize(&irq->lock, "irq.lock");
link_initialize(&irq->notif_cfg.link);
irq->inr = -1;
irq->devno = -1;
}
 
/** Register IRQ for device.
*
* The irq structure must be filled with information
* about the interrupt source and with the claim()
* function pointer and handler() function pointer.
*
* @param irq IRQ structure belonging to a device.
* @return True on success, false on failure.
*/
void irq_register(irq_t *irq)
{
ipl_t ipl;
unative_t key[] = {
(unative_t) irq->inr,
(unative_t) irq->devno
};
ipl = interrupts_disable();
spinlock_lock(&irq_kernel_hash_table_lock);
spinlock_lock(&irq->lock);
hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
spinlock_unlock(&irq->lock);
spinlock_unlock(&irq_kernel_hash_table_lock);
interrupts_restore(ipl);
}
 
/** Search and lock the uspace IRQ hash table.
*
*/
static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
{
link_t *lnk;
unative_t key[] = {
(unative_t) inr,
(unative_t) -1 /* search will use claim() instead of devno */
};
spinlock_lock(&irq_uspace_hash_table_lock);
lnk = hash_table_find(&irq_uspace_hash_table, key);
if (lnk) {
irq_t *irq;
irq = hash_table_get_instance(lnk, irq_t, link);
spinlock_unlock(&irq_uspace_hash_table_lock);
return irq;
}
spinlock_unlock(&irq_uspace_hash_table_lock);
return NULL;
}
 
/** Search and lock the kernel IRQ hash table.
*
*/
static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
{
link_t *lnk;
unative_t key[] = {
(unative_t) inr,
(unative_t) -1 /* search will use claim() instead of devno */
};
spinlock_lock(&irq_kernel_hash_table_lock);
lnk = hash_table_find(&irq_kernel_hash_table, key);
if (lnk) {
irq_t *irq;
irq = hash_table_get_instance(lnk, irq_t, link);
spinlock_unlock(&irq_kernel_hash_table_lock);
return irq;
}
spinlock_unlock(&irq_kernel_hash_table_lock);
return NULL;
}
 
/** Dispatch the IRQ.
*
* We assume this function is only called from interrupt
* context (i.e. that interrupts are disabled prior to
* this call).
*
* This function attempts to lookup a fitting IRQ
* structure. In case of success, return with interrupts
* disabled and holding the respective structure.
*
* @param inr Interrupt number (aka inr or irq).
*
* @return IRQ structure of the respective device or NULL.
*/
irq_t *irq_dispatch_and_lock(inr_t inr)
{
irq_t *irq;
/*
* If the kernel console is silenced,
* then try first the uspace handlers,
* eventually fall back to kernel handlers.
*
* If the kernel console is active,
* then do it the other way around.
*/
if (silent) {
irq = irq_dispatch_and_lock_uspace(inr);
if (irq)
return irq;
return irq_dispatch_and_lock_kernel(inr);
}
irq = irq_dispatch_and_lock_kernel(inr);
if (irq)
return irq;
return irq_dispatch_and_lock_uspace(inr);
}
 
/** Compute hash index for the key.
*
* This function computes hash index into
* the IRQ hash table for which there
* can be collisions between different
* INRs.
*
* The devno is not used to compute the hash.
*
* @param key The first of the keys is inr and the second is devno or -1.
*
* @return Index into the hash table.
*/
size_t irq_ht_hash(unative_t key[])
{
inr_t inr = (inr_t) key[KEY_INR];
return inr % buckets;
}
 
/** Compare hash table element with a key.
*
* There are two things to note about this function.
* First, it is used for the more complex architecture setup
* in which there are way too many interrupt numbers (i.e. inr's)
* to arrange the hash table so that collisions occur only
* among same inrs of different devnos. So the explicit check
* for inr match must be done.
* Second, if devno is -1, the second key (i.e. devno) is not
* used for the match and the result of the claim() function
* is used instead.
*
* This function assumes interrupts are already disabled.
*
* @param key Keys (i.e. inr and devno).
* @param keys This is 2.
* @param item The item to compare the key with.
*
* @return True on match or false otherwise.
*/
bool irq_ht_compare(unative_t key[], size_t keys, link_t *item)
{
irq_t *irq = hash_table_get_instance(item, irq_t, link);
inr_t inr = (inr_t) key[KEY_INR];
devno_t devno = (devno_t) key[KEY_DEVNO];
 
bool rv;
spinlock_lock(&irq->lock);
if (devno == -1) {
/* Invoked by irq_dispatch_and_lock(). */
rv = ((irq->inr == inr) &&
(irq->claim(irq) == IRQ_ACCEPT));
} else {
/* Invoked by irq_find_and_lock(). */
rv = ((irq->inr == inr) && (irq->devno == devno));
}
/* unlock only on non-match */
if (!rv)
spinlock_unlock(&irq->lock);
 
return rv;
}
 
/** Unlock IRQ structure after hash_table_remove().
*
* @param lnk Link in the removed and locked IRQ structure.
*/
void irq_ht_remove(link_t *lnk)
{
irq_t *irq __attribute__((unused))
= hash_table_get_instance(lnk, irq_t, link);
spinlock_unlock(&irq->lock);
}
 
/** Compute hash index for the key.
*
* This function computes hash index into
* the IRQ hash table for which there
* are no collisions between different
* INRs.
*
* @param key The first of the keys is inr and the second is devno or -1.
*
* @return Index into the hash table.
*/
size_t irq_lin_hash(unative_t key[])
{
inr_t inr = (inr_t) key[KEY_INR];
return inr;
}
 
/** Compare hash table element with a key.
*
* There are two things to note about this function.
* First, it is used for the less complex architecture setup
* in which there are not too many interrupt numbers (i.e. inr's)
* to arrange the hash table so that collisions occur only
* among same inrs of different devnos. So the explicit check
* for inr match is not done.
* Second, if devno is -1, the second key (i.e. devno) is not
* used for the match and the result of the claim() function
* is used instead.
*
* This function assumes interrupts are already disabled.
*
* @param key Keys (i.e. inr and devno).
* @param keys This is 2.
* @param item The item to compare the key with.
*
* @return True on match or false otherwise.
*/
bool irq_lin_compare(unative_t key[], size_t keys, link_t *item)
{
irq_t *irq = list_get_instance(item, irq_t, link);
devno_t devno = (devno_t) key[KEY_DEVNO];
bool rv;
spinlock_lock(&irq->lock);
if (devno == -1) {
/* Invoked by irq_dispatch_and_lock() */
rv = (irq->claim(irq) == IRQ_ACCEPT);
} else {
/* Invoked by irq_find_and_lock() */
rv = (irq->devno == devno);
}
/* unlock only on non-match */
if (!rv)
spinlock_unlock(&irq->lock);
return rv;
}
 
/** Unlock IRQ structure after hash_table_remove().
*
* @param lnk Link in the removed and locked IRQ structure.
*/
void irq_lin_remove(link_t *lnk)
{
irq_t *irq __attribute__((unused))
= hash_table_get_instance(lnk, irq_t, link);
spinlock_unlock(&irq->lock);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/ddi/device.c
0,0 → 1,62
/*
* Copyright (c) 2006 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 genericddi
* @{
*/
/**
* @file
* @brief Device numbers.
*/
 
#include <arch/types.h>
#include <ddi/device.h>
#include <atomic.h>
#include <debug.h>
 
static atomic_t last;
 
/** Assign new device number.
*
* @return Unique device number.
*/
devno_t device_assign_devno(void)
{
devno_t devno = (devno_t) atomic_postinc(&last);
ASSERT(devno >= 0);
return devno;
}
 
unative_t sys_device_assign_devno(void)
{
return (unative_t) device_assign_devno();
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/console.c
0,0 → 1,301
/*
* Copyright (c) 2003 Josef Cejka
* Copyright (c) 2005 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 genericconsole
* @{
*/
/** @file
*/
 
#include <console/console.h>
#include <console/chardev.h>
#include <sysinfo/sysinfo.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
#include <arch/types.h>
#include <ddi/irq.h>
#include <ddi/ddi.h>
#include <ipc/event.h>
#include <ipc/irq.h>
#include <arch.h>
#include <print.h>
#include <putchar.h>
#include <atomic.h>
#include <syscall/copy.h>
#include <errno.h>
#include <string.h>
 
#define KLOG_PAGES 4
#define KLOG_LENGTH (KLOG_PAGES * PAGE_SIZE / sizeof(wchar_t))
#define KLOG_LATENCY 8
 
/** Kernel log cyclic buffer */
static wchar_t klog[KLOG_LENGTH] __attribute__ ((aligned (PAGE_SIZE)));
 
/** Kernel log initialized */
static bool klog_inited = false;
/** First kernel log characters */
static size_t klog_start = 0;
/** Number of valid kernel log characters */
static size_t klog_len = 0;
/** Number of stored (not printed) kernel log characters */
static size_t klog_stored = 0;
/** Number of stored kernel log characters for uspace */
static size_t klog_uspace = 0;
 
/** Kernel log spinlock */
SPINLOCK_INITIALIZE(klog_lock);
 
/** Physical memory area used for klog buffer */
static parea_t klog_parea;
 
static indev_operations_t stdin_ops = {
.poll = NULL
};
 
/** Silence output */
bool silent = false;
 
/** Standard input and output character devices */
indev_t *stdin = NULL;
outdev_t *stdout = NULL;
 
indev_t *stdin_wire(void)
{
if (stdin == NULL) {
stdin = malloc(sizeof(indev_t), FRAME_ATOMIC);
if (stdin != NULL)
indev_initialize("stdin", stdin, &stdin_ops);
}
return stdin;
}
 
/** Initialize kernel logging facility
*
* The shared area contains kernel cyclic buffer. Userspace application may
* be notified on new data with indication of position and size
* of the data within the circular buffer.
*
*/
void klog_init(void)
{
void *faddr = (void *) KA2PA(klog);
ASSERT((uintptr_t) faddr % FRAME_SIZE == 0);
klog_parea.pbase = (uintptr_t) faddr;
klog_parea.frames = SIZE2FRAMES(sizeof(klog));
ddi_parea_register(&klog_parea);
sysinfo_set_item_val("klog.faddr", NULL, (unative_t) faddr);
sysinfo_set_item_val("klog.pages", NULL, KLOG_PAGES);
spinlock_lock(&klog_lock);
klog_inited = true;
spinlock_unlock(&klog_lock);
}
 
void grab_console(void)
{
bool prev = silent;
silent = false;
arch_grab_console();
/* Force the console to print the prompt */
if ((stdin) && (prev))
indev_push_character(stdin, '\n');
}
 
void release_console(void)
{
silent = true;
arch_release_console();
}
 
/** Tell kernel to get keyboard/console access again */
unative_t sys_debug_enable_console(void)
{
#ifdef CONFIG_KCONSOLE
grab_console();
return true;
#else
return false;
#endif
}
 
/** Tell kernel to relinquish keyboard/console access */
unative_t sys_debug_disable_console(void)
{
release_console();
return true;
}
 
/** Get string from input character device.
*
* Read characters from input character device until first occurrence
* of newline character.
*
* @param indev Input character device.
* @param buf Buffer where to store string terminated by NULL.
* @param buflen Size of the buffer.
*
* @return Number of characters read.
*
*/
size_t gets(indev_t *indev, char *buf, size_t buflen)
{
size_t offset = 0;
size_t count = 0;
buf[offset] = 0;
wchar_t ch;
while ((ch = indev_pop_character(indev)) != '\n') {
if (ch == '\b') {
if (count > 0) {
/* Space, backspace, space */
putchar('\b');
putchar(' ');
putchar('\b');
count--;
offset = str_lsize(buf, count);
buf[offset] = 0;
}
}
if (chr_encode(ch, buf, &offset, buflen - 1) == EOK) {
putchar(ch);
count++;
buf[offset] = 0;
}
}
return count;
}
 
/** Get character from input device & echo it to screen */
wchar_t getc(indev_t *indev)
{
wchar_t ch = indev_pop_character(indev);
putchar(ch);
return ch;
}
 
void klog_update(void)
{
spinlock_lock(&klog_lock);
if ((klog_inited) && (event_is_subscribed(EVENT_KLOG)) && (klog_uspace > 0)) {
event_notify_3(EVENT_KLOG, klog_start, klog_len, klog_uspace);
klog_uspace = 0;
}
spinlock_unlock(&klog_lock);
}
 
void putchar(const wchar_t ch)
{
spinlock_lock(&klog_lock);
if ((klog_stored > 0) && (stdout) && (stdout->op->write)) {
/* Print charaters stored in kernel log */
size_t i;
for (i = klog_len - klog_stored; i < klog_len; i++)
stdout->op->write(stdout, klog[(klog_start + i) % KLOG_LENGTH], silent);
klog_stored = 0;
}
/* Store character in the cyclic kernel log */
klog[(klog_start + klog_len) % KLOG_LENGTH] = ch;
if (klog_len < KLOG_LENGTH)
klog_len++;
else
klog_start = (klog_start + 1) % KLOG_LENGTH;
if ((stdout) && (stdout->op->write))
stdout->op->write(stdout, ch, silent);
else {
/* The character is just in the kernel log */
if (klog_stored < klog_len)
klog_stored++;
}
/* The character is stored for uspace */
if (klog_uspace < klog_len)
klog_uspace++;
/* Check notify uspace to update */
bool update;
if ((klog_uspace > KLOG_LATENCY) || (ch == '\n'))
update = true;
else
update = false;
spinlock_unlock(&klog_lock);
if (update)
klog_update();
}
 
/** Print using kernel facility
*
* Print to kernel log.
*
*/
unative_t sys_klog(int fd, const void *buf, size_t size)
{
char *data;
int rc;
if (size > PAGE_SIZE)
return ELIMIT;
if (size > 0) {
data = (char *) malloc(size + 1, 0);
if (!data)
return ENOMEM;
rc = copy_from_uspace(data, buf, size);
if (rc) {
free(data);
return rc;
}
data[size] = 0;
printf("%s", data);
free(data);
} else
klog_update();
return size;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/cmd.c
0,0 → 1,1172
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
 
/**
* @file cmd.c
* @brief Kernel console command wrappers.
*
* This file is meant to contain all wrapper functions for
* all kconsole commands. The point is in separating
* kconsole specific wrappers from kconsole-unaware functions
* from other subsystems.
*/
 
#include <console/cmd.h>
#include <console/console.h>
#include <console/kconsole.h>
#include <print.h>
#include <panic.h>
#include <arch/types.h>
#include <adt/list.h>
#include <arch.h>
#include <config.h>
#include <func.h>
#include <string.h>
#include <macros.h>
#include <debug.h>
#include <cpu.h>
#include <mm/tlb.h>
#include <arch/mm/tlb.h>
#include <mm/frame.h>
#include <main/version.h>
#include <mm/slab.h>
#include <proc/scheduler.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <ipc/ipc.h>
#include <ipc/irq.h>
#include <ipc/event.h>
#include <symtab.h>
#include <errno.h>
 
#ifdef CONFIG_TEST
#include <test.h>
#endif
 
/* Data and methods for 'help' command. */
static int cmd_help(cmd_arg_t *argv);
static cmd_info_t help_info = {
.name = "help",
.description = "List of supported commands.",
.func = cmd_help,
.argc = 0
};
 
static int cmd_reboot(cmd_arg_t *argv);
static cmd_info_t reboot_info = {
.name = "reboot",
.description = "Reboot.",
.func = cmd_reboot,
.argc = 0
};
 
static int cmd_uptime(cmd_arg_t *argv);
static cmd_info_t uptime_info = {
.name = "uptime",
.description = "Print uptime information.",
.func = cmd_uptime,
.argc = 0
};
 
static int cmd_continue(cmd_arg_t *argv);
static cmd_info_t continue_info = {
.name = "continue",
.description = "Return console back to userspace.",
.func = cmd_continue,
.argc = 0
};
 
#ifdef CONFIG_TEST
static int cmd_tests(cmd_arg_t *argv);
static cmd_info_t tests_info = {
.name = "tests",
.description = "Print available kernel tests.",
.func = cmd_tests,
.argc = 0
};
 
static char test_buf[MAX_CMDLINE + 1];
static int cmd_test(cmd_arg_t *argv);
static cmd_arg_t test_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = test_buf,
.len = sizeof(test_buf)
}
};
static cmd_info_t test_info = {
.name = "test",
.description = "Run kernel test.",
.func = cmd_test,
.argc = 1,
.argv = test_argv
};
 
static int cmd_bench(cmd_arg_t *argv);
static cmd_arg_t bench_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = test_buf,
.len = sizeof(test_buf)
},
{
.type = ARG_TYPE_INT,
}
};
static cmd_info_t bench_info = {
.name = "bench",
.description = "Run kernel test as benchmark.",
.func = cmd_bench,
.argc = 2,
.argv = bench_argv
};
#endif
 
/* Data and methods for 'description' command. */
static int cmd_desc(cmd_arg_t *argv);
static void desc_help(void);
static char desc_buf[MAX_CMDLINE+1];
static cmd_arg_t desc_argv = {
.type = ARG_TYPE_STRING,
.buffer = desc_buf,
.len = sizeof(desc_buf)
};
static cmd_info_t desc_info = {
.name = "describe",
.description = "Describe specified command.",
.help = desc_help,
.func = cmd_desc,
.argc = 1,
.argv = &desc_argv
};
 
/* Data and methods for 'symaddr' command. */
static int cmd_symaddr(cmd_arg_t *argv);
static char symaddr_buf[MAX_CMDLINE+1];
static cmd_arg_t symaddr_argv = {
.type = ARG_TYPE_STRING,
.buffer = symaddr_buf,
.len = sizeof(symaddr_buf)
};
static cmd_info_t symaddr_info = {
.name = "symaddr",
.description = "Return symbol address.",
.func = cmd_symaddr,
.argc = 1,
.argv = &symaddr_argv
};
 
static char set_buf[MAX_CMDLINE+1];
static int cmd_set4(cmd_arg_t *argv);
static cmd_arg_t set4_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = set_buf,
.len = sizeof(set_buf)
},
{
.type = ARG_TYPE_INT
}
};
static cmd_info_t set4_info = {
.name = "set4",
.description = "set <dest_addr> <value> - 4byte version",
.func = cmd_set4,
.argc = 2,
.argv = set4_argv
};
 
/* Data and methods for 'call0' command. */
static char call0_buf[MAX_CMDLINE + 1];
static char carg1_buf[MAX_CMDLINE + 1];
static char carg2_buf[MAX_CMDLINE + 1];
static char carg3_buf[MAX_CMDLINE + 1];
 
static int cmd_call0(cmd_arg_t *argv);
static cmd_arg_t call0_argv = {
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
};
static cmd_info_t call0_info = {
.name = "call0",
.description = "call0 <function> -> call function().",
.func = cmd_call0,
.argc = 1,
.argv = &call0_argv
};
 
/* Data and methods for 'mcall0' command. */
static int cmd_mcall0(cmd_arg_t *argv);
static cmd_arg_t mcall0_argv = {
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
};
static cmd_info_t mcall0_info = {
.name = "mcall0",
.description = "mcall0 <function> -> call function() on each CPU.",
.func = cmd_mcall0,
.argc = 1,
.argv = &mcall0_argv
};
 
/* Data and methods for 'call1' command. */
static int cmd_call1(cmd_arg_t *argv);
static cmd_arg_t call1_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg1_buf,
.len = sizeof(carg1_buf)
}
};
static cmd_info_t call1_info = {
.name = "call1",
.description = "call1 <function> <arg1> -> call function(arg1).",
.func = cmd_call1,
.argc = 2,
.argv = call1_argv
};
 
/* Data and methods for 'call2' command. */
static int cmd_call2(cmd_arg_t *argv);
static cmd_arg_t call2_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg1_buf,
.len = sizeof(carg1_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg2_buf,
.len = sizeof(carg2_buf)
}
};
static cmd_info_t call2_info = {
.name = "call2",
.description = "call2 <function> <arg1> <arg2> -> call function(arg1,arg2).",
.func = cmd_call2,
.argc = 3,
.argv = call2_argv
};
 
/* Data and methods for 'call3' command. */
static int cmd_call3(cmd_arg_t *argv);
static cmd_arg_t call3_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg1_buf,
.len = sizeof(carg1_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg2_buf,
.len = sizeof(carg2_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg3_buf,
.len = sizeof(carg3_buf)
}
 
};
static cmd_info_t call3_info = {
.name = "call3",
.description = "call3 <function> <arg1> <arg2> <arg3> -> call function(arg1,arg2,arg3).",
.func = cmd_call3,
.argc = 4,
.argv = call3_argv
};
 
/* Data and methods for 'halt' command. */
static int cmd_halt(cmd_arg_t *argv);
static cmd_info_t halt_info = {
.name = "halt",
.description = "Halt the kernel.",
.func = cmd_halt,
.argc = 0
};
 
/* Data and methods for 'physmem' command. */
static int cmd_physmem(cmd_arg_t *argv);
cmd_info_t physmem_info = {
.name = "physmem",
.description = "Print physical memory configuration.",
.help = NULL,
.func = cmd_physmem,
.argc = 0,
.argv = NULL
};
 
/* Data and methods for 'tlb' command. */
static int cmd_tlb(cmd_arg_t *argv);
cmd_info_t tlb_info = {
.name = "tlb",
.description = "Print TLB of current processor.",
.help = NULL,
.func = cmd_tlb,
.argc = 0,
.argv = NULL
};
 
static int cmd_threads(cmd_arg_t *argv);
static cmd_info_t threads_info = {
.name = "threads",
.description = "List all threads.",
.func = cmd_threads,
.argc = 0
};
 
static int cmd_tasks(cmd_arg_t *argv);
static cmd_info_t tasks_info = {
.name = "tasks",
.description = "List all tasks.",
.func = cmd_tasks,
.argc = 0
};
 
 
static int cmd_sched(cmd_arg_t *argv);
static cmd_info_t sched_info = {
.name = "scheduler",
.description = "List all scheduler information.",
.func = cmd_sched,
.argc = 0
};
 
static int cmd_slabs(cmd_arg_t *argv);
static cmd_info_t slabs_info = {
.name = "slabs",
.description = "List slab caches.",
.func = cmd_slabs,
.argc = 0
};
 
/* Data and methods for 'zones' command */
static int cmd_zones(cmd_arg_t *argv);
static cmd_info_t zones_info = {
.name = "zones",
.description = "List of memory zones.",
.func = cmd_zones,
.argc = 0
};
 
/* 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_info = {
.name = "ipc",
.description = "ipc <taskid> Show IPC information of given task.",
.func = cmd_ipc,
.argc = 1,
.argv = &ipc_argv
};
 
/* Data and methods for 'zone' command */
static int cmd_zone(cmd_arg_t *argv);
static cmd_arg_t zone_argv = {
.type = ARG_TYPE_INT,
};
 
static cmd_info_t zone_info = {
.name = "zone",
.description = "Show memory zone structure.",
.func = cmd_zone,
.argc = 1,
.argv = &zone_argv
};
 
/* Data and methods for 'cpus' command. */
static int cmd_cpus(cmd_arg_t *argv);
cmd_info_t cpus_info = {
.name = "cpus",
.description = "List all processors.",
.help = NULL,
.func = cmd_cpus,
.argc = 0,
.argv = NULL
};
 
/* Data and methods for 'version' command. */
static int cmd_version(cmd_arg_t *argv);
cmd_info_t version_info = {
.name = "version",
.description = "Print version information.",
.help = NULL,
.func = cmd_version,
.argc = 0,
.argv = NULL
};
 
static cmd_info_t *basic_commands[] = {
&call0_info,
&mcall0_info,
&call1_info,
&call2_info,
&call3_info,
&continue_info,
&cpus_info,
&desc_info,
&reboot_info,
&uptime_info,
&halt_info,
&help_info,
&ipc_info,
&set4_info,
&slabs_info,
&symaddr_info,
&sched_info,
&threads_info,
&tasks_info,
&physmem_info,
&tlb_info,
&version_info,
&zones_info,
&zone_info,
#ifdef CONFIG_TEST
&tests_info,
&test_info,
&bench_info,
#endif
NULL
};
 
 
/** Initialize command info structure.
*
* @param cmd Command info structure.
*
*/
void cmd_initialize(cmd_info_t *cmd)
{
spinlock_initialize(&cmd->lock, "cmd");
link_initialize(&cmd->link);
}
 
/** Initialize and register commands. */
void cmd_init(void)
{
unsigned int i;
 
for (i = 0; basic_commands[i]; i++) {
cmd_initialize(basic_commands[i]);
if (!cmd_register(basic_commands[i]))
printf("Cannot register command %s\n", basic_commands[i]->name);
}
}
 
 
/** List supported commands.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_help(cmd_arg_t *argv)
{
spinlock_lock(&cmd_lock);
link_t *cur;
size_t len = 0;
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp;
hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
if (str_length(hlp->name) > len)
len = str_length(hlp->name);
spinlock_unlock(&hlp->lock);
}
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp;
hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
printf("%-*s %s\n", len, hlp->name, hlp->description);
spinlock_unlock(&hlp->lock);
}
spinlock_unlock(&cmd_lock);
return 1;
}
 
 
/** Reboot the system.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_reboot(cmd_arg_t *argv)
{
reboot();
/* Not reached */
return 1;
}
 
 
/** Print system uptime information.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_uptime(cmd_arg_t *argv)
{
ASSERT(uptime);
/* This doesn't have to be very accurate */
unative_t sec = uptime->seconds1;
printf("Up %" PRIun " days, %" PRIun " hours, %" PRIun " minutes, %" PRIun " seconds\n",
sec / 86400, (sec % 86400) / 3600, (sec % 3600) / 60, sec % 60);
return 1;
}
 
/** Describe specified command.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_desc(cmd_arg_t *argv)
{
link_t *cur;
spinlock_lock(&cmd_lock);
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp;
hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
if (str_lcmp(hlp->name, (const char *) argv->buffer, str_length(hlp->name)) == 0) {
printf("%s - %s\n", hlp->name, hlp->description);
if (hlp->help)
hlp->help();
spinlock_unlock(&hlp->lock);
break;
}
spinlock_unlock(&hlp->lock);
}
spinlock_unlock(&cmd_lock);
return 1;
}
 
/** Search symbol table */
int cmd_symaddr(cmd_arg_t *argv)
{
symtab_print_search((char *) argv->buffer);
return 1;
}
 
/** Call function with zero parameters */
int cmd_call0(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(void);
fncptr_t fptr;
int rc;
 
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT)
printf("Symbol %s not found.\n", symbol);
else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(void)) arch_construct_function(&fptr,
(void *) symaddr, (void *) cmd_call0);
printf("Calling %s() (%p)\n", symbol, symaddr);
printf("Result: %#" PRIxn "\n", fnc());
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
/** Call function with zero parameters on each CPU */
int cmd_mcall0(cmd_arg_t *argv)
{
/*
* For each CPU, create a thread which will
* call the function.
*/
size_t i;
for (i = 0; i < config.cpu_count; i++) {
if (!cpus[i].active)
continue;
thread_t *t;
if ((t = thread_create((void (*)(void *)) cmd_call0, (void *) argv, TASK, THREAD_FLAG_WIRED, "call0", false))) {
spinlock_lock(&t->lock);
t->cpu = &cpus[i];
spinlock_unlock(&t->lock);
printf("cpu%u: ", i);
thread_ready(t);
thread_join(t);
thread_detach(t);
} else
printf("Unable to create thread for cpu%u\n", i);
}
return 1;
}
 
/** Call function with one parameter */
int cmd_call1(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(unative_t, ...);
unative_t arg1 = argv[1].intval;
fncptr_t fptr;
int rc;
 
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT) {
printf("Symbol %s not found.\n", symbol);
} else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(unative_t, ...)) arch_construct_function(&fptr, (void *) symaddr, (void *) cmd_call1);
printf("Calling f(%#" PRIxn "): %p: %s\n", arg1, symaddr, symbol);
printf("Result: %#" PRIxn "\n", fnc(arg1));
} else {
printf("No symbol information available.\n");
}
 
return 1;
}
 
/** Call function with two parameters */
int cmd_call2(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(unative_t, unative_t, ...);
unative_t arg1 = argv[1].intval;
unative_t arg2 = argv[2].intval;
fncptr_t fptr;
int rc;
 
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT) {
printf("Symbol %s not found.\n", symbol);
} else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(unative_t, unative_t, ...)) arch_construct_function(&fptr, (void *) symaddr, (void *) cmd_call2);
printf("Calling f(%#" PRIxn ", %#" PRIxn "): %p: %s\n",
arg1, arg2, symaddr, symbol);
printf("Result: %#" PRIxn "\n", fnc(arg1, arg2));
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
/** Call function with three parameters */
int cmd_call3(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(unative_t, unative_t, unative_t, ...);
unative_t arg1 = argv[1].intval;
unative_t arg2 = argv[2].intval;
unative_t arg3 = argv[3].intval;
fncptr_t fptr;
int rc;
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT) {
printf("Symbol %s not found.\n", symbol);
} else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(unative_t, unative_t, unative_t, ...)) arch_construct_function(&fptr, (void *) symaddr, (void *) cmd_call3);
printf("Calling f(%#" PRIxn ",%#" PRIxn ", %#" PRIxn "): %p: %s\n",
arg1, arg2, arg3, symaddr, symbol);
printf("Result: %#" PRIxn "\n", fnc(arg1, arg2, arg3));
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
 
/** Print detailed description of 'describe' command. */
void desc_help(void)
{
printf("Syntax: describe command_name\n");
}
 
/** Halt the kernel.
*
* @param argv Argument vector (ignored).
*
* @return 0 on failure, 1 on success (never returns).
*/
int cmd_halt(cmd_arg_t *argv)
{
halt();
return 1;
}
 
/** Command for printing TLB contents.
*
* @param argv Not used.
*
* @return Always returns 1.
*/
int cmd_tlb(cmd_arg_t *argv)
{
tlb_print();
return 1;
}
 
/** Command for printing physical memory configuration.
*
* @param argv Not used.
*
* @return Always returns 1.
*/
int cmd_physmem(cmd_arg_t *argv)
{
physmem_print();
return 1;
}
 
/** Write 4 byte value to address */
int cmd_set4(cmd_arg_t *argv)
{
uintptr_t addr;
uint32_t arg1 = argv[1].intval;
bool pointer = false;
int rc;
 
if (((char *)argv->buffer)[0] == '*') {
rc = symtab_addr_lookup((char *) argv->buffer + 1, &addr);
pointer = true;
} else if (((char *) argv->buffer)[0] >= '0' &&
((char *)argv->buffer)[0] <= '9') {
rc = EOK;
addr = atoi((char *)argv->buffer);
} else {
rc = symtab_addr_lookup((char *) argv->buffer, &addr);
}
 
if (rc == ENOENT)
printf("Symbol %s not found.\n", argv->buffer);
else if (rc == EOVERFLOW) {
symtab_print_search((char *) argv->buffer);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
if (pointer)
addr = *(uintptr_t *) addr;
printf("Writing %#" PRIx64 " -> %p\n", arg1, addr);
*(uint32_t *) addr = arg1;
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
/** Command for listings SLAB caches
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_slabs(cmd_arg_t * argv) {
slab_print_list();
return 1;
}
 
 
/** Command for listings Thread information
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_threads(cmd_arg_t * argv) {
thread_print_list();
return 1;
}
 
/** Command for listings Task information
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_tasks(cmd_arg_t * argv) {
task_print_list();
return 1;
}
 
/** Command for listings Thread information
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_sched(cmd_arg_t * argv) {
sched_print_list();
return 1;
}
 
/** Command for listing memory zones
*
* @param argv Ignored
*
* return Always 1
*/
int cmd_zones(cmd_arg_t * argv) {
zone_print_list();
return 1;
}
 
/** Command for memory zone details
*
* @param argv Integer argument from cmdline expected
*
* return Always 1
*/
int cmd_zone(cmd_arg_t * argv) {
zone_print_one(argv[0].intval);
return 1;
}
 
/** Command for printing task ipc details
*
* @param argv Integer argument from cmdline expected
*
* return Always 1
*/
int cmd_ipc(cmd_arg_t * argv) {
ipc_print_task(argv[0].intval);
return 1;
}
 
 
/** Command for listing processors.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_cpus(cmd_arg_t *argv)
{
cpu_list();
return 1;
}
 
/** Command for printing kernel version.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_version(cmd_arg_t *argv)
{
version_print();
return 1;
}
 
/** Command for returning console back to userspace.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_continue(cmd_arg_t *argv)
{
printf("The kernel will now relinquish the console.\n");
release_console();
event_notify_0(EVENT_KCONSOLE);
indev_pop_character(stdin);
return 1;
}
 
#ifdef CONFIG_TEST
/** Command for printing kernel tests list.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_tests(cmd_arg_t *argv)
{
size_t len = 0;
test_t *test;
for (test = tests; test->name != NULL; test++) {
if (str_length(test->name) > len)
len = str_length(test->name);
}
for (test = tests; test->name != NULL; test++)
printf("%-*s %s%s\n", len, test->name, test->desc, (test->safe ? "" : " (unsafe)"));
printf("%-*s Run all safe tests\n", len, "*");
return 1;
}
 
static bool run_test(const test_t *test)
{
printf("%s (%s)\n", test->name, test->desc);
/* Update and read thread accounting
for benchmarking */
ipl_t ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t t0 = task_get_accounting(TASK);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
/* Execute the test */
test_quiet = false;
char *ret = test->entry();
/* Update and read thread accounting */
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t dt = task_get_accounting(TASK) - t0;
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
uint64_t cycles;
char suffix;
order(dt, &cycles, &suffix);
printf("Time: %" PRIu64 "%c cycles\n", cycles, suffix);
if (ret == NULL) {
printf("Test passed\n");
return true;
}
 
printf("%s\n", ret);
return false;
}
 
static bool run_bench(const test_t *test, const uint32_t cnt)
{
uint32_t i;
bool ret = true;
uint64_t cycles;
char suffix;
if (cnt < 1)
return true;
uint64_t *data = (uint64_t *) malloc(sizeof(uint64_t) * cnt, 0);
if (data == NULL) {
printf("Error allocating memory for statistics\n");
return false;
}
for (i = 0; i < cnt; i++) {
printf("%s (%u/%u) ... ", test->name, i + 1, cnt);
/* Update and read thread accounting
for benchmarking */
ipl_t ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t t0 = task_get_accounting(TASK);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
/* Execute the test */
test_quiet = true;
char * ret = test->entry();
/* Update and read thread accounting */
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t dt = task_get_accounting(TASK) - t0;
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
if (ret != NULL) {
printf("%s\n", ret);
ret = false;
break;
}
data[i] = dt;
order(dt, &cycles, &suffix);
printf("OK (%" PRIu64 "%c cycles)\n", cycles, suffix);
}
if (ret) {
printf("\n");
uint64_t sum = 0;
for (i = 0; i < cnt; i++) {
sum += data[i];
}
order(sum / (uint64_t) cnt, &cycles, &suffix);
printf("Average\t\t%" PRIu64 "%c\n", cycles, suffix);
}
free(data);
return ret;
}
 
/** Command for returning kernel tests
*
* @param argv Argument vector.
*
* return Always 1.
*/
int cmd_test(cmd_arg_t *argv)
{
test_t *test;
if (str_cmp((char *) argv->buffer, "*") == 0) {
for (test = tests; test->name != NULL; test++) {
if (test->safe) {
printf("\n");
if (!run_test(test))
break;
}
}
} else {
bool fnd = false;
for (test = tests; test->name != NULL; test++) {
if (str_cmp(test->name, (char *) argv->buffer) == 0) {
fnd = true;
run_test(test);
break;
}
}
if (!fnd)
printf("Unknown test\n");
}
return 1;
}
 
/** Command for returning kernel tests as benchmarks
*
* @param argv Argument vector.
*
* return Always 1.
*/
int cmd_bench(cmd_arg_t *argv)
{
test_t *test;
uint32_t cnt = argv[1].intval;
if (str_cmp((char *) argv->buffer, "*") == 0) {
for (test = tests; test->name != NULL; test++) {
if (test->safe) {
if (!run_bench(test, cnt))
break;
}
}
} else {
bool fnd = false;
for (test = tests; test->name != NULL; test++) {
if (str_cmp(test->name, (char *) argv->buffer) == 0) {
fnd = true;
if (test->safe)
run_bench(test, cnt);
else
printf("Unsafe test\n");
break;
}
}
if (!fnd)
printf("Unknown test\n");
}
return 1;
}
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/kconsole.c
0,0 → 1,689
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
 
/**
* @file kconsole.c
* @brief Kernel console.
*
* This file contains kernel thread managing the kernel console.
*
*/
 
#include <console/kconsole.h>
#include <console/console.h>
#include <console/chardev.h>
#include <console/cmd.h>
#include <print.h>
#include <panic.h>
#include <arch/types.h>
#include <adt/list.h>
#include <arch.h>
#include <macros.h>
#include <debug.h>
#include <func.h>
#include <string.h>
#include <macros.h>
#include <sysinfo/sysinfo.h>
#include <ddi/device.h>
#include <symtab.h>
#include <errno.h>
#include <putchar.h>
#include <string.h>
 
/** Simple kernel console.
*
* The console is realized by kernel thread kconsole.
* It doesn't understand any useful command on its own,
* but makes it possible for other kernel subsystems to
* register their own commands.
*/
 
/** Locking.
*
* There is a list of cmd_info_t structures. This list
* is protected by cmd_lock spinlock. Note that specially
* the link elements of cmd_info_t are protected by
* this lock.
*
* Each cmd_info_t also has its own lock, which protects
* all elements thereof except the link element.
*
* cmd_lock must be acquired before any cmd_info lock.
* When locking two cmd info structures, structure with
* lower address must be locked first.
*/
 
SPINLOCK_INITIALIZE(cmd_lock); /**< Lock protecting command list. */
LIST_INITIALIZE(cmd_head); /**< Command list. */
 
static wchar_t history[KCONSOLE_HISTORY][MAX_CMDLINE] = {};
static size_t history_pos = 0;
 
/** Initialize kconsole data structures
*
* This is the most basic initialization, almost no
* other kernel subsystem is ready yet.
*
*/
void kconsole_init(void)
{
unsigned int i;
cmd_init();
for (i = 0; i < KCONSOLE_HISTORY; i++)
history[i][0] = 0;
}
 
/** Register kconsole command.
*
* @param cmd Structure describing the command.
*
* @return False on failure, true on success.
*
*/
bool cmd_register(cmd_info_t *cmd)
{
link_t *cur;
spinlock_lock(&cmd_lock);
/*
* Make sure the command is not already listed.
*/
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp = list_get_instance(cur, cmd_info_t, link);
if (hlp == cmd) {
/* The command is already there. */
spinlock_unlock(&cmd_lock);
return false;
}
/* Avoid deadlock. */
if (hlp < cmd) {
spinlock_lock(&hlp->lock);
spinlock_lock(&cmd->lock);
} else {
spinlock_lock(&cmd->lock);
spinlock_lock(&hlp->lock);
}
if (str_cmp(hlp->name, cmd->name) == 0) {
/* The command is already there. */
spinlock_unlock(&hlp->lock);
spinlock_unlock(&cmd->lock);
spinlock_unlock(&cmd_lock);
return false;
}
spinlock_unlock(&hlp->lock);
spinlock_unlock(&cmd->lock);
}
/*
* Now the command can be added.
*/
list_append(&cmd->link, &cmd_head);
spinlock_unlock(&cmd_lock);
return true;
}
 
/** Print count times a character */
static void print_cc(wchar_t ch, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
putchar(ch);
}
 
/** Try to find a command beginning with prefix */
static const char *cmdtab_search_one(const char *name, link_t **startpos)
{
size_t namelen = str_length(name);
spinlock_lock(&cmd_lock);
if (*startpos == NULL)
*startpos = cmd_head.next;
for (; *startpos != &cmd_head; *startpos = (*startpos)->next) {
cmd_info_t *hlp = list_get_instance(*startpos, cmd_info_t, link);
const char *curname = hlp->name;
if (str_length(curname) < namelen)
continue;
if (str_lcmp(curname, name, namelen) == 0) {
spinlock_unlock(&cmd_lock);
return (curname + str_lsize(curname, namelen));
}
}
spinlock_unlock(&cmd_lock);
return NULL;
}
 
/** Command completion of the commands
*
* @param name String to match, changed to hint on exit
* @param size Input buffer size
*
* @return Number of found matches
*
*/
static int cmdtab_compl(char *input, size_t size)
{
const char *name = input;
size_t found = 0;
link_t *pos = NULL;
const char *hint;
char output[MAX_CMDLINE];
output[0] = 0;
while ((hint = cmdtab_search_one(name, &pos))) {
if ((found == 0) || (str_length(output) > str_length(hint)))
str_cpy(output, MAX_CMDLINE, hint);
pos = pos->next;
found++;
}
if ((found > 1) && (str_length(output) != 0)) {
printf("\n");
pos = NULL;
while ((hint = cmdtab_search_one(name, &pos))) {
cmd_info_t *hlp = list_get_instance(pos, cmd_info_t, link);
printf("%s (%s)\n", hlp->name, hlp->description);
pos = pos->next;
}
}
if (found > 0)
str_cpy(input, size, output);
return found;
}
 
static wchar_t *clever_readline(const char *prompt, indev_t *indev)
{
printf("%s> ", prompt);
size_t position = 0;
wchar_t *current = history[history_pos];
current[0] = 0;
while (true) {
wchar_t ch = indev_pop_character(indev);
if (ch == '\n') {
/* Enter */
putchar(ch);
break;
}
if (ch == '\b') {
/* Backspace */
if (position == 0)
continue;
if (wstr_remove(current, position - 1)) {
position--;
putchar('\b');
printf("%ls ", current + position);
print_cc('\b', wstr_length(current) - position + 1);
continue;
}
}
if (ch == '\t') {
/* Tab completion */
/* Move to the end of the word */
for (; (current[position] != 0) && (!isspace(current[position]));
position++)
putchar(current[position]);
if (position == 0)
continue;
/* Find the beginning of the word
and copy it to tmp */
size_t beg;
for (beg = position - 1; (beg > 0) && (!isspace(current[beg]));
beg--);
if (isspace(current[beg]))
beg++;
char tmp[STR_BOUNDS(MAX_CMDLINE)];
wstr_nstr(tmp, current + beg, position - beg + 1);
int found;
if (beg == 0) {
/* Command completion */
found = cmdtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE));
} else {
/* Symbol completion */
found = symtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE));
}
if (found == 0)
continue;
if (found > 1) {
/* No unique hint, list was printed */
printf("%s> ", prompt);
printf("%ls", current);
print_cc('\b', wstr_length(current) - position);
continue;
}
/* We have a hint */
size_t off = 0;
size_t i = 0;
while ((ch = str_decode(tmp, &off, STR_NO_LIMIT)) != 0) {
if (!wstr_linsert(current, ch, position + i, MAX_CMDLINE))
break;
i++;
}
printf("%ls", current + position);
position += str_length(tmp);
print_cc('\b', wstr_length(current) - position);
if (position == wstr_length(current)) {
/* Insert a space after the last completed argument */
if (wstr_linsert(current, ' ', position, MAX_CMDLINE)) {
printf("%ls", current + position);
position++;
}
}
continue;
}
if (ch == U_LEFT_ARROW) {
/* Left */
if (position > 0) {
putchar('\b');
position--;
}
continue;
}
if (ch == U_RIGHT_ARROW) {
/* Right */
if (position < wstr_length(current)) {
putchar(current[position]);
position++;
}
continue;
}
if ((ch == U_UP_ARROW) || (ch == U_DOWN_ARROW)) {
/* Up, down */
print_cc('\b', position);
print_cc(' ', wstr_length(current));
print_cc('\b', wstr_length(current));
if (ch == U_UP_ARROW) {
/* Up */
if (history_pos == 0)
history_pos = KCONSOLE_HISTORY - 1;
else
history_pos--;
} else {
/* Down */
history_pos++;
history_pos = history_pos % KCONSOLE_HISTORY;
}
current = history[history_pos];
printf("%ls", current);
position = wstr_length(current);
continue;
}
if (ch == U_HOME_ARROW) {
/* Home */
print_cc('\b', position);
position = 0;
continue;
}
if (ch == U_END_ARROW) {
/* End */
printf("%ls", current + position);
position = wstr_length(current);
continue;
}
if (ch == U_DELETE) {
/* Delete */
if (position == wstr_length(current))
continue;
if (wstr_remove(current, position)) {
printf("%ls ", current + position);
print_cc('\b', wstr_length(current) - position + 1);
}
continue;
}
if (wstr_linsert(current, ch, position, MAX_CMDLINE)) {
printf("%ls", current + position);
position++;
print_cc('\b', wstr_length(current) - position);
}
}
if (wstr_length(current) > 0) {
history_pos++;
history_pos = history_pos % KCONSOLE_HISTORY;
}
return current;
}
 
bool kconsole_check_poll(void)
{
return check_poll(stdin);
}
 
static bool parse_int_arg(const char *text, size_t len, unative_t *result)
{
bool isaddr = false;
bool isptr = false;
/* If we get a name, try to find it in symbol table */
if (text[0] == '&') {
isaddr = true;
text++;
len--;
} else if (text[0] == '*') {
isptr = true;
text++;
len--;
}
if ((text[0] < '0') || (text[0] > '9')) {
char symname[MAX_SYMBOL_NAME];
str_ncpy(symname, MAX_SYMBOL_NAME, text, len + 1);
uintptr_t symaddr;
int rc = symtab_addr_lookup(symname, &symaddr);
switch (rc) {
case ENOENT:
printf("Symbol %s not found.\n", symname);
return false;
case EOVERFLOW:
printf("Duplicate symbol %s.\n", symname);
symtab_print_search(symname);
return false;
case ENOTSUP:
printf("No symbol information available.\n");
return false;
}
if (isaddr)
*result = (unative_t) symaddr;
else if (isptr)
*result = **((unative_t **) symaddr);
else
*result = *((unative_t *) symaddr);
} else {
/* It's a number - convert it */
*result = atoi(text);
if (isptr)
*result = *((unative_t *) *result);
}
return true;
}
 
/** Parse argument.
*
* Find start and end positions of command line argument.
*
* @param cmdline Command line as read from the input device.
* @param size Size (in bytes) of the string.
* @param start On entry, 'start' contains pointer to the offset
* of the first unprocessed character of cmdline.
* On successful exit, it marks beginning of the next argument.
* @param end Undefined on entry. On exit, 'end' is the offset of the first
* character behind the next argument.
*
* @return False on failure, true on success.
*
*/
static bool parse_argument(const char *cmdline, size_t size, size_t *start, size_t *end)
{
ASSERT(start != NULL);
ASSERT(end != NULL);
bool found_start = false;
size_t offset = *start;
size_t prev = *start;
wchar_t ch;
while ((ch = str_decode(cmdline, &offset, size)) != 0) {
if (!found_start) {
if (!isspace(ch)) {
*start = prev;
found_start = true;
}
} else {
if (isspace(ch))
break;
}
prev = offset;
}
*end = prev;
return found_start;
}
 
/** Parse command line.
*
* @param cmdline Command line as read from input device.
* @param size Size (in bytes) of the string.
*
* @return Structure describing the command.
*
*/
static cmd_info_t *parse_cmdline(const char *cmdline, size_t size)
{
size_t start = 0;
size_t end = 0;
if (!parse_argument(cmdline, size, &start, &end)) {
/* Command line did not contain alphanumeric word. */
return NULL;
}
spinlock_lock(&cmd_lock);
cmd_info_t *cmd = NULL;
link_t *cur;
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
if (str_lcmp(hlp->name, cmdline + start,
max(str_length(hlp->name),
str_nlength(cmdline + start, (size_t) (end - start) - 1))) == 0) {
cmd = hlp;
break;
}
spinlock_unlock(&hlp->lock);
}
spinlock_unlock(&cmd_lock);
if (!cmd) {
/* Unknown command. */
printf("Unknown command.\n");
return NULL;
}
/* cmd == hlp is locked */
/*
* The command line must be further analyzed and
* the parameters therefrom must be matched and
* converted to those specified in the cmd info
* structure.
*/
bool error = false;
size_t i;
for (i = 0; i < cmd->argc; i++) {
start = end;
if (!parse_argument(cmdline, size, &start, &end)) {
printf("Too few arguments.\n");
spinlock_unlock(&cmd->lock);
return NULL;
}
char *buf;
switch (cmd->argv[i].type) {
case ARG_TYPE_STRING:
buf = (char *) cmd->argv[i].buffer;
str_ncpy(buf, cmd->argv[i].len, cmdline + start,
end - start);
break;
case ARG_TYPE_INT:
if (!parse_int_arg(cmdline + start, end - start,
&cmd->argv[i].intval))
error = true;
break;
case ARG_TYPE_VAR:
if ((start < end - 1) && (cmdline[start] == '"')) {
if (cmdline[end - 1] == '"') {
buf = (char *) cmd->argv[i].buffer;
str_ncpy(buf, cmd->argv[i].len,
cmdline + start + 1,
(end - start) - 1);
cmd->argv[i].intval = (unative_t) buf;
cmd->argv[i].vartype = ARG_TYPE_STRING;
} else {
printf("Wrong synxtax.\n");
error = true;
}
} else if (parse_int_arg(cmdline + start,
end - start, &cmd->argv[i].intval)) {
cmd->argv[i].vartype = ARG_TYPE_INT;
} else {
printf("Unrecognized variable argument.\n");
error = true;
}
break;
case ARG_TYPE_INVALID:
default:
printf("Invalid argument type\n");
error = true;
break;
}
}
if (error) {
spinlock_unlock(&cmd->lock);
return NULL;
}
start = end;
if (parse_argument(cmdline, size, &start, &end)) {
printf("Too many arguments.\n");
spinlock_unlock(&cmd->lock);
return NULL;
}
spinlock_unlock(&cmd->lock);
return cmd;
}
 
/** Kernel console prompt.
*
* @param prompt Kernel console prompt (e.g kconsole/panic).
* @param msg Message to display in the beginning.
* @param kcon Wait for keypress to show the prompt
* and never exit.
*
*/
void kconsole(char *prompt, char *msg, bool kcon)
{
if (!stdin) {
LOG("No stdin for kernel console");
return;
}
if (msg)
printf("%s", msg);
if (kcon)
indev_pop_character(stdin);
else
printf("Type \"exit\" to leave the console.\n");
while (true) {
wchar_t *tmp = clever_readline((char *) prompt, stdin);
size_t len = wstr_length(tmp);
if (!len)
continue;
char cmdline[STR_BOUNDS(MAX_CMDLINE)];
wstr_nstr(cmdline, tmp, STR_BOUNDS(MAX_CMDLINE));
if ((!kcon) && (len == 4) && (str_lcmp(cmdline, "exit", 4) == 0))
break;
cmd_info_t *cmd_info = parse_cmdline(cmdline, STR_BOUNDS(MAX_CMDLINE));
if (!cmd_info)
continue;
(void) cmd_info->func(cmd_info->argv);
}
}
 
/** Kernel console managing thread.
*
*/
void kconsole_thread(void *data)
{
kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/chardev.c
0,0 → 1,151
/*
* Copyright (c) 2005 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 genericconsole
* @{
*/
/** @file
*/
 
#include <console/chardev.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
#include <print.h>
#include <func.h>
#include <arch.h>
 
/** Initialize input character device.
*
* @param indev Input character device.
* @param op Implementation of input character device operations.
*
*/
void indev_initialize(char *name, indev_t *indev,
indev_operations_t *op)
{
indev->name = name;
waitq_initialize(&indev->wq);
spinlock_initialize(&indev->lock, "indev");
indev->counter = 0;
indev->index = 0;
indev->op = op;
}
 
/** Push character read from input character device.
*
* @param indev Input character device.
* @param ch Character being pushed.
*
*/
void indev_push_character(indev_t *indev, wchar_t ch)
{
ASSERT(indev);
spinlock_lock(&indev->lock);
if (indev->counter == INDEV_BUFLEN - 1) {
/* Buffer full */
spinlock_unlock(&indev->lock);
return;
}
indev->counter++;
indev->buffer[indev->index++] = ch;
/* Index modulo size of buffer */
indev->index = indev->index % INDEV_BUFLEN;
waitq_wakeup(&indev->wq, WAKEUP_FIRST);
spinlock_unlock(&indev->lock);
}
 
/** Pop character from input character device.
*
* @param indev Input character device.
*
* @return Character read.
*
*/
wchar_t indev_pop_character(indev_t *indev)
{
if (atomic_get(&haltstate)) {
/* If we are here, we are hopefully on the processor that
* issued the 'halt' command, so proceed to read the character
* directly from input
*/
if (check_poll(indev))
return indev->op->poll(indev);
/* No other way of interacting with user */
interrupts_disable();
if (CPU)
printf("cpu%u: ", CPU->id);
else
printf("cpu: ");
printf("halted (no polling input)\n");
cpu_halt();
}
waitq_sleep(&indev->wq);
ipl_t ipl = interrupts_disable();
spinlock_lock(&indev->lock);
wchar_t ch = indev->buffer[(indev->index - indev->counter) % INDEV_BUFLEN];
indev->counter--;
spinlock_unlock(&indev->lock);
interrupts_restore(ipl);
return ch;
}
 
/** Initialize output character device.
*
* @param outdev Output character device.
* @param op Implementation of output character device operations.
*
*/
void outdev_initialize(char *name, outdev_t *outdev,
outdev_operations_t *op)
{
outdev->name = name;
spinlock_initialize(&outdev->lock, "outdev");
outdev->op = op;
}
 
bool check_poll(indev_t *indev)
{
if (indev == NULL)
return false;
if (indev->op == NULL)
return false;
return (indev->op->poll != NULL);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/proc/scheduler.c
0,0 → 1,739
/*
* Copyright (c) 2001-2007 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 genericproc
* @{
*/
 
/**
* @file
* @brief Scheduler and load balancing.
*
* This file contains the scheduler and kcpulb kernel thread which
* performs load-balancing of per-CPU run queues.
*/
 
#include <proc/scheduler.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <mm/frame.h>
#include <mm/page.h>
#include <mm/as.h>
#include <time/timeout.h>
#include <time/delay.h>
#include <arch/asm.h>
#include <arch/faddr.h>
#include <arch/cycle.h>
#include <atomic.h>
#include <synch/spinlock.h>
#include <config.h>
#include <context.h>
#include <fpu_context.h>
#include <func.h>
#include <arch.h>
#include <adt/list.h>
#include <panic.h>
#include <cpu.h>
#include <print.h>
#include <debug.h>
 
static void before_task_runs(void);
static void before_thread_runs(void);
static void after_thread_ran(void);
static void scheduler_separated_stack(void);
 
atomic_t nrdy; /**< Number of ready threads in the system. */
 
/** Carry out actions before new task runs. */
void before_task_runs(void)
{
before_task_runs_arch();
}
 
/** Take actions before new thread runs.
*
* Perform actions that need to be
* taken before the newly selected
* tread is passed control.
*
* THREAD->lock is locked on entry
*
*/
void before_thread_runs(void)
{
before_thread_runs_arch();
#ifdef CONFIG_FPU_LAZY
if(THREAD == CPU->fpu_owner)
fpu_enable();
else
fpu_disable();
#else
fpu_enable();
if (THREAD->fpu_context_exists)
fpu_context_restore(THREAD->saved_fpu_context);
else {
fpu_init();
THREAD->fpu_context_exists = 1;
}
#endif
}
 
/** Take actions after THREAD had run.
*
* Perform actions that need to be
* taken after the running thread
* had been preempted by the scheduler.
*
* THREAD->lock is locked on entry
*
*/
void after_thread_ran(void)
{
after_thread_ran_arch();
}
 
#ifdef CONFIG_FPU_LAZY
void scheduler_fpu_lazy_request(void)
{
restart:
fpu_enable();
spinlock_lock(&CPU->lock);
 
/* Save old context */
if (CPU->fpu_owner != NULL) {
spinlock_lock(&CPU->fpu_owner->lock);
fpu_context_save(CPU->fpu_owner->saved_fpu_context);
/* don't prevent migration */
CPU->fpu_owner->fpu_context_engaged = 0;
spinlock_unlock(&CPU->fpu_owner->lock);
CPU->fpu_owner = NULL;
}
 
spinlock_lock(&THREAD->lock);
if (THREAD->fpu_context_exists) {
fpu_context_restore(THREAD->saved_fpu_context);
} else {
/* Allocate FPU context */
if (!THREAD->saved_fpu_context) {
/* Might sleep */
spinlock_unlock(&THREAD->lock);
spinlock_unlock(&CPU->lock);
THREAD->saved_fpu_context =
(fpu_context_t *) slab_alloc(fpu_context_slab, 0);
/* We may have switched CPUs during slab_alloc */
goto restart;
}
fpu_init();
THREAD->fpu_context_exists = 1;
}
CPU->fpu_owner = THREAD;
THREAD->fpu_context_engaged = 1;
spinlock_unlock(&THREAD->lock);
 
spinlock_unlock(&CPU->lock);
}
#endif
 
/** Initialize scheduler
*
* Initialize kernel scheduler.
*
*/
void scheduler_init(void)
{
}
 
/** Get thread to be scheduled
*
* Get the optimal thread to be scheduled
* according to thread accounting and scheduler
* policy.
*
* @return Thread to be scheduled.
*
*/
static thread_t *find_best_thread(void)
{
thread_t *t;
runq_t *r;
int i;
 
ASSERT(CPU != NULL);
 
loop:
interrupts_enable();
if (atomic_get(&CPU->nrdy) == 0) {
/*
* For there was nothing to run, the CPU goes to sleep
* until a hardware interrupt or an IPI comes.
* This improves energy saving and hyperthreading.
*/
 
/*
* An interrupt might occur right now and wake up a thread.
* In such case, the CPU will continue to go to sleep
* even though there is a runnable thread.
*/
 
cpu_sleep();
goto loop;
}
 
interrupts_disable();
for (i = 0; i < RQ_COUNT; i++) {
r = &CPU->rq[i];
spinlock_lock(&r->lock);
if (r->n == 0) {
/*
* If this queue is empty, try a lower-priority queue.
*/
spinlock_unlock(&r->lock);
continue;
}
 
atomic_dec(&CPU->nrdy);
atomic_dec(&nrdy);
r->n--;
 
/*
* Take the first thread from the queue.
*/
t = list_get_instance(r->rq_head.next, thread_t, rq_link);
list_remove(&t->rq_link);
 
spinlock_unlock(&r->lock);
 
spinlock_lock(&t->lock);
t->cpu = CPU;
 
t->ticks = us2ticks((i + 1) * 10000);
t->priority = i; /* correct rq index */
 
/*
* Clear the THREAD_FLAG_STOLEN flag so that t can be migrated
* when load balancing needs emerge.
*/
t->flags &= ~THREAD_FLAG_STOLEN;
spinlock_unlock(&t->lock);
 
return t;
}
goto loop;
 
}
 
/** Prevent rq starvation
*
* Prevent low priority threads from starving in rq's.
*
* When the function decides to relink rq's, it reconnects
* respective pointers so that in result threads with 'pri'
* greater or equal start are moved to a higher-priority queue.
*
* @param start Threshold priority.
*
*/
static void relink_rq(int start)
{
link_t head;
runq_t *r;
int i, n;
 
list_initialize(&head);
spinlock_lock(&CPU->lock);
if (CPU->needs_relink > NEEDS_RELINK_MAX) {
for (i = start; i < RQ_COUNT - 1; i++) {
/* remember and empty rq[i + 1] */
r = &CPU->rq[i + 1];
spinlock_lock(&r->lock);
list_concat(&head, &r->rq_head);
n = r->n;
r->n = 0;
spinlock_unlock(&r->lock);
/* append rq[i + 1] to rq[i] */
r = &CPU->rq[i];
spinlock_lock(&r->lock);
list_concat(&r->rq_head, &head);
r->n += n;
spinlock_unlock(&r->lock);
}
CPU->needs_relink = 0;
}
spinlock_unlock(&CPU->lock);
 
}
 
/** The scheduler
*
* The thread scheduling procedure.
* Passes control directly to
* scheduler_separated_stack().
*
*/
void scheduler(void)
{
volatile ipl_t ipl;
 
ASSERT(CPU != NULL);
 
ipl = interrupts_disable();
 
if (atomic_get(&haltstate))
halt();
if (THREAD) {
spinlock_lock(&THREAD->lock);
/* Update thread accounting */
THREAD->cycles += get_cycle() - THREAD->last_cycle;
#ifndef CONFIG_FPU_LAZY
fpu_context_save(THREAD->saved_fpu_context);
#endif
if (!context_save(&THREAD->saved_context)) {
/*
* This is the place where threads leave scheduler();
*/
/* Save current CPU cycle */
THREAD->last_cycle = get_cycle();
spinlock_unlock(&THREAD->lock);
interrupts_restore(THREAD->saved_context.ipl);
return;
}
 
/*
* Interrupt priority level of preempted thread is recorded
* here to facilitate scheduler() invocations from
* interrupts_disable()'d code (e.g. waitq_sleep_timeout()).
*/
THREAD->saved_context.ipl = ipl;
}
 
/*
* Through the 'THE' structure, we keep track of THREAD, TASK, CPU, VM
* and preemption counter. At this point THE could be coming either
* from THREAD's or CPU's stack.
*/
the_copy(THE, (the_t *) CPU->stack);
 
/*
* We may not keep the old stack.
* Reason: If we kept the old stack and got blocked, for instance, in
* find_best_thread(), the old thread could get rescheduled by another
* CPU and overwrite the part of its own stack that was also used by
* the scheduler on this CPU.
*
* Moreover, we have to bypass the compiler-generated POP sequence
* which is fooled by SP being set to the very top of the stack.
* Therefore the scheduler() function continues in
* scheduler_separated_stack().
*/
context_save(&CPU->saved_context);
context_set(&CPU->saved_context, FADDR(scheduler_separated_stack),
(uintptr_t) CPU->stack, CPU_STACK_SIZE);
context_restore(&CPU->saved_context);
/* not reached */
}
 
/** Scheduler stack switch wrapper
*
* Second part of the scheduler() function
* using new stack. Handling the actual context
* switch to a new thread.
*
* Assume THREAD->lock is held.
*/
void scheduler_separated_stack(void)
{
int priority;
DEADLOCK_PROBE_INIT(p_joinwq);
 
ASSERT(CPU != NULL);
if (THREAD) {
/* must be run after the switch to scheduler stack */
after_thread_ran();
 
switch (THREAD->state) {
case Running:
spinlock_unlock(&THREAD->lock);
thread_ready(THREAD);
break;
 
case Exiting:
repeat:
if (THREAD->detached) {
thread_destroy(THREAD);
} else {
/*
* The thread structure is kept allocated until
* somebody calls thread_detach() on it.
*/
if (!spinlock_trylock(&THREAD->join_wq.lock)) {
/*
* Avoid deadlock.
*/
spinlock_unlock(&THREAD->lock);
delay(HZ);
spinlock_lock(&THREAD->lock);
DEADLOCK_PROBE(p_joinwq,
DEADLOCK_THRESHOLD);
goto repeat;
}
_waitq_wakeup_unsafe(&THREAD->join_wq,
WAKEUP_FIRST);
spinlock_unlock(&THREAD->join_wq.lock);
THREAD->state = Lingering;
spinlock_unlock(&THREAD->lock);
}
break;
case Sleeping:
/*
* Prefer the thread after it's woken up.
*/
THREAD->priority = -1;
 
/*
* We need to release wq->lock which we locked in
* waitq_sleep(). Address of wq->lock is kept in
* THREAD->sleep_queue.
*/
spinlock_unlock(&THREAD->sleep_queue->lock);
 
/*
* Check for possible requests for out-of-context
* invocation.
*/
if (THREAD->call_me) {
THREAD->call_me(THREAD->call_me_with);
THREAD->call_me = NULL;
THREAD->call_me_with = NULL;
}
 
spinlock_unlock(&THREAD->lock);
 
break;
 
default:
/*
* Entering state is unexpected.
*/
panic("tid%" PRIu64 ": unexpected state %s.",
THREAD->tid, thread_states[THREAD->state]);
break;
}
 
THREAD = NULL;
}
 
THREAD = find_best_thread();
spinlock_lock(&THREAD->lock);
priority = THREAD->priority;
spinlock_unlock(&THREAD->lock);
 
relink_rq(priority);
 
/*
* If both the old and the new task are the same, lots of work is
* avoided.
*/
if (TASK != THREAD->task) {
as_t *as1 = NULL;
as_t *as2;
 
if (TASK) {
spinlock_lock(&TASK->lock);
as1 = TASK->as;
spinlock_unlock(&TASK->lock);
}
 
spinlock_lock(&THREAD->task->lock);
as2 = THREAD->task->as;
spinlock_unlock(&THREAD->task->lock);
/*
* Note that it is possible for two tasks to share one address
* space.
*/
if (as1 != as2) {
/*
* Both tasks and address spaces are different.
* Replace the old one with the new one.
*/
as_switch(as1, as2);
}
TASK = THREAD->task;
before_task_runs();
}
 
spinlock_lock(&THREAD->lock);
THREAD->state = Running;
 
#ifdef SCHEDULER_VERBOSE
printf("cpu%u: tid %" PRIu64 " (priority=%d, ticks=%" PRIu64
", nrdy=%ld)\n", CPU->id, THREAD->tid, THREAD->priority,
THREAD->ticks, atomic_get(&CPU->nrdy));
#endif
 
/*
* Some architectures provide late kernel PA2KA(identity)
* mapping in a page fault handler. However, the page fault
* handler uses the kernel stack of the running thread and
* therefore cannot be used to map it. The kernel stack, if
* necessary, is to be mapped in before_thread_runs(). This
* function must be executed before the switch to the new stack.
*/
before_thread_runs();
 
/*
* Copy the knowledge of CPU, TASK, THREAD and preemption counter to
* thread's stack.
*/
the_copy(THE, (the_t *) THREAD->kstack);
context_restore(&THREAD->saved_context);
/* not reached */
}
 
#ifdef CONFIG_SMP
/** Load balancing thread
*
* SMP load balancing thread, supervising thread supplies
* for the CPU it's wired to.
*
* @param arg Generic thread argument (unused).
*
*/
void kcpulb(void *arg)
{
thread_t *t;
int count, average, j, k = 0;
unsigned int i;
ipl_t ipl;
 
/*
* Detach kcpulb as nobody will call thread_join_timeout() on it.
*/
thread_detach(THREAD);
loop:
/*
* Work in 1s intervals.
*/
thread_sleep(1);
 
not_satisfied:
/*
* Calculate the number of threads that will be migrated/stolen from
* other CPU's. Note that situation can have changed between two
* passes. Each time get the most up to date counts.
*/
average = atomic_get(&nrdy) / config.cpu_active + 1;
count = average - atomic_get(&CPU->nrdy);
 
if (count <= 0)
goto satisfied;
 
/*
* Searching least priority queues on all CPU's first and most priority
* queues on all CPU's last.
*/
for (j = RQ_COUNT - 1; j >= 0; j--) {
for (i = 0; i < config.cpu_active; i++) {
link_t *l;
runq_t *r;
cpu_t *cpu;
 
cpu = &cpus[(i + k) % config.cpu_active];
 
/*
* Not interested in ourselves.
* Doesn't require interrupt disabling for kcpulb has
* THREAD_FLAG_WIRED.
*/
if (CPU == cpu)
continue;
if (atomic_get(&cpu->nrdy) <= average)
continue;
 
ipl = interrupts_disable();
r = &cpu->rq[j];
spinlock_lock(&r->lock);
if (r->n == 0) {
spinlock_unlock(&r->lock);
interrupts_restore(ipl);
continue;
}
t = NULL;
l = r->rq_head.prev; /* search rq from the back */
while (l != &r->rq_head) {
t = list_get_instance(l, thread_t, rq_link);
/*
* We don't want to steal CPU-wired threads
* neither threads already stolen. The latter
* prevents threads from migrating between CPU's
* without ever being run. We don't want to
* steal threads whose FPU context is still in
* CPU.
*/
spinlock_lock(&t->lock);
if ((!(t->flags & (THREAD_FLAG_WIRED |
THREAD_FLAG_STOLEN))) &&
(!(t->fpu_context_engaged))) {
/*
* Remove t from r.
*/
spinlock_unlock(&t->lock);
atomic_dec(&cpu->nrdy);
atomic_dec(&nrdy);
 
r->n--;
list_remove(&t->rq_link);
 
break;
}
spinlock_unlock(&t->lock);
l = l->prev;
t = NULL;
}
spinlock_unlock(&r->lock);
 
if (t) {
/*
* Ready t on local CPU
*/
spinlock_lock(&t->lock);
#ifdef KCPULB_VERBOSE
printf("kcpulb%u: TID %" PRIu64 " -> cpu%u, "
"nrdy=%ld, avg=%ld\n", CPU->id, t->tid,
CPU->id, atomic_get(&CPU->nrdy),
atomic_get(&nrdy) / config.cpu_active);
#endif
t->flags |= THREAD_FLAG_STOLEN;
t->state = Entering;
spinlock_unlock(&t->lock);
thread_ready(t);
 
interrupts_restore(ipl);
if (--count == 0)
goto satisfied;
/*
* We are not satisfied yet, focus on another
* CPU next time.
*/
k++;
continue;
}
interrupts_restore(ipl);
}
}
 
if (atomic_get(&CPU->nrdy)) {
/*
* Be a little bit light-weight and let migrated threads run.
*/
scheduler();
} else {
/*
* We failed to migrate a single thread.
* Give up this turn.
*/
goto loop;
}
goto not_satisfied;
 
satisfied:
goto loop;
}
 
#endif /* CONFIG_SMP */
 
 
/** Print information about threads & scheduler queues */
void sched_print_list(void)
{
ipl_t ipl;
unsigned int cpu, i;
runq_t *r;
thread_t *t;
link_t *cur;
 
/* We are going to mess with scheduler structures,
* let's not be interrupted */
ipl = interrupts_disable();
for (cpu = 0; cpu < config.cpu_count; cpu++) {
 
if (!cpus[cpu].active)
continue;
 
spinlock_lock(&cpus[cpu].lock);
printf("cpu%u: address=%p, nrdy=%ld, needs_relink=%" PRIs "\n",
cpus[cpu].id, &cpus[cpu], atomic_get(&cpus[cpu].nrdy),
cpus[cpu].needs_relink);
for (i = 0; i < RQ_COUNT; i++) {
r = &cpus[cpu].rq[i];
spinlock_lock(&r->lock);
if (!r->n) {
spinlock_unlock(&r->lock);
continue;
}
printf("\trq[%u]: ", i);
for (cur = r->rq_head.next; cur != &r->rq_head;
cur = cur->next) {
t = list_get_instance(cur, thread_t, rq_link);
printf("%" PRIu64 "(%s) ", t->tid,
thread_states[t->state]);
}
printf("\n");
spinlock_unlock(&r->lock);
}
spinlock_unlock(&cpus[cpu].lock);
}
interrupts_restore(ipl);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/proc/task.c
0,0 → 1,467
/*
* Copyright (c) 2001-2004 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 genericproc
* @{
*/
 
/**
* @file
* @brief Task management.
*/
 
#include <proc/thread.h>
#include <proc/task.h>
#include <mm/as.h>
#include <mm/slab.h>
#include <atomic.h>
#include <synch/spinlock.h>
#include <synch/waitq.h>
#include <arch.h>
#include <arch/barrier.h>
#include <adt/avl.h>
#include <adt/btree.h>
#include <adt/list.h>
#include <ipc/ipc.h>
#include <ipc/ipcrsc.h>
#include <print.h>
#include <errno.h>
#include <func.h>
#include <string.h>
#include <syscall/copy.h>
#include <macros.h>
#include <ipc/event.h>
 
/** Spinlock protecting the tasks_tree AVL tree. */
SPINLOCK_INITIALIZE(tasks_lock);
 
/** AVL tree of active tasks.
*
* The task is guaranteed to exist after it was found in the tasks_tree as
* long as:
* @li the tasks_lock is held,
* @li the task's lock is held when task's lock is acquired before releasing
* tasks_lock or
* @li the task's refcount is greater than 0
*
*/
avltree_t tasks_tree;
 
static task_id_t task_counter = 0;
 
/** Initialize kernel tasks support. */
void task_init(void)
{
TASK = NULL;
avltree_create(&tasks_tree);
}
 
/*
* The idea behind this walker is to remember a single task different from
* TASK.
*/
static bool task_done_walker(avltree_node_t *node, void *arg)
{
task_t *t = avltree_get_instance(node, task_t, tasks_tree_node);
task_t **tp = (task_t **) arg;
 
if (t != TASK) {
*tp = t;
return false; /* stop walking */
}
 
return true; /* continue the walk */
}
 
/** Kill all tasks except the current task. */
void task_done(void)
{
task_t *t;
do { /* Repeat until there are any tasks except TASK */
/* Messing with task structures, avoid deadlock */
ipl_t ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
t = NULL;
avltree_walk(&tasks_tree, task_done_walker, &t);
if (t != NULL) {
task_id_t id = t->taskid;
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
#ifdef CONFIG_DEBUG
printf("Killing task %" PRIu64 "\n", id);
#endif
task_kill(id);
thread_usleep(10000);
} else {
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
}
} while (t != NULL);
}
 
/** Create new task with no threads.
*
* @param as Task's address space.
* @param name Symbolic name (a copy is made).
*
* @return New task's structure.
*
*/
task_t *task_create(as_t *as, char *name)
{
ipl_t ipl;
task_t *ta;
int i;
ta = (task_t *) malloc(sizeof(task_t), 0);
 
task_create_arch(ta);
 
spinlock_initialize(&ta->lock, "task_ta_lock");
list_initialize(&ta->th_head);
ta->as = as;
 
memcpy(ta->name, name, TASK_NAME_BUFLEN);
ta->name[TASK_NAME_BUFLEN - 1] = 0;
 
atomic_set(&ta->refcount, 0);
atomic_set(&ta->lifecount, 0);
ta->context = CONTEXT;
 
ta->capabilities = 0;
ta->cycles = 0;
 
#ifdef CONFIG_UDEBUG
/* Init debugging stuff */
udebug_task_init(&ta->udebug);
 
/* Init kbox stuff */
ipc_answerbox_init(&ta->kb.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]);
if ((ipc_phone_0) && (context_check(ipc_phone_0->task->context,
ta->context)))
ipc_phone_connect(&ta->phones[0], ipc_phone_0);
atomic_set(&ta->active_calls, 0);
 
mutex_initialize(&ta->futexes_lock, MUTEX_PASSIVE);
btree_create(&ta->futexes);
ipl = interrupts_disable();
 
/*
* Increment address space reference count.
*/
atomic_inc(&as->refcount);
 
spinlock_lock(&tasks_lock);
ta->taskid = ++task_counter;
avltree_node_initialize(&ta->tasks_tree_node);
ta->tasks_tree_node.key = ta->taskid;
avltree_insert(&tasks_tree, &ta->tasks_tree_node);
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
/*
* Notify about task creation.
*/
if (event_is_subscribed(EVENT_WAIT))
event_notify_3(EVENT_WAIT, TASK_CREATE, LOWER32(ta->taskid),
UPPER32(ta->taskid));
return ta;
}
 
/** Destroy task.
*
* @param t Task to be destroyed.
*/
void task_destroy(task_t *t)
{
/*
* Remove the task from the task B+tree.
*/
spinlock_lock(&tasks_lock);
avltree_delete(&tasks_tree, &t->tasks_tree_node);
spinlock_unlock(&tasks_lock);
 
/*
* Perform architecture specific task destruction.
*/
task_destroy_arch(t);
 
/*
* Free up dynamically allocated state.
*/
btree_destroy(&t->futexes);
 
/*
* Drop our reference to the address space.
*/
if (atomic_predec(&t->as->refcount) == 0)
as_destroy(t->as);
/*
* Notify about task destruction.
*/
if (event_is_subscribed(EVENT_WAIT))
event_notify_3(EVENT_WAIT, TASK_DESTROY, LOWER32(t->taskid),
UPPER32(t->taskid));
free(t);
TASK = NULL;
}
 
/** Syscall for reading task ID from userspace.
*
* @param uspace_task_id userspace address of 8-byte buffer
* where to store current task ID.
*
* @return Zero on success or an error code from @ref errno.h.
*/
unative_t sys_task_get_id(task_id_t *uspace_task_id)
{
/*
* No need to acquire lock on TASK because taskid remains constant for
* the lifespan of the task.
*/
return (unative_t) copy_to_uspace(uspace_task_id, &TASK->taskid,
sizeof(TASK->taskid));
}
 
/** Syscall for setting the task name.
*
* The name simplifies identifying the task in the task list.
*
* @param name The new name for the task. (typically the same
* as the command used to execute it).
*
* @return 0 on success or an error code from @ref errno.h.
*/
unative_t sys_task_set_name(const char *uspace_name, size_t name_len)
{
int rc;
char namebuf[TASK_NAME_BUFLEN];
 
/* Cap length of name and copy it from userspace. */
 
if (name_len > TASK_NAME_BUFLEN - 1)
name_len = TASK_NAME_BUFLEN - 1;
 
rc = copy_from_uspace(namebuf, uspace_name, name_len);
if (rc != 0)
return (unative_t) rc;
 
namebuf[name_len] = '\0';
str_cpy(TASK->name, TASK_NAME_BUFLEN, namebuf);
 
return EOK;
}
 
/** Find task structure corresponding to task ID.
*
* The tasks_lock must be already held by the caller of this function and
* interrupts must be disabled.
*
* @param id Task ID.
*
* @return Task structure address or NULL if there is no such task
* ID.
*/
task_t *task_find_by_id(task_id_t id) { avltree_node_t *node;
node = avltree_search(&tasks_tree, (avltree_key_t) id);
 
if (node)
return avltree_get_instance(node, task_t, tasks_tree_node);
return NULL;
}
 
/** Get accounting data of given task.
*
* Note that task lock of 't' must be already held and interrupts must be
* already disabled.
*
* @param t Pointer to thread.
*
* @return Number of cycles used by the task and all its threads
* so far.
*/
uint64_t task_get_accounting(task_t *t)
{
/* Accumulated value of task */
uint64_t ret = t->cycles;
/* Current values of threads */
link_t *cur;
for (cur = t->th_head.next; cur != &t->th_head; cur = cur->next) {
thread_t *thr = list_get_instance(cur, thread_t, th_link);
spinlock_lock(&thr->lock);
/* Process only counted threads */
if (!thr->uncounted) {
if (thr == THREAD) {
/* Update accounting of current thread */
thread_update_accounting();
}
ret += thr->cycles;
}
spinlock_unlock(&thr->lock);
}
return ret;
}
 
/** Kill task.
*
* This function is idempotent.
* It signals all the task's threads to bail it out.
*
* @param id ID of the task to be killed.
*
* @return Zero on success or an error code from errno.h.
*/
int task_kill(task_id_t id)
{
ipl_t ipl;
task_t *ta;
link_t *cur;
 
if (id == 1)
return EPERM;
ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
if (!(ta = task_find_by_id(id))) {
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return ENOENT;
}
spinlock_unlock(&tasks_lock);
/*
* Interrupt all threads.
*/
spinlock_lock(&ta->lock);
for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
thread_t *thr;
bool sleeping = false;
thr = list_get_instance(cur, thread_t, th_link);
spinlock_lock(&thr->lock);
thr->interrupted = true;
if (thr->state == Sleeping)
sleeping = true;
spinlock_unlock(&thr->lock);
if (sleeping)
waitq_interrupt_sleep(thr);
}
spinlock_unlock(&ta->lock);
interrupts_restore(ipl);
return 0;
}
 
static bool task_print_walker(avltree_node_t *node, void *arg)
{
task_t *t = avltree_get_instance(node, task_t, tasks_tree_node);
int j;
spinlock_lock(&t->lock);
uint64_t cycles;
char suffix;
order(task_get_accounting(t), &cycles, &suffix);
 
#ifdef __32_BITS__
printf("%-6" PRIu64 " %-12s %-3" PRIu32 " %10p %10p %9" PRIu64
"%c %7ld %6ld", t->taskid, t->name, t->context, t, t->as, cycles,
suffix, atomic_get(&t->refcount), atomic_get(&t->active_calls));
#endif
 
#ifdef __64_BITS__
printf("%-6" PRIu64 " %-12s %-3" PRIu32 " %18p %18p %9" PRIu64
"%c %7ld %6ld", t->taskid, t->name, t->context, t, t->as, cycles,
suffix, atomic_get(&t->refcount), atomic_get(&t->active_calls));
#endif
 
for (j = 0; j < IPC_MAX_PHONES; j++) {
if (t->phones[j].callee)
printf(" %d:%p", j, t->phones[j].callee);
}
printf("\n");
spinlock_unlock(&t->lock);
return true;
}
 
/** Print task list */
void task_print_list(void)
{
ipl_t ipl;
/* Messing with task structures, avoid deadlock */
ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
 
#ifdef __32_BITS__
printf("taskid name ctx address as "
"cycles threads calls callee\n");
printf("------ ------------ --- ---------- ---------- "
"---------- ------- ------ ------>\n");
#endif
 
#ifdef __64_BITS__
printf("taskid name ctx address as "
"cycles threads calls callee\n");
printf("------ ------------ --- ------------------ ------------------ "
"---------- ------- ------ ------>\n");
#endif
 
avltree_walk(&tasks_tree, task_print_walker, NULL);
 
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/proc/program.c
0,0 → 1,233
/*
* Copyright (c) 2001-2004 Jakub Jermar
* 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 genericproc
* @{
*/
 
/**
* @file
* @brief Running userspace programs.
*/
 
#include <main/uinit.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <proc/uarg.h>
#include <mm/as.h>
#include <mm/slab.h>
#include <arch.h>
#include <adt/list.h>
#include <ipc/ipc.h>
#include <ipc/ipcrsc.h>
#include <security/cap.h>
#include <lib/elf.h>
#include <errno.h>
#include <print.h>
#include <syscall/copy.h>
#include <proc/program.h>
 
#ifndef LOADED_PROG_STACK_PAGES_NO
#define LOADED_PROG_STACK_PAGES_NO 1
#endif
 
/**
* Points to the binary image used as the program loader. All non-initial
* tasks are created from this executable image.
*/
void *program_loader = NULL;
 
/** Create a program using an existing address space.
*
* @param as Address space containing a binary program image.
* @param entry_addr Program entry-point address in program address space.
* @param name Name to set for the program's task.
* @param p Buffer for storing program information.
*/
void program_create(as_t *as, uintptr_t entry_addr, char *name, program_t *p)
{
as_area_t *a;
uspace_arg_t *kernel_uarg;
 
kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);
kernel_uarg->uspace_entry = (void *) entry_addr;
kernel_uarg->uspace_stack = (void *) USTACK_ADDRESS;
kernel_uarg->uspace_thread_function = NULL;
kernel_uarg->uspace_thread_arg = NULL;
kernel_uarg->uspace_uarg = NULL;
p->task = task_create(as, name);
ASSERT(p->task);
 
/*
* Create the data as_area.
*/
a = as_area_create(as, AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE,
LOADED_PROG_STACK_PAGES_NO * PAGE_SIZE, USTACK_ADDRESS,
AS_AREA_ATTR_NONE, &anon_backend, NULL);
 
/*
* Create the main thread.
*/
p->main_thread = thread_create(uinit, kernel_uarg, p->task,
THREAD_FLAG_USPACE, "uinit", false);
ASSERT(p->main_thread);
}
 
/** Parse an executable image in the kernel memory.
*
* If the image belongs to a program loader, it is registered as such,
* (and *task is set to NULL). Otherwise a task is created from the
* executable image. The task is returned in *task.
*
* @param image_addr Address of an executable program image.
* @param name Name to set for the program's task.
* @param p Buffer for storing program info. If image_addr
* points to a loader image, p->task will be set to
* NULL and EOK will be returned.
*
* @return EOK on success or negative error code.
*/
int program_create_from_image(void *image_addr, char *name, program_t *p)
{
as_t *as;
unsigned int rc;
 
as = as_create(0);
ASSERT(as);
 
rc = elf_load((elf_header_t *) image_addr, as, 0);
if (rc != EE_OK) {
as_destroy(as);
p->task = NULL;
p->main_thread = NULL;
if (rc != EE_LOADER)
return ENOTSUP;
/* Register image as the program loader */
ASSERT(program_loader == NULL);
program_loader = image_addr;
LOG("Registered program loader at 0x%" PRIp "\n",
image_addr);
return EOK;
}
 
program_create(as, ((elf_header_t *) image_addr)->e_entry, name, p);
 
return EOK;
}
 
/** Create a task from the program loader image.
*
* @param p Buffer for storing program info.
* @param name Name to set for the program's task.
*
* @return EOK on success or negative error code.
*/
int program_create_loader(program_t *p, char *name)
{
as_t *as;
unsigned int rc;
void *loader;
 
as = as_create(0);
ASSERT(as);
 
loader = program_loader;
if (!loader) {
printf("Cannot spawn loader as none was registered\n");
return ENOENT;
}
 
rc = elf_load((elf_header_t *) program_loader, as, ELD_F_LOADER);
if (rc != EE_OK) {
as_destroy(as);
return ENOENT;
}
 
program_create(as, ((elf_header_t *) program_loader)->e_entry,
name, p);
 
return EOK;
}
 
/** Make program ready.
*
* Switch program's main thread to the ready state.
*
* @param p Program to make ready.
*/
void program_ready(program_t *p)
{
thread_ready(p->main_thread);
}
 
/** Syscall for creating a new loader instance from userspace.
*
* Creates a new task from the program loader image and sets
* the task name.
*
* @param name Name to set on the new task (typically the same
* as the command used to execute it).
*
* @return 0 on success or an error code from @ref errno.h.
*/
unative_t sys_program_spawn_loader(char *uspace_name, size_t name_len)
{
program_t p;
int rc;
char namebuf[TASK_NAME_BUFLEN];
 
/* Cap length of name and copy it from userspace. */
 
if (name_len > TASK_NAME_BUFLEN - 1)
name_len = TASK_NAME_BUFLEN - 1;
 
rc = copy_from_uspace(namebuf, uspace_name, name_len);
if (rc != 0)
return (unative_t) rc;
 
namebuf[name_len] = 0;
 
/* Spawn the new task. */
 
rc = program_create_loader(&p, namebuf);
if (rc != 0)
return rc;
 
// FIXME: control the capabilities
cap_set(p.task, cap_get(TASK));
 
program_ready(&p);
 
return EOK;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/proc/thread.c
0,0 → 1,815
/*
* Copyright (c) 2001-2004 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 genericproc
* @{
*/
 
/**
* @file
* @brief Thread management functions.
*/
 
#include <proc/scheduler.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <proc/uarg.h>
#include <mm/frame.h>
#include <mm/page.h>
#include <arch/asm.h>
#include <arch/cycle.h>
#include <arch.h>
#include <synch/synch.h>
#include <synch/spinlock.h>
#include <synch/waitq.h>
#include <synch/rwlock.h>
#include <cpu.h>
#include <func.h>
#include <context.h>
#include <adt/avl.h>
#include <adt/list.h>
#include <time/clock.h>
#include <time/timeout.h>
#include <config.h>
#include <arch/interrupt.h>
#include <smp/ipi.h>
#include <arch/faddr.h>
#include <atomic.h>
#include <memstr.h>
#include <print.h>
#include <mm/slab.h>
#include <debug.h>
#include <main/uinit.h>
#include <syscall/copy.h>
#include <errno.h>
 
 
#ifndef LOADED_PROG_STACK_PAGES_NO
#define LOADED_PROG_STACK_PAGES_NO 1
#endif
 
 
/** Thread states */
char *thread_states[] = {
"Invalid",
"Running",
"Sleeping",
"Ready",
"Entering",
"Exiting",
"Lingering"
};
 
/** Lock protecting the threads_tree AVL tree.
*
* For locking rules, see declaration thereof.
*/
SPINLOCK_INITIALIZE(threads_lock);
 
/** AVL tree of all threads.
*
* When a thread is found in the threads_tree AVL tree, it is guaranteed to
* exist as long as the threads_lock is held.
*/
avltree_t threads_tree;
 
SPINLOCK_INITIALIZE(tidlock);
thread_id_t last_tid = 0;
 
static slab_cache_t *thread_slab;
#ifdef CONFIG_FPU
slab_cache_t *fpu_context_slab;
#endif
 
/** Thread wrapper.
*
* This wrapper is provided to ensure that every thread makes a call to
* thread_exit() when its implementing function returns.
*
* interrupts_disable() is assumed.
*
*/
static void cushion(void)
{
void (*f)(void *) = THREAD->thread_code;
void *arg = THREAD->thread_arg;
THREAD->last_cycle = get_cycle();
 
/* This is where each thread wakes up after its creation */
spinlock_unlock(&THREAD->lock);
interrupts_enable();
 
f(arg);
/* Accumulate accounting to the task */
ipl_t ipl = interrupts_disable();
spinlock_lock(&THREAD->lock);
if (!THREAD->uncounted) {
thread_update_accounting();
uint64_t cycles = THREAD->cycles;
THREAD->cycles = 0;
spinlock_unlock(&THREAD->lock);
spinlock_lock(&TASK->lock);
TASK->cycles += cycles;
spinlock_unlock(&TASK->lock);
} else
spinlock_unlock(&THREAD->lock);
interrupts_restore(ipl);
thread_exit();
/* not reached */
}
 
/** Initialization and allocation for thread_t structure */
static int thr_constructor(void *obj, int kmflags)
{
thread_t *t = (thread_t *) obj;
 
spinlock_initialize(&t->lock, "thread_t_lock");
link_initialize(&t->rq_link);
link_initialize(&t->wq_link);
link_initialize(&t->th_link);
 
/* call the architecture-specific part of the constructor */
thr_constructor_arch(t);
#ifdef CONFIG_FPU
#ifdef CONFIG_FPU_LAZY
t->saved_fpu_context = NULL;
#else
t->saved_fpu_context = slab_alloc(fpu_context_slab, kmflags);
if (!t->saved_fpu_context)
return -1;
#endif
#endif
 
t->kstack = (uint8_t *) frame_alloc(STACK_FRAMES, FRAME_KA | kmflags);
if (!t->kstack) {
#ifdef CONFIG_FPU
if (t->saved_fpu_context)
slab_free(fpu_context_slab, t->saved_fpu_context);
#endif
return -1;
}
 
#ifdef CONFIG_UDEBUG
mutex_initialize(&t->udebug.lock, MUTEX_PASSIVE);
#endif
 
return 0;
}
 
/** Destruction of thread_t object */
static int thr_destructor(void *obj)
{
thread_t *t = (thread_t *) obj;
 
/* call the architecture-specific part of the destructor */
thr_destructor_arch(t);
 
frame_free(KA2PA(t->kstack));
#ifdef CONFIG_FPU
if (t->saved_fpu_context)
slab_free(fpu_context_slab, t->saved_fpu_context);
#endif
return 1; /* One page freed */
}
 
/** Initialize threads
*
* Initialize kernel threads support.
*
*/
void thread_init(void)
{
THREAD = NULL;
atomic_set(&nrdy, 0);
thread_slab = slab_cache_create("thread_slab", sizeof(thread_t), 0,
thr_constructor, thr_destructor, 0);
 
#ifdef CONFIG_FPU
fpu_context_slab = slab_cache_create("fpu_slab", sizeof(fpu_context_t),
FPU_CONTEXT_ALIGN, NULL, NULL, 0);
#endif
 
avltree_create(&threads_tree);
}
 
/** Make thread ready
*
* Switch thread t to the ready state.
*
* @param t Thread to make ready.
*
*/
void thread_ready(thread_t *t)
{
cpu_t *cpu;
runq_t *r;
ipl_t ipl;
int i, avg;
 
ipl = interrupts_disable();
 
spinlock_lock(&t->lock);
 
ASSERT(!(t->state == Ready));
 
i = (t->priority < RQ_COUNT - 1) ? ++t->priority : t->priority;
cpu = CPU;
if (t->flags & THREAD_FLAG_WIRED) {
ASSERT(t->cpu != NULL);
cpu = t->cpu;
}
t->state = Ready;
spinlock_unlock(&t->lock);
/*
* Append t to respective ready queue on respective processor.
*/
r = &cpu->rq[i];
spinlock_lock(&r->lock);
list_append(&t->rq_link, &r->rq_head);
r->n++;
spinlock_unlock(&r->lock);
 
atomic_inc(&nrdy);
avg = atomic_get(&nrdy) / config.cpu_active;
atomic_inc(&cpu->nrdy);
 
interrupts_restore(ipl);
}
 
/** Create new thread
*
* Create a new thread.
*
* @param func Thread's implementing function.
* @param arg Thread's implementing function argument.
* @param task Task to which the thread belongs. The caller must
* guarantee that the task won't cease to exist during the
* call. The task's lock may not be held.
* @param flags Thread flags.
* @param name Symbolic name (a copy is made).
* @param uncounted Thread's accounting doesn't affect accumulated task
* accounting.
*
* @return New thread's structure on success, NULL on failure.
*
*/
thread_t *thread_create(void (* func)(void *), void *arg, task_t *task,
int flags, char *name, bool uncounted)
{
thread_t *t;
ipl_t ipl;
t = (thread_t *) slab_alloc(thread_slab, 0);
if (!t)
return NULL;
/* Not needed, but good for debugging */
memsetb(t->kstack, THREAD_STACK_SIZE * 1 << STACK_FRAMES, 0);
ipl = interrupts_disable();
spinlock_lock(&tidlock);
t->tid = ++last_tid;
spinlock_unlock(&tidlock);
interrupts_restore(ipl);
context_save(&t->saved_context);
context_set(&t->saved_context, FADDR(cushion), (uintptr_t) t->kstack,
THREAD_STACK_SIZE);
the_initialize((the_t *) t->kstack);
ipl = interrupts_disable();
t->saved_context.ipl = interrupts_read();
interrupts_restore(ipl);
memcpy(t->name, name, THREAD_NAME_BUFLEN);
t->name[THREAD_NAME_BUFLEN - 1] = 0;
t->thread_code = func;
t->thread_arg = arg;
t->ticks = -1;
t->cycles = 0;
t->uncounted = uncounted;
t->priority = -1; /* start in rq[0] */
t->cpu = NULL;
t->flags = flags;
t->state = Entering;
t->call_me = NULL;
t->call_me_with = NULL;
timeout_initialize(&t->sleep_timeout);
t->sleep_interruptible = false;
t->sleep_queue = NULL;
t->timeout_pending = 0;
 
t->in_copy_from_uspace = false;
t->in_copy_to_uspace = false;
 
t->interrupted = false;
t->detached = false;
waitq_initialize(&t->join_wq);
t->rwlock_holder_type = RWLOCK_NONE;
t->task = task;
t->fpu_context_exists = 0;
t->fpu_context_engaged = 0;
 
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);
 
if (!(flags & THREAD_FLAG_NOATTACH))
thread_attach(t, task);
 
return t;
}
 
/** Destroy thread memory structure
*
* Detach thread from all queues, cpus etc. and destroy it.
*
* Assume thread->lock is held!!
*/
void thread_destroy(thread_t *t)
{
ASSERT(t->state == Exiting || t->state == Lingering);
ASSERT(t->task);
ASSERT(t->cpu);
 
spinlock_lock(&t->cpu->lock);
if (t->cpu->fpu_owner == t)
t->cpu->fpu_owner = NULL;
spinlock_unlock(&t->cpu->lock);
 
spinlock_unlock(&t->lock);
 
spinlock_lock(&threads_lock);
avltree_delete(&threads_tree, &t->threads_tree_node);
spinlock_unlock(&threads_lock);
 
/*
* Detach from the containing task.
*/
spinlock_lock(&t->task->lock);
list_remove(&t->th_link);
spinlock_unlock(&t->task->lock);
 
/*
* t is guaranteed to be the very last thread of its task.
* It is safe to destroy the task.
*/
if (atomic_predec(&t->task->refcount) == 0)
task_destroy(t->task);
slab_free(thread_slab, t);
}
 
/** Make the thread visible to the system.
*
* Attach the thread structure to the current task and make it visible in the
* threads_tree.
*
* @param t Thread to be attached to the task.
* @param task Task to which the thread is to be attached.
*/
void thread_attach(thread_t *t, task_t *task)
{
ipl_t ipl;
 
/*
* Attach to the specified task.
*/
ipl = interrupts_disable();
spinlock_lock(&task->lock);
 
atomic_inc(&task->refcount);
 
/* 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);
 
/*
* Register this thread in the system-wide list.
*/
spinlock_lock(&threads_lock);
avltree_insert(&threads_tree, &t->threads_tree_node);
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
}
 
/** Terminate thread.
*
* End current thread execution and switch it to the exiting state. All pending
* timeouts are executed.
*/
void thread_exit(void)
{
ipl_t ipl;
 
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);
}
}
 
restart:
ipl = interrupts_disable();
spinlock_lock(&THREAD->lock);
if (THREAD->timeout_pending) {
/* busy waiting for timeouts in progress */
spinlock_unlock(&THREAD->lock);
interrupts_restore(ipl);
goto restart;
}
THREAD->state = Exiting;
spinlock_unlock(&THREAD->lock);
scheduler();
 
/* Not reached */
while (1)
;
}
 
 
/** Thread sleep
*
* Suspend execution of the current thread.
*
* @param sec Number of seconds to sleep.
*
*/
void thread_sleep(uint32_t sec)
{
thread_usleep(sec * 1000000);
}
 
/** Wait for another thread to exit.
*
* @param t Thread to join on exit.
* @param usec Timeout in microseconds.
* @param flags Mode of operation.
*
* @return An error code from errno.h or an error code from synch.h.
*/
int thread_join_timeout(thread_t *t, uint32_t usec, int flags)
{
ipl_t ipl;
int rc;
 
if (t == THREAD)
return EINVAL;
 
/*
* Since thread join can only be called once on an undetached thread,
* the thread pointer is guaranteed to be still valid.
*/
ipl = interrupts_disable();
spinlock_lock(&t->lock);
ASSERT(!t->detached);
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
rc = waitq_sleep_timeout(&t->join_wq, usec, flags);
return rc;
}
 
/** Detach thread.
*
* Mark the thread as detached, if the thread is already in the Lingering
* state, deallocate its resources.
*
* @param t Thread to be detached.
*/
void thread_detach(thread_t *t)
{
ipl_t ipl;
 
/*
* Since the thread is expected not to be already detached,
* pointer to it must be still valid.
*/
ipl = interrupts_disable();
spinlock_lock(&t->lock);
ASSERT(!t->detached);
if (t->state == Lingering) {
thread_destroy(t); /* unlocks &t->lock */
interrupts_restore(ipl);
return;
} else {
t->detached = true;
}
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
}
 
/** Thread usleep
*
* Suspend execution of the current thread.
*
* @param usec Number of microseconds to sleep.
*
*/
void thread_usleep(uint32_t usec)
{
waitq_t wq;
waitq_initialize(&wq);
 
(void) waitq_sleep_timeout(&wq, usec, SYNCH_FLAGS_NON_BLOCKING);
}
 
/** Register thread out-of-context invocation
*
* Register a function and its argument to be executed
* on next context switch to the current thread.
*
* @param call_me Out-of-context function.
* @param call_me_with Out-of-context function argument.
*
*/
void thread_register_call_me(void (* call_me)(void *), void *call_me_with)
{
ipl_t ipl;
ipl = interrupts_disable();
spinlock_lock(&THREAD->lock);
THREAD->call_me = call_me;
THREAD->call_me_with = call_me_with;
spinlock_unlock(&THREAD->lock);
interrupts_restore(ipl);
}
 
static bool thread_walker(avltree_node_t *node, void *arg)
{
thread_t *t = avltree_get_instance(node, thread_t, threads_tree_node);
uint64_t cycles;
char suffix;
order(t->cycles, &cycles, &suffix);
 
#ifdef __32_BITS__
printf("%-6" PRIu64" %-10s %10p %-8s %10p %-3" PRIu32 " %10p %10p %9" PRIu64 "%c ",
t->tid, t->name, t, thread_states[t->state], t->task,
t->task->context, t->thread_code, t->kstack, cycles, suffix);
#endif
 
#ifdef __64_BITS__
printf("%-6" PRIu64" %-10s %18p %-8s %18p %-3" PRIu32 " %18p %18p %9" PRIu64 "%c ",
t->tid, t->name, t, thread_states[t->state], t->task,
t->task->context, t->thread_code, t->kstack, cycles, suffix);
#endif
if (t->cpu)
printf("%-4u", t->cpu->id);
else
printf("none");
if (t->state == Sleeping) {
#ifdef __32_BITS__
printf(" %10p", t->sleep_queue);
#endif
 
#ifdef __64_BITS__
printf(" %18p", t->sleep_queue);
#endif
}
printf("\n");
 
return true;
}
 
/** Print list of threads debug info */
void thread_print_list(void)
{
ipl_t ipl;
/* Messing with thread structures, avoid deadlock */
ipl = interrupts_disable();
spinlock_lock(&threads_lock);
 
#ifdef __32_BITS__
printf("tid name address state task "
"ctx code stack cycles cpu "
"waitqueue\n");
printf("------ ---------- ---------- -------- ---------- "
"--- ---------- ---------- ---------- ---- "
"----------\n");
#endif
 
#ifdef __64_BITS__
printf("tid name address state task "
"ctx code stack cycles cpu "
"waitqueue\n");
printf("------ ---------- ------------------ -------- ------------------ "
"--- ------------------ ------------------ ---------- ---- "
"------------------\n");
#endif
 
avltree_walk(&threads_tree, thread_walker, NULL);
 
spinlock_unlock(&threads_lock);
interrupts_restore(ipl);
}
 
/** Check whether thread exists.
*
* Note that threads_lock must be already held and
* interrupts must be already disabled.
*
* @param t Pointer to thread.
*
* @return True if thread t is known to the system, false otherwise.
*/
bool thread_exists(thread_t *t)
{
avltree_node_t *node;
 
node = avltree_search(&threads_tree, (avltree_key_t) ((uintptr_t) t));
return node != NULL;
}
 
/** Update accounting of current thread.
*
* Note that thread_lock on THREAD must be already held and
* interrupts must be already disabled.
*
*/
void thread_update_accounting(void)
{
uint64_t time = get_cycle();
THREAD->cycles += time - THREAD->last_cycle;
THREAD->last_cycle = time;
}
 
/** Process syscall to create new thread.
*
*/
unative_t sys_thread_create(uspace_arg_t *uspace_uarg, char *uspace_name,
size_t name_len, thread_id_t *uspace_thread_id)
{
thread_t *t;
char namebuf[THREAD_NAME_BUFLEN];
uspace_arg_t *kernel_uarg;
int rc;
 
if (name_len > THREAD_NAME_BUFLEN - 1)
name_len = THREAD_NAME_BUFLEN - 1;
 
rc = copy_from_uspace(namebuf, uspace_name, name_len);
if (rc != 0)
return (unative_t) rc;
 
namebuf[name_len] = 0;
 
/*
* In case of failure, kernel_uarg will be deallocated in this function.
* In case of success, kernel_uarg will be freed in uinit().
*/
kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);
rc = copy_from_uspace(kernel_uarg, uspace_uarg, sizeof(uspace_arg_t));
if (rc != 0) {
free(kernel_uarg);
return (unative_t) rc;
}
 
t = thread_create(uinit, kernel_uarg, TASK,
THREAD_FLAG_USPACE | THREAD_FLAG_NOATTACH, namebuf, false);
if (t) {
if (uspace_thread_id != NULL) {
int rc;
 
rc = copy_to_uspace(uspace_thread_id, &t->tid,
sizeof(t->tid));
if (rc != 0) {
/*
* We have encountered a failure, but the thread
* has already been created. We need to undo its
* creation now.
*/
 
/*
* The new thread structure is initialized, but
* is still not visible to the system.
* We can safely deallocate it.
*/
slab_free(thread_slab, t);
free(kernel_uarg);
 
return (unative_t) rc;
}
}
#ifdef CONFIG_UDEBUG
/*
* Generate udebug THREAD_B event and attach the thread.
* This must be done atomically (with the debug locks held),
* otherwise we would either miss some thread or receive
* THREAD_B events for threads that already existed
* and could be detected with THREAD_READ before.
*/
udebug_thread_b_event_attach(t, TASK);
#else
thread_attach(t, TASK);
#endif
thread_ready(t);
 
return 0;
} else
free(kernel_uarg);
 
return (unative_t) ENOMEM;
}
 
/** Process syscall to terminate thread.
*
*/
unative_t sys_thread_exit(int uspace_status)
{
thread_exit();
/* Unreachable */
return 0;
}
 
/** Syscall for getting TID.
*
* @param uspace_thread_id Userspace address of 8-byte buffer where to store
* current thread ID.
*
* @return 0 on success or an error code from @ref errno.h.
*/
unative_t sys_thread_get_id(thread_id_t *uspace_thread_id)
{
/*
* No need to acquire lock on THREAD because tid
* remains constant for the lifespan of the thread.
*/
return (unative_t) copy_to_uspace(uspace_thread_id, &THREAD->tid,
sizeof(THREAD->tid));
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/proc/tasklet.c
0,0 → 1,64
/*
* Copyright (c) 2007 Jan Hudecek
* Copyright (c) 2008 Martin Decky
* 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 genericproc
* @{
*/
/** @file tasklet.c
* @brief Tasklet implementation
*/
 
#include <proc/tasklet.h>
#include <synch/spinlock.h>
#include <mm/slab.h>
#include <config.h>
 
/** Spinlock protecting list of tasklets */
SPINLOCK_INITIALIZE(tasklet_lock);
 
/** Array of tasklet lists for every CPU */
tasklet_descriptor_t **tasklet_list;
 
void tasklet_init(void)
{
unsigned int i;
tasklet_list = malloc(sizeof(tasklet_descriptor_t *) * config.cpu_count, 0);
if (!tasklet_list)
panic("Error initializing tasklets.");
for (i = 0; i < config.cpu_count; i++)
tasklet_list[i] = NULL;
spinlock_initialize(&tasklet_lock, "tasklet_lock");
}
 
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/proc/the.c
0,0 → 1,75
/*
* Copyright (c) 2005 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 genericproc
* @{
*/
 
/**
* @file
* @brief THE structure functions.
*
* This file contains functions to manage the THE structure.
* The THE structure exists at the base address of every kernel
* stack and carries information about current settings
* (e.g. current CPU, current thread, task and address space
* and current preemption counter).
*/
 
#include <arch.h>
 
 
/** Initialize THE structure
*
* Initialize THE structure passed as argument.
*
* @param the THE structure to be initialized.
*/
void the_initialize(the_t *the)
{
the->preemption_disabled = 0;
the->cpu = NULL;
the->thread = NULL;
the->task = NULL;
the->as = NULL;
}
 
/** Copy THE structure
*
* Copy the source THE structure to the destination THE structure.
*
* @param src The source THE structure.
* @param dst The destination THE structure.
*/
void the_copy(the_t *src, the_t *dst)
{
*dst = *src;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/adt/btree.c
0,0 → 1,1002
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
 
/**
* @file
* @brief B+tree implementation.
*
* This file implements B+tree type and operations.
*
* The B+tree has the following properties:
* @li it is a ballanced 3-4-5 tree (i.e. BTREE_M = 5)
* @li values (i.e. pointers to values) are stored only in leaves
* @li leaves are linked in a list
*
* Be carefull when using these trees. They need to allocate
* and deallocate memory for their index nodes and as such
* can sleep.
*/
 
#include <adt/btree.h>
#include <adt/list.h>
#include <mm/slab.h>
#include <debug.h>
#include <panic.h>
#include <print.h>
 
static void btree_destroy_subtree(btree_node_t *root);
static void _btree_insert(btree_t *t, btree_key_t key, void *value, btree_node_t *rsubtree, btree_node_t *node);
static void _btree_remove(btree_t *t, btree_key_t key, btree_node_t *node);
static void node_initialize(btree_node_t *node);
static void node_insert_key_and_lsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *lsubtree);
static void node_insert_key_and_rsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree);
static void node_remove_key_and_lsubtree(btree_node_t *node, btree_key_t key);
static void node_remove_key_and_rsubtree(btree_node_t *node, btree_key_t key);
static btree_node_t *node_split(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree, btree_key_t *median);
static btree_node_t *node_combine(btree_node_t *node);
static size_t find_key_by_subtree(btree_node_t *node, btree_node_t *subtree, bool right);
static void rotate_from_right(btree_node_t *lnode, btree_node_t *rnode, size_t idx);
static void rotate_from_left(btree_node_t *lnode, btree_node_t *rnode, size_t idx);
static bool try_insert_by_rotation_to_left(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree);
static bool try_insert_by_rotation_to_right(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree);
static bool try_rotation_from_left(btree_node_t *rnode);
static bool try_rotation_from_right(btree_node_t *lnode);
 
#define ROOT_NODE(n) (!(n)->parent)
#define INDEX_NODE(n) ((n)->subtree[0] != NULL)
#define LEAF_NODE(n) ((n)->subtree[0] == NULL)
 
#define FILL_FACTOR ((BTREE_M-1)/2)
 
#define MEDIAN_LOW_INDEX(n) (((n)->keys-1)/2)
#define MEDIAN_HIGH_INDEX(n) ((n)->keys/2)
#define MEDIAN_LOW(n) ((n)->key[MEDIAN_LOW_INDEX((n))]);
#define MEDIAN_HIGH(n) ((n)->key[MEDIAN_HIGH_INDEX((n))]);
 
static slab_cache_t *btree_node_slab;
 
/** Initialize B-trees. */
void btree_init(void)
{
btree_node_slab = slab_cache_create("btree_node_slab", sizeof(btree_node_t), 0, NULL, NULL, SLAB_CACHE_MAGDEFERRED);
}
 
/** Create empty B-tree.
*
* @param t B-tree.
*/
void btree_create(btree_t *t)
{
list_initialize(&t->leaf_head);
t->root = (btree_node_t *) slab_alloc(btree_node_slab, 0);
node_initialize(t->root);
list_append(&t->root->leaf_link, &t->leaf_head);
}
 
/** Destroy empty B-tree. */
void btree_destroy(btree_t *t)
{
btree_destroy_subtree(t->root);
}
 
/** Insert key-value pair into B-tree.
*
* @param t B-tree.
* @param key Key to be inserted.
* @param value Value to be inserted.
* @param leaf_node Leaf node where the insertion should begin.
*/
void btree_insert(btree_t *t, btree_key_t key, void *value, btree_node_t *leaf_node)
{
btree_node_t *lnode;
ASSERT(value);
lnode = leaf_node;
if (!lnode) {
if (btree_search(t, key, &lnode)) {
panic("B-tree %p already contains key %" PRIu64 ".", t, key);
}
}
_btree_insert(t, key, value, NULL, lnode);
}
 
/** Destroy subtree rooted in a node.
*
* @param root Root of the subtree.
*/
void btree_destroy_subtree(btree_node_t *root)
{
size_t i;
 
if (root->keys) {
for (i = 0; i < root->keys + 1; i++) {
if (root->subtree[i])
btree_destroy_subtree(root->subtree[i]);
}
}
slab_free(btree_node_slab, root);
}
 
/** Recursively insert into B-tree.
*
* @param t B-tree.
* @param key Key to be inserted.
* @param value Value to be inserted.
* @param rsubtree Right subtree of the inserted key.
* @param node Start inserting into this node.
*/
void _btree_insert(btree_t *t, btree_key_t key, void *value, btree_node_t *rsubtree, btree_node_t *node)
{
if (node->keys < BTREE_MAX_KEYS) {
/*
* Node conatins enough space, the key can be stored immediately.
*/
node_insert_key_and_rsubtree(node, key, value, rsubtree);
} else if (try_insert_by_rotation_to_left(node, key, value, rsubtree)) {
/*
* The key-value-rsubtree triplet has been inserted because
* some keys could have been moved to the left sibling.
*/
} else if (try_insert_by_rotation_to_right(node, key, value, rsubtree)) {
/*
* The key-value-rsubtree triplet has been inserted because
* some keys could have been moved to the right sibling.
*/
} else {
btree_node_t *rnode;
btree_key_t median;
/*
* Node is full and both siblings (if both exist) are full too.
* Split the node and insert the smallest key from the node containing
* bigger keys (i.e. the new node) into its parent.
*/
 
rnode = node_split(node, key, value, rsubtree, &median);
 
if (LEAF_NODE(node)) {
list_prepend(&rnode->leaf_link, &node->leaf_link);
}
if (ROOT_NODE(node)) {
/*
* We split the root node. Create new root.
*/
t->root = (btree_node_t *) slab_alloc(btree_node_slab, 0);
node->parent = t->root;
rnode->parent = t->root;
node_initialize(t->root);
/*
* Left-hand side subtree will be the old root (i.e. node).
* Right-hand side subtree will be rnode.
*/
t->root->subtree[0] = node;
 
t->root->depth = node->depth + 1;
}
_btree_insert(t, median, NULL, rnode, node->parent);
}
}
 
/** Remove B-tree node.
*
* @param t B-tree.
* @param key Key to be removed from the B-tree along with its associated value.
* @param leaf_node If not NULL, pointer to the leaf node where the key is found.
*/
void btree_remove(btree_t *t, btree_key_t key, btree_node_t *leaf_node)
{
btree_node_t *lnode;
lnode = leaf_node;
if (!lnode) {
if (!btree_search(t, key, &lnode)) {
panic("B-tree %p does not contain key %" PRIu64 ".", t, key);
}
}
_btree_remove(t, key, lnode);
}
 
/** Recursively remove B-tree node.
*
* @param t B-tree.
* @param key Key to be removed from the B-tree along with its associated value.
* @param node Node where the key being removed resides.
*/
void _btree_remove(btree_t *t, btree_key_t key, btree_node_t *node)
{
if (ROOT_NODE(node)) {
if (node->keys == 1 && node->subtree[0]) {
/*
* Free the current root and set new root.
*/
t->root = node->subtree[0];
t->root->parent = NULL;
slab_free(btree_node_slab, node);
} else {
/*
* Remove the key from the root node.
* Note that the right subtree is removed because when
* combining two nodes, the left-side sibling is preserved
* and the right-side sibling is freed.
*/
node_remove_key_and_rsubtree(node, key);
}
return;
}
if (node->keys <= FILL_FACTOR) {
/*
* If the node is below the fill factor,
* try to borrow keys from left or right sibling.
*/
if (!try_rotation_from_left(node))
try_rotation_from_right(node);
}
if (node->keys > FILL_FACTOR) {
size_t i;
 
/*
* The key can be immediatelly removed.
*
* Note that the right subtree is removed because when
* combining two nodes, the left-side sibling is preserved
* and the right-side sibling is freed.
*/
node_remove_key_and_rsubtree(node, key);
for (i = 0; i < node->parent->keys; i++) {
if (node->parent->key[i] == key)
node->parent->key[i] = node->key[0];
}
} else {
size_t idx;
btree_node_t *rnode, *parent;
 
/*
* The node is below the fill factor as well as its left and right sibling.
* Resort to combining the node with one of its siblings.
* The node which is on the left is preserved and the node on the right is
* freed.
*/
parent = node->parent;
node_remove_key_and_rsubtree(node, key);
rnode = node_combine(node);
if (LEAF_NODE(rnode))
list_remove(&rnode->leaf_link);
idx = find_key_by_subtree(parent, rnode, true);
ASSERT((int) idx != -1);
slab_free(btree_node_slab, rnode);
_btree_remove(t, parent->key[idx], parent);
}
}
 
/** Search key in a B-tree.
*
* @param t B-tree.
* @param key Key to be searched.
* @param leaf_node Address where to put pointer to visited leaf node.
*
* @return Pointer to value or NULL if there is no such key.
*/
void *btree_search(btree_t *t, btree_key_t key, btree_node_t **leaf_node)
{
btree_node_t *cur, *next;
/*
* Iteratively descend to the leaf that can contain the searched key.
*/
for (cur = t->root; cur; cur = next) {
 
/* Last iteration will set this with proper leaf node address. */
*leaf_node = cur;
/*
* The key can be in the leftmost subtree.
* Test it separately.
*/
if (key < cur->key[0]) {
next = cur->subtree[0];
continue;
} else {
void *val;
size_t i;
/*
* Now if the key is smaller than cur->key[i]
* it can only mean that the value is in cur->subtree[i]
* or it is not in the tree at all.
*/
for (i = 1; i < cur->keys; i++) {
if (key < cur->key[i]) {
next = cur->subtree[i];
val = cur->value[i - 1];
 
if (LEAF_NODE(cur))
return key == cur->key[i - 1] ? val : NULL;
 
goto descend;
}
}
/*
* Last possibility is that the key is in the rightmost subtree.
*/
next = cur->subtree[i];
val = cur->value[i - 1];
if (LEAF_NODE(cur))
return key == cur->key[i - 1] ? val : NULL;
}
descend:
;
}
 
/*
* The key was not found in the *leaf_node and is smaller than any of its keys.
*/
return NULL;
}
 
/** Return pointer to B-tree leaf node's left neighbour.
*
* @param t B-tree.
* @param node Node whose left neighbour will be returned.
*
* @return Left neighbour of the node or NULL if the node does not have the left neighbour.
*/
btree_node_t *btree_leaf_node_left_neighbour(btree_t *t, btree_node_t *node)
{
ASSERT(LEAF_NODE(node));
if (node->leaf_link.prev != &t->leaf_head)
return list_get_instance(node->leaf_link.prev, btree_node_t, leaf_link);
else
return NULL;
}
 
/** Return pointer to B-tree leaf node's right neighbour.
*
* @param t B-tree.
* @param node Node whose right neighbour will be returned.
*
* @return Right neighbour of the node or NULL if the node does not have the right neighbour.
*/
btree_node_t *btree_leaf_node_right_neighbour(btree_t *t, btree_node_t *node)
{
ASSERT(LEAF_NODE(node));
if (node->leaf_link.next != &t->leaf_head)
return list_get_instance(node->leaf_link.next, btree_node_t, leaf_link);
else
return NULL;
}
 
/** Initialize B-tree node.
*
* @param node B-tree node.
*/
void node_initialize(btree_node_t *node)
{
int i;
 
node->keys = 0;
/* Clean also space for the extra key. */
for (i = 0; i < BTREE_MAX_KEYS + 1; i++) {
node->key[i] = 0;
node->value[i] = NULL;
node->subtree[i] = NULL;
}
node->subtree[i] = NULL;
node->parent = NULL;
link_initialize(&node->leaf_link);
 
link_initialize(&node->bfs_link);
node->depth = 0;
}
 
/** Insert key-value-lsubtree triplet into B-tree node.
*
* It is actually possible to have more keys than BTREE_MAX_KEYS.
* This feature is used during insert by right rotation.
*
* @param node B-tree node into wich the new key is to be inserted.
* @param key The key to be inserted.
* @param value Pointer to value to be inserted.
* @param lsubtree Pointer to the left subtree.
*/
void node_insert_key_and_lsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *lsubtree)
{
size_t i;
 
for (i = 0; i < node->keys; i++) {
if (key < node->key[i]) {
size_t j;
for (j = node->keys; j > i; j--) {
node->key[j] = node->key[j - 1];
node->value[j] = node->value[j - 1];
node->subtree[j + 1] = node->subtree[j];
}
node->subtree[j + 1] = node->subtree[j];
break;
}
}
node->key[i] = key;
node->value[i] = value;
node->subtree[i] = lsubtree;
node->keys++;
}
 
/** Insert key-value-rsubtree triplet into B-tree node.
*
* It is actually possible to have more keys than BTREE_MAX_KEYS.
* This feature is used during splitting the node when the
* number of keys is BTREE_MAX_KEYS + 1. Insert by left rotation
* also makes use of this feature.
*
* @param node B-tree node into wich the new key is to be inserted.
* @param key The key to be inserted.
* @param value Pointer to value to be inserted.
* @param rsubtree Pointer to the right subtree.
*/
void node_insert_key_and_rsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree)
{
size_t i;
 
for (i = 0; i < node->keys; i++) {
if (key < node->key[i]) {
size_t j;
for (j = node->keys; j > i; j--) {
node->key[j] = node->key[j - 1];
node->value[j] = node->value[j - 1];
node->subtree[j + 1] = node->subtree[j];
}
break;
}
}
node->key[i] = key;
node->value[i] = value;
node->subtree[i + 1] = rsubtree;
node->keys++;
}
 
/** Remove key and its left subtree pointer from B-tree node.
*
* Remove the key and eliminate gaps in node->key array.
* Note that the value pointer and the left subtree pointer
* is removed from the node as well.
*
* @param node B-tree node.
* @param key Key to be removed.
*/
void node_remove_key_and_lsubtree(btree_node_t *node, btree_key_t key)
{
size_t i, j;
for (i = 0; i < node->keys; i++) {
if (key == node->key[i]) {
for (j = i + 1; j < node->keys; j++) {
node->key[j - 1] = node->key[j];
node->value[j - 1] = node->value[j];
node->subtree[j - 1] = node->subtree[j];
}
node->subtree[j - 1] = node->subtree[j];
node->keys--;
return;
}
}
panic("Node %p does not contain key %" PRIu64 ".", node, key);
}
 
/** Remove key and its right subtree pointer from B-tree node.
*
* Remove the key and eliminate gaps in node->key array.
* Note that the value pointer and the right subtree pointer
* is removed from the node as well.
*
* @param node B-tree node.
* @param key Key to be removed.
*/
void node_remove_key_and_rsubtree(btree_node_t *node, btree_key_t key)
{
size_t i, j;
for (i = 0; i < node->keys; i++) {
if (key == node->key[i]) {
for (j = i + 1; j < node->keys; j++) {
node->key[j - 1] = node->key[j];
node->value[j - 1] = node->value[j];
node->subtree[j] = node->subtree[j + 1];
}
node->keys--;
return;
}
}
panic("Node %p does not contain key %" PRIu64 ".", node, key);
}
 
/** Split full B-tree node and insert new key-value-right-subtree triplet.
*
* This function will split a node and return a pointer to a newly created
* node containing keys greater than or equal to the greater of medians
* (or median) of the old keys and the newly added key. It will also write
* the median key to a memory address supplied by the caller.
*
* If the node being split is an index node, the median will not be
* included in the new node. If the node is a leaf node,
* the median will be copied there.
*
* @param node B-tree node wich is going to be split.
* @param key The key to be inserted.
* @param value Pointer to the value to be inserted.
* @param rsubtree Pointer to the right subtree of the key being added.
* @param median Address in memory, where the median key will be stored.
*
* @return Newly created right sibling of node.
*/
btree_node_t *node_split(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree, btree_key_t *median)
{
btree_node_t *rnode;
size_t i, j;
 
ASSERT(median);
ASSERT(node->keys == BTREE_MAX_KEYS);
 
/*
* Use the extra space to store the extra node.
*/
node_insert_key_and_rsubtree(node, key, value, rsubtree);
 
/*
* Compute median of keys.
*/
*median = MEDIAN_HIGH(node);
/*
* Allocate and initialize new right sibling.
*/
rnode = (btree_node_t *) slab_alloc(btree_node_slab, 0);
node_initialize(rnode);
rnode->parent = node->parent;
rnode->depth = node->depth;
/*
* Copy big keys, values and subtree pointers to the new right sibling.
* If this is an index node, do not copy the median.
*/
i = (size_t) INDEX_NODE(node);
for (i += MEDIAN_HIGH_INDEX(node), j = 0; i < node->keys; i++, j++) {
rnode->key[j] = node->key[i];
rnode->value[j] = node->value[i];
rnode->subtree[j] = node->subtree[i];
/*
* Fix parent links in subtrees.
*/
if (rnode->subtree[j])
rnode->subtree[j]->parent = rnode;
}
rnode->subtree[j] = node->subtree[i];
if (rnode->subtree[j])
rnode->subtree[j]->parent = rnode;
 
rnode->keys = j; /* Set number of keys of the new node. */
node->keys /= 2; /* Shrink the old node. */
return rnode;
}
 
/** Combine node with any of its siblings.
*
* The siblings are required to be below the fill factor.
*
* @param node Node to combine with one of its siblings.
*
* @return Pointer to the rightmost of the two nodes.
*/
btree_node_t *node_combine(btree_node_t *node)
{
size_t idx;
btree_node_t *rnode;
size_t i;
 
ASSERT(!ROOT_NODE(node));
idx = find_key_by_subtree(node->parent, node, false);
if (idx == node->parent->keys) {
/*
* Rightmost subtree of its parent, combine with the left sibling.
*/
idx--;
rnode = node;
node = node->parent->subtree[idx];
} else {
rnode = node->parent->subtree[idx + 1];
}
 
/* Index nodes need to insert parent node key in between left and right node. */
if (INDEX_NODE(node))
node->key[node->keys++] = node->parent->key[idx];
/* Copy the key-value-subtree triplets from the right node. */
for (i = 0; i < rnode->keys; i++) {
node->key[node->keys + i] = rnode->key[i];
node->value[node->keys + i] = rnode->value[i];
if (INDEX_NODE(node)) {
node->subtree[node->keys + i] = rnode->subtree[i];
rnode->subtree[i]->parent = node;
}
}
if (INDEX_NODE(node)) {
node->subtree[node->keys + i] = rnode->subtree[i];
rnode->subtree[i]->parent = node;
}
 
node->keys += rnode->keys;
 
return rnode;
}
 
/** Find key by its left or right subtree.
*
* @param node B-tree node.
* @param subtree Left or right subtree of a key found in node.
* @param right If true, subtree is a right subtree. If false, subtree is a left subtree.
*
* @return Index of the key associated with the subtree.
*/
size_t find_key_by_subtree(btree_node_t *node, btree_node_t *subtree, bool right)
{
size_t i;
for (i = 0; i < node->keys + 1; i++) {
if (subtree == node->subtree[i])
return i - (int) (right != false);
}
panic("Node %p does not contain subtree %p.", node, subtree);
}
 
/** Rotate one key-value-rsubtree triplet from the left sibling to the right sibling.
*
* The biggest key and its value and right subtree is rotated from the left node
* to the right. If the node is an index node, than the parent node key belonging to
* the left node takes part in the rotation.
*
* @param lnode Left sibling.
* @param rnode Right sibling.
* @param idx Index of the parent node key that is taking part in the rotation.
*/
void rotate_from_left(btree_node_t *lnode, btree_node_t *rnode, size_t idx)
{
btree_key_t key;
 
key = lnode->key[lnode->keys - 1];
if (LEAF_NODE(lnode)) {
void *value;
 
value = lnode->value[lnode->keys - 1];
node_remove_key_and_rsubtree(lnode, key);
node_insert_key_and_lsubtree(rnode, key, value, NULL);
lnode->parent->key[idx] = key;
} else {
btree_node_t *rsubtree;
 
rsubtree = lnode->subtree[lnode->keys];
node_remove_key_and_rsubtree(lnode, key);
node_insert_key_and_lsubtree(rnode, lnode->parent->key[idx], NULL, rsubtree);
lnode->parent->key[idx] = key;
 
/* Fix parent link of the reconnected right subtree. */
rsubtree->parent = rnode;
}
 
}
 
/** Rotate one key-value-lsubtree triplet from the right sibling to the left sibling.
*
* The smallest key and its value and left subtree is rotated from the right node
* to the left. If the node is an index node, than the parent node key belonging to
* the right node takes part in the rotation.
*
* @param lnode Left sibling.
* @param rnode Right sibling.
* @param idx Index of the parent node key that is taking part in the rotation.
*/
void rotate_from_right(btree_node_t *lnode, btree_node_t *rnode, size_t idx)
{
btree_key_t key;
 
key = rnode->key[0];
if (LEAF_NODE(rnode)) {
void *value;
 
value = rnode->value[0];
node_remove_key_and_lsubtree(rnode, key);
node_insert_key_and_rsubtree(lnode, key, value, NULL);
rnode->parent->key[idx] = rnode->key[0];
} else {
btree_node_t *lsubtree;
 
lsubtree = rnode->subtree[0];
node_remove_key_and_lsubtree(rnode, key);
node_insert_key_and_rsubtree(lnode, rnode->parent->key[idx], NULL, lsubtree);
rnode->parent->key[idx] = key;
 
/* Fix parent link of the reconnected left subtree. */
lsubtree->parent = lnode;
}
 
}
 
/** Insert key-value-rsubtree triplet and rotate the node to the left, if this operation can be done.
*
* Left sibling of the node (if it exists) is checked for free space.
* If there is free space, the key is inserted and the smallest key of
* the node is moved there. The index node which is the parent of both
* nodes is fixed.
*
* @param node B-tree node.
* @param inskey Key to be inserted.
* @param insvalue Value to be inserted.
* @param rsubtree Right subtree of inskey.
*
* @return True if the rotation was performed, false otherwise.
*/
bool try_insert_by_rotation_to_left(btree_node_t *node, btree_key_t inskey, void *insvalue, btree_node_t *rsubtree)
{
size_t idx;
btree_node_t *lnode;
 
/*
* If this is root node, the rotation can not be done.
*/
if (ROOT_NODE(node))
return false;
idx = find_key_by_subtree(node->parent, node, true);
if ((int) idx == -1) {
/*
* If this node is the leftmost subtree of its parent,
* the rotation can not be done.
*/
return false;
}
lnode = node->parent->subtree[idx];
if (lnode->keys < BTREE_MAX_KEYS) {
/*
* The rotaion can be done. The left sibling has free space.
*/
node_insert_key_and_rsubtree(node, inskey, insvalue, rsubtree);
rotate_from_right(lnode, node, idx);
return true;
}
 
return false;
}
 
/** Insert key-value-rsubtree triplet and rotate the node to the right, if this operation can be done.
*
* Right sibling of the node (if it exists) is checked for free space.
* If there is free space, the key is inserted and the biggest key of
* the node is moved there. The index node which is the parent of both
* nodes is fixed.
*
* @param node B-tree node.
* @param inskey Key to be inserted.
* @param insvalue Value to be inserted.
* @param rsubtree Right subtree of inskey.
*
* @return True if the rotation was performed, false otherwise.
*/
bool try_insert_by_rotation_to_right(btree_node_t *node, btree_key_t inskey, void *insvalue, btree_node_t *rsubtree)
{
size_t idx;
btree_node_t *rnode;
 
/*
* If this is root node, the rotation can not be done.
*/
if (ROOT_NODE(node))
return false;
idx = find_key_by_subtree(node->parent, node, false);
if (idx == node->parent->keys) {
/*
* If this node is the rightmost subtree of its parent,
* the rotation can not be done.
*/
return false;
}
rnode = node->parent->subtree[idx + 1];
if (rnode->keys < BTREE_MAX_KEYS) {
/*
* The rotaion can be done. The right sibling has free space.
*/
node_insert_key_and_rsubtree(node, inskey, insvalue, rsubtree);
rotate_from_left(node, rnode, idx);
return true;
}
 
return false;
}
 
/** Rotate in a key from the left sibling or from the index node, if this operation can be done.
*
* @param rnode Node into which to add key from its left sibling or from the index node.
*
* @return True if the rotation was performed, false otherwise.
*/
bool try_rotation_from_left(btree_node_t *rnode)
{
size_t idx;
btree_node_t *lnode;
 
/*
* If this is root node, the rotation can not be done.
*/
if (ROOT_NODE(rnode))
return false;
idx = find_key_by_subtree(rnode->parent, rnode, true);
if ((int) idx == -1) {
/*
* If this node is the leftmost subtree of its parent,
* the rotation can not be done.
*/
return false;
}
lnode = rnode->parent->subtree[idx];
if (lnode->keys > FILL_FACTOR) {
rotate_from_left(lnode, rnode, idx);
return true;
}
return false;
}
 
/** Rotate in a key from the right sibling or from the index node, if this operation can be done.
*
* @param lnode Node into which to add key from its right sibling or from the index node.
*
* @return True if the rotation was performed, false otherwise.
*/
bool try_rotation_from_right(btree_node_t *lnode)
{
size_t idx;
btree_node_t *rnode;
 
/*
* If this is root node, the rotation can not be done.
*/
if (ROOT_NODE(lnode))
return false;
idx = find_key_by_subtree(lnode->parent, lnode, false);
if (idx == lnode->parent->keys) {
/*
* If this node is the rightmost subtree of its parent,
* the rotation can not be done.
*/
return false;
}
rnode = lnode->parent->subtree[idx + 1];
if (rnode->keys > FILL_FACTOR) {
rotate_from_right(lnode, rnode, idx);
return true;
}
 
return false;
}
 
/** Print B-tree.
*
* @param t Print out B-tree.
*/
void btree_print(btree_t *t)
{
size_t i;
int depth = t->root->depth;
link_t head, *cur;
 
printf("Printing B-tree:\n");
list_initialize(&head);
list_append(&t->root->bfs_link, &head);
 
/*
* Use BFS search to print out the tree.
* Levels are distinguished from one another by node->depth.
*/
while (!list_empty(&head)) {
link_t *hlp;
btree_node_t *node;
hlp = head.next;
ASSERT(hlp != &head);
node = list_get_instance(hlp, btree_node_t, bfs_link);
list_remove(hlp);
ASSERT(node);
if (node->depth != depth) {
printf("\n");
depth = node->depth;
}
 
printf("(");
for (i = 0; i < node->keys; i++) {
printf("%" PRIu64 "%s", node->key[i], i < node->keys - 1 ? "," : "");
if (node->depth && node->subtree[i]) {
list_append(&node->subtree[i]->bfs_link, &head);
}
}
if (node->depth && node->subtree[i]) {
list_append(&node->subtree[i]->bfs_link, &head);
}
printf(")");
}
printf("\n");
printf("Printing list of leaves:\n");
for (cur = t->leaf_head.next; cur != &t->leaf_head; cur = cur->next) {
btree_node_t *node;
node = list_get_instance(cur, btree_node_t, leaf_link);
ASSERT(node);
 
printf("(");
for (i = 0; i < node->keys; i++)
printf("%" PRIu64 "%s", node->key[i], i < node->keys - 1 ? "," : "");
printf(")");
}
printf("\n");
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/adt/hash_table.c
0,0 → 1,189
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
 
/**
* @file
* @brief Implementation of generic chained hash table.
*
* This file contains implementation of generic chained hash table.
*/
 
#include <adt/hash_table.h>
#include <adt/list.h>
#include <arch/types.h>
#include <debug.h>
#include <mm/slab.h>
#include <memstr.h>
 
/** Create chained hash table.
*
* @param h Hash table structure. Will be initialized by this call.
* @param m Number of slots in the hash table.
* @param max_keys Maximal number of keys needed to identify an item.
* @param op Hash table operations structure.
*/
void hash_table_create(hash_table_t *h, size_t m, size_t max_keys, hash_table_operations_t *op)
{
size_t i;
 
ASSERT(h);
ASSERT(op);
ASSERT(op->hash);
ASSERT(op->compare);
ASSERT(max_keys > 0);
h->entry = (link_t *) malloc(m * sizeof(link_t), 0);
if (!h->entry)
panic("Cannot allocate memory for hash table.");
memsetb(h->entry, m * sizeof(link_t), 0);
for (i = 0; i < m; i++)
list_initialize(&h->entry[i]);
h->entries = m;
h->max_keys = max_keys;
h->op = op;
}
 
/** Insert item into hash table.
*
* @param h Hash table.
* @param key Array of all keys necessary to compute hash index.
* @param item Item to be inserted into the hash table.
*/
void hash_table_insert(hash_table_t *h, unative_t key[], link_t *item)
{
size_t chain;
ASSERT(item);
ASSERT(h);
ASSERT(h->op);
ASSERT(h->op->hash);
ASSERT(h->op->compare);
chain = h->op->hash(key);
ASSERT(chain < h->entries);
list_append(item, &h->entry[chain]);
}
 
/** Search hash table for an item matching keys.
*
* @param h Hash table.
* @param key Array of all keys needed to compute hash index.
*
* @return Matching item on success, NULL if there is no such item.
*/
link_t *hash_table_find(hash_table_t *h, unative_t key[])
{
link_t *cur;
size_t chain;
ASSERT(h);
ASSERT(h->op);
ASSERT(h->op->hash);
ASSERT(h->op->compare);
chain = h->op->hash(key);
ASSERT(chain < h->entries);
for (cur = h->entry[chain].next; cur != &h->entry[chain]; cur = cur->next) {
if (h->op->compare(key, h->max_keys, cur)) {
/*
* The entry is there.
*/
return cur;
}
}
return NULL;
}
 
/** Remove all matching items from hash table.
*
* For each removed item, h->remove_callback() is called (if not NULL).
*
* @param h Hash table.
* @param key Array of keys that will be compared against items of the hash table.
* @param keys Number of keys in the key array.
*/
void hash_table_remove(hash_table_t *h, unative_t key[], size_t keys)
{
size_t chain;
link_t *cur;
ASSERT(h);
ASSERT(h->op);
ASSERT(h->op->hash);
ASSERT(h->op->compare);
ASSERT(keys <= h->max_keys);
if (keys == h->max_keys) {
/*
* All keys are known, hash_table_find() can be used to find the entry.
*/
cur = hash_table_find(h, key);
if (cur) {
list_remove(cur);
if (h->op->remove_callback)
h->op->remove_callback(cur);
}
return;
}
/*
* Fewer keys were passed.
* Any partially matching entries are to be removed.
*/
for (chain = 0; chain < h->entries; chain++) {
for (cur = h->entry[chain].next; cur != &h->entry[chain]; cur = cur->next) {
if (h->op->compare(key, keys, cur)) {
link_t *hlp;
hlp = cur;
cur = cur->prev;
list_remove(hlp);
if (h->op->remove_callback)
h->op->remove_callback(hlp);
continue;
}
}
}
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/adt/bitmap.c
0,0 → 1,188
/*
* Copyright (c) 2006 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 genericadt
* @{
*/
/**
* @file
* @brief Implementation of bitmap ADT.
*
* This file implements bitmap ADT and provides functions for
* setting and clearing ranges of bits.
*/
 
#include <adt/bitmap.h>
#include <arch/types.h>
#include <align.h>
#include <debug.h>
#include <macros.h>
 
#define ALL_ONES 0xff
#define ALL_ZEROES 0x00
 
/** Initialize bitmap.
*
* No portion of the bitmap is set or cleared by this function.
*
* @param bitmap Bitmap structure.
* @param map Address of the memory used to hold the map.
* @param bits Number of bits stored in bitmap.
*/
void bitmap_initialize(bitmap_t *bitmap, uint8_t *map, size_t bits)
{
bitmap->map = map;
bitmap->bits = bits;
}
 
/** Set range of bits.
*
* @param bitmap Bitmap structure.
* @param start Starting bit.
* @param bits Number of bits to set.
*/
void bitmap_set_range(bitmap_t *bitmap, size_t start, size_t bits)
{
size_t i = 0;
size_t aligned_start;
size_t lub; /* leading unaligned bits */
size_t amb; /* aligned middle bits */
size_t tab; /* trailing aligned bits */
ASSERT(start + bits <= bitmap->bits);
aligned_start = ALIGN_UP(start, 8);
lub = min(aligned_start - start, bits);
amb = bits > lub ? bits - lub : 0;
tab = amb % 8;
if ( start + bits < aligned_start ) {
/*
* Set bits in the middle of byte
*/
bitmap->map[start / 8] |= ((1 << lub)-1) << (start&7);
return;
}
if (lub) {
/*
* Make sure to set any leading unaligned bits.
*/
bitmap->map[start / 8] |= ~((1 << (8 - lub)) - 1);
}
for (i = 0; i < amb / 8; i++) {
/*
* The middle bits can be set byte by byte.
*/
bitmap->map[aligned_start / 8 + i] = ALL_ONES;
}
if (tab) {
/*
* Make sure to set any trailing aligned bits.
*/
bitmap->map[aligned_start / 8 + i] |= (1 << tab) - 1;
}
}
 
/** Clear range of bits.
*
* @param bitmap Bitmap structure.
* @param start Starting bit.
* @param bits Number of bits to clear.
*/
void bitmap_clear_range(bitmap_t *bitmap, size_t start, size_t bits)
{
size_t i = 0;
size_t aligned_start;
size_t lub; /* leading unaligned bits */
size_t amb; /* aligned middle bits */
size_t tab; /* trailing aligned bits */
ASSERT(start + bits <= bitmap->bits);
aligned_start = ALIGN_UP(start, 8);
lub = min(aligned_start - start, bits);
amb = bits > lub ? bits - lub : 0;
tab = amb % 8;
 
if ( start + bits < aligned_start )
{
/*
* Set bits in the middle of byte
*/
bitmap->map[start / 8] &= ~(((1 << lub)-1) << (start&7));
return;
}
 
 
if (lub) {
/*
* Make sure to clear any leading unaligned bits.
*/
bitmap->map[start / 8] &= (1 << (8 - lub)) - 1;
}
for (i = 0; i < amb / 8; i++) {
/*
* The middle bits can be cleared byte by byte.
*/
bitmap->map[aligned_start / 8 + i] = ALL_ZEROES;
}
if (tab) {
/*
* Make sure to clear any trailing aligned bits.
*/
bitmap->map[aligned_start / 8 + i] &= ~((1 << tab) - 1);
}
 
}
 
/** Copy portion of one bitmap into another bitmap.
*
* @param dst Destination bitmap.
* @param src Source bitmap.
* @param bits Number of bits to copy.
*/
void bitmap_copy(bitmap_t *dst, bitmap_t *src, size_t bits)
{
size_t i;
ASSERT(bits <= dst->bits);
ASSERT(bits <= src->bits);
for (i = 0; i < bits / 8; i++)
dst->map[i] = src->map[i];
if (bits % 8) {
bitmap_clear_range(dst, i * 8, bits % 8);
dst->map[i] |= src->map[i] & ((1 << (bits % 8)) - 1);
}
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/adt/avl.c
0,0 → 1,730
/*
* Copyright (c) 2007 Vojtech Mencl
* 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 genericadt
* @{
*/
 
/**
* @file
* @brief AVL tree implementation.
*
* This file implements AVL tree type and operations.
*
* Implemented AVL tree has the following properties:
* @li It is a binary search tree with non-unique keys.
* @li Difference of heights of the left and the right subtree of every node is
* one at maximum.
*
* Every node has a pointer to its parent which allows insertion of multiple
* identical keys into the tree.
*
* Be careful when using this tree because of the base atribute which is added
* to every inserted node key. There is no rule in which order nodes with the
* same key are visited.
*/
 
#include <adt/avl.h>
#include <debug.h>
 
#define LEFT 0
#define RIGHT 1
 
/** Search for the first occurence of the given key in an AVL tree.
*
* @param t AVL tree.
* @param key Key to be searched.
*
* @return Pointer to a node or NULL if there is no such key.
*/
avltree_node_t *avltree_search(avltree_t *t, avltree_key_t key)
{
avltree_node_t *p;
/*
* Iteratively descend to the leaf that can contain the searched key.
*/
p = t->root;
while (p != NULL) {
if (p->key > key)
p = p->lft;
else if (p->key < key)
p = p->rgt;
else
return p;
}
return NULL;
}
 
 
/** Find the node with the smallest key in an AVL tree.
*
* @param t AVL tree.
*
* @return Pointer to a node or NULL if there is no node in the tree.
*/
avltree_node_t *avltree_find_min(avltree_t *t)
{
avltree_node_t *p = t->root;
/*
* Check whether the tree is empty.
*/
if (!p)
return NULL;
/*
* Iteratively descend to the leftmost leaf in the tree.
*/
while (p->lft != NULL)
p = p->lft;
return p;
}
 
#define REBALANCE_INSERT_XX(DIR1, DIR2) \
top->DIR1 = par->DIR2; \
if (top->DIR1 != NULL) \
top->DIR1->par = top; \
par->par = top->par; \
top->par = par; \
par->DIR2 = top; \
par->balance = 0; \
top->balance = 0; \
*dpc = par;
 
#define REBALANCE_INSERT_LL() REBALANCE_INSERT_XX(lft, rgt)
#define REBALANCE_INSERT_RR() REBALANCE_INSERT_XX(rgt, lft)
 
#define REBALANCE_INSERT_XY(DIR1, DIR2, SGN) \
gpa = par->DIR2; \
par->DIR2 = gpa->DIR1; \
if (gpa->DIR1 != NULL) \
gpa->DIR1->par = par; \
gpa->DIR1 = par; \
par->par = gpa; \
top->DIR1 = gpa->DIR2; \
if (gpa->DIR2 != NULL) \
gpa->DIR2->par = top; \
gpa->DIR2 = top; \
gpa->par = top->par; \
top->par = gpa; \
\
if (gpa->balance == -1 * SGN) { \
par->balance = 0; \
top->balance = 1 * SGN; \
} else if (gpa->balance == 0) { \
par->balance = 0; \
top->balance = 0; \
} else { \
par->balance = -1 * SGN; \
top->balance = 0; \
} \
gpa->balance = 0; \
*dpc = gpa;
 
#define REBALANCE_INSERT_LR() REBALANCE_INSERT_XY(lft, rgt, 1)
#define REBALANCE_INSERT_RL() REBALANCE_INSERT_XY(rgt, lft, -1)
/** Insert new node into AVL tree.
*
* @param t AVL tree.
* @param newnode New node to be inserted.
*/
void avltree_insert(avltree_t *t, avltree_node_t *newnode)
{
avltree_node_t *par;
avltree_node_t *gpa;
avltree_node_t *top;
avltree_node_t **dpc;
avltree_key_t key;
 
ASSERT(t);
ASSERT(newnode);
 
/*
* Creating absolute key.
*/
key = newnode->key + t->base;
/*
* Iteratively descend to the leaf that can contain the new node.
* Last node with non-zero balance in the way to leaf is stored as top -
* it is a place of possible inbalance.
*/
dpc = &t->root;
gpa = NULL;
top = t->root;
while ((par = (*dpc)) != NULL) {
if (par->balance != 0) {
top = par;
}
gpa = par;
dpc = par->key > key ? &par->lft: &par->rgt;
}
 
/*
* Initialize the new node.
*/
newnode->key = key;
newnode->lft = NULL;
newnode->rgt = NULL;
newnode->par = gpa;
newnode->balance = 0;
 
/*
* Insert first node into the empty tree.
*/
if (t->root == NULL) {
*dpc = newnode;
return;
}
 
/*
* Insert the new node into the previously found leaf position.
*/
*dpc = newnode;
 
/*
* If the tree contains one node - end.
*/
if (top == NULL)
return;
 
/*
* Store pointer of top's father which points to the node with
* potentially broken balance (top).
*/
if (top->par == NULL) {
dpc = &t->root;
} else {
if (top->par->lft == top)
dpc = &top->par->lft;
else
dpc = &top->par->rgt;
}
 
/*
* Repair all balances on the way from top node to the newly inserted
* node.
*/
par = top;
while (par != newnode) {
if (par->key > key) {
par->balance--;
par = par->lft;
} else {
par->balance++;
par = par->rgt;
}
}
/*
* To balance the tree, we must check and balance top node.
*/
if (top->balance == -2) {
par = top->lft;
if (par->balance == -1) {
/*
* LL rotation.
*/
REBALANCE_INSERT_LL();
} else {
/*
* LR rotation.
*/
ASSERT(par->balance == 1);
REBALANCE_INSERT_LR();
}
} else if (top->balance == 2) {
par = top->rgt;
if (par->balance == 1) {
/*
* RR rotation.
*/
REBALANCE_INSERT_RR();
} else {
/*
* RL rotation.
*/
ASSERT(par->balance == -1);
REBALANCE_INSERT_RL();
}
} else {
/*
* Balance is not broken, insertion is finised.
*/
return;
}
 
}
 
/** Repair the tree after reparenting node u.
*
* If node u has no parent, mark it as the root of the whole tree. Otherwise
* node v represents stale address of one of the children of node u's parent.
* Replace v with w as node u parent's child (for most uses, u and w will be the
* same).
*
* @param t AVL tree.
* @param u Node whose new parent has a stale child pointer.
* @param v Stale child of node u's new parent.
* @param w New child of node u's new parent.
* @param dir If not NULL, address of the variable where to store information
* about whether w replaced v in the left or the right subtree of
* u's new parent.
* @param ro Read only operation; do not modify any tree pointers. This is
* useful for tracking direction via the dir pointer.
*
* @return Zero if w became the new root of the tree, otherwise return
* non-zero.
*/
static int
repair(avltree_t *t, avltree_node_t *u, avltree_node_t *v, avltree_node_t *w,
int *dir, int ro)
{
if (u->par == NULL) {
if (!ro)
t->root = w;
return 0;
} else {
if (u->par->lft == v) {
if (!ro)
u->par->lft = w;
if (dir)
*dir = LEFT;
} else {
ASSERT(u->par->rgt == v);
if (!ro)
u->par->rgt = w;
if (dir)
*dir = RIGHT;
}
}
return 1;
}
 
#define REBALANCE_DELETE(DIR1, DIR2, SIGN) \
if (cur->balance == -1 * SIGN) { \
par->balance = 0; \
gpa->balance = 1 * SIGN; \
if (gpa->DIR1) \
gpa->DIR1->par = gpa; \
par->DIR2->par = par; \
} else if (cur->balance == 0) { \
par->balance = 0; \
gpa->balance = 0; \
if (gpa->DIR1) \
gpa->DIR1->par = gpa; \
if (par->DIR2) \
par->DIR2->par = par; \
} else { \
par->balance = -1 * SIGN; \
gpa->balance = 0; \
if (par->DIR2) \
par->DIR2->par = par; \
gpa->DIR1->par = gpa; \
} \
cur->balance = 0;
 
#define REBALANCE_DELETE_LR() REBALANCE_DELETE(lft, rgt, 1)
#define REBALANCE_DELETE_RL() REBALANCE_DELETE(rgt, lft, -1)
 
/** Delete a node from the AVL tree.
*
* Because multiple identical keys are allowed, the parent pointers are
* essential during deletion.
*
* @param t AVL tree structure.
* @param node Address of the node which will be deleted.
*/
void avltree_delete(avltree_t *t, avltree_node_t *node)
{
avltree_node_t *cur;
avltree_node_t *par;
avltree_node_t *gpa;
int dir;
 
ASSERT(t);
ASSERT(node);
if (node->lft == NULL) {
if (node->rgt) {
/*
* Replace the node with its only right son.
*
* Balance of the right son will be repaired in the
* balancing cycle.
*/
cur = node->rgt;
cur->par = node->par;
gpa = cur;
dir = RIGHT;
cur->balance = node->balance;
} else {
if (node->par == NULL) {
/*
* The tree has only one node - it will become
* an empty tree and the balancing can end.
*/
t->root = NULL;
return;
}
/*
* The node has no child, it will be deleted with no
* substitution.
*/
gpa = node->par;
cur = NULL;
dir = (gpa->lft == node) ? LEFT: RIGHT;
}
} else {
/*
* The node has the left son. Find a node with the smallest key
* in the left subtree and replace the deleted node with that
* node.
*/
for (cur = node->lft; cur->rgt != NULL; cur = cur->rgt)
;
 
if (cur != node->lft) {
/*
* The rightmost node of the deleted node's left subtree
* was found. Replace the deleted node with this node.
* Cutting off of the found node has two cases that
* depend on its left son.
*/
if (cur->lft) {
/*
* The found node has a left son.
*/
gpa = cur->lft;
gpa->par = cur->par;
dir = LEFT;
gpa->balance = cur->balance;
} else {
dir = RIGHT;
gpa = cur->par;
}
cur->par->rgt = cur->lft;
cur->lft = node->lft;
cur->lft->par = cur;
} else {
/*
* The left son of the node hasn't got a right son. The
* left son will take the deleted node's place.
*/
dir = LEFT;
gpa = cur;
}
if (node->rgt)
node->rgt->par = cur;
cur->rgt = node->rgt;
cur->balance = node->balance;
cur->par = node->par;
}
/*
* Repair the parent node's pointer which pointed previously to the
* deleted node.
*/
(void) repair(t, node, node, cur, NULL, false);
/*
* Repair cycle which repairs balances of nodes on the way from from the
* cut-off node up to the root.
*/
for (;;) {
if (dir == LEFT) {
/*
* Deletion was made in the left subtree.
*/
gpa->balance++;
if (gpa->balance == 1) {
/*
* Stop balancing, the tree is balanced.
*/
break;
} else if (gpa->balance == 2) {
/*
* Bad balance, heights of left and right
* subtrees differ more than by one.
*/
par = gpa->rgt;
 
if (par->balance == -1) {
/*
* RL rotation.
*/
cur = par->lft;
par->lft = cur->rgt;
cur->rgt = par;
gpa->rgt = cur->lft;
cur->lft = gpa;
/*
* Repair balances and paternity of
* children, depending on the balance
* factor of the grand child (cur).
*/
REBALANCE_DELETE_RL();
/*
* Repair paternity.
*/
cur->par = gpa->par;
gpa->par = cur;
par->par = cur;
 
if (!repair(t, cur, gpa, cur, &dir,
false))
break;
gpa = cur->par;
} else {
/*
* RR rotation.
*/
gpa->rgt = par->lft;
if (par->lft)
par->lft->par = gpa;
par->lft = gpa;
/*
* Repair paternity.
*/
par->par = gpa->par;
gpa->par = par;
if (par->balance == 0) {
/*
* The right child of the
* balanced node is balanced,
* after RR rotation is done,
* the whole tree will be
* balanced.
*/
par->balance = -1;
gpa->balance = 1;
 
(void) repair(t, par, gpa, par,
NULL, false);
break;
} else {
par->balance = 0;
gpa->balance = 0;
if (!repair(t, par, gpa, par,
&dir, false))
break;
}
gpa = par->par;
}
} else {
/*
* Repair the pointer which pointed to the
* balanced node. If it was root then balancing
* is finished else continue with the next
* iteration (parent node).
*/
if (!repair(t, gpa, gpa, NULL, &dir, true))
break;
gpa = gpa->par;
}
} else {
/*
* Deletion was made in the right subtree.
*/
gpa->balance--;
if (gpa->balance == -1) {
/*
* Stop balancing, the tree is balanced.
*/
break;
} else if (gpa->balance == -2) {
/*
* Bad balance, heights of left and right
* subtrees differ more than by one.
*/
par = gpa->lft;
if (par->balance == 1) {
/*
* LR rotation.
*/
cur = par->rgt;
par->rgt = cur->lft;
cur->lft = par;
gpa->lft = cur->rgt;
cur->rgt = gpa;
/*
* Repair balances and paternity of
* children, depending on the balance
* factor of the grand child (cur).
*/
REBALANCE_DELETE_LR();
 
/*
* Repair paternity.
*/
cur->par = gpa->par;
gpa->par = cur;
par->par = cur;
 
if (!repair(t, cur, gpa, cur, &dir,
false))
break;
gpa = cur->par;
} else {
/*
* LL rotation.
*/
 
gpa->lft = par->rgt;
if (par->rgt)
par->rgt->par = gpa;
par->rgt = gpa;
/*
* Repair paternity.
*/
par->par = gpa->par;
gpa->par = par;
if (par->balance == 0) {
/*
* The left child of the
* balanced node is balanced,
* after LL rotation is done,
* the whole tree will be
* balanced.
*/
par->balance = 1;
gpa->balance = -1;
(void) repair(t, par, gpa, par,
NULL, false);
break;
} else {
par->balance = 0;
gpa->balance = 0;
if (!repair(t, par, gpa, par,
&dir, false))
break;
}
gpa = par->par;
}
} else {
/*
* Repair the pointer which pointed to the
* balanced node. If it was root then balancing
* is finished. Otherwise continue with the next
* iteration (parent node).
*/
if (!repair(t, gpa, gpa, NULL, &dir, true))
break;
gpa = gpa->par;
}
}
}
}
 
 
/** Delete a node with the smallest key from the AVL tree.
*
* @param t AVL tree structure.
*/
bool avltree_delete_min(avltree_t *t)
{
avltree_node_t *node;
/*
* Start searching for the smallest key in the tree starting in the root
* node and continue in cycle to the leftmost node in the tree (which
* must have the smallest key).
*/
node = t->root;
if (!node)
return false;
while (node->lft != NULL)
node = node->lft;
avltree_delete(t, node);
 
return true;
}
 
/** Walk a subtree of an AVL tree in-order and apply a supplied walker on each
* visited node.
*
* @param node Node representing the root of an AVL subtree to be
* walked.
* @param walker Walker function that will be appliad on each visited
* node.
* @param arg Argument for the walker.
*
* @return Zero if the walk should stop or non-zero otherwise.
*/
static bool _avltree_walk(avltree_node_t *node, avltree_walker_t walker,
void *arg)
{
if (node->lft) {
if (!_avltree_walk(node->lft, walker, arg))
return false;
}
if (!walker(node, arg))
return false;
if (node->rgt) {
if (!_avltree_walk(node->rgt, walker, arg))
return false;
}
return true;
}
 
/** Walk the AVL tree in-order and apply the walker function on each visited
* node.
*
* @param t AVL tree to be walked.
* @param walker Walker function that will be called on each visited
* node.
* @param arg Argument for the walker.
*/
void avltree_walk(avltree_t *t, avltree_walker_t walker, void *arg)
{
_avltree_walk(t->root, walker, arg);
}
 
/** @}
*/
 
/tags/0.4.1/kernel/generic/src/adt/list.c
0,0 → 1,94
/*
* Copyright (c) 2004 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 genericadt
* @{
*/
 
/**
* @file
* @brief Functions completing doubly linked circular list implementaion.
*
* This file contains some of the functions implementing doubly linked circular lists.
* However, this ADT is mostly implemented in @ref list.h.
*/
 
#include <adt/list.h>
 
/** Check for membership
*
* Check whether link is contained in the list head.
* The membership is defined as pointer equivalence.
*
* @param link Item to look for.
* @param head List to look in.
*
* @return true if link is contained in head, false otherwise.
*
*/
bool list_member(const link_t *link, const link_t *head)
{
bool found = false;
link_t *hlp = head->next;
while (hlp != head) {
if (hlp == link) {
found = true;
break;
}
hlp = hlp->next;
}
return found;
}
 
 
/** Concatenate two lists
*
* Concatenate lists head1 and head2, producing a single
* list head1 containing items from both (in head1, head2
* order) and empty list head2.
*
* @param head1 First list and concatenated output
* @param head2 Second list and empty output.
*
*/
void list_concat(link_t *head1, link_t *head2)
{
if (list_empty(head2))
return;
 
head2->next->prev = head1->prev;
head2->prev->next = head1;
head1->prev->next = head2->next;
head1->prev = head2->prev;
list_initialize(head2);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/slab.c
0,0 → 1,986
/*
* Copyright (c) 2006 Ondrej Palkovsky
* 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 genericmm
* @{
*/
 
/**
* @file
* @brief Slab allocator.
*
* The slab allocator is closely modelled after OpenSolaris slab allocator.
* @see http://www.usenix.org/events/usenix01/full_papers/bonwick/bonwick_html/
*
* with the following exceptions:
* @li empty slabs are deallocated immediately
* (in Linux they are kept in linked list, in Solaris ???)
* @li empty magazines are deallocated when not needed
* (in Solaris they are held in linked list in slab cache)
*
* Following features are not currently supported but would be easy to do:
* @li cache coloring
* @li dynamic magazine growing (different magazine sizes are already
* supported, but we would need to adjust allocation strategy)
*
* The slab allocator supports per-CPU caches ('magazines') to facilitate
* good SMP scaling.
*
* When a new object is being allocated, it is first checked, if it is
* available in a CPU-bound magazine. If it is not found there, it is
* allocated from a CPU-shared slab - if a partially full one is found,
* it is used, otherwise a new one is allocated.
*
* When an object is being deallocated, it is put to a CPU-bound magazine.
* If there is no such magazine, a new one is allocated (if this fails,
* the object is deallocated into slab). If the magazine is full, it is
* put into cpu-shared list of magazines and a new one is allocated.
*
* The CPU-bound magazine is actually a pair of magazines in order to avoid
* thrashing when somebody is allocating/deallocating 1 item at the magazine
* size boundary. LIFO order is enforced, which should avoid fragmentation
* as much as possible.
*
* Every cache contains list of full slabs and list of partially full slabs.
* Empty slabs are immediately freed (thrashing will be avoided because
* of magazines).
*
* The slab information structure is kept inside the data area, if possible.
* The cache can be marked that it should not use magazines. This is used
* only for slab related caches to avoid deadlocks and infinite recursion
* (the slab allocator uses itself for allocating all it's control structures).
*
* The slab allocator allocates a lot of space and does not free it. When
* the frame allocator fails to allocate a frame, it calls slab_reclaim().
* It tries 'light reclaim' first, then brutal reclaim. The light reclaim
* releases slabs from cpu-shared magazine-list, until at least 1 slab
* is deallocated in each cache (this algorithm should probably change).
* The brutal reclaim removes all cached objects, even from CPU-bound
* magazines.
*
* @todo
* For better CPU-scaling the magazine allocation strategy should
* be extended. Currently, if the cache does not have magazine, it asks
* for non-cpu cached magazine cache to provide one. It might be feasible
* to add cpu-cached magazine cache (which would allocate it's magazines
* from non-cpu-cached mag. cache). This would provide a nice per-cpu
* buffer. The other possibility is to use the per-cache
* 'empty-magazine-list', which decreases competing for 1 per-system
* magazine cache.
*
* @todo
* it might be good to add granularity of locks even to slab level,
* we could then try_spinlock over all partial slabs and thus improve
* scalability even on slab level
*/
 
#include <synch/spinlock.h>
#include <mm/slab.h>
#include <adt/list.h>
#include <memstr.h>
#include <align.h>
#include <mm/frame.h>
#include <config.h>
#include <print.h>
#include <arch.h>
#include <panic.h>
#include <debug.h>
#include <bitops.h>
#include <macros.h>
 
SPINLOCK_INITIALIZE(slab_cache_lock);
static LIST_INITIALIZE(slab_cache_list);
 
/** Magazine cache */
static slab_cache_t mag_cache;
/** Cache for cache descriptors */
static slab_cache_t slab_cache_cache;
/** Cache for external slab descriptors
* This time we want per-cpu cache, so do not make it static
* - using slab for internal slab structures will not deadlock,
* as all slab structures are 'small' - control structures of
* their caches do not require further allocation
*/
static slab_cache_t *slab_extern_cache;
/** Caches for malloc */
static slab_cache_t *malloc_caches[SLAB_MAX_MALLOC_W - SLAB_MIN_MALLOC_W + 1];
static char *malloc_names[] = {
"malloc-16",
"malloc-32",
"malloc-64",
"malloc-128",
"malloc-256",
"malloc-512",
"malloc-1K",
"malloc-2K",
"malloc-4K",
"malloc-8K",
"malloc-16K",
"malloc-32K",
"malloc-64K",
"malloc-128K",
"malloc-256K",
"malloc-512K",
"malloc-1M",
"malloc-2M",
"malloc-4M"
};
 
/** Slab descriptor */
typedef struct {
slab_cache_t *cache; /**< Pointer to parent cache. */
link_t link; /**< List of full/partial slabs. */
void *start; /**< Start address of first available item. */
size_t available; /**< Count of available items in this slab. */
size_t nextavail; /**< The index of next available item. */
} slab_t;
 
#ifdef CONFIG_DEBUG
static int _slab_initialized = 0;
#endif
 
/**************************************/
/* Slab allocation functions */
 
/**
* Allocate frames for slab space and initialize
*
*/
static slab_t *slab_space_alloc(slab_cache_t *cache, int flags)
{
void *data;
slab_t *slab;
size_t fsize;
unsigned int i;
size_t zone = 0;
data = frame_alloc_generic(cache->order, FRAME_KA | flags, &zone);
if (!data) {
return NULL;
}
if (!(cache->flags & SLAB_CACHE_SLINSIDE)) {
slab = slab_alloc(slab_extern_cache, flags);
if (!slab) {
frame_free(KA2PA(data));
return NULL;
}
} else {
fsize = (PAGE_SIZE << cache->order);
slab = data + fsize - sizeof(*slab);
}
/* Fill in slab structures */
for (i = 0; i < ((unsigned int) 1 << cache->order); i++)
frame_set_parent(ADDR2PFN(KA2PA(data)) + i, slab, zone);
 
slab->start = data;
slab->available = cache->objects;
slab->nextavail = 0;
slab->cache = cache;
 
for (i = 0; i < cache->objects; i++)
*((int *) (slab->start + i*cache->size)) = i + 1;
 
atomic_inc(&cache->allocated_slabs);
return slab;
}
 
/**
* Deallocate space associated with slab
*
* @return number of freed frames
*/
static size_t slab_space_free(slab_cache_t *cache, slab_t *slab)
{
frame_free(KA2PA(slab->start));
if (! (cache->flags & SLAB_CACHE_SLINSIDE))
slab_free(slab_extern_cache, slab);
 
atomic_dec(&cache->allocated_slabs);
return 1 << cache->order;
}
 
/** Map object to slab structure */
static slab_t * obj2slab(void *obj)
{
return (slab_t *) frame_get_parent(ADDR2PFN(KA2PA(obj)), 0);
}
 
/**************************************/
/* Slab functions */
 
 
/**
* Return object to slab and call a destructor
*
* @param slab If the caller knows directly slab of the object, otherwise NULL
*
* @return Number of freed pages
*/
static size_t slab_obj_destroy(slab_cache_t *cache, void *obj, slab_t *slab)
{
int freed = 0;
 
if (!slab)
slab = obj2slab(obj);
 
ASSERT(slab->cache == cache);
 
if (cache->destructor)
freed = cache->destructor(obj);
spinlock_lock(&cache->slablock);
ASSERT(slab->available < cache->objects);
 
*((int *)obj) = slab->nextavail;
slab->nextavail = (obj - slab->start) / cache->size;
slab->available++;
 
/* Move it to correct list */
if (slab->available == cache->objects) {
/* Free associated memory */
list_remove(&slab->link);
spinlock_unlock(&cache->slablock);
 
return freed + slab_space_free(cache, slab);
 
} else if (slab->available == 1) {
/* It was in full, move to partial */
list_remove(&slab->link);
list_prepend(&slab->link, &cache->partial_slabs);
}
spinlock_unlock(&cache->slablock);
return freed;
}
 
/**
* Take new object from slab or create new if needed
*
* @return Object address or null
*/
static void *slab_obj_create(slab_cache_t *cache, int flags)
{
slab_t *slab;
void *obj;
 
spinlock_lock(&cache->slablock);
 
if (list_empty(&cache->partial_slabs)) {
/* Allow recursion and reclaiming
* - this should work, as the slab control structures
* are small and do not need to allocate with anything
* other than frame_alloc when they are allocating,
* that's why we should get recursion at most 1-level deep
*/
spinlock_unlock(&cache->slablock);
slab = slab_space_alloc(cache, flags);
if (!slab)
return NULL;
spinlock_lock(&cache->slablock);
} else {
slab = list_get_instance(cache->partial_slabs.next, slab_t,
link);
list_remove(&slab->link);
}
obj = slab->start + slab->nextavail * cache->size;
slab->nextavail = *((int *)obj);
slab->available--;
 
if (!slab->available)
list_prepend(&slab->link, &cache->full_slabs);
else
list_prepend(&slab->link, &cache->partial_slabs);
 
spinlock_unlock(&cache->slablock);
 
if (cache->constructor && cache->constructor(obj, flags)) {
/* Bad, bad, construction failed */
slab_obj_destroy(cache, obj, slab);
return NULL;
}
return obj;
}
 
/**************************************/
/* CPU-Cache slab functions */
 
/**
* Finds a full magazine in cache, takes it from list
* and returns it
*
* @param first If true, return first, else last mag
*/
static slab_magazine_t *get_mag_from_cache(slab_cache_t *cache, int first)
{
slab_magazine_t *mag = NULL;
link_t *cur;
 
spinlock_lock(&cache->maglock);
if (!list_empty(&cache->magazines)) {
if (first)
cur = cache->magazines.next;
else
cur = cache->magazines.prev;
mag = list_get_instance(cur, slab_magazine_t, link);
list_remove(&mag->link);
atomic_dec(&cache->magazine_counter);
}
spinlock_unlock(&cache->maglock);
return mag;
}
 
/** Prepend magazine to magazine list in cache */
static void put_mag_to_cache(slab_cache_t *cache, slab_magazine_t *mag)
{
spinlock_lock(&cache->maglock);
 
list_prepend(&mag->link, &cache->magazines);
atomic_inc(&cache->magazine_counter);
spinlock_unlock(&cache->maglock);
}
 
/**
* Free all objects in magazine and free memory associated with magazine
*
* @return Number of freed pages
*/
static size_t magazine_destroy(slab_cache_t *cache, slab_magazine_t *mag)
{
unsigned int i;
size_t frames = 0;
 
for (i = 0; i < mag->busy; i++) {
frames += slab_obj_destroy(cache, mag->objs[i], NULL);
atomic_dec(&cache->cached_objs);
}
slab_free(&mag_cache, mag);
 
return frames;
}
 
/**
* Find full magazine, set it as current and return it
*
* Assume cpu_magazine lock is held
*/
static slab_magazine_t *get_full_current_mag(slab_cache_t *cache)
{
slab_magazine_t *cmag, *lastmag, *newmag;
 
cmag = cache->mag_cache[CPU->id].current;
lastmag = cache->mag_cache[CPU->id].last;
if (cmag) { /* First try local CPU magazines */
if (cmag->busy)
return cmag;
 
if (lastmag && lastmag->busy) {
cache->mag_cache[CPU->id].current = lastmag;
cache->mag_cache[CPU->id].last = cmag;
return lastmag;
}
}
/* Local magazines are empty, import one from magazine list */
newmag = get_mag_from_cache(cache, 1);
if (!newmag)
return NULL;
 
if (lastmag)
magazine_destroy(cache, lastmag);
 
cache->mag_cache[CPU->id].last = cmag;
cache->mag_cache[CPU->id].current = newmag;
return newmag;
}
 
/**
* Try to find object in CPU-cache magazines
*
* @return Pointer to object or NULL if not available
*/
static void *magazine_obj_get(slab_cache_t *cache)
{
slab_magazine_t *mag;
void *obj;
 
if (!CPU)
return NULL;
 
spinlock_lock(&cache->mag_cache[CPU->id].lock);
 
mag = get_full_current_mag(cache);
if (!mag) {
spinlock_unlock(&cache->mag_cache[CPU->id].lock);
return NULL;
}
obj = mag->objs[--mag->busy];
spinlock_unlock(&cache->mag_cache[CPU->id].lock);
atomic_dec(&cache->cached_objs);
return obj;
}
 
/**
* Assure that the current magazine is empty, return pointer to it, or NULL if
* no empty magazine is available and cannot be allocated
*
* Assume mag_cache[CPU->id].lock is held
*
* We have 2 magazines bound to processor.
* First try the current.
* If full, try the last.
* If full, put to magazines list.
* allocate new, exchange last & current
*
*/
static slab_magazine_t *make_empty_current_mag(slab_cache_t *cache)
{
slab_magazine_t *cmag,*lastmag,*newmag;
 
cmag = cache->mag_cache[CPU->id].current;
lastmag = cache->mag_cache[CPU->id].last;
 
if (cmag) {
if (cmag->busy < cmag->size)
return cmag;
if (lastmag && lastmag->busy < lastmag->size) {
cache->mag_cache[CPU->id].last = cmag;
cache->mag_cache[CPU->id].current = lastmag;
return lastmag;
}
}
/* current | last are full | nonexistent, allocate new */
/* We do not want to sleep just because of caching */
/* Especially we do not want reclaiming to start, as
* this would deadlock */
newmag = slab_alloc(&mag_cache, FRAME_ATOMIC | FRAME_NO_RECLAIM);
if (!newmag)
return NULL;
newmag->size = SLAB_MAG_SIZE;
newmag->busy = 0;
 
/* Flush last to magazine list */
if (lastmag)
put_mag_to_cache(cache, lastmag);
 
/* Move current as last, save new as current */
cache->mag_cache[CPU->id].last = cmag;
cache->mag_cache[CPU->id].current = newmag;
 
return newmag;
}
 
/**
* Put object into CPU-cache magazine
*
* @return 0 - success, -1 - could not get memory
*/
static int magazine_obj_put(slab_cache_t *cache, void *obj)
{
slab_magazine_t *mag;
 
if (!CPU)
return -1;
 
spinlock_lock(&cache->mag_cache[CPU->id].lock);
 
mag = make_empty_current_mag(cache);
if (!mag) {
spinlock_unlock(&cache->mag_cache[CPU->id].lock);
return -1;
}
mag->objs[mag->busy++] = obj;
 
spinlock_unlock(&cache->mag_cache[CPU->id].lock);
atomic_inc(&cache->cached_objs);
return 0;
}
 
 
/**************************************/
/* Slab cache functions */
 
/** Return number of objects that fit in certain cache size */
static unsigned int comp_objects(slab_cache_t *cache)
{
if (cache->flags & SLAB_CACHE_SLINSIDE)
return ((PAGE_SIZE << cache->order) - sizeof(slab_t)) /
cache->size;
else
return (PAGE_SIZE << cache->order) / cache->size;
}
 
/** Return wasted space in slab */
static unsigned int badness(slab_cache_t *cache)
{
unsigned int objects;
unsigned int ssize;
 
objects = comp_objects(cache);
ssize = PAGE_SIZE << cache->order;
if (cache->flags & SLAB_CACHE_SLINSIDE)
ssize -= sizeof(slab_t);
return ssize - objects * cache->size;
}
 
/**
* Initialize mag_cache structure in slab cache
*/
static void make_magcache(slab_cache_t *cache)
{
unsigned int i;
ASSERT(_slab_initialized >= 2);
 
cache->mag_cache = malloc(sizeof(slab_mag_cache_t) * config.cpu_count,
0);
for (i = 0; i < config.cpu_count; i++) {
memsetb(&cache->mag_cache[i], sizeof(cache->mag_cache[i]), 0);
spinlock_initialize(&cache->mag_cache[i].lock,
"slab_maglock_cpu");
}
}
 
/** Initialize allocated memory as a slab cache */
static void
_slab_cache_create(slab_cache_t *cache, char *name, size_t size, size_t align,
int (*constructor)(void *obj, int kmflag), int (*destructor)(void *obj),
int flags)
{
int pages;
ipl_t ipl;
 
memsetb(cache, sizeof(*cache), 0);
cache->name = name;
 
if (align < sizeof(unative_t))
align = sizeof(unative_t);
size = ALIGN_UP(size, align);
cache->size = size;
 
cache->constructor = constructor;
cache->destructor = destructor;
cache->flags = flags;
 
list_initialize(&cache->full_slabs);
list_initialize(&cache->partial_slabs);
list_initialize(&cache->magazines);
spinlock_initialize(&cache->slablock, "slab_lock");
spinlock_initialize(&cache->maglock, "slab_maglock");
if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
make_magcache(cache);
 
/* Compute slab sizes, object counts in slabs etc. */
if (cache->size < SLAB_INSIDE_SIZE)
cache->flags |= SLAB_CACHE_SLINSIDE;
 
/* Minimum slab order */
pages = SIZE2FRAMES(cache->size);
/* We need the 2^order >= pages */
if (pages == 1)
cache->order = 0;
else
cache->order = fnzb(pages - 1) + 1;
 
while (badness(cache) > SLAB_MAX_BADNESS(cache)) {
cache->order += 1;
}
cache->objects = comp_objects(cache);
/* If info fits in, put it inside */
if (badness(cache) > sizeof(slab_t))
cache->flags |= SLAB_CACHE_SLINSIDE;
 
/* Add cache to cache list */
ipl = interrupts_disable();
spinlock_lock(&slab_cache_lock);
 
list_append(&cache->link, &slab_cache_list);
 
spinlock_unlock(&slab_cache_lock);
interrupts_restore(ipl);
}
 
/** Create slab cache */
slab_cache_t *
slab_cache_create(char *name, size_t size, size_t align,
int (*constructor)(void *obj, int kmflag), int (*destructor)(void *obj),
int flags)
{
slab_cache_t *cache;
 
cache = slab_alloc(&slab_cache_cache, 0);
_slab_cache_create(cache, name, size, align, constructor, destructor,
flags);
return cache;
}
 
/**
* Reclaim space occupied by objects that are already free
*
* @param flags If contains SLAB_RECLAIM_ALL, do aggressive freeing
* @return Number of freed pages
*/
static size_t _slab_reclaim(slab_cache_t *cache, int flags)
{
unsigned int i;
slab_magazine_t *mag;
size_t frames = 0;
int magcount;
if (cache->flags & SLAB_CACHE_NOMAGAZINE)
return 0; /* Nothing to do */
 
/* We count up to original magazine count to avoid
* endless loop
*/
magcount = atomic_get(&cache->magazine_counter);
while (magcount-- && (mag=get_mag_from_cache(cache, 0))) {
frames += magazine_destroy(cache,mag);
if (!(flags & SLAB_RECLAIM_ALL) && frames)
break;
}
if (flags & SLAB_RECLAIM_ALL) {
/* Free cpu-bound magazines */
/* Destroy CPU magazines */
for (i = 0; i < config.cpu_count; i++) {
spinlock_lock(&cache->mag_cache[i].lock);
 
mag = cache->mag_cache[i].current;
if (mag)
frames += magazine_destroy(cache, mag);
cache->mag_cache[i].current = NULL;
mag = cache->mag_cache[i].last;
if (mag)
frames += magazine_destroy(cache, mag);
cache->mag_cache[i].last = NULL;
 
spinlock_unlock(&cache->mag_cache[i].lock);
}
}
 
return frames;
}
 
/** Check that there are no slabs and remove cache from system */
void slab_cache_destroy(slab_cache_t *cache)
{
ipl_t ipl;
 
/* First remove cache from link, so that we don't need
* to disable interrupts later
*/
 
ipl = interrupts_disable();
spinlock_lock(&slab_cache_lock);
 
list_remove(&cache->link);
 
spinlock_unlock(&slab_cache_lock);
interrupts_restore(ipl);
 
/* Do not lock anything, we assume the software is correct and
* does not touch the cache when it decides to destroy it */
/* Destroy all magazines */
_slab_reclaim(cache, SLAB_RECLAIM_ALL);
 
/* All slabs must be empty */
if (!list_empty(&cache->full_slabs) ||
!list_empty(&cache->partial_slabs))
panic("Destroying cache that is not empty.");
 
if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
free(cache->mag_cache);
slab_free(&slab_cache_cache, cache);
}
 
/** Allocate new object from cache - if no flags given, always returns memory */
void *slab_alloc(slab_cache_t *cache, int flags)
{
ipl_t ipl;
void *result = NULL;
/* Disable interrupts to avoid deadlocks with interrupt handlers */
ipl = interrupts_disable();
 
if (!(cache->flags & SLAB_CACHE_NOMAGAZINE)) {
result = magazine_obj_get(cache);
}
if (!result)
result = slab_obj_create(cache, flags);
 
interrupts_restore(ipl);
 
if (result)
atomic_inc(&cache->allocated_objs);
 
return result;
}
 
/** Return object to cache, use slab if known */
static void _slab_free(slab_cache_t *cache, void *obj, slab_t *slab)
{
ipl_t ipl;
 
ipl = interrupts_disable();
 
if ((cache->flags & SLAB_CACHE_NOMAGAZINE) ||
magazine_obj_put(cache, obj)) {
slab_obj_destroy(cache, obj, slab);
 
}
interrupts_restore(ipl);
atomic_dec(&cache->allocated_objs);
}
 
/** Return slab object to cache */
void slab_free(slab_cache_t *cache, void *obj)
{
_slab_free(cache, obj, NULL);
}
 
/* Go through all caches and reclaim what is possible */
size_t slab_reclaim(int flags)
{
slab_cache_t *cache;
link_t *cur;
size_t frames = 0;
 
spinlock_lock(&slab_cache_lock);
 
/* TODO: Add assert, that interrupts are disabled, otherwise
* memory allocation from interrupts can deadlock.
*/
 
for (cur = slab_cache_list.next; cur != &slab_cache_list;
cur = cur->next) {
cache = list_get_instance(cur, slab_cache_t, link);
frames += _slab_reclaim(cache, flags);
}
 
spinlock_unlock(&slab_cache_lock);
 
return frames;
}
 
 
/* Print list of slabs */
void slab_print_list(void)
{
int skip = 0;
 
printf("slab name size pages obj/pg slabs cached allocated"
" ctl\n");
printf("---------------- -------- ------ ------ ------ ------ ---------"
" ---\n");
 
while (true) {
slab_cache_t *cache;
link_t *cur;
ipl_t ipl;
int i;
 
/*
* We must not hold the slab_cache_lock spinlock when printing
* the statistics. Otherwise we can easily deadlock if the print
* needs to allocate memory.
*
* Therefore, we walk through the slab cache list, skipping some
* amount of already processed caches during each iteration and
* gathering statistics about the first unprocessed cache. For
* the sake of printing the statistics, we realese the
* slab_cache_lock and reacquire it afterwards. Then the walk
* starts again.
*
* This limits both the efficiency and also accuracy of the
* obtained statistics. The efficiency is decreased because the
* time complexity of the algorithm is quadratic instead of
* linear. The accuracy is impacted because we drop the lock
* after processing one cache. If there is someone else
* manipulating the cache list, we might omit an arbitrary
* number of caches or process one cache multiple times.
* However, we don't bleed for this algorithm for it is only
* statistics.
*/
 
ipl = interrupts_disable();
spinlock_lock(&slab_cache_lock);
 
for (i = 0, cur = slab_cache_list.next;
i < skip && cur != &slab_cache_list;
i++, cur = cur->next)
;
 
if (cur == &slab_cache_list) {
spinlock_unlock(&slab_cache_lock);
interrupts_restore(ipl);
break;
}
 
skip++;
 
cache = list_get_instance(cur, slab_cache_t, link);
 
char *name = cache->name;
uint8_t order = cache->order;
size_t size = cache->size;
unsigned int objects = cache->objects;
long allocated_slabs = atomic_get(&cache->allocated_slabs);
long cached_objs = atomic_get(&cache->cached_objs);
long allocated_objs = atomic_get(&cache->allocated_objs);
int flags = cache->flags;
spinlock_unlock(&slab_cache_lock);
interrupts_restore(ipl);
printf("%-16s %8" PRIs " %6d %6u %6ld %6ld %9ld %-3s\n",
name, size, (1 << order), objects, allocated_slabs,
cached_objs, allocated_objs,
flags & SLAB_CACHE_SLINSIDE ? "in" : "out");
}
}
 
void slab_cache_init(void)
{
int i, size;
 
/* Initialize magazine cache */
_slab_cache_create(&mag_cache, "slab_magazine",
sizeof(slab_magazine_t) + SLAB_MAG_SIZE * sizeof(void*),
sizeof(uintptr_t), NULL, NULL, SLAB_CACHE_NOMAGAZINE |
SLAB_CACHE_SLINSIDE);
/* Initialize slab_cache cache */
_slab_cache_create(&slab_cache_cache, "slab_cache",
sizeof(slab_cache_cache), sizeof(uintptr_t), NULL, NULL,
SLAB_CACHE_NOMAGAZINE | SLAB_CACHE_SLINSIDE);
/* Initialize external slab cache */
slab_extern_cache = slab_cache_create("slab_extern", sizeof(slab_t), 0,
NULL, NULL, SLAB_CACHE_SLINSIDE | SLAB_CACHE_MAGDEFERRED);
 
/* Initialize structures for malloc */
for (i = 0, size = (1 << SLAB_MIN_MALLOC_W);
i < (SLAB_MAX_MALLOC_W - SLAB_MIN_MALLOC_W + 1);
i++, size <<= 1) {
malloc_caches[i] = slab_cache_create(malloc_names[i], size, 0,
NULL, NULL, SLAB_CACHE_MAGDEFERRED);
}
#ifdef CONFIG_DEBUG
_slab_initialized = 1;
#endif
}
 
/** Enable cpu_cache
*
* Kernel calls this function, when it knows the real number of
* processors.
* Allocate slab for cpucache and enable it on all existing
* slabs that are SLAB_CACHE_MAGDEFERRED
*/
void slab_enable_cpucache(void)
{
link_t *cur;
slab_cache_t *s;
 
#ifdef CONFIG_DEBUG
_slab_initialized = 2;
#endif
 
spinlock_lock(&slab_cache_lock);
for (cur = slab_cache_list.next; cur != &slab_cache_list;
cur = cur->next){
s = list_get_instance(cur, slab_cache_t, link);
if ((s->flags & SLAB_CACHE_MAGDEFERRED) !=
SLAB_CACHE_MAGDEFERRED)
continue;
make_magcache(s);
s->flags &= ~SLAB_CACHE_MAGDEFERRED;
}
 
spinlock_unlock(&slab_cache_lock);
}
 
/**************************************/
/* kalloc/kfree functions */
void *malloc(unsigned int size, int flags)
{
ASSERT(_slab_initialized);
ASSERT(size <= (1 << SLAB_MAX_MALLOC_W));
if (size < (1 << SLAB_MIN_MALLOC_W))
size = (1 << SLAB_MIN_MALLOC_W);
 
int idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
 
return slab_alloc(malloc_caches[idx], flags);
}
 
void *realloc(void *ptr, unsigned int size, int flags)
{
ASSERT(_slab_initialized);
ASSERT(size <= (1 << SLAB_MAX_MALLOC_W));
void *new_ptr;
if (size > 0) {
if (size < (1 << SLAB_MIN_MALLOC_W))
size = (1 << SLAB_MIN_MALLOC_W);
int idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
new_ptr = slab_alloc(malloc_caches[idx], flags);
} else
new_ptr = NULL;
if ((new_ptr != NULL) && (ptr != NULL)) {
slab_t *slab = obj2slab(ptr);
memcpy(new_ptr, ptr, min(size, slab->cache->size));
}
if (ptr != NULL)
free(ptr);
return new_ptr;
}
 
void free(void *ptr)
{
if (!ptr)
return;
 
slab_t *slab = obj2slab(ptr);
_slab_free(slab->cache, ptr, slab);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/tlb.c
0,0 → 1,190
/*
* Copyright (c) 2001-2004 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 genericmm
* @{
*/
 
/**
* @file
* @brief Generic TLB shootdown algorithm.
*
* The algorithm implemented here is based on the CMU TLB shootdown
* algorithm and is further simplified (e.g. all CPUs receive all TLB
* shootdown messages).
*/
 
#include <mm/tlb.h>
#include <mm/asid.h>
#include <arch/mm/tlb.h>
#include <smp/ipi.h>
#include <synch/spinlock.h>
#include <atomic.h>
#include <arch/interrupt.h>
#include <config.h>
#include <arch.h>
#include <panic.h>
#include <debug.h>
#include <cpu.h>
 
/**
* This lock is used for synchronisation between sender and
* recipients of TLB shootdown message. It must be acquired
* before CPU structure lock.
*/
SPINLOCK_INITIALIZE(tlblock);
 
void tlb_init(void)
{
tlb_arch_init();
}
 
#ifdef CONFIG_SMP
 
/** Send TLB shootdown message.
*
* This function attempts to deliver TLB shootdown message
* to all other processors.
*
* This function must be called with interrupts disabled.
*
* @param type Type describing scope of shootdown.
* @param asid Address space, if required by type.
* @param page Virtual page address, if required by type.
* @param count Number of pages, if required by type.
*/
void tlb_shootdown_start(tlb_invalidate_type_t type, asid_t asid,
uintptr_t page, size_t count)
{
unsigned int i;
 
CPU->tlb_active = 0;
spinlock_lock(&tlblock);
for (i = 0; i < config.cpu_count; i++) {
cpu_t *cpu;
if (i == CPU->id)
continue;
 
cpu = &cpus[i];
spinlock_lock(&cpu->lock);
if (cpu->tlb_messages_count == TLB_MESSAGE_QUEUE_LEN) {
/*
* The message queue is full.
* Erase the queue and store one TLB_INVL_ALL message.
*/
cpu->tlb_messages_count = 1;
cpu->tlb_messages[0].type = TLB_INVL_ALL;
cpu->tlb_messages[0].asid = ASID_INVALID;
cpu->tlb_messages[0].page = 0;
cpu->tlb_messages[0].count = 0;
} else {
/*
* Enqueue the message.
*/
size_t idx = cpu->tlb_messages_count++;
cpu->tlb_messages[idx].type = type;
cpu->tlb_messages[idx].asid = asid;
cpu->tlb_messages[idx].page = page;
cpu->tlb_messages[idx].count = count;
}
spinlock_unlock(&cpu->lock);
}
tlb_shootdown_ipi_send();
 
busy_wait:
for (i = 0; i < config.cpu_count; i++)
if (cpus[i].tlb_active)
goto busy_wait;
}
 
/** Finish TLB shootdown sequence. */
void tlb_shootdown_finalize(void)
{
spinlock_unlock(&tlblock);
CPU->tlb_active = 1;
}
 
void tlb_shootdown_ipi_send(void)
{
ipi_broadcast(VECTOR_TLB_SHOOTDOWN_IPI);
}
 
/** Receive TLB shootdown message. */
void tlb_shootdown_ipi_recv(void)
{
tlb_invalidate_type_t type;
asid_t asid;
uintptr_t page;
size_t count;
unsigned int i;
ASSERT(CPU);
CPU->tlb_active = 0;
spinlock_lock(&tlblock);
spinlock_unlock(&tlblock);
spinlock_lock(&CPU->lock);
ASSERT(CPU->tlb_messages_count <= TLB_MESSAGE_QUEUE_LEN);
 
for (i = 0; i < CPU->tlb_messages_count; CPU->tlb_messages_count--) {
type = CPU->tlb_messages[i].type;
asid = CPU->tlb_messages[i].asid;
page = CPU->tlb_messages[i].page;
count = CPU->tlb_messages[i].count;
 
switch (type) {
case TLB_INVL_ALL:
tlb_invalidate_all();
break;
case TLB_INVL_ASID:
tlb_invalidate_asid(asid);
break;
case TLB_INVL_PAGES:
ASSERT(count);
tlb_invalidate_pages(asid, page, count);
break;
default:
panic("Unknown type (%d).", type);
break;
}
if (type == TLB_INVL_ALL)
break;
}
spinlock_unlock(&CPU->lock);
CPU->tlb_active = 1;
}
 
#endif /* CONFIG_SMP */
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/backend_anon.c
0,0 → 1,224
/*
* Copyright (c) 2006 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 genericmm
* @{
*/
 
/**
* @file
* @brief Backend for anonymous memory address space areas.
*
*/
 
#include <mm/as.h>
#include <mm/page.h>
#include <genarch/mm/page_pt.h>
#include <genarch/mm/page_ht.h>
#include <mm/frame.h>
#include <mm/slab.h>
#include <synch/mutex.h>
#include <adt/list.h>
#include <adt/btree.h>
#include <errno.h>
#include <arch/types.h>
#include <align.h>
#include <arch.h>
 
#ifdef CONFIG_VIRT_IDX_DCACHE
#include <arch/mm/cache.h>
#endif
 
static int anon_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access);
static void anon_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame);
static void anon_share(as_area_t *area);
 
mem_backend_t anon_backend = {
.page_fault = anon_page_fault,
.frame_free = anon_frame_free,
.share = anon_share
};
 
/** Service a page fault in the anonymous memory address space area.
*
* The address space area and page tables must be already locked.
*
* @param area Pointer to the address space area.
* @param addr Faulting virtual address.
* @param access Access mode that caused the fault (i.e. read/write/exec).
*
* @return AS_PF_FAULT on failure (i.e. page fault) or AS_PF_OK on success (i.e.
* serviced).
*/
int anon_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access)
{
uintptr_t frame;
 
if (!as_area_check_access(area, access))
return AS_PF_FAULT;
 
if (area->sh_info) {
btree_node_t *leaf;
/*
* The area is shared, chances are that the mapping can be found
* in the pagemap of the address space area share info
* structure.
* In the case that the pagemap does not contain the respective
* mapping, a new frame is allocated and the mapping is created.
*/
mutex_lock(&area->sh_info->lock);
frame = (uintptr_t) btree_search(&area->sh_info->pagemap,
ALIGN_DOWN(addr, PAGE_SIZE) - area->base, &leaf);
if (!frame) {
bool allocate = true;
unsigned int i;
/*
* Zero can be returned as a valid frame address.
* Just a small workaround.
*/
for (i = 0; i < leaf->keys; i++) {
if (leaf->key[i] ==
ALIGN_DOWN(addr, PAGE_SIZE) - area->base) {
allocate = false;
break;
}
}
if (allocate) {
frame = (uintptr_t) frame_alloc(ONE_FRAME, 0);
memsetb((void *) PA2KA(frame), FRAME_SIZE, 0);
/*
* Insert the address of the newly allocated
* frame to the pagemap.
*/
btree_insert(&area->sh_info->pagemap,
ALIGN_DOWN(addr, PAGE_SIZE) - area->base,
(void *) frame, leaf);
}
}
frame_reference_add(ADDR2PFN(frame));
mutex_unlock(&area->sh_info->lock);
} else {
 
/*
* In general, there can be several reasons that
* can have caused this fault.
*
* - non-existent mapping: the area is an anonymous
* area (e.g. heap or stack) and so far has not been
* allocated a frame for the faulting page
*
* - non-present mapping: another possibility,
* currently not implemented, would be frame
* reuse; when this becomes a possibility,
* do not forget to distinguish between
* the different causes
*/
frame = (uintptr_t) frame_alloc(ONE_FRAME, 0);
memsetb((void *) PA2KA(frame), FRAME_SIZE, 0);
}
/*
* Map 'page' to 'frame'.
* Note that TLB shootdown is not attempted as only new information is
* being inserted into page tables.
*/
page_mapping_insert(AS, addr, frame, as_area_get_flags(area));
if (!used_space_insert(area, ALIGN_DOWN(addr, PAGE_SIZE), 1))
panic("Cannot insert used space.");
return AS_PF_OK;
}
 
/** Free a frame that is backed by the anonymous memory backend.
*
* The address space area and page tables must be already locked.
*
* @param area Ignored.
* @param page Virtual address of the page corresponding to the frame.
* @param frame Frame to be released.
*/
void anon_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame)
{
frame_free(frame);
}
 
/** Share the anonymous address space area.
*
* Sharing of anonymous area is done by duplicating its entire mapping
* to the pagemap. Page faults will primarily search for frames there.
*
* The address space and address space area must be already locked.
*
* @param area Address space area to be shared.
*/
void anon_share(as_area_t *area)
{
link_t *cur;
 
/*
* Copy used portions of the area to sh_info's page map.
*/
mutex_lock(&area->sh_info->lock);
for (cur = area->used_space.leaf_head.next;
cur != &area->used_space.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
uintptr_t base = node->key[i];
size_t count = (size_t) node->value[i];
unsigned int j;
for (j = 0; j < count; j++) {
pte_t *pte;
page_table_lock(area->as, false);
pte = page_mapping_find(area->as,
base + j * PAGE_SIZE);
ASSERT(pte && PTE_VALID(pte) &&
PTE_PRESENT(pte));
btree_insert(&area->sh_info->pagemap,
(base + j * PAGE_SIZE) - area->base,
(void *) PTE_GET_FRAME(pte), NULL);
page_table_unlock(area->as, false);
 
pfn_t pfn = ADDR2PFN(PTE_GET_FRAME(pte));
frame_reference_add(pfn);
}
 
}
}
mutex_unlock(&area->sh_info->lock);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/as.c
0,0 → 1,1958
/*
* Copyright (c) 2001-2006 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 genericmm
* @{
*/
 
/**
* @file
* @brief Address space related functions.
*
* This file contains address space manipulation functions.
* Roughly speaking, this is a higher-level client of
* Virtual Address Translation (VAT) subsystem.
*
* Functionality provided by this file allows one to
* create address spaces and create, resize and share
* address space areas.
*
* @see page.c
*
*/
 
#include <mm/as.h>
#include <arch/mm/as.h>
#include <mm/page.h>
#include <mm/frame.h>
#include <mm/slab.h>
#include <mm/tlb.h>
#include <arch/mm/page.h>
#include <genarch/mm/page_pt.h>
#include <genarch/mm/page_ht.h>
#include <mm/asid.h>
#include <arch/mm/asid.h>
#include <preemption.h>
#include <synch/spinlock.h>
#include <synch/mutex.h>
#include <adt/list.h>
#include <adt/btree.h>
#include <proc/task.h>
#include <proc/thread.h>
#include <arch/asm.h>
#include <panic.h>
#include <debug.h>
#include <print.h>
#include <memstr.h>
#include <macros.h>
#include <arch.h>
#include <errno.h>
#include <config.h>
#include <align.h>
#include <arch/types.h>
#include <syscall/copy.h>
#include <arch/interrupt.h>
 
#ifdef CONFIG_VIRT_IDX_DCACHE
#include <arch/mm/cache.h>
#endif /* CONFIG_VIRT_IDX_DCACHE */
 
/**
* Each architecture decides what functions will be used to carry out
* address space operations such as creating or locking page tables.
*/
as_operations_t *as_operations = NULL;
 
/**
* Slab for as_t objects.
*/
static slab_cache_t *as_slab;
 
/**
* This lock serializes access to the ASID subsystem.
* It protects:
* - inactive_as_with_asid_head list
* - as->asid for each as of the as_t type
* - asids_allocated counter
*/
SPINLOCK_INITIALIZE(asidlock);
 
/**
* This list contains address spaces that are not active on any
* processor and that have valid ASID.
*/
LIST_INITIALIZE(inactive_as_with_asid_head);
 
/** Kernel address space. */
as_t *AS_KERNEL = NULL;
 
static int area_flags_to_page_flags(int);
static as_area_t *find_area_and_lock(as_t *, uintptr_t);
static bool check_area_conflicts(as_t *, uintptr_t, size_t, as_area_t *);
static void sh_info_remove_reference(share_info_t *);
 
static int as_constructor(void *obj, int flags)
{
as_t *as = (as_t *) obj;
int rc;
 
link_initialize(&as->inactive_as_with_asid_link);
mutex_initialize(&as->lock, MUTEX_PASSIVE);
rc = as_constructor_arch(as, flags);
return rc;
}
 
static int as_destructor(void *obj)
{
as_t *as = (as_t *) obj;
 
return as_destructor_arch(as);
}
 
/** Initialize address space subsystem. */
void as_init(void)
{
as_arch_init();
 
as_slab = slab_cache_create("as_slab", sizeof(as_t), 0,
as_constructor, as_destructor, SLAB_CACHE_MAGDEFERRED);
AS_KERNEL = as_create(FLAG_AS_KERNEL);
if (!AS_KERNEL)
panic("Cannot create kernel address space.");
/* Make sure the kernel address space
* reference count never drops to zero.
*/
atomic_set(&AS_KERNEL->refcount, 1);
}
 
/** Create address space.
*
* @param flags Flags that influence the way in wich the address space
* is created.
*/
as_t *as_create(int flags)
{
as_t *as;
 
as = (as_t *) slab_alloc(as_slab, 0);
(void) as_create_arch(as, 0);
btree_create(&as->as_area_btree);
if (flags & FLAG_AS_KERNEL)
as->asid = ASID_KERNEL;
else
as->asid = ASID_INVALID;
atomic_set(&as->refcount, 0);
as->cpu_refcount = 0;
#ifdef AS_PAGE_TABLE
as->genarch.page_table = page_table_create(flags);
#else
page_table_create(flags);
#endif
return as;
}
 
/** Destroy adress space.
*
* When there are no tasks referencing this address space (i.e. its refcount is
* zero), the address space can be destroyed.
*
* We know that we don't hold any spinlock.
*
* @param as Address space to be destroyed.
*/
void as_destroy(as_t *as)
{
ipl_t ipl;
bool cond;
DEADLOCK_PROBE_INIT(p_asidlock);
 
ASSERT(atomic_get(&as->refcount) == 0);
/*
* Since there is no reference to this area,
* it is safe not to lock its mutex.
*/
 
/*
* We need to avoid deadlock between TLB shootdown and asidlock.
* We therefore try to take asid conditionally and if we don't succeed,
* we enable interrupts and try again. This is done while preemption is
* disabled to prevent nested context switches. We also depend on the
* fact that so far no spinlocks are held.
*/
preemption_disable();
ipl = interrupts_read();
retry:
interrupts_disable();
if (!spinlock_trylock(&asidlock)) {
interrupts_enable();
DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
goto retry;
}
preemption_enable(); /* Interrupts disabled, enable preemption */
if (as->asid != ASID_INVALID && as != AS_KERNEL) {
if (as != AS && as->cpu_refcount == 0)
list_remove(&as->inactive_as_with_asid_link);
asid_put(as->asid);
}
spinlock_unlock(&asidlock);
 
/*
* Destroy address space areas of the address space.
* The B+tree must be walked carefully because it is
* also being destroyed.
*/
for (cond = true; cond; ) {
btree_node_t *node;
 
ASSERT(!list_empty(&as->as_area_btree.leaf_head));
node = list_get_instance(as->as_area_btree.leaf_head.next,
btree_node_t, leaf_link);
 
if ((cond = node->keys)) {
as_area_destroy(as, node->key[0]);
}
}
 
btree_destroy(&as->as_area_btree);
#ifdef AS_PAGE_TABLE
page_table_destroy(as->genarch.page_table);
#else
page_table_destroy(NULL);
#endif
 
interrupts_restore(ipl);
 
slab_free(as_slab, as);
}
 
/** Create address space area of common attributes.
*
* The created address space area is added to the target address space.
*
* @param as Target address space.
* @param flags Flags of the area memory.
* @param size Size of area.
* @param base Base address of area.
* @param attrs Attributes of the area.
* @param backend Address space area backend. NULL if no backend is used.
* @param backend_data NULL or a pointer to an array holding two void *.
*
* @return Address space area on success or NULL on failure.
*/
as_area_t *
as_area_create(as_t *as, int flags, size_t size, uintptr_t base, int attrs,
mem_backend_t *backend, mem_backend_data_t *backend_data)
{
ipl_t ipl;
as_area_t *a;
if (base % PAGE_SIZE)
return NULL;
 
if (!size)
return NULL;
 
/* Writeable executable areas are not supported. */
if ((flags & AS_AREA_EXEC) && (flags & AS_AREA_WRITE))
return NULL;
ipl = interrupts_disable();
mutex_lock(&as->lock);
if (!check_area_conflicts(as, base, size, NULL)) {
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return NULL;
}
a = (as_area_t *) malloc(sizeof(as_area_t), 0);
 
mutex_initialize(&a->lock, MUTEX_PASSIVE);
a->as = as;
a->flags = flags;
a->attributes = attrs;
a->pages = SIZE2FRAMES(size);
a->base = base;
a->sh_info = NULL;
a->backend = backend;
if (backend_data)
a->backend_data = *backend_data;
else
memsetb(&a->backend_data, sizeof(a->backend_data), 0);
 
btree_create(&a->used_space);
btree_insert(&as->as_area_btree, base, (void *) a, NULL);
 
mutex_unlock(&as->lock);
interrupts_restore(ipl);
 
return a;
}
 
/** Find address space area and change it.
*
* @param as Address space.
* @param address Virtual address belonging to the area to be changed.
* Must be page-aligned.
* @param size New size of the virtual memory block starting at
* address.
* @param flags Flags influencing the remap operation. Currently unused.
*
* @return Zero on success or a value from @ref errno.h otherwise.
*/
int as_area_resize(as_t *as, uintptr_t address, size_t size, int flags)
{
as_area_t *area;
ipl_t ipl;
size_t pages;
ipl = interrupts_disable();
mutex_lock(&as->lock);
/*
* Locate the area.
*/
area = find_area_and_lock(as, address);
if (!area) {
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return ENOENT;
}
 
if (area->backend == &phys_backend) {
/*
* Remapping of address space areas associated
* with memory mapped devices is not supported.
*/
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return ENOTSUP;
}
if (area->sh_info) {
/*
* Remapping of shared address space areas
* is not supported.
*/
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return ENOTSUP;
}
 
pages = SIZE2FRAMES((address - area->base) + size);
if (!pages) {
/*
* Zero size address space areas are not allowed.
*/
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return EPERM;
}
if (pages < area->pages) {
bool cond;
uintptr_t start_free = area->base + pages * PAGE_SIZE;
 
/*
* Shrinking the area.
* No need to check for overlaps.
*/
 
/*
* Start TLB shootdown sequence.
*/
tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base +
pages * PAGE_SIZE, area->pages - pages);
 
/*
* Remove frames belonging to used space starting from
* the highest addresses downwards until an overlap with
* the resized address space area is found. Note that this
* is also the right way to remove part of the used_space
* B+tree leaf list.
*/
for (cond = true; cond;) {
btree_node_t *node;
ASSERT(!list_empty(&area->used_space.leaf_head));
node =
list_get_instance(area->used_space.leaf_head.prev,
btree_node_t, leaf_link);
if ((cond = (bool) node->keys)) {
uintptr_t b = node->key[node->keys - 1];
size_t c =
(size_t) node->value[node->keys - 1];
unsigned int i = 0;
if (overlaps(b, c * PAGE_SIZE, area->base,
pages * PAGE_SIZE)) {
if (b + c * PAGE_SIZE <= start_free) {
/*
* The whole interval fits
* completely in the resized
* address space area.
*/
break;
}
/*
* Part of the interval corresponding
* to b and c overlaps with the resized
* address space area.
*/
cond = false; /* we are almost done */
i = (start_free - b) >> PAGE_WIDTH;
if (!used_space_remove(area, start_free,
c - i))
panic("Cannot remove used "
"space.");
} else {
/*
* The interval of used space can be
* completely removed.
*/
if (!used_space_remove(area, b, c))
panic("Cannot remove used "
"space.");
}
for (; i < c; i++) {
pte_t *pte;
page_table_lock(as, false);
pte = page_mapping_find(as, b +
i * PAGE_SIZE);
ASSERT(pte && PTE_VALID(pte) &&
PTE_PRESENT(pte));
if (area->backend &&
area->backend->frame_free) {
area->backend->frame_free(area,
b + i * PAGE_SIZE,
PTE_GET_FRAME(pte));
}
page_mapping_remove(as, b +
i * PAGE_SIZE);
page_table_unlock(as, false);
}
}
}
 
/*
* Finish TLB shootdown sequence.
*/
 
tlb_invalidate_pages(as->asid, area->base + pages * PAGE_SIZE,
area->pages - pages);
/*
* Invalidate software translation caches (e.g. TSB on sparc64).
*/
as_invalidate_translation_cache(as, area->base +
pages * PAGE_SIZE, area->pages - pages);
tlb_shootdown_finalize();
} else {
/*
* Growing the area.
* Check for overlaps with other address space areas.
*/
if (!check_area_conflicts(as, address, pages * PAGE_SIZE,
area)) {
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return EADDRNOTAVAIL;
}
}
 
area->pages = pages;
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
 
return 0;
}
 
/** Destroy address space area.
*
* @param as Address space.
* @param address Address within the area to be deleted.
*
* @return Zero on success or a value from @ref errno.h on failure.
*/
int as_area_destroy(as_t *as, uintptr_t address)
{
as_area_t *area;
uintptr_t base;
link_t *cur;
ipl_t ipl;
 
ipl = interrupts_disable();
mutex_lock(&as->lock);
 
area = find_area_and_lock(as, address);
if (!area) {
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return ENOENT;
}
 
base = area->base;
 
/*
* Start TLB shootdown sequence.
*/
tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base, area->pages);
 
/*
* Visit only the pages mapped by used_space B+tree.
*/
for (cur = area->used_space.leaf_head.next;
cur != &area->used_space.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
uintptr_t b = node->key[i];
size_t j;
pte_t *pte;
for (j = 0; j < (size_t) node->value[i]; j++) {
page_table_lock(as, false);
pte = page_mapping_find(as, b + j * PAGE_SIZE);
ASSERT(pte && PTE_VALID(pte) &&
PTE_PRESENT(pte));
if (area->backend &&
area->backend->frame_free) {
area->backend->frame_free(area, b +
j * PAGE_SIZE, PTE_GET_FRAME(pte));
}
page_mapping_remove(as, b + j * PAGE_SIZE);
page_table_unlock(as, false);
}
}
}
 
/*
* Finish TLB shootdown sequence.
*/
 
tlb_invalidate_pages(as->asid, area->base, area->pages);
/*
* Invalidate potential software translation caches (e.g. TSB on
* sparc64).
*/
as_invalidate_translation_cache(as, area->base, area->pages);
tlb_shootdown_finalize();
btree_destroy(&area->used_space);
 
area->attributes |= AS_AREA_ATTR_PARTIAL;
if (area->sh_info)
sh_info_remove_reference(area->sh_info);
mutex_unlock(&area->lock);
 
/*
* Remove the empty area from address space.
*/
btree_remove(&as->as_area_btree, base, NULL);
free(area);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return 0;
}
 
/** Share address space area with another or the same address space.
*
* Address space area mapping is shared with a new address space area.
* If the source address space area has not been shared so far,
* a new sh_info is created. The new address space area simply gets the
* sh_info of the source area. The process of duplicating the
* mapping is done through the backend share function.
*
* @param src_as Pointer to source address space.
* @param src_base Base address of the source address space area.
* @param acc_size Expected size of the source area.
* @param dst_as Pointer to destination address space.
* @param dst_base Target base address.
* @param dst_flags_mask Destination address space area flags mask.
*
* @return Zero on success or ENOENT if there is no such task or if
* there is no such address space area, EPERM if there was
* a problem in accepting the area or ENOMEM if there was a
* problem in allocating destination address space area.
* ENOTSUP is returned if the address space area backend
* does not support sharing.
*/
int as_area_share(as_t *src_as, uintptr_t src_base, size_t acc_size,
as_t *dst_as, uintptr_t dst_base, int dst_flags_mask)
{
ipl_t ipl;
int src_flags;
size_t src_size;
as_area_t *src_area, *dst_area;
share_info_t *sh_info;
mem_backend_t *src_backend;
mem_backend_data_t src_backend_data;
ipl = interrupts_disable();
mutex_lock(&src_as->lock);
src_area = find_area_and_lock(src_as, src_base);
if (!src_area) {
/*
* Could not find the source address space area.
*/
mutex_unlock(&src_as->lock);
interrupts_restore(ipl);
return ENOENT;
}
 
if (!src_area->backend || !src_area->backend->share) {
/*
* There is no backend or the backend does not
* know how to share the area.
*/
mutex_unlock(&src_area->lock);
mutex_unlock(&src_as->lock);
interrupts_restore(ipl);
return ENOTSUP;
}
src_size = src_area->pages * PAGE_SIZE;
src_flags = src_area->flags;
src_backend = src_area->backend;
src_backend_data = src_area->backend_data;
 
/* Share the cacheable flag from the original mapping */
if (src_flags & AS_AREA_CACHEABLE)
dst_flags_mask |= AS_AREA_CACHEABLE;
 
if (src_size != acc_size ||
(src_flags & dst_flags_mask) != dst_flags_mask) {
mutex_unlock(&src_area->lock);
mutex_unlock(&src_as->lock);
interrupts_restore(ipl);
return EPERM;
}
 
/*
* Now we are committed to sharing the area.
* First, prepare the area for sharing.
* Then it will be safe to unlock it.
*/
sh_info = src_area->sh_info;
if (!sh_info) {
sh_info = (share_info_t *) malloc(sizeof(share_info_t), 0);
mutex_initialize(&sh_info->lock, MUTEX_PASSIVE);
sh_info->refcount = 2;
btree_create(&sh_info->pagemap);
src_area->sh_info = sh_info;
/*
* Call the backend to setup sharing.
*/
src_area->backend->share(src_area);
} else {
mutex_lock(&sh_info->lock);
sh_info->refcount++;
mutex_unlock(&sh_info->lock);
}
 
mutex_unlock(&src_area->lock);
mutex_unlock(&src_as->lock);
 
/*
* Create copy of the source address space area.
* The destination area is created with AS_AREA_ATTR_PARTIAL
* attribute set which prevents race condition with
* preliminary as_page_fault() calls.
* The flags of the source area are masked against dst_flags_mask
* to support sharing in less privileged mode.
*/
dst_area = as_area_create(dst_as, dst_flags_mask, src_size, dst_base,
AS_AREA_ATTR_PARTIAL, src_backend, &src_backend_data);
if (!dst_area) {
/*
* Destination address space area could not be created.
*/
sh_info_remove_reference(sh_info);
interrupts_restore(ipl);
return ENOMEM;
}
 
/*
* Now the destination address space area has been
* fully initialized. Clear the AS_AREA_ATTR_PARTIAL
* attribute and set the sh_info.
*/
mutex_lock(&dst_as->lock);
mutex_lock(&dst_area->lock);
dst_area->attributes &= ~AS_AREA_ATTR_PARTIAL;
dst_area->sh_info = sh_info;
mutex_unlock(&dst_area->lock);
mutex_unlock(&dst_as->lock);
 
interrupts_restore(ipl);
return 0;
}
 
/** Check access mode for address space area.
*
* The address space area must be locked prior to this call.
*
* @param area Address space area.
* @param access Access mode.
*
* @return False if access violates area's permissions, true
* otherwise.
*/
bool as_area_check_access(as_area_t *area, pf_access_t access)
{
int flagmap[] = {
[PF_ACCESS_READ] = AS_AREA_READ,
[PF_ACCESS_WRITE] = AS_AREA_WRITE,
[PF_ACCESS_EXEC] = AS_AREA_EXEC
};
 
if (!(area->flags & flagmap[access]))
return false;
return true;
}
 
/** Change adress space area flags.
*
* The idea is to have the same data, but with a different access mode.
* This is needed e.g. for writing code into memory and then executing it.
* In order for this to work properly, this may copy the data
* into private anonymous memory (unless it's already there).
*
* @param as Address space.
* @param flags Flags of the area memory.
* @param address Address within the area to be changed.
*
* @return Zero on success or a value from @ref errno.h on failure.
*
*/
int as_area_change_flags(as_t *as, int flags, uintptr_t address)
{
as_area_t *area;
uintptr_t base;
link_t *cur;
ipl_t ipl;
int page_flags;
uintptr_t *old_frame;
size_t frame_idx;
size_t used_pages;
/* Flags for the new memory mapping */
page_flags = area_flags_to_page_flags(flags);
 
ipl = interrupts_disable();
mutex_lock(&as->lock);
 
area = find_area_and_lock(as, address);
if (!area) {
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return ENOENT;
}
 
if ((area->sh_info) || (area->backend != &anon_backend)) {
/* Copying shared areas not supported yet */
/* Copying non-anonymous memory not supported yet */
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
return ENOTSUP;
}
 
base = area->base;
 
/*
* Compute total number of used pages in the used_space B+tree
*/
used_pages = 0;
 
for (cur = area->used_space.leaf_head.next;
cur != &area->used_space.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
used_pages += (size_t) node->value[i];
}
}
 
/* An array for storing frame numbers */
old_frame = malloc(used_pages * sizeof(uintptr_t), 0);
 
/*
* Start TLB shootdown sequence.
*/
tlb_shootdown_start(TLB_INVL_PAGES, as->asid, area->base, area->pages);
 
/*
* Remove used pages from page tables and remember their frame
* numbers.
*/
frame_idx = 0;
 
for (cur = area->used_space.leaf_head.next;
cur != &area->used_space.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
uintptr_t b = node->key[i];
size_t j;
pte_t *pte;
for (j = 0; j < (size_t) node->value[i]; j++) {
page_table_lock(as, false);
pte = page_mapping_find(as, b + j * PAGE_SIZE);
ASSERT(pte && PTE_VALID(pte) &&
PTE_PRESENT(pte));
old_frame[frame_idx++] = PTE_GET_FRAME(pte);
 
/* Remove old mapping */
page_mapping_remove(as, b + j * PAGE_SIZE);
page_table_unlock(as, false);
}
}
}
 
/*
* Finish TLB shootdown sequence.
*/
 
tlb_invalidate_pages(as->asid, area->base, area->pages);
/*
* Invalidate potential software translation caches (e.g. TSB on
* sparc64).
*/
as_invalidate_translation_cache(as, area->base, area->pages);
tlb_shootdown_finalize();
 
/*
* Set the new flags.
*/
area->flags = flags;
 
/*
* Map pages back in with new flags. This step is kept separate
* so that the memory area could not be accesed with both the old and
* the new flags at once.
*/
frame_idx = 0;
 
for (cur = area->used_space.leaf_head.next;
cur != &area->used_space.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
uintptr_t b = node->key[i];
size_t j;
for (j = 0; j < (size_t) node->value[i]; j++) {
page_table_lock(as, false);
 
/* Insert the new mapping */
page_mapping_insert(as, b + j * PAGE_SIZE,
old_frame[frame_idx++], page_flags);
 
page_table_unlock(as, false);
}
}
}
 
free(old_frame);
 
mutex_unlock(&area->lock);
mutex_unlock(&as->lock);
interrupts_restore(ipl);
 
return 0;
}
 
 
/** Handle page fault within the current address space.
*
* This is the high-level page fault handler. It decides whether the page fault
* can be resolved by any backend and if so, it invokes the backend to resolve
* the page fault.
*
* Interrupts are assumed disabled.
*
* @param page Faulting page.
* @param access Access mode that caused the page fault (i.e.
* read/write/exec).
* @param istate Pointer to the interrupted state.
*
* @return AS_PF_FAULT on page fault, AS_PF_OK on success or
* AS_PF_DEFER if the fault was caused by copy_to_uspace()
* or copy_from_uspace().
*/
int as_page_fault(uintptr_t page, pf_access_t access, istate_t *istate)
{
pte_t *pte;
as_area_t *area;
if (!THREAD)
return AS_PF_FAULT;
ASSERT(AS);
 
mutex_lock(&AS->lock);
area = find_area_and_lock(AS, page);
if (!area) {
/*
* No area contained mapping for 'page'.
* Signal page fault to low-level handler.
*/
mutex_unlock(&AS->lock);
goto page_fault;
}
 
if (area->attributes & AS_AREA_ATTR_PARTIAL) {
/*
* The address space area is not fully initialized.
* Avoid possible race by returning error.
*/
mutex_unlock(&area->lock);
mutex_unlock(&AS->lock);
goto page_fault;
}
 
if (!area->backend || !area->backend->page_fault) {
/*
* The address space area is not backed by any backend
* or the backend cannot handle page faults.
*/
mutex_unlock(&area->lock);
mutex_unlock(&AS->lock);
goto page_fault;
}
 
page_table_lock(AS, false);
/*
* To avoid race condition between two page faults on the same address,
* we need to make sure the mapping has not been already inserted.
*/
if ((pte = page_mapping_find(AS, page))) {
if (PTE_PRESENT(pte)) {
if (((access == PF_ACCESS_READ) && PTE_READABLE(pte)) ||
(access == PF_ACCESS_WRITE && PTE_WRITABLE(pte)) ||
(access == PF_ACCESS_EXEC && PTE_EXECUTABLE(pte))) {
page_table_unlock(AS, false);
mutex_unlock(&area->lock);
mutex_unlock(&AS->lock);
return AS_PF_OK;
}
}
}
/*
* Resort to the backend page fault handler.
*/
if (area->backend->page_fault(area, page, access) != AS_PF_OK) {
page_table_unlock(AS, false);
mutex_unlock(&area->lock);
mutex_unlock(&AS->lock);
goto page_fault;
}
page_table_unlock(AS, false);
mutex_unlock(&area->lock);
mutex_unlock(&AS->lock);
return AS_PF_OK;
 
page_fault:
if (THREAD->in_copy_from_uspace) {
THREAD->in_copy_from_uspace = false;
istate_set_retaddr(istate,
(uintptr_t) &memcpy_from_uspace_failover_address);
} else if (THREAD->in_copy_to_uspace) {
THREAD->in_copy_to_uspace = false;
istate_set_retaddr(istate,
(uintptr_t) &memcpy_to_uspace_failover_address);
} else {
return AS_PF_FAULT;
}
 
return AS_PF_DEFER;
}
 
/** Switch address spaces.
*
* Note that this function cannot sleep as it is essentially a part of
* scheduling. Sleeping here would lead to deadlock on wakeup. Another
* thing which is forbidden in this context is locking the address space.
*
* When this function is enetered, no spinlocks may be held.
*
* @param old Old address space or NULL.
* @param new New address space.
*/
void as_switch(as_t *old_as, as_t *new_as)
{
DEADLOCK_PROBE_INIT(p_asidlock);
preemption_disable();
retry:
(void) interrupts_disable();
if (!spinlock_trylock(&asidlock)) {
/*
* Avoid deadlock with TLB shootdown.
* We can enable interrupts here because
* preemption is disabled. We should not be
* holding any other lock.
*/
(void) interrupts_enable();
DEADLOCK_PROBE(p_asidlock, DEADLOCK_THRESHOLD);
goto retry;
}
preemption_enable();
 
/*
* First, take care of the old address space.
*/
if (old_as) {
ASSERT(old_as->cpu_refcount);
if((--old_as->cpu_refcount == 0) && (old_as != AS_KERNEL)) {
/*
* The old address space is no longer active on
* any processor. It can be appended to the
* list of inactive address spaces with assigned
* ASID.
*/
ASSERT(old_as->asid != ASID_INVALID);
list_append(&old_as->inactive_as_with_asid_link,
&inactive_as_with_asid_head);
}
 
/*
* Perform architecture-specific tasks when the address space
* is being removed from the CPU.
*/
as_deinstall_arch(old_as);
}
 
/*
* Second, prepare the new address space.
*/
if ((new_as->cpu_refcount++ == 0) && (new_as != AS_KERNEL)) {
if (new_as->asid != ASID_INVALID)
list_remove(&new_as->inactive_as_with_asid_link);
else
new_as->asid = asid_get();
}
#ifdef AS_PAGE_TABLE
SET_PTL0_ADDRESS(new_as->genarch.page_table);
#endif
/*
* Perform architecture-specific steps.
* (e.g. write ASID to hardware register etc.)
*/
as_install_arch(new_as);
 
spinlock_unlock(&asidlock);
AS = new_as;
}
 
/** Convert address space area flags to page flags.
*
* @param aflags Flags of some address space area.
*
* @return Flags to be passed to page_mapping_insert().
*/
int area_flags_to_page_flags(int aflags)
{
int flags;
 
flags = PAGE_USER | PAGE_PRESENT;
if (aflags & AS_AREA_READ)
flags |= PAGE_READ;
if (aflags & AS_AREA_WRITE)
flags |= PAGE_WRITE;
if (aflags & AS_AREA_EXEC)
flags |= PAGE_EXEC;
if (aflags & AS_AREA_CACHEABLE)
flags |= PAGE_CACHEABLE;
return flags;
}
 
/** Compute flags for virtual address translation subsytem.
*
* The address space area must be locked.
* Interrupts must be disabled.
*
* @param a Address space area.
*
* @return Flags to be used in page_mapping_insert().
*/
int as_area_get_flags(as_area_t *a)
{
return area_flags_to_page_flags(a->flags);
}
 
/** Create page table.
*
* Depending on architecture, create either address space private or global page
* table.
*
* @param flags Flags saying whether the page table is for the kernel
* address space.
*
* @return First entry of the page table.
*/
pte_t *page_table_create(int flags)
{
ASSERT(as_operations);
ASSERT(as_operations->page_table_create);
return as_operations->page_table_create(flags);
}
 
/** Destroy page table.
*
* Destroy page table in architecture specific way.
*
* @param page_table Physical address of PTL0.
*/
void page_table_destroy(pte_t *page_table)
{
ASSERT(as_operations);
ASSERT(as_operations->page_table_destroy);
as_operations->page_table_destroy(page_table);
}
 
/** Lock page table.
*
* This function should be called before any page_mapping_insert(),
* page_mapping_remove() and page_mapping_find().
*
* Locking order is such that address space areas must be locked
* prior to this call. Address space can be locked prior to this
* call in which case the lock argument is false.
*
* @param as Address space.
* @param lock If false, do not attempt to lock as->lock.
*/
void page_table_lock(as_t *as, bool lock)
{
ASSERT(as_operations);
ASSERT(as_operations->page_table_lock);
as_operations->page_table_lock(as, lock);
}
 
/** Unlock page table.
*
* @param as Address space.
* @param unlock If false, do not attempt to unlock as->lock.
*/
void page_table_unlock(as_t *as, bool unlock)
{
ASSERT(as_operations);
ASSERT(as_operations->page_table_unlock);
as_operations->page_table_unlock(as, unlock);
}
 
 
/** Find address space area and lock it.
*
* The address space must be locked and interrupts must be disabled.
*
* @param as Address space.
* @param va Virtual address.
*
* @return Locked address space area containing va on success or
* NULL on failure.
*/
as_area_t *find_area_and_lock(as_t *as, uintptr_t va)
{
as_area_t *a;
btree_node_t *leaf, *lnode;
unsigned int i;
a = (as_area_t *) btree_search(&as->as_area_btree, va, &leaf);
if (a) {
/* va is the base address of an address space area */
mutex_lock(&a->lock);
return a;
}
/*
* Search the leaf node and the righmost record of its left neighbour
* to find out whether this is a miss or va belongs to an address
* space area found there.
*/
/* First, search the leaf node itself. */
for (i = 0; i < leaf->keys; i++) {
a = (as_area_t *) leaf->value[i];
mutex_lock(&a->lock);
if ((a->base <= va) && (va < a->base + a->pages * PAGE_SIZE)) {
return a;
}
mutex_unlock(&a->lock);
}
 
/*
* Second, locate the left neighbour and test its last record.
* Because of its position in the B+tree, it must have base < va.
*/
lnode = btree_leaf_node_left_neighbour(&as->as_area_btree, leaf);
if (lnode) {
a = (as_area_t *) lnode->value[lnode->keys - 1];
mutex_lock(&a->lock);
if (va < a->base + a->pages * PAGE_SIZE) {
return a;
}
mutex_unlock(&a->lock);
}
 
return NULL;
}
 
/** Check area conflicts with other areas.
*
* The address space must be locked and interrupts must be disabled.
*
* @param as Address space.
* @param va Starting virtual address of the area being tested.
* @param size Size of the area being tested.
* @param avoid_area Do not touch this area.
*
* @return True if there is no conflict, false otherwise.
*/
bool
check_area_conflicts(as_t *as, uintptr_t va, size_t size, as_area_t *avoid_area)
{
as_area_t *a;
btree_node_t *leaf, *node;
unsigned int i;
/*
* We don't want any area to have conflicts with NULL page.
*/
if (overlaps(va, size, NULL, PAGE_SIZE))
return false;
/*
* The leaf node is found in O(log n), where n is proportional to
* the number of address space areas belonging to as.
* The check for conflicts is then attempted on the rightmost
* record in the left neighbour, the leftmost record in the right
* neighbour and all records in the leaf node itself.
*/
if ((a = (as_area_t *) btree_search(&as->as_area_btree, va, &leaf))) {
if (a != avoid_area)
return false;
}
/* First, check the two border cases. */
if ((node = btree_leaf_node_left_neighbour(&as->as_area_btree, leaf))) {
a = (as_area_t *) node->value[node->keys - 1];
mutex_lock(&a->lock);
if (overlaps(va, size, a->base, a->pages * PAGE_SIZE)) {
mutex_unlock(&a->lock);
return false;
}
mutex_unlock(&a->lock);
}
node = btree_leaf_node_right_neighbour(&as->as_area_btree, leaf);
if (node) {
a = (as_area_t *) node->value[0];
mutex_lock(&a->lock);
if (overlaps(va, size, a->base, a->pages * PAGE_SIZE)) {
mutex_unlock(&a->lock);
return false;
}
mutex_unlock(&a->lock);
}
/* Second, check the leaf node. */
for (i = 0; i < leaf->keys; i++) {
a = (as_area_t *) leaf->value[i];
if (a == avoid_area)
continue;
mutex_lock(&a->lock);
if (overlaps(va, size, a->base, a->pages * PAGE_SIZE)) {
mutex_unlock(&a->lock);
return false;
}
mutex_unlock(&a->lock);
}
 
/*
* So far, the area does not conflict with other areas.
* Check if it doesn't conflict with kernel address space.
*/
if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
return !overlaps(va, size,
KERNEL_ADDRESS_SPACE_START,
KERNEL_ADDRESS_SPACE_END - KERNEL_ADDRESS_SPACE_START);
}
 
return true;
}
 
/** Return size of the address space area with given base.
*
* @param base Arbitrary address insede the address space area.
*
* @return Size of the address space area in bytes or zero if it
* does not exist.
*/
size_t as_area_get_size(uintptr_t base)
{
ipl_t ipl;
as_area_t *src_area;
size_t size;
 
ipl = interrupts_disable();
src_area = find_area_and_lock(AS, base);
if (src_area) {
size = src_area->pages * PAGE_SIZE;
mutex_unlock(&src_area->lock);
} else {
size = 0;
}
interrupts_restore(ipl);
return size;
}
 
/** Mark portion of address space area as used.
*
* The address space area must be already locked.
*
* @param a Address space area.
* @param page First page to be marked.
* @param count Number of page to be marked.
*
* @return Zero on failure and non-zero on success.
*/
int used_space_insert(as_area_t *a, uintptr_t page, size_t count)
{
btree_node_t *leaf, *node;
size_t pages;
unsigned int i;
 
ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
ASSERT(count);
 
pages = (size_t) btree_search(&a->used_space, page, &leaf);
if (pages) {
/*
* We hit the beginning of some used space.
*/
return 0;
}
 
if (!leaf->keys) {
btree_insert(&a->used_space, page, (void *) count, leaf);
return 1;
}
 
node = btree_leaf_node_left_neighbour(&a->used_space, leaf);
if (node) {
uintptr_t left_pg = node->key[node->keys - 1];
uintptr_t right_pg = leaf->key[0];
size_t left_cnt = (size_t) node->value[node->keys - 1];
size_t right_cnt = (size_t) leaf->value[0];
/*
* Examine the possibility that the interval fits
* somewhere between the rightmost interval of
* the left neigbour and the first interval of the leaf.
*/
if (page >= right_pg) {
/* Do nothing. */
} else if (overlaps(page, count * PAGE_SIZE, left_pg,
left_cnt * PAGE_SIZE)) {
/* The interval intersects with the left interval. */
return 0;
} else if (overlaps(page, count * PAGE_SIZE, right_pg,
right_cnt * PAGE_SIZE)) {
/* The interval intersects with the right interval. */
return 0;
} else if ((page == left_pg + left_cnt * PAGE_SIZE) &&
(page + count * PAGE_SIZE == right_pg)) {
/*
* The interval can be added by merging the two already
* present intervals.
*/
node->value[node->keys - 1] += count + right_cnt;
btree_remove(&a->used_space, right_pg, leaf);
return 1;
} else if (page == left_pg + left_cnt * PAGE_SIZE) {
/*
* The interval can be added by simply growing the left
* interval.
*/
node->value[node->keys - 1] += count;
return 1;
} else if (page + count * PAGE_SIZE == right_pg) {
/*
* The interval can be addded by simply moving base of
* the right interval down and increasing its size
* accordingly.
*/
leaf->value[0] += count;
leaf->key[0] = page;
return 1;
} else {
/*
* The interval is between both neigbouring intervals,
* but cannot be merged with any of them.
*/
btree_insert(&a->used_space, page, (void *) count,
leaf);
return 1;
}
} else if (page < leaf->key[0]) {
uintptr_t right_pg = leaf->key[0];
size_t right_cnt = (size_t) leaf->value[0];
/*
* Investigate the border case in which the left neighbour does
* not exist but the interval fits from the left.
*/
if (overlaps(page, count * PAGE_SIZE, right_pg,
right_cnt * PAGE_SIZE)) {
/* The interval intersects with the right interval. */
return 0;
} else if (page + count * PAGE_SIZE == right_pg) {
/*
* The interval can be added by moving the base of the
* right interval down and increasing its size
* accordingly.
*/
leaf->key[0] = page;
leaf->value[0] += count;
return 1;
} else {
/*
* The interval doesn't adjoin with the right interval.
* It must be added individually.
*/
btree_insert(&a->used_space, page, (void *) count,
leaf);
return 1;
}
}
 
node = btree_leaf_node_right_neighbour(&a->used_space, leaf);
if (node) {
uintptr_t left_pg = leaf->key[leaf->keys - 1];
uintptr_t right_pg = node->key[0];
size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
size_t right_cnt = (size_t) node->value[0];
/*
* Examine the possibility that the interval fits
* somewhere between the leftmost interval of
* the right neigbour and the last interval of the leaf.
*/
 
if (page < left_pg) {
/* Do nothing. */
} else if (overlaps(page, count * PAGE_SIZE, left_pg,
left_cnt * PAGE_SIZE)) {
/* The interval intersects with the left interval. */
return 0;
} else if (overlaps(page, count * PAGE_SIZE, right_pg,
right_cnt * PAGE_SIZE)) {
/* The interval intersects with the right interval. */
return 0;
} else if ((page == left_pg + left_cnt * PAGE_SIZE) &&
(page + count * PAGE_SIZE == right_pg)) {
/*
* The interval can be added by merging the two already
* present intervals.
* */
leaf->value[leaf->keys - 1] += count + right_cnt;
btree_remove(&a->used_space, right_pg, node);
return 1;
} else if (page == left_pg + left_cnt * PAGE_SIZE) {
/*
* The interval can be added by simply growing the left
* interval.
* */
leaf->value[leaf->keys - 1] += count;
return 1;
} else if (page + count * PAGE_SIZE == right_pg) {
/*
* The interval can be addded by simply moving base of
* the right interval down and increasing its size
* accordingly.
*/
node->value[0] += count;
node->key[0] = page;
return 1;
} else {
/*
* The interval is between both neigbouring intervals,
* but cannot be merged with any of them.
*/
btree_insert(&a->used_space, page, (void *) count,
leaf);
return 1;
}
} else if (page >= leaf->key[leaf->keys - 1]) {
uintptr_t left_pg = leaf->key[leaf->keys - 1];
size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
/*
* Investigate the border case in which the right neighbour
* does not exist but the interval fits from the right.
*/
if (overlaps(page, count * PAGE_SIZE, left_pg,
left_cnt * PAGE_SIZE)) {
/* The interval intersects with the left interval. */
return 0;
} else if (left_pg + left_cnt * PAGE_SIZE == page) {
/*
* The interval can be added by growing the left
* interval.
*/
leaf->value[leaf->keys - 1] += count;
return 1;
} else {
/*
* The interval doesn't adjoin with the left interval.
* It must be added individually.
*/
btree_insert(&a->used_space, page, (void *) count,
leaf);
return 1;
}
}
/*
* Note that if the algorithm made it thus far, the interval can fit
* only between two other intervals of the leaf. The two border cases
* were already resolved.
*/
for (i = 1; i < leaf->keys; i++) {
if (page < leaf->key[i]) {
uintptr_t left_pg = leaf->key[i - 1];
uintptr_t right_pg = leaf->key[i];
size_t left_cnt = (size_t) leaf->value[i - 1];
size_t right_cnt = (size_t) leaf->value[i];
 
/*
* The interval fits between left_pg and right_pg.
*/
 
if (overlaps(page, count * PAGE_SIZE, left_pg,
left_cnt * PAGE_SIZE)) {
/*
* The interval intersects with the left
* interval.
*/
return 0;
} else if (overlaps(page, count * PAGE_SIZE, right_pg,
right_cnt * PAGE_SIZE)) {
/*
* The interval intersects with the right
* interval.
*/
return 0;
} else if ((page == left_pg + left_cnt * PAGE_SIZE) &&
(page + count * PAGE_SIZE == right_pg)) {
/*
* The interval can be added by merging the two
* already present intervals.
*/
leaf->value[i - 1] += count + right_cnt;
btree_remove(&a->used_space, right_pg, leaf);
return 1;
} else if (page == left_pg + left_cnt * PAGE_SIZE) {
/*
* The interval can be added by simply growing
* the left interval.
*/
leaf->value[i - 1] += count;
return 1;
} else if (page + count * PAGE_SIZE == right_pg) {
/*
* The interval can be addded by simply moving
* base of the right interval down and
* increasing its size accordingly.
*/
leaf->value[i] += count;
leaf->key[i] = page;
return 1;
} else {
/*
* The interval is between both neigbouring
* intervals, but cannot be merged with any of
* them.
*/
btree_insert(&a->used_space, page,
(void *) count, leaf);
return 1;
}
}
}
 
panic("Inconsistency detected while adding %" PRIs " pages of used "
"space at %p.", count, page);
}
 
/** Mark portion of address space area as unused.
*
* The address space area must be already locked.
*
* @param a Address space area.
* @param page First page to be marked.
* @param count Number of page to be marked.
*
* @return Zero on failure and non-zero on success.
*/
int used_space_remove(as_area_t *a, uintptr_t page, size_t count)
{
btree_node_t *leaf, *node;
size_t pages;
unsigned int i;
 
ASSERT(page == ALIGN_DOWN(page, PAGE_SIZE));
ASSERT(count);
 
pages = (size_t) btree_search(&a->used_space, page, &leaf);
if (pages) {
/*
* We are lucky, page is the beginning of some interval.
*/
if (count > pages) {
return 0;
} else if (count == pages) {
btree_remove(&a->used_space, page, leaf);
return 1;
} else {
/*
* Find the respective interval.
* Decrease its size and relocate its start address.
*/
for (i = 0; i < leaf->keys; i++) {
if (leaf->key[i] == page) {
leaf->key[i] += count * PAGE_SIZE;
leaf->value[i] -= count;
return 1;
}
}
goto error;
}
}
 
node = btree_leaf_node_left_neighbour(&a->used_space, leaf);
if (node && page < leaf->key[0]) {
uintptr_t left_pg = node->key[node->keys - 1];
size_t left_cnt = (size_t) node->value[node->keys - 1];
 
if (overlaps(left_pg, left_cnt * PAGE_SIZE, page,
count * PAGE_SIZE)) {
if (page + count * PAGE_SIZE ==
left_pg + left_cnt * PAGE_SIZE) {
/*
* The interval is contained in the rightmost
* interval of the left neighbour and can be
* removed by updating the size of the bigger
* interval.
*/
node->value[node->keys - 1] -= count;
return 1;
} else if (page + count * PAGE_SIZE <
left_pg + left_cnt*PAGE_SIZE) {
size_t new_cnt;
/*
* The interval is contained in the rightmost
* interval of the left neighbour but its
* removal requires both updating the size of
* the original interval and also inserting a
* new interval.
*/
new_cnt = ((left_pg + left_cnt * PAGE_SIZE) -
(page + count*PAGE_SIZE)) >> PAGE_WIDTH;
node->value[node->keys - 1] -= count + new_cnt;
btree_insert(&a->used_space, page +
count * PAGE_SIZE, (void *) new_cnt, leaf);
return 1;
}
}
return 0;
} else if (page < leaf->key[0]) {
return 0;
}
if (page > leaf->key[leaf->keys - 1]) {
uintptr_t left_pg = leaf->key[leaf->keys - 1];
size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
 
if (overlaps(left_pg, left_cnt * PAGE_SIZE, page,
count * PAGE_SIZE)) {
if (page + count * PAGE_SIZE ==
left_pg + left_cnt * PAGE_SIZE) {
/*
* The interval is contained in the rightmost
* interval of the leaf and can be removed by
* updating the size of the bigger interval.
*/
leaf->value[leaf->keys - 1] -= count;
return 1;
} else if (page + count * PAGE_SIZE < left_pg +
left_cnt * PAGE_SIZE) {
size_t new_cnt;
/*
* The interval is contained in the rightmost
* interval of the leaf but its removal
* requires both updating the size of the
* original interval and also inserting a new
* interval.
*/
new_cnt = ((left_pg + left_cnt * PAGE_SIZE) -
(page + count * PAGE_SIZE)) >> PAGE_WIDTH;
leaf->value[leaf->keys - 1] -= count + new_cnt;
btree_insert(&a->used_space, page +
count * PAGE_SIZE, (void *) new_cnt, leaf);
return 1;
}
}
return 0;
}
/*
* The border cases have been already resolved.
* Now the interval can be only between intervals of the leaf.
*/
for (i = 1; i < leaf->keys - 1; i++) {
if (page < leaf->key[i]) {
uintptr_t left_pg = leaf->key[i - 1];
size_t left_cnt = (size_t) leaf->value[i - 1];
 
/*
* Now the interval is between intervals corresponding
* to (i - 1) and i.
*/
if (overlaps(left_pg, left_cnt * PAGE_SIZE, page,
count * PAGE_SIZE)) {
if (page + count * PAGE_SIZE ==
left_pg + left_cnt*PAGE_SIZE) {
/*
* The interval is contained in the
* interval (i - 1) of the leaf and can
* be removed by updating the size of
* the bigger interval.
*/
leaf->value[i - 1] -= count;
return 1;
} else if (page + count * PAGE_SIZE <
left_pg + left_cnt * PAGE_SIZE) {
size_t new_cnt;
/*
* The interval is contained in the
* interval (i - 1) of the leaf but its
* removal requires both updating the
* size of the original interval and
* also inserting a new interval.
*/
new_cnt = ((left_pg +
left_cnt * PAGE_SIZE) -
(page + count * PAGE_SIZE)) >>
PAGE_WIDTH;
leaf->value[i - 1] -= count + new_cnt;
btree_insert(&a->used_space, page +
count * PAGE_SIZE, (void *) new_cnt,
leaf);
return 1;
}
}
return 0;
}
}
 
error:
panic("Inconsistency detected while removing %" PRIs " pages of used "
"space from %p.", count, page);
}
 
/** Remove reference to address space area share info.
*
* If the reference count drops to 0, the sh_info is deallocated.
*
* @param sh_info Pointer to address space area share info.
*/
void sh_info_remove_reference(share_info_t *sh_info)
{
bool dealloc = false;
 
mutex_lock(&sh_info->lock);
ASSERT(sh_info->refcount);
if (--sh_info->refcount == 0) {
dealloc = true;
link_t *cur;
/*
* Now walk carefully the pagemap B+tree and free/remove
* reference from all frames found there.
*/
for (cur = sh_info->pagemap.leaf_head.next;
cur != &sh_info->pagemap.leaf_head; cur = cur->next) {
btree_node_t *node;
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++)
frame_free((uintptr_t) node->value[i]);
}
}
mutex_unlock(&sh_info->lock);
if (dealloc) {
btree_destroy(&sh_info->pagemap);
free(sh_info);
}
}
 
/*
* Address space related syscalls.
*/
 
/** Wrapper for as_area_create(). */
unative_t sys_as_area_create(uintptr_t address, size_t size, int flags)
{
if (as_area_create(AS, flags | AS_AREA_CACHEABLE, size, address,
AS_AREA_ATTR_NONE, &anon_backend, NULL))
return (unative_t) address;
else
return (unative_t) -1;
}
 
/** Wrapper for as_area_resize(). */
unative_t sys_as_area_resize(uintptr_t address, size_t size, int flags)
{
return (unative_t) as_area_resize(AS, address, size, 0);
}
 
/** Wrapper for as_area_change_flags(). */
unative_t sys_as_area_change_flags(uintptr_t address, int flags)
{
return (unative_t) as_area_change_flags(AS, flags, address);
}
 
/** Wrapper for as_area_destroy(). */
unative_t sys_as_area_destroy(uintptr_t address)
{
return (unative_t) as_area_destroy(AS, address);
}
 
/** Print out information about address space.
*
* @param as Address space.
*/
void as_print(as_t *as)
{
ipl_t ipl;
ipl = interrupts_disable();
mutex_lock(&as->lock);
/* print out info about address space areas */
link_t *cur;
for (cur = as->as_area_btree.leaf_head.next;
cur != &as->as_area_btree.leaf_head; cur = cur->next) {
btree_node_t *node;
node = list_get_instance(cur, btree_node_t, leaf_link);
unsigned int i;
for (i = 0; i < node->keys; i++) {
as_area_t *area = node->value[i];
mutex_lock(&area->lock);
printf("as_area: %p, base=%p, pages=%" PRIs
" (%p - %p)\n", area, area->base, area->pages,
area->base, area->base + FRAMES2SIZE(area->pages));
mutex_unlock(&area->lock);
}
}
mutex_unlock(&as->lock);
interrupts_restore(ipl);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/frame.c
0,0 → 1,1344
/*
* Copyright (c) 2001-2005 Jakub Jermar
* Copyright (c) 2005 Sergey Bondari
* Copyright (c) 2009 Martin Decky
* 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 genericmm
* @{
*/
 
/**
* @file
* @brief Physical frame allocator.
*
* This file contains the physical frame allocator and memory zone management.
* The frame allocator is built on top of the buddy allocator.
*
* @see buddy.c
*/
 
#include <arch/types.h>
#include <mm/frame.h>
#include <mm/as.h>
#include <panic.h>
#include <debug.h>
#include <adt/list.h>
#include <synch/mutex.h>
#include <synch/condvar.h>
#include <arch/asm.h>
#include <arch.h>
#include <print.h>
#include <align.h>
#include <mm/slab.h>
#include <bitops.h>
#include <macros.h>
#include <config.h>
 
zones_t zones;
 
/*
* Synchronization primitives used to sleep when there is no memory
* available.
*/
mutex_t mem_avail_mtx;
condvar_t mem_avail_cv;
size_t mem_avail_req = 0; /**< Number of frames requested. */
size_t mem_avail_gen = 0; /**< Generation counter. */
 
/********************/
/* Helper functions */
/********************/
 
static inline size_t frame_index(zone_t *zone, frame_t *frame)
{
return (size_t) (frame - zone->frames);
}
 
static inline size_t frame_index_abs(zone_t *zone, frame_t *frame)
{
return (size_t) (frame - zone->frames) + zone->base;
}
 
static inline bool frame_index_valid(zone_t *zone, size_t index)
{
return (index < zone->count);
}
 
static inline size_t make_frame_index(zone_t *zone, frame_t *frame)
{
return (frame - zone->frames);
}
 
/** Initialize frame structure.
*
* @param frame Frame structure to be initialized.
*
*/
static void frame_initialize(frame_t *frame)
{
frame->refcount = 1;
frame->buddy_order = 0;
}
 
/*******************/
/* Zones functions */
/*******************/
 
/** Insert-sort zone into zones list.
*
* Assume interrupts are disabled and zones lock is
* locked.
*
* @param base Base frame of the newly inserted zone.
* @param count Number of frames of the newly inserted zone.
*
* @return Zone number on success, -1 on error.
*
*/
static size_t zones_insert_zone(pfn_t base, size_t count)
{
if (zones.count + 1 == ZONES_MAX) {
printf("Maximum zone count %u exceeded!\n", ZONES_MAX);
return (size_t) -1;
}
size_t i;
for (i = 0; i < zones.count; i++) {
/* Check for overlap */
if (overlaps(base, count,
zones.info[i].base, zones.info[i].count)) {
printf("Zones overlap!\n");
return (size_t) -1;
}
if (base < zones.info[i].base)
break;
}
/* Move other zones up */
size_t j;
for (j = zones.count; j > i; j--) {
zones.info[j] = zones.info[j - 1];
zones.info[j].buddy_system->data =
(void *) &zones.info[j - 1];
}
zones.count++;
return i;
}
 
/** Get total available frames.
*
* Assume interrupts are disabled and zones lock is
* locked.
*
* @return Total number of available frames.
*
*/
#ifdef CONFIG_DEBUG
static size_t total_frames_free(void)
{
size_t total = 0;
size_t i;
for (i = 0; i < zones.count; i++)
total += zones.info[i].free_count;
return total;
}
#endif
 
/** Find a zone with a given frames.
*
* Assume interrupts are disabled and zones lock is
* locked.
*
* @param frame Frame number contained in zone.
* @param count Number of frames to look for.
* @param hint Used as zone hint.
*
* @return Zone index or -1 if not found.
*
*/
size_t find_zone(pfn_t frame, size_t count, size_t hint)
{
if (hint >= zones.count)
hint = 0;
size_t i = hint;
do {
if ((zones.info[i].base <= frame)
&& (zones.info[i].base + zones.info[i].count >= frame + count))
return i;
i++;
if (i >= zones.count)
i = 0;
} while (i != hint);
return (size_t) -1;
}
 
/** @return True if zone can allocate specified order */
static bool zone_can_alloc(zone_t *zone, uint8_t order)
{
return (zone_flags_available(zone->flags)
&& buddy_system_can_alloc(zone->buddy_system, order));
}
 
/** Find a zone that can allocate order frames.
*
* Assume interrupts are disabled and zones lock is
* locked.
*
* @param order Size (2^order) of free space we are trying to find.
* @param flags Required flags of the target zone.
* @param hind Preferred zone.
*
*/
static size_t find_free_zone(uint8_t order, zone_flags_t flags, size_t hint)
{
if (hint >= zones.count)
hint = 0;
size_t i = hint;
do {
/*
* Check whether the zone meets the search criteria.
*/
if ((zones.info[i].flags & flags) == flags) {
/*
* Check if the zone has 2^order frames area available.
*/
if (zone_can_alloc(&zones.info[i], order))
return i;
}
i++;
if (i >= zones.count)
i = 0;
} while (i != hint);
return (size_t) -1;
}
 
/**************************/
/* Buddy system functions */
/**************************/
 
/** Buddy system find_block implementation.
*
* Find block that is parent of current list.
* That means go to lower addresses, until such block is found
*
* @param order Order of parent must be different then this
* parameter!!
*
*/
static link_t *zone_buddy_find_block(buddy_system_t *buddy, link_t *child,
uint8_t order)
{
frame_t *frame = list_get_instance(child, frame_t, buddy_link);
zone_t *zone = (zone_t *) buddy->data;
size_t index = frame_index(zone, frame);
do {
if (zone->frames[index].buddy_order != order)
return &zone->frames[index].buddy_link;
} while (index-- > 0);
return NULL;
}
 
/** Buddy system find_buddy implementation.
*
* @param buddy Buddy system.
* @param block Block for which buddy should be found.
*
* @return Buddy for given block if found.
*
*/
static link_t *zone_buddy_find_buddy(buddy_system_t *buddy, link_t *block)
{
frame_t *frame = list_get_instance(block, frame_t, buddy_link);
zone_t *zone = (zone_t *) buddy->data;
ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),
frame->buddy_order));
bool is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
size_t index;
if (is_left) {
index = (frame_index(zone, frame)) +
(1 << frame->buddy_order);
} else { /* is_right */
index = (frame_index(zone, frame)) -
(1 << frame->buddy_order);
}
if (frame_index_valid(zone, index)) {
if ((zone->frames[index].buddy_order == frame->buddy_order) &&
(zone->frames[index].refcount == 0)) {
return &zone->frames[index].buddy_link;
}
}
return NULL;
}
 
/** Buddy system bisect implementation.
*
* @param buddy Buddy system.
* @param block Block to bisect.
*
* @return Right block.
*
*/
static link_t *zone_buddy_bisect(buddy_system_t *buddy, link_t *block)
{
frame_t *frame_l = list_get_instance(block, frame_t, buddy_link);
frame_t *frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
return &frame_r->buddy_link;
}
 
/** Buddy system coalesce implementation.
*
* @param buddy Buddy system.
* @param block_1 First block.
* @param block_2 First block's buddy.
*
* @return Coalesced block (actually block that represents lower
* address).
*
*/
static link_t *zone_buddy_coalesce(buddy_system_t *buddy, link_t *block_1,
link_t *block_2)
{
frame_t *frame1 = list_get_instance(block_1, frame_t, buddy_link);
frame_t *frame2 = list_get_instance(block_2, frame_t, buddy_link);
return ((frame1 < frame2) ? block_1 : block_2);
}
 
/** Buddy system set_order implementation.
*
* @param buddy Buddy system.
* @param block Buddy system block.
* @param order Order to set.
*
*/
static void zone_buddy_set_order(buddy_system_t *buddy, link_t *block,
uint8_t order)
{
list_get_instance(block, frame_t, buddy_link)->buddy_order = order;
}
 
/** Buddy system get_order implementation.
*
* @param buddy Buddy system.
* @param block Buddy system block.
*
* @return Order of block.
*
*/
static uint8_t zone_buddy_get_order(buddy_system_t *buddy, link_t *block)
{
return list_get_instance(block, frame_t, buddy_link)->buddy_order;
}
 
/** Buddy system mark_busy implementation.
*
* @param buddy Buddy system.
* @param block Buddy system block.
*
*/
static void zone_buddy_mark_busy(buddy_system_t *buddy, link_t * block)
{
list_get_instance(block, frame_t, buddy_link)->refcount = 1;
}
 
/** Buddy system mark_available implementation.
*
* @param buddy Buddy system.
* @param block Buddy system block.
*/
static void zone_buddy_mark_available(buddy_system_t *buddy, link_t *block)
{
list_get_instance(block, frame_t, buddy_link)->refcount = 0;
}
 
static buddy_system_operations_t zone_buddy_system_operations = {
.find_buddy = zone_buddy_find_buddy,
.bisect = zone_buddy_bisect,
.coalesce = zone_buddy_coalesce,
.set_order = zone_buddy_set_order,
.get_order = zone_buddy_get_order,
.mark_busy = zone_buddy_mark_busy,
.mark_available = zone_buddy_mark_available,
.find_block = zone_buddy_find_block
};
 
/******************/
/* Zone functions */
/******************/
 
/** Allocate frame in particular zone.
*
* Assume zone is locked and is available for allocation.
* Panics if allocation is impossible.
*
* @param zone Zone to allocate from.
* @param order Allocate exactly 2^order frames.
*
* @return Frame index in zone.
*
*/
static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
{
ASSERT(zone_flags_available(zone->flags));
/* Allocate frames from zone buddy system */
link_t *link = buddy_system_alloc(zone->buddy_system, order);
ASSERT(link);
/* Update zone information. */
zone->free_count -= (1 << order);
zone->busy_count += (1 << order);
/* Frame will be actually a first frame of the block. */
frame_t *frame = list_get_instance(link, frame_t, buddy_link);
/* Get frame address */
return make_frame_index(zone, frame);
}
 
/** Free frame from zone.
*
* Assume zone is locked and is available for deallocation.
*
* @param zone Pointer to zone from which the frame is to be freed.
* @param frame_idx Frame index relative to zone.
*
*/
static void zone_frame_free(zone_t *zone, size_t frame_idx)
{
ASSERT(zone_flags_available(zone->flags));
frame_t *frame = &zone->frames[frame_idx];
/* Remember frame order */
uint8_t order = frame->buddy_order;
ASSERT(frame->refcount);
if (!--frame->refcount) {
buddy_system_free(zone->buddy_system, &frame->buddy_link);
/* Update zone information. */
zone->free_count += (1 << order);
zone->busy_count -= (1 << order);
}
}
 
/** Return frame from zone. */
static frame_t *zone_get_frame(zone_t *zone, size_t frame_idx)
{
ASSERT(frame_idx < zone->count);
return &zone->frames[frame_idx];
}
 
/** Mark frame in zone unavailable to allocation. */
static void zone_mark_unavailable(zone_t *zone, size_t frame_idx)
{
ASSERT(zone_flags_available(zone->flags));
frame_t *frame = zone_get_frame(zone, frame_idx);
if (frame->refcount)
return;
link_t *link __attribute__ ((unused));
link = buddy_system_alloc_block(zone->buddy_system,
&frame->buddy_link);
ASSERT(link);
zone->free_count--;
}
 
/** Merge two zones.
*
* Expect buddy to point to space at least zone_conf_size large.
* Assume z1 & z2 are locked and compatible and zones lock is
* locked.
*
* @param z1 First zone to merge.
* @param z2 Second zone to merge.
* @param old_z1 Original date of the first zone.
* @param buddy Merged zone buddy.
*
*/
static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1, buddy_system_t *buddy)
{
ASSERT(zone_flags_available(zones.info[z1].flags));
ASSERT(zone_flags_available(zones.info[z2].flags));
ASSERT(zones.info[z1].flags == zones.info[z2].flags);
ASSERT(zones.info[z1].base < zones.info[z2].base);
ASSERT(!overlaps(zones.info[z1].base, zones.info[z1].count,
zones.info[z2].base, zones.info[z2].count));
/* Difference between zone bases */
pfn_t base_diff = zones.info[z2].base - zones.info[z1].base;
zones.info[z1].count = base_diff + zones.info[z2].count;
zones.info[z1].free_count += zones.info[z2].free_count;
zones.info[z1].busy_count += zones.info[z2].busy_count;
zones.info[z1].buddy_system = buddy;
uint8_t order = fnzb(zones.info[z1].count);
buddy_system_create(zones.info[z1].buddy_system, order,
&zone_buddy_system_operations, (void *) &zones.info[z1]);
zones.info[z1].frames =
(frame_t *) ((uint8_t *) zones.info[z1].buddy_system
+ buddy_conf_size(order));
/* This marks all frames busy */
size_t i;
for (i = 0; i < zones.info[z1].count; i++)
frame_initialize(&zones.info[z1].frames[i]);
/* Copy frames from both zones to preserve full frame orders,
* parents etc. Set all free frames with refcount = 0 to 1, because
* we add all free frames to buddy allocator later again, clearing
* order to 0. Don't set busy frames with refcount = 0, as they
* will not be reallocated during merge and it would make later
* problems with allocation/free.
*/
for (i = 0; i < old_z1->count; i++)
zones.info[z1].frames[i] = old_z1->frames[i];
for (i = 0; i < zones.info[z2].count; i++)
zones.info[z1].frames[base_diff + i]
= zones.info[z2].frames[i];
i = 0;
while (i < zones.info[z1].count) {
if (zones.info[z1].frames[i].refcount) {
/* Skip busy frames */
i += 1 << zones.info[z1].frames[i].buddy_order;
} else {
/* Free frames, set refcount = 1
* (all free frames have refcount == 0, we need not
* to check the order)
*/
zones.info[z1].frames[i].refcount = 1;
zones.info[z1].frames[i].buddy_order = 0;
i++;
}
}
/* Add free blocks from the original zone z1 */
while (zone_can_alloc(old_z1, 0)) {
/* Allocate from the original zone */
pfn_t frame_idx = zone_frame_alloc(old_z1, 0);
/* Free the frame from the merged zone */
frame_t *frame = &zones.info[z1].frames[frame_idx];
frame->refcount = 0;
buddy_system_free(zones.info[z1].buddy_system, &frame->buddy_link);
}
/* Add free blocks from the original zone z2 */
while (zone_can_alloc(&zones.info[z2], 0)) {
/* Allocate from the original zone */
pfn_t frame_idx = zone_frame_alloc(&zones.info[z2], 0);
/* Free the frame from the merged zone */
frame_t *frame = &zones.info[z1].frames[base_diff + frame_idx];
frame->refcount = 0;
buddy_system_free(zones.info[z1].buddy_system, &frame->buddy_link);
}
}
 
/** Return old configuration frames into the zone.
*
* We have two cases:
* - The configuration data is outside the zone
* -> do nothing (perhaps call frame_free?)
* - The configuration data was created by zone_create
* or updated by reduce_region -> free every frame
*
* @param znum The actual zone where freeing should occur.
* @param pfn Old zone configuration frame.
* @param count Old zone frame count.
*
*/
static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
{
ASSERT(zone_flags_available(zones.info[znum].flags));
size_t cframes = SIZE2FRAMES(zone_conf_size(count));
if ((pfn < zones.info[znum].base)
|| (pfn >= zones.info[znum].base + zones.info[znum].count))
return;
frame_t *frame __attribute__ ((unused));
 
frame = &zones.info[znum].frames[pfn - zones.info[znum].base];
ASSERT(!frame->buddy_order);
size_t i;
for (i = 0; i < cframes; i++) {
zones.info[znum].busy_count++;
zone_frame_free(&zones.info[znum],
pfn - zones.info[znum].base + i);
}
}
 
/** Reduce allocated block to count of order 0 frames.
*
* The allocated block needs 2^order frames. Reduce all frames
* in the block to order 0 and free the unneeded frames. This means that
* when freeing the previously allocated block starting with frame_idx,
* you have to free every frame.
*
* @param znum Zone.
* @param frame_idx Index the first frame of the block.
* @param count Allocated frames in block.
*
*/
static void zone_reduce_region(size_t znum, pfn_t frame_idx, size_t count)
{
ASSERT(zone_flags_available(zones.info[znum].flags));
ASSERT(frame_idx + count < zones.info[znum].count);
uint8_t order = zones.info[znum].frames[frame_idx].buddy_order;
ASSERT((size_t) (1 << order) >= count);
/* Reduce all blocks to order 0 */
size_t i;
for (i = 0; i < (size_t) (1 << order); i++) {
frame_t *frame = &zones.info[znum].frames[i + frame_idx];
frame->buddy_order = 0;
if (!frame->refcount)
frame->refcount = 1;
ASSERT(frame->refcount == 1);
}
/* Free unneeded frames */
for (i = count; i < (size_t) (1 << order); i++)
zone_frame_free(&zones.info[znum], i + frame_idx);
}
 
/** Merge zones z1 and z2.
*
* The merged zones must be 2 zones with no zone existing in between
* (which means that z2 = z1 + 1). Both zones must be available zones
* with the same flags.
*
* When you create a new zone, the frame allocator configuration does
* not to be 2^order size. Once the allocator is running it is no longer
* possible, merged configuration data occupies more space :-/
*
* The function uses
*
*/
bool zone_merge(size_t z1, size_t z2)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
bool ret = true;
/* We can join only 2 zones with none existing inbetween,
* the zones have to be available and with the same
* set of flags
*/
if ((z1 >= zones.count) || (z2 >= zones.count)
|| (z2 - z1 != 1)
|| (!zone_flags_available(zones.info[z1].flags))
|| (!zone_flags_available(zones.info[z2].flags))
|| (zones.info[z1].flags != zones.info[z2].flags)) {
ret = false;
goto errout;
}
pfn_t cframes = SIZE2FRAMES(zone_conf_size(
zones.info[z2].base - zones.info[z1].base
+ zones.info[z2].count));
uint8_t order;
if (cframes == 1)
order = 0;
else
order = fnzb(cframes - 1) + 1;
/* Allocate merged zone data inside one of the zones */
pfn_t pfn;
if (zone_can_alloc(&zones.info[z1], order)) {
pfn = zones.info[z1].base + zone_frame_alloc(&zones.info[z1], order);
} else if (zone_can_alloc(&zones.info[z2], order)) {
pfn = zones.info[z2].base + zone_frame_alloc(&zones.info[z2], order);
} else {
ret = false;
goto errout;
}
/* Preserve original data from z1 */
zone_t old_z1 = zones.info[z1];
old_z1.buddy_system->data = (void *) &old_z1;
/* Do zone merging */
buddy_system_t *buddy = (buddy_system_t *) PA2KA(PFN2ADDR(pfn));
zone_merge_internal(z1, z2, &old_z1, buddy);
/* Free unneeded config frames */
zone_reduce_region(z1, pfn - zones.info[z1].base, cframes);
/* Subtract zone information from busy frames */
zones.info[z1].busy_count -= cframes;
/* Free old zone information */
return_config_frames(z1,
ADDR2PFN(KA2PA((uintptr_t) old_z1.frames)), old_z1.count);
return_config_frames(z1,
ADDR2PFN(KA2PA((uintptr_t) zones.info[z2].frames)),
zones.info[z2].count);
/* Move zones down */
size_t i;
for (i = z2 + 1; i < zones.count; i++) {
zones.info[i - 1] = zones.info[i];
zones.info[i - 1].buddy_system->data =
(void *) &zones.info[i - 1];
}
zones.count--;
errout:
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return ret;
}
 
/** Merge all mergeable zones into one big zone.
*
* It is reasonable to do this on systems where
* BIOS reports parts in chunks, so that we could
* have 1 zone (it's faster).
*
*/
void zone_merge_all(void)
{
size_t i = 0;
while (i < zones.count) {
if (!zone_merge(i, i + 1))
i++;
}
}
 
/** Create new frame zone.
*
* @param zone Zone to construct.
* @param buddy Address of buddy system configuration information.
* @param start Physical address of the first frame within the zone.
* @param count Count of frames in zone.
* @param flags Zone flags.
*
* @return Initialized zone.
*
*/
static void zone_construct(zone_t *zone, buddy_system_t *buddy, pfn_t start, size_t count, zone_flags_t flags)
{
zone->base = start;
zone->count = count;
zone->flags = flags;
zone->free_count = count;
zone->busy_count = 0;
zone->buddy_system = buddy;
if (zone_flags_available(flags)) {
/*
* Compute order for buddy system and initialize
*/
uint8_t order = fnzb(count);
buddy_system_create(zone->buddy_system, order,
&zone_buddy_system_operations, (void *) zone);
/* Allocate frames _after_ the confframe */
/* Check sizes */
zone->frames = (frame_t *) ((uint8_t *) zone->buddy_system +
buddy_conf_size(order));
size_t i;
for (i = 0; i < count; i++)
frame_initialize(&zone->frames[i]);
/* Stuffing frames */
for (i = 0; i < count; i++) {
zone->frames[i].refcount = 0;
buddy_system_free(zone->buddy_system, &zone->frames[i].buddy_link);
}
} else
zone->frames = NULL;
}
 
/** Compute configuration data size for zone.
*
* @param count Size of zone in frames.
*
* @return Size of zone configuration info (in bytes).
*
*/
uintptr_t zone_conf_size(size_t count)
{
return (count * sizeof(frame_t) + buddy_conf_size(fnzb(count)));
}
 
/** Create and add zone to system.
*
* @param start First frame number (absolute).
* @param count Size of zone in frames.
* @param confframe Where configuration frames are supposed to be.
* Automatically checks, that we will not disturb the
* kernel and possibly init. If confframe is given
* _outside_ this zone, it is expected, that the area is
* already marked BUSY and big enough to contain
* zone_conf_size() amount of data. If the confframe is
* inside the area, the zone free frame information is
* modified not to include it.
*
* @return Zone number or -1 on error.
*
*/
size_t zone_create(pfn_t start, size_t count, pfn_t confframe, zone_flags_t flags)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
if (zone_flags_available(flags)) { /* Create available zone */
/* Theoretically we could have NULL here, practically make sure
* nobody tries to do that. If some platform requires, remove
* the assert
*/
ASSERT(confframe != NULL);
/* If confframe is supposed to be inside our zone, then make sure
* it does not span kernel & init
*/
size_t confcount = SIZE2FRAMES(zone_conf_size(count));
if ((confframe >= start) && (confframe < start + count)) {
for (; confframe < start + count; confframe++) {
uintptr_t addr = PFN2ADDR(confframe);
if (overlaps(addr, PFN2ADDR(confcount),
KA2PA(config.base), config.kernel_size))
continue;
if (overlaps(addr, PFN2ADDR(confcount),
KA2PA(config.stack_base), config.stack_size))
continue;
bool overlap = false;
size_t i;
for (i = 0; i < init.cnt; i++)
if (overlaps(addr, PFN2ADDR(confcount),
KA2PA(init.tasks[i].addr),
init.tasks[i].size)) {
overlap = true;
break;
}
if (overlap)
continue;
break;
}
if (confframe >= start + count)
panic("Cannot find configuration data for zone.");
}
size_t znum = zones_insert_zone(start, count);
if (znum == (size_t) -1) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return (size_t) -1;
}
buddy_system_t *buddy = (buddy_system_t *) PA2KA(PFN2ADDR(confframe));
zone_construct(&zones.info[znum], buddy, start, count, flags);
/* If confdata in zone, mark as unavailable */
if ((confframe >= start) && (confframe < start + count)) {
size_t i;
for (i = confframe; i < confframe + confcount; i++)
zone_mark_unavailable(&zones.info[znum],
i - zones.info[znum].base);
}
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return znum;
}
/* Non-available zone */
size_t znum = zones_insert_zone(start, count);
if (znum == (size_t) -1) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return (size_t) -1;
}
zone_construct(&zones.info[znum], NULL, start, count, flags);
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return znum;
}
 
/*******************/
/* Frame functions */
/*******************/
 
/** Set parent of frame. */
void frame_set_parent(pfn_t pfn, void *data, size_t hint)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
size_t znum = find_zone(pfn, 1, hint);
ASSERT(znum != (size_t) -1);
zone_get_frame(&zones.info[znum],
pfn - zones.info[znum].base)->parent = data;
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
}
 
void *frame_get_parent(pfn_t pfn, size_t hint)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
size_t znum = find_zone(pfn, 1, hint);
ASSERT(znum != (size_t) -1);
void *res = zone_get_frame(&zones.info[znum],
pfn - zones.info[znum].base)->parent;
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return res;
}
 
/** Allocate power-of-two frames of physical memory.
*
* @param order Allocate exactly 2^order frames.
* @param flags Flags for host zone selection and address processing.
* @param pzone Preferred zone.
*
* @return Physical address of the allocated frame.
*
*/
void *frame_alloc_generic(uint8_t order, frame_flags_t flags, size_t *pzone)
{
size_t size = ((size_t) 1) << order;
ipl_t ipl;
size_t hint = pzone ? (*pzone) : 0;
loop:
ipl = interrupts_disable();
spinlock_lock(&zones.lock);
/*
* First, find suitable frame zone.
*/
size_t znum = find_free_zone(order,
FRAME_TO_ZONE_FLAGS(flags), hint);
/* If no memory, reclaim some slab memory,
if it does not help, reclaim all */
if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
size_t freed = slab_reclaim(0);
ipl = interrupts_disable();
spinlock_lock(&zones.lock);
if (freed > 0)
znum = find_free_zone(order,
FRAME_TO_ZONE_FLAGS(flags), hint);
if (znum == (size_t) -1) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
freed = slab_reclaim(SLAB_RECLAIM_ALL);
ipl = interrupts_disable();
spinlock_lock(&zones.lock);
if (freed > 0)
znum = find_free_zone(order,
FRAME_TO_ZONE_FLAGS(flags), hint);
}
}
if (znum == (size_t) -1) {
if (flags & FRAME_ATOMIC) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return NULL;
}
#ifdef CONFIG_DEBUG
size_t avail = total_frames_free();
#endif
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
/*
* Sleep until some frames are available again.
*/
#ifdef CONFIG_DEBUG
printf("Thread %" PRIu64 " waiting for %" PRIs " frames, "
"%" PRIs " available.\n", THREAD->tid, size, avail);
#endif
mutex_lock(&mem_avail_mtx);
if (mem_avail_req > 0)
mem_avail_req = min(mem_avail_req, size);
else
mem_avail_req = size;
size_t gen = mem_avail_gen;
while (gen == mem_avail_gen)
condvar_wait(&mem_avail_cv, &mem_avail_mtx);
mutex_unlock(&mem_avail_mtx);
#ifdef CONFIG_DEBUG
printf("Thread %" PRIu64 " woken up.\n", THREAD->tid);
#endif
goto loop;
}
pfn_t pfn = zone_frame_alloc(&zones.info[znum], order)
+ zones.info[znum].base;
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
if (pzone)
*pzone = znum;
if (flags & FRAME_KA)
return (void *) PA2KA(PFN2ADDR(pfn));
return (void *) PFN2ADDR(pfn);
}
 
/** Free a frame.
*
* Find respective frame structure for supplied physical frame address.
* Decrement frame reference count. If it drops to zero, move the frame
* structure to free list.
*
* @param frame Physical Address of of the frame to be freed.
*
*/
void frame_free(uintptr_t frame)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
/*
* First, find host frame zone for addr.
*/
pfn_t pfn = ADDR2PFN(frame);
size_t znum = find_zone(pfn, 1, NULL);
ASSERT(znum != (size_t) -1);
zone_frame_free(&zones.info[znum], pfn - zones.info[znum].base);
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
/*
* Signal that some memory has been freed.
*/
mutex_lock(&mem_avail_mtx);
if (mem_avail_req > 0)
mem_avail_req--;
if (mem_avail_req == 0) {
mem_avail_gen++;
condvar_broadcast(&mem_avail_cv);
}
mutex_unlock(&mem_avail_mtx);
}
 
/** Add reference to frame.
*
* Find respective frame structure for supplied PFN and
* increment frame reference count.
*
* @param pfn Frame number of the frame to be freed.
*
*/
void frame_reference_add(pfn_t pfn)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
/*
* First, find host frame zone for addr.
*/
size_t znum = find_zone(pfn, 1, NULL);
ASSERT(znum != (size_t) -1);
zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
}
 
/** Mark given range unavailable in frame zones. */
void frame_mark_unavailable(pfn_t start, size_t count)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
size_t i;
for (i = 0; i < count; i++) {
size_t znum = find_zone(start + i, 1, 0);
if (znum == (size_t) -1) /* PFN not found */
continue;
zone_mark_unavailable(&zones.info[znum],
start + i - zones.info[znum].base);
}
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
}
 
/** Initialize physical memory management. */
void frame_init(void)
{
if (config.cpu_active == 1) {
zones.count = 0;
spinlock_initialize(&zones.lock, "zones.lock");
mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
condvar_initialize(&mem_avail_cv);
}
/* Tell the architecture to create some memory */
frame_arch_init();
if (config.cpu_active == 1) {
frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
SIZE2FRAMES(config.kernel_size));
frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
SIZE2FRAMES(config.stack_size));
size_t i;
for (i = 0; i < init.cnt; i++) {
pfn_t pfn = ADDR2PFN(KA2PA(init.tasks[i].addr));
frame_mark_unavailable(pfn,
SIZE2FRAMES(init.tasks[i].size));
}
if (ballocs.size)
frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
SIZE2FRAMES(ballocs.size));
/* Black list first frame, as allocating NULL would
* fail in some places
*/
frame_mark_unavailable(0, 1);
}
}
 
/** Return total size of all zones. */
uint64_t zone_total_size(void)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
uint64_t total = 0;
size_t i;
for (i = 0; i < zones.count; i++)
total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
return total;
}
 
/** Prints list of zones. */
void zone_print_list(void)
{
#ifdef __32_BITS__
printf("# base address frames flags free frames busy frames\n");
printf("-- ------------ ------------ -------- ------------ ------------\n");
#endif
 
#ifdef __64_BITS__
printf("# base address frames flags free frames busy frames\n");
printf("-- -------------------- ------------ -------- ------------ ------------\n");
#endif
/*
* Because printing may require allocation of memory, we may not hold
* the frame allocator locks when printing zone statistics. Therefore,
* we simply gather the statistics under the protection of the locks and
* print the statistics when the locks have been released.
*
* When someone adds/removes zones while we are printing the statistics,
* we may end up with inaccurate output (e.g. a zone being skipped from
* the listing).
*/
size_t i;
for (i = 0;; i++) {
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
if (i >= zones.count) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
break;
}
uintptr_t base = PFN2ADDR(zones.info[i].base);
size_t count = zones.info[i].count;
zone_flags_t flags = zones.info[i].flags;
size_t free_count = zones.info[i].free_count;
size_t busy_count = zones.info[i].busy_count;
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
bool available = zone_flags_available(flags);
printf("%-2" PRIs, i);
#ifdef __32_BITS__
printf(" %10p", base);
#endif
#ifdef __64_BITS__
printf(" %18p", base);
#endif
printf(" %12" PRIs " %c%c%c ", count,
available ? 'A' : ' ',
(flags & ZONE_RESERVED) ? 'R' : ' ',
(flags & ZONE_FIRMWARE) ? 'F' : ' ');
if (available)
printf("%12" PRIs " %12" PRIs,
free_count, busy_count);
printf("\n");
}
}
 
/** Prints zone details.
*
* @param num Zone base address or zone number.
*
*/
void zone_print_one(size_t num)
{
ipl_t ipl = interrupts_disable();
spinlock_lock(&zones.lock);
size_t znum = (size_t) -1;
size_t i;
for (i = 0; i < zones.count; i++) {
if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
znum = i;
break;
}
}
if (znum == (size_t) -1) {
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
printf("Zone not found.\n");
return;
}
uintptr_t base = PFN2ADDR(zones.info[i].base);
zone_flags_t flags = zones.info[i].flags;
size_t count = zones.info[i].count;
size_t free_count = zones.info[i].free_count;
size_t busy_count = zones.info[i].busy_count;
spinlock_unlock(&zones.lock);
interrupts_restore(ipl);
bool available = zone_flags_available(flags);
printf("Zone number: %" PRIs "\n", znum);
printf("Zone base address: %p\n", base);
printf("Zone size: %" PRIs " frames (%" PRIs " KiB)\n", count,
SIZE2KB(FRAMES2SIZE(count)));
printf("Zone flags: %c%c%c\n",
available ? 'A' : ' ',
(flags & ZONE_RESERVED) ? 'R' : ' ',
(flags & ZONE_FIRMWARE) ? 'F' : ' ');
if (available) {
printf("Allocated space: %" PRIs " frames (%" PRIs " KiB)\n",
busy_count, SIZE2KB(FRAMES2SIZE(busy_count)));
printf("Available space: %" PRIs " frames (%" PRIs " KiB)\n",
free_count, SIZE2KB(FRAMES2SIZE(free_count)));
}
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/backend_elf.c
0,0 → 1,352
/*
* Copyright (c) 2006 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 genericmm
* @{
*/
 
/**
* @file
* @brief Backend for address space areas backed by an ELF image.
*/
 
#include <lib/elf.h>
#include <debug.h>
#include <arch/types.h>
#include <mm/as.h>
#include <mm/frame.h>
#include <mm/slab.h>
#include <mm/page.h>
#include <genarch/mm/page_pt.h>
#include <genarch/mm/page_ht.h>
#include <align.h>
#include <memstr.h>
#include <macros.h>
#include <arch.h>
#include <arch/barrier.h>
 
#ifdef CONFIG_VIRT_IDX_DCACHE
#include <arch/mm/cache.h>
#endif
 
static int elf_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access);
static void elf_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame);
static void elf_share(as_area_t *area);
 
mem_backend_t elf_backend = {
.page_fault = elf_page_fault,
.frame_free = elf_frame_free,
.share = elf_share
};
 
/** Service a page fault in the ELF backend address space area.
*
* The address space area and page tables must be already locked.
*
* @param area Pointer to the address space area.
* @param addr Faulting virtual address.
* @param access Access mode that caused the fault (i.e.
* read/write/exec).
*
* @return AS_PF_FAULT on failure (i.e. page fault) or AS_PF_OK
* on success (i.e. serviced).
*/
int elf_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access)
{
elf_header_t *elf = area->backend_data.elf;
elf_segment_header_t *entry = area->backend_data.segment;
btree_node_t *leaf;
uintptr_t base, frame, page, start_anon;
size_t i;
bool dirty = false;
 
if (!as_area_check_access(area, access))
return AS_PF_FAULT;
 
ASSERT((addr >= ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE)) &&
(addr < entry->p_vaddr + entry->p_memsz));
i = (addr - ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE)) >> PAGE_WIDTH;
base = (uintptr_t)
(((void *) elf) + ALIGN_DOWN(entry->p_offset, PAGE_SIZE));
 
/* Virtual address of faulting page*/
page = ALIGN_DOWN(addr, PAGE_SIZE);
 
/* Virtual address of the end of initialized part of segment */
start_anon = entry->p_vaddr + entry->p_filesz;
 
if (area->sh_info) {
bool found = false;
 
/*
* The address space area is shared.
*/
mutex_lock(&area->sh_info->lock);
frame = (uintptr_t) btree_search(&area->sh_info->pagemap,
page - area->base, &leaf);
if (!frame) {
unsigned int i;
 
/*
* Workaround for valid NULL address.
*/
 
for (i = 0; i < leaf->keys; i++) {
if (leaf->key[i] == page - area->base) {
found = true;
break;
}
}
}
if (frame || found) {
frame_reference_add(ADDR2PFN(frame));
page_mapping_insert(AS, addr, frame,
as_area_get_flags(area));
if (!used_space_insert(area, page, 1))
panic("Cannot insert used space.");
mutex_unlock(&area->sh_info->lock);
return AS_PF_OK;
}
}
 
/*
* The area is either not shared or the pagemap does not contain the
* mapping.
*/
if (page >= entry->p_vaddr && page + PAGE_SIZE <= start_anon) {
/*
* Initialized portion of the segment. The memory is backed
* directly by the content of the ELF image. Pages are
* only copied if the segment is writable so that there
* can be more instantions of the same memory ELF image
* used at a time. Note that this could be later done
* as COW.
*/
if (entry->p_flags & PF_W) {
frame = (uintptr_t)frame_alloc(ONE_FRAME, 0);
memcpy((void *) PA2KA(frame),
(void *) (base + i * FRAME_SIZE), FRAME_SIZE);
if (entry->p_flags & PF_X) {
smc_coherence_block((void *) PA2KA(frame),
FRAME_SIZE);
}
dirty = true;
} else {
frame = KA2PA(base + i * FRAME_SIZE);
}
} else if (page >= start_anon) {
/*
* This is the uninitialized portion of the segment.
* It is not physically present in the ELF image.
* To resolve the situation, a frame must be allocated
* and cleared.
*/
frame = (uintptr_t)frame_alloc(ONE_FRAME, 0);
memsetb((void *) PA2KA(frame), FRAME_SIZE, 0);
dirty = true;
} else {
size_t pad_lo, pad_hi;
/*
* The mixed case.
*
* The middle part is backed by the ELF image and
* the lower and upper parts are anonymous memory.
* (The segment can be and often is shorter than 1 page).
*/
if (page < entry->p_vaddr)
pad_lo = entry->p_vaddr - page;
else
pad_lo = 0;
 
if (start_anon < page + PAGE_SIZE)
pad_hi = page + PAGE_SIZE - start_anon;
else
pad_hi = 0;
 
frame = (uintptr_t)frame_alloc(ONE_FRAME, 0);
memcpy((void *) (PA2KA(frame) + pad_lo),
(void *) (base + i * FRAME_SIZE + pad_lo),
FRAME_SIZE - pad_lo - pad_hi);
if (entry->p_flags & PF_X) {
smc_coherence_block((void *) (PA2KA(frame) + pad_lo),
FRAME_SIZE - pad_lo - pad_hi);
}
memsetb((void *) PA2KA(frame), pad_lo, 0);
memsetb((void *) (PA2KA(frame) + FRAME_SIZE - pad_hi), pad_hi,
0);
dirty = true;
}
 
if (dirty && area->sh_info) {
frame_reference_add(ADDR2PFN(frame));
btree_insert(&area->sh_info->pagemap, page - area->base,
(void *) frame, leaf);
}
 
if (area->sh_info)
mutex_unlock(&area->sh_info->lock);
 
page_mapping_insert(AS, addr, frame, as_area_get_flags(area));
if (!used_space_insert(area, page, 1))
panic("Cannot insert used space.");
 
return AS_PF_OK;
}
 
/** Free a frame that is backed by the ELF backend.
*
* The address space area and page tables must be already locked.
*
* @param area Pointer to the address space area.
* @param page Page that is mapped to frame. Must be aligned to
* PAGE_SIZE.
* @param frame Frame to be released.
*
*/
void elf_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame)
{
elf_header_t *elf = area->backend_data.elf;
elf_segment_header_t *entry = area->backend_data.segment;
uintptr_t base, start_anon;
size_t i;
 
ASSERT((page >= ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE)) &&
(page < entry->p_vaddr + entry->p_memsz));
i = (page - ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE)) >> PAGE_WIDTH;
base = (uintptr_t) (((void *) elf) +
ALIGN_DOWN(entry->p_offset, FRAME_SIZE));
start_anon = entry->p_vaddr + entry->p_filesz;
 
if (page >= entry->p_vaddr && page + PAGE_SIZE <= start_anon) {
if (entry->p_flags & PF_W) {
/*
* Free the frame with the copy of writable segment
* data.
*/
frame_free(frame);
}
} else {
/*
* The frame is either anonymous memory or the mixed case (i.e.
* lower part is backed by the ELF image and the upper is
* anonymous). In any case, a frame needs to be freed.
*/
frame_free(frame);
}
}
 
/** Share ELF image backed address space area.
*
* If the area is writable, then all mapped pages are duplicated in the pagemap.
* Otherwise only portions of the area that are not backed by the ELF image
* are put into the pagemap.
*
* The address space and address space area must be locked prior to the call.
*
* @param area Address space area.
*/
void elf_share(as_area_t *area)
{
elf_segment_header_t *entry = area->backend_data.segment;
link_t *cur;
btree_node_t *leaf, *node;
uintptr_t start_anon = entry->p_vaddr + entry->p_filesz;
 
/*
* Find the node in which to start linear search.
*/
if (area->flags & AS_AREA_WRITE) {
node = list_get_instance(area->used_space.leaf_head.next,
btree_node_t, leaf_link);
} else {
(void) btree_search(&area->sh_info->pagemap, start_anon, &leaf);
node = btree_leaf_node_left_neighbour(&area->sh_info->pagemap,
leaf);
if (!node)
node = leaf;
}
 
/*
* Copy used anonymous portions of the area to sh_info's page map.
*/
mutex_lock(&area->sh_info->lock);
for (cur = &node->leaf_link; cur != &area->used_space.leaf_head;
cur = cur->next) {
unsigned int i;
node = list_get_instance(cur, btree_node_t, leaf_link);
for (i = 0; i < node->keys; i++) {
uintptr_t base = node->key[i];
size_t count = (size_t) node->value[i];
unsigned int j;
/*
* Skip read-only areas of used space that are backed
* by the ELF image.
*/
if (!(area->flags & AS_AREA_WRITE))
if (base >= entry->p_vaddr &&
base + count * PAGE_SIZE <= start_anon)
continue;
for (j = 0; j < count; j++) {
pte_t *pte;
/*
* Skip read-only pages that are backed by the
* ELF image.
*/
if (!(area->flags & AS_AREA_WRITE))
if (base >= entry->p_vaddr &&
base + (j + 1) * PAGE_SIZE <=
start_anon)
continue;
page_table_lock(area->as, false);
pte = page_mapping_find(area->as,
base + j * PAGE_SIZE);
ASSERT(pte && PTE_VALID(pte) &&
PTE_PRESENT(pte));
btree_insert(&area->sh_info->pagemap,
(base + j * PAGE_SIZE) - area->base,
(void *) PTE_GET_FRAME(pte), NULL);
page_table_unlock(area->as, false);
 
pfn_t pfn = ADDR2PFN(PTE_GET_FRAME(pte));
frame_reference_add(pfn);
}
}
}
mutex_unlock(&area->sh_info->lock);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/buddy.c
0,0 → 1,284
/*
* Copyright (c) 2005 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 genericmm
* @{
*/
 
/**
* @file
* @brief Buddy allocator framework.
*
* This file contains buddy system allocator framework.
* Specialized functions are needed for this abstract framework to be useful.
*/
 
#include <mm/buddy.h>
#include <mm/frame.h>
#include <arch/types.h>
#include <debug.h>
#include <print.h>
#include <macros.h>
 
/** Return size needed for the buddy configuration data. */
size_t buddy_conf_size(size_t max_order)
{
return sizeof(buddy_system_t) + (max_order + 1) * sizeof(link_t);
}
 
 
/** Create buddy system.
*
* Allocate memory for and initialize new buddy system.
*
* @param b Preallocated buddy system control data.
* @param max_order The biggest allocable size will be 2^max_order.
* @param op Operations for new buddy system.
* @param data Pointer to be used by implementation.
*
* @return New buddy system.
*/
void
buddy_system_create(buddy_system_t *b, uint8_t max_order,
buddy_system_operations_t *op, void *data)
{
int i;
 
ASSERT(max_order < BUDDY_SYSTEM_INNER_BLOCK);
 
ASSERT(op->find_buddy);
ASSERT(op->set_order);
ASSERT(op->get_order);
ASSERT(op->bisect);
ASSERT(op->coalesce);
ASSERT(op->mark_busy);
 
/*
* Use memory after our own structure.
*/
b->order = (link_t *) (&b[1]);
for (i = 0; i <= max_order; i++)
list_initialize(&b->order[i]);
 
b->max_order = max_order;
b->op = op;
b->data = data;
}
 
/** Check if buddy system can allocate block.
*
* @param b Buddy system pointer.
* @param i Size of the block (2^i).
*
* @return True if block can be allocated.
*/
bool buddy_system_can_alloc(buddy_system_t *b, uint8_t i)
{
uint8_t k;
/*
* If requested block is greater then maximal block
* we know immediatly that we cannot satisfy the request.
*/
if (i > b->max_order)
return false;
 
/*
* Check if any bigger or equal order has free elements
*/
for (k = i; k <= b->max_order; k++) {
if (!list_empty(&b->order[k])) {
return true;
}
}
return false;
}
 
/** Allocate PARTICULAR block from buddy system.
*
* @return Block of data or NULL if no such block was found.
*/
link_t *buddy_system_alloc_block(buddy_system_t *b, link_t *block)
{
link_t *left,*right, *tmp;
uint8_t order;
 
left = b->op->find_block(b, block, BUDDY_SYSTEM_INNER_BLOCK);
ASSERT(left);
list_remove(left);
while (1) {
if (!b->op->get_order(b, left)) {
b->op->mark_busy(b, left);
return left;
}
order = b->op->get_order(b, left);
 
right = b->op->bisect(b, left);
b->op->set_order(b, left, order - 1);
b->op->set_order(b, right, order - 1);
 
tmp = b->op->find_block(b, block, BUDDY_SYSTEM_INNER_BLOCK);
 
if (tmp == right) {
right = left;
left = tmp;
}
ASSERT(tmp == left);
b->op->mark_busy(b, left);
buddy_system_free(b, right);
b->op->mark_available(b, left);
}
}
 
/** Allocate block from buddy system.
*
* @param b Buddy system pointer.
* @param i Returned block will be 2^i big.
*
* @return Block of data represented by link_t.
*/
link_t *buddy_system_alloc(buddy_system_t *b, uint8_t i)
{
link_t *res, *hlp;
 
ASSERT(i <= b->max_order);
 
/*
* If the list of order i is not empty,
* the request can be immediatelly satisfied.
*/
if (!list_empty(&b->order[i])) {
res = b->order[i].next;
list_remove(res);
b->op->mark_busy(b, res);
return res;
}
/*
* If order i is already the maximal order,
* the request cannot be satisfied.
*/
if (i == b->max_order)
return NULL;
 
/*
* Try to recursively satisfy the request from higher order lists.
*/
hlp = buddy_system_alloc(b, i + 1);
/*
* The request could not be satisfied
* from higher order lists.
*/
if (!hlp)
return NULL;
res = hlp;
/*
* Bisect the block and set order of both of its parts to i.
*/
hlp = b->op->bisect(b, res);
b->op->set_order(b, res, i);
b->op->set_order(b, hlp, i);
/*
* Return the other half to buddy system. Mark the first part
* full, so that it won't coalesce again.
*/
b->op->mark_busy(b, res);
buddy_system_free(b, hlp);
return res;
}
 
/** Return block to buddy system.
*
* @param b Buddy system pointer.
* @param block Block to return.
*/
void buddy_system_free(buddy_system_t *b, link_t *block)
{
link_t *buddy, *hlp;
uint8_t i;
 
/*
* Determine block's order.
*/
i = b->op->get_order(b, block);
 
ASSERT(i <= b->max_order);
 
if (i != b->max_order) {
/*
* See if there is any buddy in the list of order i.
*/
buddy = b->op->find_buddy(b, block);
if (buddy) {
 
ASSERT(b->op->get_order(b, buddy) == i);
/*
* Remove buddy from the list of order i.
*/
list_remove(buddy);
/*
* Invalidate order of both block and buddy.
*/
b->op->set_order(b, block, BUDDY_SYSTEM_INNER_BLOCK);
b->op->set_order(b, buddy, BUDDY_SYSTEM_INNER_BLOCK);
/*
* Coalesce block and buddy into one block.
*/
hlp = b->op->coalesce(b, block, buddy);
 
/*
* Set order of the coalesced block to i + 1.
*/
b->op->set_order(b, hlp, i + 1);
 
/*
* Recursively add the coalesced block to the list of
* order i + 1.
*/
buddy_system_free(b, hlp);
return;
}
}
 
/*
* Insert block into the list of order i.
*/
list_append(block, &b->order[i]);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/backend_phys.c
0,0 → 1,97
/*
* Copyright (c) 2006 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 genericmm
* @{
*/
 
/**
* @file
* @brief Backend for address space areas backed by continuous physical
* memory.
*/
 
#include <debug.h>
#include <arch/types.h>
#include <mm/as.h>
#include <mm/frame.h>
#include <mm/slab.h>
#include <memstr.h>
#include <macros.h>
#include <arch.h>
#include <align.h>
 
static int phys_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access);
static void phys_share(as_area_t *area);
 
mem_backend_t phys_backend = {
.page_fault = phys_page_fault,
.frame_free = NULL,
.share = phys_share
};
 
/** Service a page fault in the address space area backed by physical memory.
*
* The address space area and page tables must be already locked.
*
* @param area Pointer to the address space area.
* @param addr Faulting virtual address.
* @param access Access mode that caused the fault (i.e. read/write/exec).
*
* @return AS_PF_FAULT on failure (i.e. page fault) or AS_PF_OK on success (i.e.
* serviced).
*/
int phys_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access)
{
uintptr_t base = area->backend_data.base;
 
if (!as_area_check_access(area, access))
return AS_PF_FAULT;
 
ASSERT(addr - area->base < area->backend_data.frames * FRAME_SIZE);
page_mapping_insert(AS, addr, base + (addr - area->base),
as_area_get_flags(area));
if (!used_space_insert(area, ALIGN_DOWN(addr, PAGE_SIZE), 1))
panic("Cannot insert used space.");
 
return AS_PF_OK;
}
 
/** Share address space area backed by physical memory.
*
* Do actually nothing as sharing of address space areas
* that are backed up by physical memory is very easy.
* Note that the function must be defined so that
* as_area_share() will succeed.
*/
void phys_share(as_area_t *area)
{
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/mm/page.c
0,0 → 1,170
/*
* Copyright (c) 2001-2006 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 genericmm
* @{
*/
 
/**
* @file
* @brief Virtual Address Translation subsystem.
*
* This file contains code for creating, destroying and searching
* mappings between virtual addresses and physical addresses.
* Functions here are mere wrappers that call the real implementation.
* They however, define the single interface.
*/
 
/*
* Note on memory prefetching and updating memory mappings, also described in:
* AMD x86-64 Architecture Programmer's Manual, Volume 2, System Programming,
* 7.2.1 Special Coherency Considerations.
*
* The processor which modifies a page table mapping can access prefetched data
* from the old mapping. In order to prevent this, we place a memory barrier
* after a mapping is updated.
*
* We assume that the other processors are either not using the mapping yet
* (i.e. during the bootstrap) or are executing the TLB shootdown code. While
* we don't care much about the former case, the processors in the latter case
* will do an implicit serialization by virtue of running the TLB shootdown
* interrupt handler.
*/
 
#include <mm/page.h>
#include <arch/mm/page.h>
#include <arch/mm/asid.h>
#include <mm/as.h>
#include <mm/frame.h>
#include <arch/barrier.h>
#include <arch/types.h>
#include <arch/asm.h>
#include <memstr.h>
#include <debug.h>
#include <arch.h>
 
/** Virtual operations for page subsystem. */
page_mapping_operations_t *page_mapping_operations = NULL;
 
void page_init(void)
{
page_arch_init();
}
 
/** Map memory structure
*
* Identity-map memory structure
* considering possible crossings
* of page boundaries.
*
* @param s Address of the structure.
* @param size Size of the structure.
*/
void map_structure(uintptr_t s, size_t size)
{
int i, cnt, length;
 
length = size + (s - (s & ~(PAGE_SIZE - 1)));
cnt = length / PAGE_SIZE + (length % PAGE_SIZE > 0);
 
for (i = 0; i < cnt; i++)
page_mapping_insert(AS_KERNEL, s + i * PAGE_SIZE,
s + i * PAGE_SIZE, PAGE_NOT_CACHEABLE | PAGE_WRITE);
 
/* Repel prefetched accesses to the old mapping. */
memory_barrier();
}
 
/** Insert mapping of page to frame.
*
* Map virtual address page to physical address frame
* using flags. Allocate and setup any missing page tables.
*
* The page table must be locked and interrupts must be disabled.
*
* @param as Address space to wich page belongs.
* @param page Virtual address of the page to be mapped.
* @param frame Physical address of memory frame to which the mapping is
* done.
* @param flags Flags to be used for mapping.
*/
void page_mapping_insert(as_t *as, uintptr_t page, uintptr_t frame, int flags)
{
ASSERT(page_mapping_operations);
ASSERT(page_mapping_operations->mapping_insert);
page_mapping_operations->mapping_insert(as, page, frame, flags);
/* Repel prefetched accesses to the old mapping. */
memory_barrier();
}
 
/** Remove mapping of page.
*
* Remove any mapping of page within address space as.
* TLB shootdown should follow in order to make effects of
* this call visible.
*
* The page table must be locked and interrupts must be disabled.
*
* @param as Address space to wich page belongs.
* @param page Virtual address of the page to be demapped.
*/
void page_mapping_remove(as_t *as, uintptr_t page)
{
ASSERT(page_mapping_operations);
ASSERT(page_mapping_operations->mapping_remove);
page_mapping_operations->mapping_remove(as, page);
 
/* Repel prefetched accesses to the old mapping. */
memory_barrier();
}
 
/** Find mapping for virtual page
*
* Find mapping for virtual page.
*
* The page table must be locked and interrupts must be disabled.
*
* @param as Address space to wich page belongs.
* @param page Virtual page.
*
* @return NULL if there is no such mapping; requested mapping
* otherwise.
*/
pte_t *page_mapping_find(as_t *as, uintptr_t page)
{
ASSERT(page_mapping_operations);
ASSERT(page_mapping_operations->mapping_find);
 
return page_mapping_operations->mapping_find(as, page);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/syscall/syscall.c
0,0 → 1,168
/*
* Copyright (c) 2005 Martin Decky
* 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 Syscall table and syscall wrappers.
*/
 
#include <syscall/syscall.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <proc/program.h>
#include <mm/as.h>
#include <print.h>
#include <arch.h>
#include <debug.h>
#include <ddi/device.h>
#include <ipc/sysipc.h>
#include <synch/futex.h>
#include <synch/smc.h>
#include <ddi/ddi.h>
#include <ipc/event.h>
#include <security/cap.h>
#include <sysinfo/sysinfo.h>
#include <console/console.h>
#include <udebug/udebug.h>
 
/** Dispatch system call */
unative_t syscall_handler(unative_t a1, unative_t a2, unative_t a3,
unative_t a4, unative_t a5, unative_t a6, unative_t id)
{
unative_t rc;
 
#ifdef CONFIG_UDEBUG
bool debug;
 
/*
* Early check for undebugged tasks. We do not lock anything as this
* test need not be precise in either way.
*/
debug = THREAD->udebug.active;
if (debug) {
udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, 0, false);
}
#endif
if (id < SYSCALL_END) {
rc = syscall_table[id](a1, a2, a3, a4, a5, a6);
} else {
printf("Task %" PRIu64": Unknown syscall %#" PRIxn, TASK->taskid, id);
task_kill(TASK->taskid);
thread_exit();
}
if (THREAD->interrupted)
thread_exit();
#ifdef CONFIG_UDEBUG
if (debug) {
udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, rc, true);
/*
* Stopping point needed for tasks that only invoke
* non-blocking system calls. Not needed if the task
* is not being debugged (it cannot block here).
*/
udebug_stoppable_begin();
udebug_stoppable_end();
}
#endif
return rc;
}
 
syshandler_t syscall_table[SYSCALL_END] = {
(syshandler_t) sys_klog,
(syshandler_t) sys_tls_set,
/* Thread and task related syscalls. */
(syshandler_t) sys_thread_create,
(syshandler_t) sys_thread_exit,
(syshandler_t) sys_thread_get_id,
(syshandler_t) sys_task_get_id,
(syshandler_t) sys_task_set_name,
(syshandler_t) sys_program_spawn_loader,
/* Synchronization related syscalls. */
(syshandler_t) sys_futex_sleep_timeout,
(syshandler_t) sys_futex_wakeup,
(syshandler_t) sys_smc_coherence,
/* Address space related syscalls. */
(syshandler_t) sys_as_area_create,
(syshandler_t) sys_as_area_resize,
(syshandler_t) sys_as_area_change_flags,
(syshandler_t) sys_as_area_destroy,
/* IPC related syscalls. */
(syshandler_t) sys_ipc_call_sync_fast,
(syshandler_t) sys_ipc_call_sync_slow,
(syshandler_t) sys_ipc_call_async_fast,
(syshandler_t) sys_ipc_call_async_slow,
(syshandler_t) sys_ipc_answer_fast,
(syshandler_t) sys_ipc_answer_slow,
(syshandler_t) sys_ipc_forward_fast,
(syshandler_t) sys_ipc_forward_slow,
(syshandler_t) sys_ipc_wait_for_call,
(syshandler_t) sys_ipc_hangup,
(syshandler_t) sys_ipc_register_irq,
(syshandler_t) sys_ipc_unregister_irq,
 
/* Event notification syscalls. */
(syshandler_t) sys_event_subscribe,
/* Capabilities related syscalls. */
(syshandler_t) sys_cap_grant,
(syshandler_t) sys_cap_revoke,
/* DDI related syscalls. */
(syshandler_t) sys_device_assign_devno,
(syshandler_t) sys_physmem_map,
(syshandler_t) sys_iospace_enable,
(syshandler_t) sys_preempt_control,
/* Sysinfo syscalls */
(syshandler_t) sys_sysinfo_valid,
(syshandler_t) sys_sysinfo_value,
/* Debug calls */
(syshandler_t) sys_debug_enable_console,
(syshandler_t) sys_debug_disable_console,
(syshandler_t) sys_ipc_connect_kbox
};
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/syscall/copy.c
0,0 → 1,131
/*
* Copyright (c) 2006 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 generic
* @{
*/
 
/**
* @file
* @brief Copying between kernel and userspace.
*
* This file contains sanitized functions for copying data
* between kernel and userspace.
*/
 
#include <syscall/copy.h>
#include <proc/thread.h>
#include <mm/as.h>
#include <macros.h>
#include <arch.h>
#include <errno.h>
 
/** Copy data from userspace to kernel.
*
* Provisions are made to return value even after page fault.
*
* This function can be called only from syscall.
*
* @param dst Destination kernel address.
* @param uspace_src Source userspace address.
* @param size Size of the data to be copied.
*
* @return 0 on success or error code from @ref errno.h.
*/
int copy_from_uspace(void *dst, const void *uspace_src, size_t size)
{
ipl_t ipl;
int rc;
ASSERT(THREAD);
ASSERT(!THREAD->in_copy_from_uspace);
if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
if (overlaps((uintptr_t) uspace_src, size,
KERNEL_ADDRESS_SPACE_START, KERNEL_ADDRESS_SPACE_END-KERNEL_ADDRESS_SPACE_START)) {
/*
* The userspace source block conflicts with kernel address space.
*/
return EPERM;
}
}
ipl = interrupts_disable();
THREAD->in_copy_from_uspace = true;
rc = memcpy_from_uspace(dst, uspace_src, size);
 
THREAD->in_copy_from_uspace = false;
 
interrupts_restore(ipl);
return !rc ? EPERM : 0;
}
 
/** Copy data from kernel to userspace.
*
* Provisions are made to return value even after page fault.
*
* This function can be called only from syscall.
*
* @param uspace_dst Destination userspace address.
* @param src Source kernel address.
* @param size Size of the data to be copied.
*
* @return 0 on success or error code from @ref errno.h.
*/
int copy_to_uspace(void *uspace_dst, const void *src, size_t size)
{
ipl_t ipl;
int rc;
ASSERT(THREAD);
ASSERT(!THREAD->in_copy_to_uspace);
if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
if (overlaps((uintptr_t) uspace_dst, size,
KERNEL_ADDRESS_SPACE_START, KERNEL_ADDRESS_SPACE_END-KERNEL_ADDRESS_SPACE_START)) {
/*
* The userspace destination block conflicts with kernel address space.
*/
return EPERM;
}
}
ipl = interrupts_disable();
THREAD->in_copy_to_uspace = true;
rc = memcpy_to_uspace(uspace_dst, src, size);
 
THREAD->in_copy_to_uspace = false;
 
interrupts_restore(ipl);
return !rc ? EPERM : 0;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/udebug/udebug.c
0,0 → 1,461
/*
* 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.
*/
#include <synch/waitq.h>
#include <debug.h>
#include <udebug/udebug.h>
#include <errno.h>
#include <print.h>
#include <arch.h>
 
 
/** 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);
 
ut->go_call = NULL;
ut->uspace_state = NULL;
ut->go = false;
ut->stoppable = true;
ut->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);
}
 
/** 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);
 
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.active == true &&
THREAD->udebug.go == false) {
/*
* 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)
{
restart:
mutex_lock(&TASK->udebug.lock);
mutex_lock(&THREAD->udebug.lock);
 
if (THREAD->udebug.active && THREAD->udebug.go == false) {
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);
}
}
 
/** Upon being scheduled to run, check if the current thread should stop.
*
* This function is called from clock().
*/
void udebug_before_thread_runs(void)
{
/* Check if we're supposed to stop */
udebug_stoppable_begin();
udebug_stoppable_end();
}
 
/** 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;
 
mutex_lock(&TASK->udebug.lock);
mutex_lock(&THREAD->udebug.lock);
 
/* Must only generate events when in debugging session and is go. */
if (THREAD->udebug.active != true || THREAD->udebug.go == false ||
(TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
mutex_unlock(&THREAD->udebug.lock);
mutex_unlock(&TASK->udebug.lock);
return;
}
 
/* Fill in the GO response. */
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);
 
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.go is false 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.go = false;
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);
}
 
/** Thread-creation event hook combined with attaching the thread.
*
* Must be called when a new userspace thread is created in the debugged
* task. Generates a THREAD_B event. Also attaches the thread @a t
* to the task @a ta.
*
* This is necessary to avoid a race condition where the BEGIN and THREAD_READ
* requests would be handled inbetween attaching the thread and checking it
* for being in a debugging session to send the THREAD_B event. We could then
* either miss threads or get some threads both in the thread list
* and get a THREAD_B event for them.
*
* @param t Structure of the thread being created. Not locked, as the
* thread is not executing yet.
* @param ta Task to which the thread should be attached.
*/
void udebug_thread_b_event_attach(struct thread *t, struct task *ta)
{
call_t *call;
 
mutex_lock(&TASK->udebug.lock);
mutex_lock(&THREAD->udebug.lock);
 
thread_attach(t, ta);
 
LOG("Check state");
 
/* Must only generate events when in debugging session */
if (THREAD->udebug.active != true) {
LOG("udebug.active: %s, udebug.go: %s",
THREAD->udebug.active ? "Yes(+)" : "No",
THREAD->udebug.go ? "Yes(-)" : "No");
mutex_unlock(&THREAD->udebug.lock);
mutex_unlock(&TASK->udebug.lock);
return;
}
 
LOG("Trigger event");
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.go is false 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.go = false;
THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
 
ipc_answer(&TASK->answerbox, call);
 
mutex_unlock(&THREAD->udebug.lock);
mutex_unlock(&TASK->udebug.lock);
 
LOG("Wait for Go");
udebug_wait_for_go(&THREAD->udebug.go_wq);
}
 
/** 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;
 
mutex_lock(&TASK->udebug.lock);
mutex_lock(&THREAD->udebug.lock);
 
LOG("Check state");
 
/* Must only generate events when in debugging session. */
if (THREAD->udebug.active != true) {
LOG("udebug.active: %s, udebug.go: %s",
THREAD->udebug.active ? "Yes" : "No",
THREAD->udebug.go ? "Yes" : "No");
mutex_unlock(&THREAD->udebug.lock);
mutex_unlock(&TASK->udebug.lock);
return;
}
 
LOG("Trigger event");
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.active = false;
THREAD->udebug.cur_event = 0; /* none */
THREAD->udebug.go = false; /* set to initial value */
 
ipc_answer(&TASK->answerbox, call);
 
mutex_unlock(&THREAD->udebug.lock);
mutex_unlock(&TASK->udebug.lock);
 
/*
* 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;
 
if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING &&
ta->udebug.dt_state != UDEBUG_TS_ACTIVE) {
return EINVAL;
}
 
LOG("Task %" PRIu64, ta->taskid);
 
/* 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.active = false;
t->udebug.cur_event = 0; /* none */
 
/* Is the thread still go? */
if (t->udebug.go == true) {
/*
* Yes, so clear go. As active == false,
* this doesn't affect anything.
*/
t->udebug.go = false;
 
/* Answer GO call */
LOG("Answer GO call with EVENT_FINISHED.");
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;
 
return 0;
}
 
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/udebug/udebug_ops.c
0,0 → 1,507
/*
* 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 <print.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
* is (or is not) go according to being_go (typically false).
* It also locks t->udebug.lock, making sure that t->udebug.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 being_go Required thread state.
*
* Returns EOK if all went well, or an error code otherwise.
*/
static int _thread_op_begin(thread_t *t, bool being_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.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 active == true, TASK->udebug.lock
* is enough to ensure its existence and that 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.go != being_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("Debugging task %llu", TASK->taskid);
mutex_lock(&TASK->udebug.lock);
 
if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
mutex_unlock(&TASK->udebug.lock);
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.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.active = true;
mutex_unlock(&t->udebug.lock);
}
 
mutex_unlock(&TASK->udebug.lock);
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("Task %" PRIu64, TASK->taskid);
 
mutex_lock(&TASK->udebug.lock);
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("mask = 0x%x", mask);
 
mutex_lock(&TASK->udebug.lock);
 
if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
mutex_unlock(&TASK->udebug.lock);
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. Being 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.go = true;
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()");
 
/*
* 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.go = false;
 
if (t->udebug.stoppable != true) {
/* Answer will be sent when the thread becomes stoppable. */
_thread_op_end(t);
return 0;
}
 
/*
* Answer GO call.
*/
 
/* 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);
 
THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
 
_thread_op_end(t);
 
mutex_lock(&TASK->udebug.lock);
ipc_answer(&TASK->answerbox, call);
mutex_unlock(&TASK->udebug.lock);
 
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()");
 
/* 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;
}
 
/** @}
*/
/tags/0.4.1/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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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->kb.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;
}
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/interrupt/interrupt.c
0,0 → 1,188
/*
* Copyright (c) 2005 Ondrej Palkovsky
* 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 genericinterrupt
* @{
*/
/**
* @file
* @brief Interrupt redirector.
*
* This file provides means of registering interrupt handlers
* by kernel functions and calling the handlers when interrupts
* occur.
*/
 
#include <interrupt.h>
#include <debug.h>
#include <console/kconsole.h>
#include <console/console.h>
#include <console/cmd.h>
#include <panic.h>
#include <print.h>
#include <symtab.h>
 
static struct {
const char *name;
iroutine f;
} exc_table[IVT_ITEMS];
 
SPINLOCK_INITIALIZE(exctbl_lock);
 
/** Register exception handler
*
* @param n Exception number
* @param name Description
* @param f Exception handler
*/
iroutine exc_register(int n, const char *name, iroutine f)
{
ASSERT(n < IVT_ITEMS);
iroutine old;
spinlock_lock(&exctbl_lock);
old = exc_table[n].f;
exc_table[n].f = f;
exc_table[n].name = name;
spinlock_unlock(&exctbl_lock);
return old;
}
 
/** Dispatch exception according to exception table
*
* Called directly from the assembler code.
* CPU is interrupts_disable()'d.
*/
void exc_dispatch(int n, istate_t *istate)
{
ASSERT(n < IVT_ITEMS);
 
#ifdef CONFIG_UDEBUG
if (THREAD) THREAD->udebug.uspace_state = istate;
#endif
exc_table[n].f(n + IVT_FIRST, istate);
 
#ifdef CONFIG_UDEBUG
if (THREAD) THREAD->udebug.uspace_state = NULL;
#endif
 
/* This is a safe place to exit exiting thread */
if (THREAD && THREAD->interrupted && istate_from_uspace(istate))
thread_exit();
}
 
/** Default 'null' exception handler */
static void exc_undef(int n, istate_t *istate)
{
fault_if_from_uspace(istate, "Unhandled exception %d.", n);
panic("Unhandled exception %d.", n);
}
 
#ifdef CONFIG_KCONSOLE
 
/** kconsole cmd - print all exceptions */
static int cmd_exc_print(cmd_arg_t *argv)
{
#if (IVT_ITEMS > 0)
unsigned int i;
char *symbol;
 
spinlock_lock(&exctbl_lock);
 
#ifdef __32_BITS__
printf("Exc Description Handler Symbol\n");
printf("--- -------------------- ---------- --------\n");
#endif
 
#ifdef __64_BITS__
printf("Exc Description Handler Symbol\n");
printf("--- -------------------- ------------------ --------\n");
#endif
for (i = 0; i < IVT_ITEMS; i++) {
symbol = symtab_fmt_name_lookup((unative_t) exc_table[i].f);
 
#ifdef __32_BITS__
printf("%-3u %-20s %10p %s\n", i + IVT_FIRST, exc_table[i].name,
exc_table[i].f, symbol);
#endif
 
#ifdef __64_BITS__
printf("%-3u %-20s %18p %s\n", i + IVT_FIRST, exc_table[i].name,
exc_table[i].f, symbol);
#endif
if (((i + 1) % 20) == 0) {
printf(" -- Press any key to continue -- ");
spinlock_unlock(&exctbl_lock);
indev_pop_character(stdin);
spinlock_lock(&exctbl_lock);
printf("\n");
}
}
spinlock_unlock(&exctbl_lock);
#endif
return 1;
}
 
 
static cmd_info_t exc_info = {
.name = "exc",
.description = "Print exception table.",
.func = cmd_exc_print,
.help = NULL,
.argc = 0,
.argv = NULL
};
 
#endif
 
/** Initialize generic exception handling support */
void exc_init(void)
{
int i;
 
for (i = 0; i < IVT_ITEMS; i++)
exc_register(i, "undef", (iroutine) exc_undef);
 
#ifdef CONFIG_KCONSOLE
cmd_initialize(&exc_info);
if (!cmd_register(&exc_info))
printf("Cannot register command %s\n", exc_info.name);
#endif
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/cpu/cpu.c
0,0 → 1,113
/*
* Copyright (c) 2001-2004 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 generic
* @{
*/
 
/**
* @file
* @brief CPU subsystem initialization and listing.
*/
#include <cpu.h>
#include <arch.h>
#include <arch/cpu.h>
#include <mm/slab.h>
#include <mm/page.h>
#include <mm/frame.h>
#include <arch/types.h>
#include <config.h>
#include <panic.h>
#include <memstr.h>
#include <adt/list.h>
#include <print.h>
 
cpu_t *cpus;
 
/** Initialize CPUs
*
* Initialize kernel CPUs support.
*
*/
void cpu_init(void) {
unsigned int i, j;
#ifdef CONFIG_SMP
if (config.cpu_active == 1) {
#endif /* CONFIG_SMP */
cpus = (cpu_t *) malloc(sizeof(cpu_t) * config.cpu_count,
FRAME_ATOMIC);
if (!cpus)
panic("Cannot allocate CPU structures.");
 
/* initialize everything */
memsetb(cpus, sizeof(cpu_t) * config.cpu_count, 0);
 
for (i = 0; i < config.cpu_count; i++) {
cpus[i].stack = (uint8_t *) frame_alloc(STACK_FRAMES, FRAME_KA | FRAME_ATOMIC);
cpus[i].id = i;
spinlock_initialize(&cpus[i].lock, "cpu_t.lock");
 
for (j = 0; j < RQ_COUNT; j++) {
spinlock_initialize(&cpus[i].rq[j].lock, "rq_t.lock");
list_initialize(&cpus[i].rq[j].rq_head);
}
}
#ifdef CONFIG_SMP
}
#endif /* CONFIG_SMP */
 
CPU = &cpus[config.cpu_active - 1];
CPU->active = 1;
CPU->tlb_active = 1;
cpu_identify();
cpu_arch_init();
}
 
/** List all processors. */
void cpu_list(void)
{
unsigned int i;
 
for (i = 0; i < config.cpu_count; i++) {
if (cpus[i].active)
cpu_print_report(&cpus[i]);
else
printf("cpu%u: not active\n", i);
}
}
 
/** @}
*/
 
/tags/0.4.1/kernel/generic/src/sysinfo/sysinfo.c
0,0 → 1,322
/*
* Copyright (c) 2006 Jakub Vana
* 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
*/
 
#include <sysinfo/sysinfo.h>
#include <mm/slab.h>
#include <print.h>
#include <syscall/copy.h>
 
sysinfo_item_t *_root = NULL;
 
 
static sysinfo_item_t *sysinfo_find_item(const char *name, sysinfo_item_t *subtree)
{
if (subtree == NULL)
return NULL;
while (subtree != NULL) {
int i = 0;
char *a = (char *) name;
char *b = subtree->name;
while ((a[i] == b[i]) && (b[i]))
i++;
if ((!a[i]) && (!b[i])) /* Last name in path matches */
return subtree;
if ((a[i] == '.') && (!b[i])) { /* Middle name in path matches */
if (subtree->subinfo_type == SYSINFO_SUBINFO_TABLE)
return sysinfo_find_item(a + i + 1, subtree->subinfo.table);
//if (subtree->subinfo_type == SYSINFO_SUBINFO_FUNCTION) /* Subinfo managed by subsystem */
// return NULL;
return NULL; /* No subinfo */
}
/* No matches try next */
subtree = subtree->next;
i = 0;
}
return NULL;
}
 
static sysinfo_item_t *sysinfo_create_path(const char *name, sysinfo_item_t **psubtree)
{
sysinfo_item_t *subtree;
subtree = *psubtree;
if (subtree == NULL) {
sysinfo_item_t *item = malloc(sizeof(sysinfo_item_t), 0);
int i = 0, j;
ASSERT(item);
*psubtree = item;
item->next = NULL;
item->val_type = SYSINFO_VAL_UNDEFINED;
item->subinfo.table = NULL;
 
while (name[i] && (name[i] != '.'))
i++;
item->name = malloc(i, 0);
ASSERT(item->name);
 
for (j = 0; j < i; j++)
item->name[j] = name[j];
item->name[j] = 0;
if (name[i]) { /* =='.' */
item->subinfo_type = SYSINFO_SUBINFO_TABLE;
return sysinfo_create_path(name + i + 1, &(item->subinfo.table));
}
item->subinfo_type = SYSINFO_SUBINFO_NONE;
return item;
}
 
while (subtree != NULL) {
int i = 0, j;
char *a = (char *) name;
char *b = subtree->name;
while ((a[i] == b[i]) && (b[i]))
i++;
if ((!a[i]) && (!b[i])) /* Last name in path matches */
return subtree;
if ((a[i] == '.') && (!b[i])) { /* Middle name in path matches */
if (subtree->subinfo_type == SYSINFO_SUBINFO_TABLE)
return sysinfo_create_path(a + i + 1, &(subtree->subinfo.table));
if (subtree->subinfo_type == SYSINFO_SUBINFO_NONE) {
subtree->subinfo_type = SYSINFO_SUBINFO_TABLE;
return sysinfo_create_path(a + i + 1,&(subtree->subinfo.table));
}
//if (subtree->subinfo_type == SYSINFO_SUBINFO_FUNCTION) /* Subinfo managed by subsystem */
// return NULL;
return NULL;
}
/* No matches try next or create new*/
if (subtree->next == NULL) {
sysinfo_item_t *item = malloc(sizeof(sysinfo_item_t), 0);
ASSERT(item);
subtree->next = item;
item->next = NULL;
item->val_type = SYSINFO_VAL_UNDEFINED;
item->subinfo.table = NULL;
 
i = 0;
while (name[i] && (name[i] != '.'))
i++;
 
item->name = malloc(i, 0);
ASSERT(item->name);
for (j = 0; j < i; j++)
item->name[j] = name[j];
item->name[j] = 0;
 
if(name[i]) { /* =='.' */
item->subinfo_type = SYSINFO_SUBINFO_TABLE;
return sysinfo_create_path(name + i + 1, &(item->subinfo.table));
}
item->subinfo_type = SYSINFO_SUBINFO_NONE;
return item;
} else {
subtree = subtree->next;
i = 0;
}
}
 
panic("Not reached.");
return NULL;
}
 
void sysinfo_set_item_val(const char *name, sysinfo_item_t **root, unative_t val)
{
if (root == NULL)
root = &_root;
/* If already created create only returns pointer
If not, create it */
sysinfo_item_t *item = sysinfo_create_path(name, root);
if (item != NULL) { /* If in subsystem, unable to create or return so unable to set */
item->val.val = val;
item->val_type = SYSINFO_VAL_VAL;
}
}
 
void sysinfo_set_item_function(const char *name, sysinfo_item_t **root, sysinfo_val_fn_t fn)
{
if (root == NULL)
root = &_root;
/* If already created create only returns pointer
If not, create it */
sysinfo_item_t *item = sysinfo_create_path(name, root);
if (item != NULL) { /* If in subsystem, unable to create or return so unable to set */
item->val.fn = fn;
item->val_type = SYSINFO_VAL_FUNCTION;
}
}
 
 
void sysinfo_set_item_undefined(const char *name, sysinfo_item_t **root)
{
if (root == NULL)
root = &_root;
/* If already created create only returns pointer
If not, create it */
sysinfo_item_t *item = sysinfo_create_path(name, root);
if (item != NULL)
item->val_type = SYSINFO_VAL_UNDEFINED;
}
 
 
void sysinfo_dump(sysinfo_item_t **proot, int depth)
{
sysinfo_item_t *root;
if (proot == NULL)
proot = &_root;
root = *proot;
while (root != NULL) {
int i;
unative_t val = 0;
char *vtype = NULL;
for (i = 0; i < depth; i++)
printf(" ");
switch (root->val_type) {
case SYSINFO_VAL_UNDEFINED:
val = 0;
vtype = "UND";
break;
case SYSINFO_VAL_VAL:
val = root->val.val;
vtype = "VAL";
break;
case SYSINFO_VAL_FUNCTION:
val = ((sysinfo_val_fn_t) (root->val.fn)) (root);
vtype = "FUN";
break;
}
printf("%s %s val:%" PRIun "(%" PRIxn ") sub:%s\n", root->name, vtype, val,
val, (root->subinfo_type == SYSINFO_SUBINFO_NONE) ?
"NON" : ((root->subinfo_type == SYSINFO_SUBINFO_TABLE) ?
"TAB" : "FUN"));
if (root->subinfo_type == SYSINFO_SUBINFO_TABLE)
sysinfo_dump(&(root -> subinfo.table), depth + 1);
root = root->next;
}
}
 
sysinfo_rettype_t sysinfo_get_val(const char *name, sysinfo_item_t **root)
{
// TODO: Implement Subsystem subinfo (by function implemented subinfo)
 
sysinfo_rettype_t ret = {0, false};
 
if (root == NULL)
root = &_root;
sysinfo_item_t *item = sysinfo_find_item(name, *root);
if (item != NULL) {
if (item->val_type == SYSINFO_VAL_UNDEFINED)
return ret;
else
ret.valid = true;
if (item->val_type == SYSINFO_VAL_VAL)
ret.val = item->val.val;
else
ret.val = ((sysinfo_val_fn_t) (item->val.fn)) (item);
}
return ret;
}
 
#define SYSINFO_MAX_LEN 1024
 
unative_t sys_sysinfo_valid(unative_t ptr, unative_t len)
{
char *str;
sysinfo_rettype_t ret = {0, 0};
 
if (len > SYSINFO_MAX_LEN)
return ret.valid;
str = malloc(len + 1, 0);
ASSERT(str);
if (!((copy_from_uspace(str, (void *) ptr, len + 1)) || (str[len])))
ret = sysinfo_get_val(str, NULL);
free(str);
return ret.valid;
}
 
unative_t sys_sysinfo_value(unative_t ptr, unative_t len)
{
char *str;
sysinfo_rettype_t ret = {0, 0};
if (len > SYSINFO_MAX_LEN)
return ret.val;
str = malloc(len + 1, 0);
ASSERT(str);
if (!((copy_from_uspace(str, (void *) ptr, len + 1)) || (str[len])))
ret = sysinfo_get_val(str, NULL);
free(str);
return ret.val;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/security/cap.c
0,0 → 1,181
/*
* Copyright (c) 2006 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 generic
* @{
*/
 
/**
* @file cap.c
* @brief Capabilities control.
*
* @see cap.h
*/
#include <security/cap.h>
#include <proc/task.h>
#include <synch/spinlock.h>
#include <syscall/sysarg64.h>
#include <syscall/copy.h>
#include <arch.h>
#include <errno.h>
 
/** Set capabilities.
*
* @param t Task whose capabilities are to be changed.
* @param caps New set of capabilities.
*/
void cap_set(task_t *t, cap_t caps)
{
ipl_t ipl;
ipl = interrupts_disable();
spinlock_lock(&t->lock);
t->capabilities = caps;
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
}
 
/** Get capabilities.
*
* @param t Task whose capabilities are to be returned.
* @return Task's capabilities.
*/
cap_t cap_get(task_t *t)
{
ipl_t ipl;
cap_t caps;
ipl = interrupts_disable();
spinlock_lock(&t->lock);
caps = t->capabilities;
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
return caps;
}
 
/** Grant capabilities to a task.
*
* The calling task must have the CAP_CAP capability.
*
* @param uspace_taskid_arg Userspace structure holding destination task ID.
* @param caps Capabilities to grant.
*
* @return Zero on success or an error code from @ref errno.h.
*/
unative_t sys_cap_grant(sysarg64_t *uspace_taskid_arg, cap_t caps)
{
sysarg64_t taskid_arg;
task_t *t;
ipl_t ipl;
int rc;
if (!(cap_get(TASK) & CAP_CAP))
return (unative_t) EPERM;
rc = copy_from_uspace(&taskid_arg, uspace_taskid_arg, sizeof(sysarg64_t));
if (rc != 0)
return (unative_t) rc;
ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
t = task_find_by_id((task_id_t) taskid_arg.value);
if ((!t) || (!context_check(CONTEXT, t->context))) {
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return (unative_t) ENOENT;
}
spinlock_lock(&t->lock);
cap_set(t, cap_get(t) | caps);
spinlock_unlock(&t->lock);
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return 0;
}
 
/** Revoke capabilities from a task.
*
* The calling task must have the CAP_CAP capability or the caller must
* attempt to revoke capabilities from itself.
*
* @param uspace_taskid_arg Userspace structure holding destination task ID.
* @param caps Capabilities to revoke.
*
* @return Zero on success or an error code from @ref errno.h.
*/
unative_t sys_cap_revoke(sysarg64_t *uspace_taskid_arg, cap_t caps)
{
sysarg64_t taskid_arg;
task_t *t;
ipl_t ipl;
int rc;
rc = copy_from_uspace(&taskid_arg, uspace_taskid_arg, sizeof(sysarg64_t));
if (rc != 0)
return (unative_t) rc;
 
ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
t = task_find_by_id((task_id_t) taskid_arg.value);
if ((!t) || (!context_check(CONTEXT, t->context))) {
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return (unative_t) ENOENT;
}
 
/*
* Revoking capabilities is different from granting them in that
* a task can revoke capabilities from itself even if it
* doesn't have CAP_CAP.
*/
if (!(cap_get(TASK) & CAP_CAP) || !(t == TASK)) {
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return (unative_t) EPERM;
}
spinlock_lock(&t->lock);
cap_set(t, cap_get(t) & ~caps);
spinlock_unlock(&t->lock);
 
spinlock_unlock(&tasks_lock);
 
interrupts_restore(ipl);
return 0;
}
 
/** @}
*/
 
/tags/0.4.1/kernel/generic/src/smp/smp.c
0,0 → 1,46
/*
* Copyright (c) 2006 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 generic
* @{
*/
 
/**
* @file
*/
#include <smp/smp.h>
 
#ifdef CONFIG_SMP
 
waitq_t ap_completion_wq;
 
#endif /* CONFIG_SMP */
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/smp/ipi.c
0,0 → 1,70
/*
* Copyright (c) 2005 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 generic
* @{
*/
 
/**
* @file
* @brief Generic IPI interface.
*/
#ifdef CONFIG_SMP
 
#include <smp/ipi.h>
#include <config.h>
 
 
/** Broadcast IPI message
*
* Broadcast IPI message to all CPUs.
*
* @param ipi Message to broadcast.
*
* @bug The decision whether to actually send the IPI must be based
* on a different criterion. The current version has
* problems when some of the detected CPUs are marked
* disabled in machine configuration.
*/
void ipi_broadcast(int ipi)
{
/*
* Provisions must be made to avoid sending IPI:
* - before all CPU's were configured to accept the IPI
* - if there is only one CPU but the kernel was compiled with CONFIG_SMP
*/
 
if ((config.cpu_active > 1) && (config.cpu_active == config.cpu_count))
ipi_broadcast_arch(ipi);
}
 
#endif /* CONFIG_SMP */
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/preempt/preemption.c
0,0 → 1,60
/*
* Copyright (c) 2005 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 generic
* @{
*/
 
/**
* @file preemption.c
* @brief Preemption control.
*/
#include <preemption.h>
#include <arch.h>
#include <arch/asm.h>
#include <arch/barrier.h>
#include <debug.h>
 
/** Increment preemption disabled counter. */
void preemption_disable(void)
{
THE->preemption_disabled++;
memory_barrier();
}
 
/** Decrement preemption disabled counter. */
void preemption_enable(void)
{
ASSERT(THE->preemption_disabled);
memory_barrier();
THE->preemption_disabled--;
}
 
/** @}
*/