From 1cc167e20f21d2e81527ca47ccd4a21a6198f86f Mon Sep 17 00:00:00 2001 From: bernie Date: Sun, 23 May 2004 17:27:00 +0000 Subject: [PATCH] Import kern/ subdirectory. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@2 38d2e660-2303-0410-9eaa-f027e97ec537 --- kern/event.h | 123 +++++++++++++++ kern/file.h | 55 +++++++ kern/heap.c | 178 ++++++++++++++++++++++ kern/heap.h | 41 +++++ kern/proc.c | 355 +++++++++++++++++++++++++++++++++++++++++++ kern/proc.h | 50 ++++++ kern/proc_p.h | 91 +++++++++++ kern/sem.c | 144 ++++++++++++++++++ kern/sem.h | 51 +++++++ kern/signal.c | 125 +++++++++++++++ kern/signal.h | 54 +++++++ kern/switch_avr.S | 102 +++++++++++++ kern/switch_dsp56k.c | 136 +++++++++++++++++ kern/switch_i196.s32 | 88 +++++++++++ kern/switch_i386.s | 62 ++++++++ kern/switch_win32.s | 56 +++++++ 16 files changed, 1711 insertions(+) create mode 100755 kern/event.h create mode 100755 kern/file.h create mode 100755 kern/heap.c create mode 100755 kern/heap.h create mode 100755 kern/proc.c create mode 100755 kern/proc.h create mode 100755 kern/proc_p.h create mode 100755 kern/sem.c create mode 100755 kern/sem.h create mode 100755 kern/signal.c create mode 100755 kern/signal.h create mode 100755 kern/switch_avr.S create mode 100755 kern/switch_dsp56k.c create mode 100755 kern/switch_i196.s32 create mode 100755 kern/switch_i386.s create mode 100755 kern/switch_win32.s diff --git a/kern/event.h b/kern/event.h new file mode 100755 index 00000000..23d3e525 --- /dev/null +++ b/kern/event.h @@ -0,0 +1,123 @@ +/*! + * \file + * + * + * \brief Events handling + * + * This module implements a common system for executing + * a user defined action calling a hook function. + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ +#ifndef KERN_EVENT_H +#define KERN_EVENT_H + +#include "config.h" + +#ifdef CONFIG_KERNEL + #include "config_kern.h" + #if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS + #include "signal.h" + #endif +#endif + + +/* Forward decl */ +struct Process; + + +/*! Event types */ +enum EventAction +{ + EVENT_IGNORE, /*!< No-op event */ +#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS + EVENT_SIGNAL, /*!< Send a signal to a process */ +#endif + EVENT_SOFTINT /*!< Trigger a software interrupt (i.e.: call user hook) */ +}; + +//! User defined callback type +typedef void (*Hook)(void *); + +typedef struct Event +{ + enum EventAction action; + union + { +#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS + struct + { + struct Process *sig_proc; /* Process to be signalled */ + sig_t sig_bit; /* Signal to send */ + } Sig; +#endif + struct + { + Hook func; /* Pointer to softint hook */ + void *user_data; /* Data to be passed back to user hook */ + } Int; + } Ev; +} Event; + + +/*! Initialize an event with a softint */ +#define INITEVENT_INT(e,f,u) \ + ((e)->action = EVENT_SOFTINT,(e)->Ev.Int.func = (f), (e)->Ev.Int.user_data = (u)) + + +#if defined(CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS + +/*! Initialize an event with a signal */ +#define INITEVENT_SIG(e,p,s) \ + ((e)->action = EVENT_SIGNAL,(e)->Ev.Sig.sig_proc = (p), (e)->Ev.Sig.sig_bit = (s)) + +/*! Trigger an event */ +#define DOEVENT(e) \ +do { \ + if ((e)->action == EVENT_SIGNAL) \ + sig_signal((e)->Ev.Sig.sig_proc, (e)->Sig.sig_bit); \ + else if ((e)->action == EVENT_SOFTINT) \ + (e)->Ev.Int.func((e)->Ev.Int.user_data); \ +} while (0) + +/*! Trigger an event (to be used inside interrupts) */ +#define DOEVENT_INTR(e) \ +do { \ + if ((e)->action == EVENT_SIGNAL) \ + _sig_signal((e)->Ev.Sig.sig_proc, (e)->Ev.Sig.sig_bit); \ + else if ((e)->action == EVENT_SOFTINT) \ + (e)->Ev.Int.func((e)->Ev.Int.user_data); \ +} while (0) + +#else /* !CONFIG_KERN_SIGNALS */ + +/*! Trigger an event */ +#define DOEVENT(e) \ +do { \ + if ((e)->action == EVENT_SOFTINT) \ + (e)->Ev.Int.func((e)->Ev.Int.user_data); \ +} while (0) + +/*! Trigger an event (to be used inside interrupts) */ +#define DOEVENT_INTR(e) \ +do { \ + if ((e)->action == EVENT_SOFTINT) \ + (e)->Ev.Int.func((e)->Ev.Int.user_data); \ +} while (0) + +#endif + +#endif /* KERN_EVENT_H */ diff --git a/kern/file.h b/kern/file.h new file mode 100755 index 00000000..e39fae61 --- /dev/null +++ b/kern/file.h @@ -0,0 +1,55 @@ +/*! + * \file + * + * + * \brief Interface to KFile virtual class + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ +#ifndef KERN_KFILE_H +#define KERN_KFILE_H + +#ifndef COMPILER_H +#include +#endif + +struct _KFile; + +typedef size_t (*ReadFunc_t) (struct _KFile *fd, char *buf, size_t size); +typedef size_t (*WriteFunc_t) (struct _KFile *fd, const char *buf, size_t size); +typedef bool (*SeekFunc_t) (struct _KFile *fd, int32_t offset); +typedef bool (*CloseFunc_t) (struct _KFile *fd); +typedef bool (*OpenFunc_t) (struct _KFile *fd, const char *name, int mode); + + +/* Context data for callback functions which operate on + * pseudo files. + */ +typedef struct _KFile +{ + ReadFunc_t Read; + WriteFunc_t Write; + SeekFunc_t Seek; +/* OpenFunc_t Open; unused */ + CloseFunc_t Close; + + /* NOTE: these must _NOT_ be size_t on 16bit CPUs! */ + uint32_t SeekPos; + uint32_t Size; +} KFile; + + +#endif /* KERN_KFILE_H */ diff --git a/kern/heap.c b/kern/heap.c new file mode 100755 index 00000000..b836cabf --- /dev/null +++ b/kern/heap.c @@ -0,0 +1,178 @@ +/*! + * \file + * + * + * \brief Heap subsystem (public interface). + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#include "heap.h" + +/* NOTE: struct size must be a 2's power! */ +typedef struct _MemChunk +{ + struct _MemChunk *next; + size_t size; +} MemChunk; + +static REGISTER MemChunk *FreeList; /* Head of the free list */ +static uint8_t Heap[HEAP_SIZE]; /* Heap memory block */ + + + +void heap_init(void) +{ +#ifdef _DEBUG + int i; + + /* Fill the heap with a "DEAD" code to help debugging */ + for (i = 0; i < HEAP_SIZE / sizeof (uint16_t); i++) + ((uint16_t *)Heap)[i] = MEM_FILL_CODE; +#endif + + /* Initialize heap with a single big chunk */ + FreeList = (MemChunk *)Heap; + FreeList->next = NULL; + FreeList->size = sizeof(Heap); +} + + +void *heap_alloc(size_t size) +{ + MemChunk *chunk, *prev; + + /* Round size up to the allocation granularity */ + size = ROUND2(size, sizeof(MemChunk)); + + /* Walk on the free list looking for any chunk big enough to + * fit the requested block size. + */ + for (prev = (MemChunk *)&FreeList, chunk = FreeList; + chunk; + prev = chunk, chunk = chunk->next) + { + if (chunk->size <= size) + { + if (chunk->size == size) + { + /* Just remove this chunk from the free list */ + prev->next = chunk->next; + return (void *)chunk; + } + else + { + /* Allocate from the END of an existing chunk */ + prev->size -= size; + return (void *)(((uint8_t *)prev) + prev->size); + } + } + } + + return NULL; /* fail */ +} + + +void heap_free(void *mem, size_t size) +{ + MemChunk *prev; + + /* Round size up to the allocation granularity */ + size = ROUND2(size, sizeof(MemChunk)); + + /* Special case: first chunk in the free list */ + if (((uint8_t *)mem) < ((uint8_t *)FreeList)) + { + /* Insert memory block before the current free list head */ + prev = (MemChunk *)mem; + prev->next = FreeList; + prev->size = size; + FreeList = prev; + } + else /* Normal case: not the first chunk in the free list */ + { + /* Walk on the free list. Stop at the insertion point */ + prev = FreeList; + while ((prev->next >= ((MemChunk *)mem)) || (!prev->next)) + prev = prev->next; + + /* Should it be merged with previous block? */ + if (((uint8_t *)prev) + prev->Size == ((uint8_t *)mem)) + { + /* Yes */ + prev->size += size; + } + else /* not merged with previous chunk */ + { + /* insert it after the previous node + * and move the 'prev' pointer forward + * for the following operations + */ + ((MemChunk *)mem)->next = prev->next; + prev->next = (MemChunk *)mem; + prev = (MemChunk *)mem; + } + } + + /* Also merge with next chunk? */ + if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next)) + { + prev->size += prev->next->size; + prev->next = prev->next->next; + } +} + +#ifdef __POSIX__ + +/*! ANSI/POSIX-like malloc() implementation based on heap_alloc()/heap_free() */ +void *malloc(size_t size) +{ + void *mem; + + size += sizeof(size_t); + + if (mem = heap_alloc(size)) + *((size_t *)mem)++ = size; + + return mem; +} + + +/*! ANSI/POSIX-like calloc() implementation based on heap_alloc()/heap_free() */ +void *calloc(size_t nelem, size_t size) +{ + void *mem, *tmp; + + size *= nelem; + + if (mem = malloc(size)) + { + tmp = mem; + while (size--) + ((uint8_t *)tmp++) = 0; + } + + return mem; +} + +/*! ANSI/POSIX-like free() implementation based on heap_alloc()/heap_free() */ +void free(void *mem) +{ + ((size_t *)mem)--; + heap_free(mem, *((size_t *)mem)); +} + +#endif /* __POSIX__ */ diff --git a/kern/heap.h b/kern/heap.h new file mode 100755 index 00000000..54174e6e --- /dev/null +++ b/kern/heap.h @@ -0,0 +1,41 @@ +/*! + * \file + * + * + * \brief Heap subsystem (public interface). + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#ifndef KERN_HEAP_H +#define KERN_HEAP_H + +#include "compiler.h" + + +/* Memory allocation services */ +void heap_init(void); +void *heap_alloc(size_t size); +void heap_free(void *mem, size_t size); + +#ifdef __POSIX__ /* unused */ +void *malloc(size_t size); +void *calloc(unsigned int nelem, size_t size); +void free(void * mem); +#endif /* __POSIX__ */ + +#endif /* KERN_HEAP_H */ + diff --git a/kern/proc.c b/kern/proc.c new file mode 100755 index 00000000..a11e4985 --- /dev/null +++ b/kern/proc.c @@ -0,0 +1,355 @@ +/** + * \file + * + * + * \brief Simple realtime multitasking scheduler. + * Context switching is only done cooperatively. + * + * \version $Id$ + * + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#include "cpu.h" +#include "proc_p.h" +#include "proc.h" +#include "event.h" +#include "hw.h" +#include + +#include /* memset() */ + +/* CPU dependent context switching routines */ +extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp); + +/* + * The scheduer tracks ready and waiting processes + * by enqueuing them in these lists. A pointer to the currently + * running process is stored in the CurrentProcess pointer. + * + * NOTE: these variables are protected by DI/EI locking + */ +REGISTER Process *CurrentProcess; +REGISTER List ProcReadyList; + +#if CONFIG_KERN_PREEMPTIVE +/* + * The time sharing scheduler forces a task switch when + * the current process has consumed its quantum. + */ +uint16_t Quantum; +#endif + + +/* In Win32 we must emulate stack on the real process stack */ +#if (ARCH & ARCH_EMUL) +extern List StackFreeList; +#endif + +/* The main process (the one that executes main()) */ +struct Process MainProcess; + +static void proc_init_struct(Process* proc) +{ +#if CONFIG_KERN_TIMER + INITEVENT_SIG(&proc->proc_timer.expire, proc, SIG_SINGLE); +#endif + +#if CONFIG_KERN_SIGNALS + proc->sig_recv = 0; +#endif + +#if CONFIG_KERN_HEAP + proc->flags = 0; +#endif +} + +void proc_init(void) +{ + INITLIST(&ProcReadyList); + + /* We "promote" the current context into a real process. The only thing we have + to do is create a PCB and make it current. We don't need to setup the stack + pointer because it will be written the first time we switch to another process. */ + proc_init_struct(&MainProcess); + CurrentProcess = &MainProcess; +} + + +/*! + * Create a new process, starting at the provided entry point. + * + * \return Process structure of new created process + * if successful, NULL otherwise. + */ +Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base) +{ + Process *proc; + size_t i; + size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t); +#if CONFIG_KERN_HEAP + bool free_stack = false; +#endif + +#if (ARCH & ARCH_EMUL) + /* Ignore stack provided by caller + * and use the large enough default instead + */ + stack_base = (cpustack_t *)StackFreeList.head; + REMOVE((Node *)stack_base); + stacksize = DEF_STACKSIZE; +#elif CONFIG_KERN_HEAP + /* Did the caller provide a stack for us? */ + if (!stack_base) + { + /* Did the caller specify the desired stack size? */ + if (!stacksize) + stacksize = CONFIG_KERN_DEFSTACKSIZE + sizeof(Process); + + /* Allocate stack dinamically */ + if (!(stack_base = heap_alloc(stacksize))) + return NULL; + + free_stack = true; + } +#else + /* Stack must have been provided by the user */ + ASSERT(stack_base); + ASSERT(stacksize); +#endif + +#ifdef _DEBUG + /* Fill-in the stack with a special marker to help debugging */ + memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t)); +#endif /* _DEBUG */ + + /* Initialize the process control block */ + if (CPU_STACK_GROWS_UPWARD) + { + proc = (Process*)stack_base; + proc->stack = stack_base + proc_size_words; + if (CPU_SP_ON_EMPTY_SLOT) + proc->stack++; + } + else + { + proc = (Process*)(stack_base + stacksize / sizeof(cpustack_t) - proc_size_words); + proc->stack = (cpustack_t*)proc; + if (CPU_SP_ON_EMPTY_SLOT) + proc->stack--; + } + + proc_init_struct(proc); + +#if CONFIG_KERN_HEAP + proc->stack_base = stack_base; + proc->stack_size = stack_size; + if (free_stack) + proc->flags |= PF_FREESTACK; +#endif + + /* Initialize process stack frame */ + CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit); + CPU_PUSH_CALL_CONTEXT(proc->stack, entry); + + /* Push a clean set of CPU registers for asm_switch_context() */ + for (i = 0; i < CPU_SAVED_REGS_CNT; i++) + CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i)); + + /* Add to ready list */ + DISABLE_INTS; + SCHED_ENQUEUE(proc); + ENABLE_INTS; + + return proc; +} + + +/*! + * System scheduler: pass CPU control to the next process in + * the ready queue. + * + * Saving and restoring the context on the stack is done + * by a CPU-dependent support routine which must usually be + * written in assembly. + */ +void proc_schedule(void) +{ + /* This function must not have any "auto" variables, otherwise + * the compiler might put them on the stack of the process + * being switched out. + */ + static Process *old_process; + + /* Remember old process to save its context later */ + old_process = CurrentProcess; + CurrentProcess = NULL; + + /* Poll on the ready queue for the first ready process + */ + for(;;) /* forever */ + { + /* Do CPU specific idle processing (ARGH, should be moved to the end of the loop!) */ + SCHEDULER_IDLE; + + DISABLE_INTS; + if (!ISLISTEMPTY(&ProcReadyList)) + { + /* Get process from ready list */ + CurrentProcess = (Process *)ProcReadyList.head; + REMOVE((Node *)CurrentProcess); + ENABLE_INTS; + break; + } + ENABLE_INTS; + } + + /* Optimization: don't switch contexts when the active + * process has not changed. + */ + if (CurrentProcess != old_process) + { + static cpustack_t* dummy; + +#if CONFIG_KERN_PREEMPTIVE + /* Reset quantum for this process */ + Quantum = CONFIG_KERN_QUANTUM; +#endif + + /* Save context of old process and switch to new process. If there is no + * old process, we save the old stack pointer into a dummy variable that + * we ignore. In fact, this happens only when the old process has just + * exited. + * TODO: Instead of physically clearing the process at exit time, a zombie + * list should be created. + */ + asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy); + } + + /* This RET resumes the execution on the new process */ +} + + +/*! + * Terminate the current process + */ +void proc_exit(void) +{ +#if CONFIG_KERN_HEAP + /* The following code is BROKEN. + * We are freeing our own stack before entering proc_schedule() + * BAJO: A correct fix would be to rearrange the scheduler with + * an additional parameter which frees the old stack/process + * after a context switch. + */ + if (CurrentProcess->flags & PF_FREESTACK) + heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size); + heap_free(CurrentProcess); +#endif + +#if (ARCH & ARCH_EMUL) +#error This is wrong + /* Reinsert process stack in free list */ + ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack + - (DEF_STACKSIZE / sizeof(cpustack_t)))); + + /* NOTE: At this point the first two words of what used + * to be our stack contain a list node. From now on, we + * rely on the compiler not reading/writing the stack. + */ +#endif /* ARCH_EMUL */ + + CurrentProcess = NULL; + proc_schedule(); + /* not reached */ +} + + +/*! + * Co-operative context switch + */ +void proc_switch(void) +{ + DISABLE_INTS; + SCHED_ENQUEUE(CurrentProcess); + ENABLE_INTS; + proc_schedule(); +} + + +/*! + * Get the pointer to the current process + */ +struct Process *proc_current(void) +{ + return CurrentProcess; +} + + +#if 0 /* Simple testcase for the scheduler */ + +/*! + * Proc scheduling test subthread 1 + */ +static void NORETURN proc_test_thread1(void) +{ + for (;;) + { + kputs(">task 1\n"); + timer_delay(50); + proc_switch(); + } +} + +/*! + * Proc scheduling test subthread 2 + */ +static void NORETURN proc_test_thread2(void) +{ + for (;;) + { + kputs(">task 2\n"); + timer_delay(75); + proc_switch(); + } +} + +static cpustack_t proc_test_stack1[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)]; +static cpustack_t proc_test_stack2[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)]; + +/*! + * Proc scheduling test + */ +void NORETURN proc_test(void) +{ + proc_new(proc_test_thread1, sizeof(proc_test_stack1), proc_test_stack1); + proc_new(proc_test_thread2, sizeof(proc_test_stack2), proc_test_stack2); + kputs("Created tasks\n"); + + kputs("stack1:\n"); + kdump(proc_test_stack1+sizeof(proc_test_stack1)-64, 64); + kputs("stack2:\n"); + kdump(proc_test_stack2+sizeof(proc_test_stack1)-64, 64); + + for (;;) + { + kputs(">main task\n"); + timer_delay(93); + proc_switch(); + } + + ASSERT(false); +} +#endif diff --git a/kern/proc.h b/kern/proc.h new file mode 100755 index 00000000..d5866993 --- /dev/null +++ b/kern/proc.h @@ -0,0 +1,50 @@ +/** + * \file + * + * + * \brief Process scheduler (public interface). + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#ifndef KERN_PROC_H +#define KERN_PROC_H + +#include "compiler.h" +#include "cpu.h" +#include "config_kern.h" + +/* Fwd decl */ +struct Process; + +/* Task scheduling services */ +void proc_init(void); +struct Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack); +void proc_exit(void); +void proc_switch(void); +void proc_test(void); +struct Process* proc_current(void); + +#if CONFIG_KERN_PREEMPTIVE + #define FORBID proc_forbid() + #define PERMIT proc_permit() +#else + #define FORBID + #define PERMIT +#endif + +#endif /* KERN_PROC_H */ + diff --git a/kern/proc_p.h b/kern/proc_p.h new file mode 100755 index 00000000..5cd44a94 --- /dev/null +++ b/kern/proc_p.h @@ -0,0 +1,91 @@ +/*! + * \file + * + * + * \brief Internal scheduler structures and definitions for processes. + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + * Revision 1.3 2004/05/14 12:52:13 rasky + * Importato supporto kernel per AVR da Stefano + * + * Revision 1.2 2004/04/28 16:13:49 rasky + * proc_schedule() is now semi-private (used only within the kernel) + * + * Revision 1.1 2004/04/26 18:02:40 rasky + * Importato microkernel + * + * Revision 1.1 2004/04/04 17:40:26 aleph + * Add multithreading kernel + * + */ + +#ifndef KERN_PROC_P_H +#define KERN_PROC_P_H + +#include "compiler.h" +#include "config.h" +#include "config_kern.h" +#include +#include + + +typedef struct Process +{ + Node link; /*!< Link Process into scheduler lists */ + cpustack_t *stack; /*!< Per-process SP */ + +#if CONFIG_KERN_SIGNALS + sigset_t sig_wait; /*!< Signals the process is waiting for */ + sigset_t sig_recv; /*!< Received signals */ +#endif + +#if CONFIG_KERN_TIMER + struct Timer proc_timer; /*!< Process own timer */ +#endif + +#if CONFIG_KERN_HEAP + uint16_t flags; /*!< Flags */ + cpustack_t *stack_base; /*!< Base of process stack */ + size_t stack_size; /*!< Size of process stack */ +#endif +} Process; + + +/*! + * \name Flags for Process.flags + * \{ + */ +#define PF_FREESTACK BV(0) /*!< Free the stack when process dies */ +/*\}*/ + + +/*! Track running processes */ +extern REGISTER Process *CurrentProcess; + +/*! Track ready processes */ +extern REGISTER List ProcReadyList; + + +/*! + * Enqueue a task in the ready list + */ +#define SCHED_ENQUEUE(proc) ADDTAIL(&ProcReadyList, &(proc)->link) + +/*! Schedule to another process *without* adding the current to the ready list */ +void proc_schedule(void); + +#endif /* KERN_PROC_P_H */ + diff --git a/kern/sem.c b/kern/sem.c new file mode 100755 index 00000000..98646e93 --- /dev/null +++ b/kern/sem.c @@ -0,0 +1,144 @@ +/*! + * \file + * + * + * \brief Semaphore based synchronization services. + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#include "sem.h" +#include "proc.h" +#include "proc_p.h" +#include "signal.h" +#include "hw.h" + + +/*! + * \brief Initialize a Semaphore structure + */ +void sem_init(struct Semaphore *s) +{ + INITLIST(&s->wait_queue); + s->owner = NULL; + s->nest_count = 0; +} + + +/*! + * \brief Attempt to lock a semaphore without waiting. + * + * \return true in case of success, false if the semaphore + * was already locked by someone else. + * + * \note each call to sem_attempt() must be matched by a + * call to sem_release(). + */ +bool sem_attempt(struct Semaphore *s) +{ + DISABLE_INTS; + if ((!s->owner) || (s->owner == CurrentProcess)) + { + s->owner = CurrentProcess; + s->nest_count++; + ENABLE_INTS; + return true; + } + ENABLE_INTS; + return false; +} + + +/*! + * \brief Lock a semaphore. + * + * If the semaphore is already owned by another process, the caller + * process will be enqueued into the waiting list and sleep until + * the semaphore is available. + * + * \note Each call to sem_obtain() must be matched by a + * call to sem_release(). + * + * \note This routine is optimized for highest speed in + * the most common case: the semaphore is free or locked + * by the calling process itself. Rearranging this code + * is probably a bad idea. + */ +void sem_obtain(struct Semaphore *s) +{ + DISABLE_INTS; + + /* Is the semaphore already locked by another process? */ + if (s->owner && (s->owner != CurrentProcess)) + { + /* Append calling process to the wait queue */ + ADDTAIL(&s->wait_queue, (Node *)CurrentProcess); + ENABLE_INTS; + + /* We will awake only when the current owner calls + * ReleaseSemaphore(). Then, the semaphore will already + * be locked for us. + */ + proc_schedule(); + } + else + { + /* The semaphore is free: lock it */ + s->owner = CurrentProcess; + s->nest_count++; + ENABLE_INTS; + } +} + + +/*! + * \brief Releases a lock on a previously locked semaphore. + * + * If the nesting count of the semaphore reaches zero, + * the next process waiting for it will be awaken. + * + * \note This routine is optimized for highest speed in + * the most common case: the semaphore has been locked just + * once and nobody else was waiting for it. Rearranging + * this code is probably a bad idea. + */ +void sem_release(struct Semaphore *s) +{ + DISABLE_INTS; + + /* Decremement nesting count and check if the semaphore + * has been fully unlocked + */ + if (--s->nest_count == 0) + { + /* Disown semaphore */ + s->owner = NULL; + + /* Anybody still waiting for this semaphore? */ + if (!ISLISTEMPTY(&s->wait_queue)) + { + /* Give semaphore to the first applicant */ + Process *proc = (Process *)s->wait_queue.head; + REMOVE((Node *)proc); + s->nest_count = 1; + s->owner = proc; + SCHED_ENQUEUE(proc); + } + } + + ENABLE_INTS; +} + diff --git a/kern/sem.h b/kern/sem.h new file mode 100755 index 00000000..91b56157 --- /dev/null +++ b/kern/sem.h @@ -0,0 +1,51 @@ +/** + * \file + * + * + * \brief Mutually exclusive semaphores. + * Shared locking not supported in this implementation. + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#ifndef KERN_SEM_H +#define KERN_SEM_H + +#include "compiler.h" +#include + +/* Fwd decl */ +struct Process; + + +struct Semaphore +{ + struct Process *owner; + List wait_queue; + int nest_count; +}; + +/*! + * \name Process synchronization services + * \{ + */ +void sem_init(struct Semaphore *s); +bool sem_attempt(struct Semaphore *s); +void sem_obtain(struct Semaphore *s); +void sem_release(struct Semaphore *s); +/* \} */ + +#endif /* KERN_SEM_H */ diff --git a/kern/signal.c b/kern/signal.c new file mode 100755 index 00000000..3bc9aab7 --- /dev/null +++ b/kern/signal.c @@ -0,0 +1,125 @@ +/** + * \file + * + * + * \brief IPC signals implementation. + * + * Each process can wait for just one signal. + * Multiple processes can wait for the same signal. In this + * case, each signal will wake-up one of them. + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#include "signal.h" +#include "proc.h" +#include "proc_p.h" +#include "hw.h" + +// FIXME +#if CONFIG_KERN_SIGNALS + +/*! + * Check if any of the signals in \a sigs has occurred and clear them. + * Return the signals that have occurred. + */ +sigset_t sig_check(sigset_t sigs) +{ + sigset_t result; + + DISABLE_INTS; + result = CurrentProcess->sig_recv & sigs; + CurrentProcess->sig_recv &= ~sigs; + ENABLE_INTS; + return result; +} + + +/*! + * Sleep until any of the signals in \a sigs occurs. + * Return the signal(s) that have awaked the process. + */ +sigset_t sig_wait(sigset_t sigs) +{ + sigset_t result; + + DISABLE_INTS; + + for(;;) + { + /* Check if we got at least one of the signals */ + if ((result = CurrentProcess->sig_recv & sigs)) + { + /* Yes, clear signals and return */ + CurrentProcess->sig_recv &= ~sigs; + ENABLE_INTS; + return result; + } + + /* No, go to sleep and proc_schedule() another process */ + CurrentProcess->sig_wait = sigs; + proc_schedule(); + } +} + + +/*! + * Send the signals \a sigs to the process \a proc. + * The process will be awaken if it was waiting for any of them. + * + * This call is interrupt safe (no \c DISABLE_INTS/ENABLE_INTS protection) + */ +void _sig_signal(Process *proc, sigset_t sigs) +{ + /* Set the signals */ + proc->sig_recv |= sigs; + + /* Check if process needs to be awaken */ + if (proc->sig_recv & proc->sig_wait) + { + /* Wake up process and enqueue in ready list */ + proc->sig_wait = 0; + SCHED_ENQUEUE(proc); + } +} + + +/*! + * Same as _sig_signal() with interrupt protection. + * + * \note Inlined manually because some compilers are too + * stupid to it automatically. + */ +void sig_signal(Process *proc, sigset_t sigs) +{ + DISABLE_INTS; + + /* Set the signals */ + proc->sig_recv |= sigs; + + /* Check if process needs to be awaken */ + if (proc->sig_recv & proc->sig_wait) + { + /* Wake up process and enqueue in ready list */ + proc->sig_wait = 0; + SCHED_ENQUEUE(proc); + } + + ENABLE_INTS; +} + +#endif /* CONFIG_KERN_SIGNALS */ + diff --git a/kern/signal.h b/kern/signal.h new file mode 100755 index 00000000..27a821ac --- /dev/null +++ b/kern/signal.h @@ -0,0 +1,54 @@ +/** + * \file + * + * + * \brief Signal module (public interface). + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +#ifndef KERN_SIGNAL_H +#define KERN_SIGNAL_H + +#include "compiler.h" + + +/* Fwd decl */ +struct Process; + +/* Inter-process Communication services */ +sigset_t sig_check(sigset_t sigs); +void sig_signal(struct Process *proc, sigset_t sig); +void _sig_signal(struct Process *proc, sigset_t sig); +sigset_t sig_wait(sigset_t sigs); + + +/*! + * \name Signal definitions + * + * \{ + */ +#define SIG_USER0 BV(0) /*!< Free for user usage */ +#define SIG_USER1 BV(1) /*!< Free for user usage */ +#define SIG_USER2 BV(2) /*!< Free for user usage */ +#define SIG_USER3 BV(3) /*!< Free for user usage */ +#define SIG_SYSTEM4 BV(4) /*!< Reserved for system use */ +#define SIG_SYSTEM5 BV(5) /*!< Reserved for system use */ +#define SIG_SYSTEM6 BV(6) /*!< Reserved for system use */ +#define SIG_SINGLE BV(7) /*!< Used to wait for a single event */ +/*\}*/ + +#endif /* KERN_SIGNAL_H */ diff --git a/kern/switch_avr.S b/kern/switch_avr.S new file mode 100755 index 00000000..266a77e4 --- /dev/null +++ b/kern/switch_avr.S @@ -0,0 +1,102 @@ +/*! + * \file + * + * + * \brief AVR context switch + * + * \version $Id$ + * + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +#include + +/* void asm_switch_context(void **new_sp, void **save_sp) */ +.globl asm_switch_context +asm_switch_context: + +; push r0 caller-save +; push r1 caller-save + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push r16 + push r17 +; push r18 caller-save +; push r19 caller-save +; push r20 caller-save +; push r21 caller-save +; push r22 caller-save +; push r23 caller-save +; push r24 caller-save +; push r25 caller-save +; push r26 caller-save +; push r27 caller-save + push r28 + push r29 +; push r30 caller-save +; push r31 caller-save + +; First parameter (new_sp) is in r24:r25, second (save_sp) in r22:r23 + + in r0,SPL-__SFR_OFFSET ; r0:r1 = SP + in r1,SPH-__SFR_OFFSET + movw r26,r22 ; X = save_sp + st X+,r0 ; *save_sp = SP + st X,r1 + movw r26,r24 ; X = new_sp + ld r0,X+ + ld r1,X + out SPL-__SFR_OFFSET,r0 ; SP = *new_sp + out SPH-__SFR_OFFSET,r1 + +; pop r31 caller-save +; pop r30 caller-save + pop r29 + pop r28 +; pop r27 caller-save +; pop r26 caller-save +; pop r25 caller-save +; pop r24 caller-save +; pop r23 caller-save +; pop r22 caller-save +; pop r21 caller-save +; pop r20 caller-save +; pop r19 caller-save +; pop r18 caller-save + pop r17 + pop r16 + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop r7 + pop r6 + pop r5 + pop r4 + pop r3 + pop r2 +; pop r1 caller-save +; pop r0 caller-save + + ret diff --git a/kern/switch_dsp56k.c b/kern/switch_dsp56k.c new file mode 100755 index 00000000..e10216cb --- /dev/null +++ b/kern/switch_dsp56k.c @@ -0,0 +1,136 @@ +/*! + * \file + * Copyright 2004 Develer S.r.l. (http://www.develer.com/) + * All Rights Reserved. + * + * \version $Id$ + * + * \author Giovanni Bajo + * + * \brief DSP5680x task switching support + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +void asm_switch_context(void ** new_sp/*R2*/, void ** save_sp/*R3*/); +asm void asm_switch_context(void ** new_sp, void ** save_sp) +{ + lea (SP)+ + move n,x:(SP)+ + move x0,x:(SP)+ + move y0,x:(SP)+ + move y1,x:(SP)+ + move a0,x:(SP)+ + move a1,x:(SP)+ + move a2,x:(SP)+ + move b0,x:(SP)+ + move b1,x:(SP)+ + move b2,x:(SP)+ + move r0,x:(SP)+ + move r1,x:(SP)+ + move r2,x:(SP)+ + move r3,x:(SP)+ + + move omr,x:(SP)+ + move la,x:(SP)+ + move m01,x:(SP)+ + move lc,x:(SP)+ + + ; + ; save hardware stack + ; + move hws,x:(SP)+ + move hws,x:(SP)+ + + ; From the manual: + ; The compiler uses page 0 address locations X: 0x0030 - 0x003F as register + ; variables. Frequently accessed local variables are assigned to the page 0 + ; registers instead of to stack locations so that load and store instructions + ; are shortened. Addresses X: 0x0030 - 0x0037 (page 0 registers MR0-MR7) are + ; volatile registers and can be overwritten. The remaining registers (page 0 + ; registers MR8-MR15) are treated as non-volatile and, if used by a routine, + ; must be saved on entry and restored on exit. + ; + ; So, register 0x30-0x37 are caller-save, while 0x38-0x3F are callee-save. + move x:<$38,y1 + move y1,x:(SP)+ + move x:<$39,y1 + move y1,x:(SP)+ + move x:<$3A,y1 + move y1,x:(SP)+ + move x:<$3B,y1 + move y1,x:(SP)+ + move x:<$3C,y1 + move y1,x:(SP)+ + move x:<$3D,y1 + move y1,x:(SP)+ + move x:<$3E,y1 + move y1,x:(SP)+ + move x:<$3F,y1 + move y1,x:(SP) + + ; + ; 28 words have been pushed on the stack. + nop + move SP, x:(R3) + nop + move x:(R2), SP + nop + + pop y1 + move y1,x:<$3F + pop y1 + move y1,x:<$3E + pop y1 + move y1,x:<$3D + pop y1 + move y1,x:<$3C + pop y1 + move y1,x:<$3B + pop y1 + move y1,x:<$3A + pop y1 + move y1,x:<$39 + pop y1 + move y1,x:<$38 + + ; + ; restore hardware stack + ; + move hws,la ; Clear HWS to ensure proper reload + move hws,la + pop HWS + pop HWS + + ; + ; restore all saved registers + ; + pop lc + pop m01 + pop la + pop omr + + pop r3 + pop r2 + pop r1 + pop r0 + pop b2 + pop b1 + pop b0 + pop a2 + pop a1 + pop a0 + + pop y1 + pop y0 + pop x0 + + pop n + + rts +} diff --git a/kern/switch_i196.s32 b/kern/switch_i196.s32 new file mode 100755 index 00000000..6b9d6af2 --- /dev/null +++ b/kern/switch_i196.s32 @@ -0,0 +1,88 @@ +;* Copyright 2004 Develer S.r.l. (http://www.develer.com/) +;* Copyright 1999,2000,2001 Bernardo Innocenti +;* +;* \version $Id$ +;* +;* \author Bernardo Innocenti +;* + +;* $Log$ +;* Revision 1.1 2004/05/23 17:27:00 bernie +;* Import kern/ subdirectory. +;* +;* + +!!!!!! THIS FILE HAS NOT BEEN REVISED FOR THE NEW SCHEDULER API !!!!!! + + + + NAME AsmSwitch + RSEG CODE + + EXTERN ?LR + EXTERN ?GR + PUBLIC AsmSwitchContext + PUBLIC AsmReplaceContext + + +;* Perform low-level process context switching +;* +;* void AsmSwitchContext(cpustack_t *new_sp, cpustack_t **save_sp) +;* GR+0 SP+2 +;* +;* Replace current context with new process +;* +;* void AsmReplaceContext(cpustack_t *new_sp, cpustack_t **dummy) +;* GR+0 SP+2 +;* + +AsmSwitchContext: + +; pop 2nd parameter from the stack + ld ?GR+2,2[SP] + +; save all registers + push ?LR+0 + push ?LR+2 + push ?LR+4 + push ?LR+6 + push ?LR+8 + push ?LR+10 + push ?LR+12 + push ?LR+14 + push ?LR+16 + push ?LR+18 + push ?LR+20 + push ?LR+22 + push ?LR+24 + push ?LR+26 + push ?LR+28 + push ?LR+30 + st SP,[?GR+2] ; save old stack pointer + ; fall-thru + +AsmReplaceContext: + ld SP,?GR+0 ; load new stack pointer + +; restore all registers + pop ?LR+30 + pop ?LR+28 + pop ?LR+26 + pop ?LR+24 + pop ?LR+22 + pop ?LR+20 + pop ?LR+18 + pop ?LR+16 + pop ?LR+14 + pop ?LR+12 + pop ?LR+10 + pop ?LR+8 + pop ?LR+6 + pop ?LR+4 + pop ?LR+2 + pop ?LR+0 + +; restore execution in new context + ret + + END diff --git a/kern/switch_i386.s b/kern/switch_i386.s new file mode 100755 index 00000000..c4a23da1 --- /dev/null +++ b/kern/switch_i386.s @@ -0,0 +1,62 @@ +/*! + * \file + * + * + * \brief i386 context switch + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + + * + * $Log$ + * Revision 1.1 2004/05/23 17:27:00 bernie + * Import kern/ subdirectory. + * + */ + +!!!!!! THIS FILE HAS NOT BEEN REVISED FOR THE NEW SCHEDULER API !!!!!! + +/* I know it's ugly... */ +#.intel_syntax + +/* void AsmSwitchContext(void * new_sp, void ** save_sp) */ +.globl AsmSwitchContext +AsmSwitchContext: + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + pushl %ebp + movl 0x24(%esp),%ebp /* ebp = save_sp */ + movl %esp,(%ebp) /* *save_sp = esp */ + movl 0x20(%esp),%esp /* esp = new_sp */ + popl %ebp + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + +/* void AsmReplaceContext(void * new_sp, void ** dummy) */ +.globl AsmReplaceContext +AsmReplaceContext: + movl 4(%esp),%esp /* esp = new_sp */ + popl %ebp + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + diff --git a/kern/switch_win32.s b/kern/switch_win32.s new file mode 100755 index 00000000..3e5ce54c --- /dev/null +++ b/kern/switch_win32.s @@ -0,0 +1,56 @@ +/*! + * \file + * + * + * \brief i386 context switch for WIN32 + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +__declspec(naked) void AsmSwitchContext(void * new_sp, void ** save_sp) +{ + __asm + { + push eax + push ebx + push ecx + push edx + push esi + push edi + push ebp + mov ebp,dword ptr [esp+24h] ; ebp <- save_sp + mov dword ptr [ebp],esp ; *save_sp = esp + mov esp,dword ptr [esp+20h] ; new_sp + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + ret + } +} + +__declspec(naked) void AsmReplaceContext(void * new_sp, void ** dummy) +{ + __asm + { + mov esp,dword ptr [esp + 4] ; new_sp + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + ret + } +} + -- 2.25.1