diff options
Diffstat (limited to 'pk/kernel')
-rw-r--r-- | pk/kernel/.empty | 0 | ||||
-rw-r--r-- | pk/kernel/Makefile | 26 | ||||
-rw-r--r-- | pk/kernel/pk.h | 125 | ||||
-rw-r--r-- | pk/kernel/pk_api.h | 1016 | ||||
-rw-r--r-- | pk/kernel/pk_bh_core.c | 29 | ||||
-rw-r--r-- | pk/kernel/pk_core.c | 79 | ||||
-rw-r--r-- | pk/kernel/pk_debug_ptrs.c | 53 | ||||
-rw-r--r-- | pk/kernel/pk_debug_ptrs.h | 39 | ||||
-rw-r--r-- | pk/kernel/pk_init.c | 196 | ||||
-rw-r--r-- | pk/kernel/pk_kernel.h | 246 | ||||
-rw-r--r-- | pk/kernel/pk_macros.h | 110 | ||||
-rw-r--r-- | pk/kernel/pk_semaphore_core.c | 318 | ||||
-rw-r--r-- | pk/kernel/pk_semaphore_init.c | 82 | ||||
-rw-r--r-- | pk/kernel/pk_stack_init.c | 85 | ||||
-rw-r--r-- | pk/kernel/pk_thread.h | 56 | ||||
-rw-r--r-- | pk/kernel/pk_thread_core.c | 573 | ||||
-rw-r--r-- | pk/kernel/pk_thread_init.c | 134 | ||||
-rw-r--r-- | pk/kernel/pk_thread_util.c | 291 | ||||
-rw-r--r-- | pk/kernel/pk_timer_core.c | 401 | ||||
-rw-r--r-- | pk/kernel/pk_timer_init.c | 62 | ||||
-rw-r--r-- | pk/kernel/pkkernelfiles.mk | 32 |
21 files changed, 0 insertions, 3953 deletions
diff --git a/pk/kernel/.empty b/pk/kernel/.empty deleted file mode 100644 index e69de29b..00000000 --- a/pk/kernel/.empty +++ /dev/null diff --git a/pk/kernel/Makefile b/pk/kernel/Makefile deleted file mode 100644 index 3fad153e..00000000 --- a/pk/kernel/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# This Makefile is designed to be invoked with the -I argument set to -# the location of the "pk.mk" for the build - -include img_defs.mk -include pkkernelfiles.mk - -ifeq "$(PK_TIMER_SUPPORT)" "1" -PK_OBJECTS += ${PK-TIMER-C-SOURCES:.c=.o} -endif - -ifeq "$(PK_THREAD_SUPPORT)" "1" -PK_OBJECTS += ${PK-THREAD-C-SOURCES:.c=.o} -endif - -OBJS := $(addprefix $(OBJDIR)/, $(PK_OBJECTS)) - -all: $(OBJS) - -$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) - -$(OBJDIR): - mkdir -p $(OBJDIR) - -ifneq ($(MAKECMDGOALS),clean) -include $(OBJS:.o=.d) -endif diff --git a/pk/kernel/pk.h b/pk/kernel/pk.h deleted file mode 100644 index 2efcdf11..00000000 --- a/pk/kernel/pk.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef __PK_H__ -#define __PK_H__ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk.h -/// \brief The combined header of the PK kernel. -/// -/// This header will be included in any C or assembler source file that -/// requires any of the PK API. All headers defined by PK and co-compiled -/// code should be protected such that they can be included without error into -/// assembly. - -#ifndef __ASSEMBLER__ -#include <stdint.h> -#include <stddef.h> -#endif /* __ASSEMBLER__ */ - -#ifndef __PK__ -#define __PK__ 1 -#endif - -/// The application environment specifies whether or not it will provide an -/// application configuration file, which must be named "pk_app_cfg.h". - -#ifndef USE_PK_APP_CFG_H -#define USE_PK_APP_CFG_H 0 -#endif - -#if USE_PK_APP_CFG_H -#include "pk_app_cfg.h" -#endif - -#include "pk_macros.h" -#include "pk_api.h" -#include "pk_port.h" -#include "pk_kernel.h" -//#include "pk_io.h" - -#ifndef __ASSEMBLER__ - -#define MIN(X, Y) \ - ({ \ - typeof (X) __x = (X); \ - typeof (Y) __y = (Y); \ - (__x < __y) ? __x : __y; }) - -#define MAX(X, Y) \ - ({ \ - typeof (X) __x = (X); \ - typeof (Y) __y = (Y); \ - (__x > __y) ? __x : __y; \ - }) - -/// \todo These don't require 32/64 bit versions, can always promote 32->64. - -#define FLOOR_LOG2_32(x) (32 - 1 - cntlz32(x)) -#define FLOOR_LOG2_64(x) (64 - 1 - cntlz64(x)) - -#define CEILING_LOG2(x) \ - ({ \ - uint64_t __x = (uint64_t)(x); \ - int __y; \ - __y = FLOOR_LOG2_64(__x); \ - if ((__x & (__x - 1)) != 0) { \ - __y++; \ - } \ - __y;}) - - -#define POW2_32(x) ((uint32_t)1 << (x)) -#define POW2_64(x) ((uint64_t)1 << (x)) - -/// Cast a pointer to another type -/// -/// This macro is necessary when casting a pointer to a longer integer type. -/// The pointer is first cast to the equivalent integer type 'unsigned long', -/// then cast to the final type. You can also use this to cast integers longer -/// than pointers back to pointers. - -#define CAST_POINTER(t, p) ((t)((unsigned long)(p))) - - -/// Create an alignment attribute. -#define ALIGNED_ATTRIBUTE(alignment) __attribute__ ((aligned (alignment))) - -/// Create a specific-section attribute -/// -/// Note that the section \a s here must be a string. Also note that data -/// specified to reside in specific sections must always be -/// initialized. Otherwise it confuses the linker which wants to put -/// uninitialized data into .bss sections. -/// -/// \code -/// -/// int foo SECTION_ATTRIBUTE(".noncacheable") = 0; -/// int bar[10] SECTION_ATTRIBUTE(".noncacheable") = {0}; -/// -/// \endcode -#define SECTION_ATTRIBUTE(s) __attribute__ ((section (s))) - -/// Create a 'used' attribute -/// -/// This is required for example to avoid "function unused" warnings when a -/// function is declared static but only referenced by inline assembler: -/// -/// \code -/// -/// static USED_ATTRIBUTE void -/// _checkstop(void* arg, PkIrqId irq, int priority) -/// { -/// PK_PANIC(VALIDATION_CHECKSTOP); -/// } -/// -/// PK_IRQ_FAST2FULL(_validationCheckstopHandler, _checkstop); -/// -/// \endcode -#define USED_ATTRIBUTE __attribute__ ((used)) - -#endif /* __ASSEMBLER__ */ - -#endif /* __PK_H__ */ diff --git a/pk/kernel/pk_api.h b/pk/kernel/pk_api.h deleted file mode 100644 index 66ed1324..00000000 --- a/pk/kernel/pk_api.h +++ /dev/null @@ -1,1016 +0,0 @@ -#ifndef __PK_API_H__ -#define __PK_API_H__ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_api.h -/// \brief Macros and declarations for the PK API. - -// Basic constants - -/// Although the number of threads is defined as a manifest constant, -/// numerous parts of the PK code assume this definition. The number of -/// supported threads _can not_ be changed simply by changing this constant. - -#define PK_THREADS 32 - -#define PK_IDLE_THREAD_PRIORITY PK_THREADS - -// Interrupt API - -#define PK_IRQ_POLARITY_ACTIVE_LOW 0 -#define PK_IRQ_POLARITY_ACTIVE_HIGH 1 - -#define PK_IRQ_TRIGGER_LEVEL_SENSITIVE 0 -#define PK_IRQ_TRIGGER_EDGE_SENSITIVE 1 - -// API return codes - -#define PK_OK 0 -#define PK_ILLEGAL_CONTEXT_THREAD_CONTEXT 0x00779002 -#define PK_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT 0x00779003 -#define PK_ILLEGAL_CONTEXT_THREAD 0x00779004 -#define PK_ILLEGAL_CONTEXT_TIMER 0x00779005 -#define PK_INVALID_THREAD_AT_RESUME1 0x00779007 -#define PK_INVALID_THREAD_AT_RESUME2 0x00779008 -#define PK_INVALID_THREAD_AT_SUSPEND1 0x00779009 -#define PK_INVALID_THREAD_AT_SUSPEND2 0x0077900a -#define PK_INVALID_THREAD_AT_DELETE 0x0077900b -#define PK_INVALID_THREAD_AT_INFO 0x0077900c -#define PK_INVALID_THREAD_AT_CHANGE 0x0077900d -#define PK_INVALID_THREAD_AT_SWAP1 0x0077900e -#define PK_INVALID_THREAD_AT_SWAP2 0x0077900f -#define PK_INVALID_THREAD_AT_CREATE 0x00779010 -#define PK_INVALID_SEMAPHORE_AT_POST 0x00779011 -#define PK_INVALID_SEMAPHORE_AT_PEND 0x00779012 -#define PK_INVALID_SEMAPHORE_AT_RELEASE 0x00779013 -#define PK_INVALID_SEMAPHORE_AT_INFO 0x00779014 -#define PK_INVALID_SEMAPHORE_AT_CREATE 0x00779015 -#define PK_INVALID_TIMER_AT_SCHEDULE 0x00779016 -#define PK_INVALID_TIMER_AT_CANCEL 0x00779017 -#define PK_INVALID_TIMER_AT_INFO 0x00779018 -#define PK_INVALID_TIMER_AT_CREATE 0x00779019 -#define PK_INVALID_ARGUMENT_IRQ_SETUP 0x0077901a -#define PK_INVALID_ARGUMENT_IRQ_HANDLER 0x0077901b -#define PK_INVALID_ARGUMENT_INTERRUPT 0x00779024 -#define PK_INVALID_ARGUMENT_CONTEXT_SET 0x00779025 -#define PK_INVALID_ARGUMENT_CONTEXT_GET 0x00779026 -#define PK_INVALID_ARGUMENT_FIT 0x00779027 -#define PK_INVALID_ARGUMENT_WATCHDOG 0x00779028 -#define PK_INVALID_ARGUMENT_INIT 0x00779029 -#define PK_INVALID_ARGUMENT_SEMAPHORE 0x0077902a -#define PK_INVALID_ARGUMENT_THREAD_CHANGE 0x0077902b -#define PK_INVALID_ARGUMENT_THREAD_PRIORITY 0x0077902c -#define PK_INVALID_ARGUMENT_THREAD1 0x0077902d -#define PK_INVALID_ARGUMENT_THREAD2 0x0077902e -#define PK_INVALID_ARGUMENT_THREAD3 0x0077902f -#define PK_STACK_OVERFLOW 0x00779030 -#define PK_TIMER_ACTIVE 0x00779031 -#define PK_TIMER_NOT_ACTIVE 0x00779032 -#define PK_PRIORITY_IN_USE_AT_RESUME 0x00779033 -#define PK_PRIORITY_IN_USE_AT_CHANGE 0x00779034 -#define PK_PRIORITY_IN_USE_AT_SWAP 0x00779035 -#define PK_SEMAPHORE_OVERFLOW 0x00779036 -#define PK_SEMAPHORE_PEND_NO_WAIT 0x00779037 -#define PK_SEMAPHORE_PEND_TIMED_OUT 0x00779038 -#define PK_SEMAPHORE_PEND_WOULD_BLOCK 0x00779039 -#define PK_INVALID_DEQUE_SENTINEL 0x0077903a -#define PK_INVALID_DEQUE_ELEMENT 0x0077903b -#define PK_INVALID_OBJECT 0x0077903c - -// Kernel panics - -#define PK_NO_TIMER_SUPPORT 0x0077903d -#define PK_START_THREADS_RETURNED 0x0077903e -#define PK_UNIMPLEMENTED 0x0077903f -#define PK_SCHEDULING_INVARIANT 0x00779040 -#define PK_TIMER_HANDLER_INVARIANT 0x00779041 -#define PK_THREAD_TIMEOUT_STATE 0x00779045 - -// Application-level panic offsets -// (Use these as offsets for your application code panics and keep -// track of them locally in your application code domain, including -// sharing the panic defines with other developers making codes -// for the same engine.) - -#define PK_APP_OFFSET_SBE 0x0077a000 -#define PK_APP_OFFSET_GPE0 0x0077b000 -#define PK_APP_OFFSET_GPE1 0x0077c000 -#define PK_APP_OFFSET_GPE2 0x0077d000 -#define PK_APP_OFFSET_GPE3 0x0077e000 -#define PK_APP_OFFSET_CME 0x0077f000 - -/// \defgroup pk_thread_states PK Thread States -/// -/// Threads are created in the state PK_THREAD_STATE_SUSPENDED_RUNNABLE. -/// When the thread is mapped it transitions to state PK_THREAD_STATE_MAPPED. -/// A mapped thread is runnable if it appears in the run queue; there is no -/// other flag or status to indicate a runnable thread. If a blocked thread -/// is suspended it goes into state PK_THREAD_STATE_SUSPENDED_BLOCKED. For -/// all threads the reason for blockage is detailed in the \a flags field of -/// the thread; See \ref pk_thread_flags. PK_THREAD_STATE_DELETED and -/// PK_THREAD_STATE_COMPLETED are effectively equivalent but named -/// individually for reporting purposes. -/// -/// \note This separation of the thread \a state and \a flags allows the use -/// of an PK semaphore as a thread barrier, as it supports a non-iterative -/// implementation of pk_semaphore_release_all() in which all threads blocked -/// on the semaphore are simultaneously inserted into the run queue with an -/// atomic operation, followed by each individual thread readjusting its flags -/// appropriately once the thread runs again. -/// -/// @{ - -#define PK_THREAD_STATE_SUSPENDED_RUNNABLE 1 -#define PK_THREAD_STATE_MAPPED 2 -#define PK_THREAD_STATE_SUSPENDED_BLOCKED 3 -#define PK_THREAD_STATE_COMPLETED 4 -#define PK_THREAD_STATE_DELETED 5 - -/// @} - - -/// \defgroup pk_thread_flags PK Thread Flags -/// -/// The \a flag field of the thread extends the information contained in the -/// \a state field; See \ref pk_thread_states. Blocked threads will show -/// PK_THREAD_FLAG_SEMAPHORE_PEND, PK_THREAD_FLAG_TIMER_PEND or both (if -/// blocked on a semaphore with timeout). The flag PK_THREAD_FLAG_TIMED_OUT -/// indicates that a thread timer timed out before the thread became -/// runnable. Currently only the semaphore-pend-with-timeout code uses this -/// flag. -/// -/// Note that a thread can be mapped and runnable (in the run queue) even -/// though PK_THREAD_FLAG_SEMAPHORE_PEND and/or PK_THREAD_FLAG_TIMER_PEND -/// are set. These flags are always cleared by the thread itself, not the code -/// that unblocks the thread. This allows the implementation of the -/// pk_semaphore_release_all() as explained in \ref pk_thread_states. -/// -/// @{ - -#define PK_THREAD_FLAG_SEMAPHORE_PEND 0x1 -#define PK_THREAD_FLAG_TIMER_PEND 0x2 -#define PK_THREAD_FLAG_TIMED_OUT 0x4 - -/// @} - - -// Critical Sections - -/// Enter a critical section, saving the current machine -/// context. - -#define pk_critical_section_enter(pctx) \ - pk_interrupt_disable(pctx) - -/// Exit a critical section by restoring the previous machine context. - -#define pk_critical_section_exit(pctx) \ - pk_machine_context_set(pctx) - - -/// Execute a statement atomically - -#define PK_ATOMIC(stmt) \ - do { \ - PkMachineContext __ctx; \ - pk_critical_section_enter(&__ctx); \ - stmt; \ - pk_critical_section_exit(&__ctx); \ - } while (0) - - -// Application-overrideable definitions - -/// Control whether or not the API functions check for errors. -/// -/// This definition can be overriden by the application. - -#ifndef PK_ERROR_CHECK_API -#define PK_ERROR_CHECK_API 1 -#endif - -/// Control whether API errors cause kernel panics or return negative error -/// codes. -/// -/// This selection is only valid if \c PK_ERROR_CHECK_API is defined -/// non-0. This definition can be overriden by the application. - -#ifndef PK_ERROR_PANIC -#define PK_ERROR_PANIC 1 -#endif - -/// Control whether or not the PK kernel checks key invariants. -/// -/// Violations of kernel invariants always cause kernel panics. This -/// definition can be overriden by the application. - -#ifndef PK_ERROR_CHECK_KERNEL -#define PK_ERROR_CHECK_KERNEL 1 -#endif - -/// Define the time interval type, which must be an unsigned type of a size -/// less then or equal to the size of \c PkTimebase. This definition can be -/// overridden by the application. - -#ifndef PK_TIME_INTERVAL_TYPE -#define PK_TIME_INTERVAL_TYPE uint32_t -#endif - -/// Provide support for the PkTimer APIs in addition to the default -/// initerrupt APIs. This definition can be overridden by the application. - -#ifndef PK_TIMER_SUPPORT -#define PK_TIMER_SUPPORT 1 -#endif - -/// Provide support for the all PK APIs. Thread support requires/implies -/// support for time services and semaphores. This definition can be -/// overridden by the application. - -#ifndef PK_THREAD_SUPPORT -#define PK_THREAD_SUPPORT 1 -#endif - -/// Control the level of stack checking. -/// -/// This definition can be overriden by the application. -/// -/// 0 : No stack prepatterning or checking is made for thread and kernel -/// stacks. -/// -/// 1 : Kernel interrupt stacks are prepatterned during -/// \c pk_initialize(). Thread stacks are prepatterned during -/// \c pk_thread_create(). -/// -/// 2 : (\b Default - Currently Unimplemented) In addition to prepatterning, -/// stack utilization is computed at the exit of context switches and -/// interrupt processing. The maximum utilization is stored in -/// the thread data structure. The kernel will panic if stack overflow is -/// detected. Stack utilization is not computed for the idle thread. - -#ifndef PK_STACK_CHECK -#define PK_STACK_CHECK 1 -#endif - -/// A hook for main() -/// -/// This hook macro is expanded in the body of __pk_main() prior to the call -/// of the application main(). The application can redefine this hook macro -/// in (or in headers referred to in) the application header -/// pk_app_cfg.h. The PK_MAIN_HOOK will run on the stack of main(). - -#ifndef PK_MAIN_HOOK -#define PK_MAIN_HOOK do {} while (0) -#endif - -/// A hook for pk_start_threads() -/// -/// This hook macro is expanded in the call-tree of pk_start_threads() before -/// threads are actually started. The application can redefine this hook -/// macro in (or in headers referred to in) the application header -/// pk_app_cfg.h. -/// -/// The PK_START_THREADS_HOOK runs as a pseudo-interrupt handler on the -/// kernel stack, with external interrupts disabled. - -#ifndef PK_START_THREADS_HOOK -#define PK_START_THREADS_HOOK do {} while (0) -#endif - -/// The maximum value of the \c PkTimebase type. - -#define PK_TIMEBASE_MAX ((PkTimebase)-1) - -/// A special value that specifies that the timebase will not be reset during -/// pk_init(). - -#define PK_TIMEBASE_CONTINUES PK_TIMEBASE_MAX - -/// By convention, a timeout value indicating 'no waiting' in a call of \c -/// pk_semaphore_pend(). - -#define PK_NO_WAIT 0 - -/// By convention, a timeout value indicating 'wait forever' in a call of \c -/// pk_semaphore_pend(). - -#define PK_WAIT_FOREVER ((PkInterval)-1) - -/// The PK timebase frequency in Hz -/// -/// Earlier version of PK defined the timbase frequency as a preprocessor -/// macro. Now, the timebase frequency is specified as a parameter of the -/// pk_initialize() API. The macro remains defined for backwards -/// compatibility, however all kernel uses of the timebase frequency are now -/// optimized around the timebase parameter. - -#define PK_TIMEBASE_FREQUENCY_HZ __pk_timebase_frequency_hz - -/// This is the unscaled timebase frequency in Hz. -#ifdef APPCFG_USE_EXT_TIMEBASE -#define PK_BASE_FREQ_HZ (uint32_t)25000000 -#else -#define PK_BASE_FREQ_HZ (uint32_t)400000000 -#endif /* APPCFG_USE_EXT_TIMEBASE */ -#define PK_BASE_FREQ_KHZ (PK_BASE_FREQ_HZ / 1000) -#define PK_BASE_FREQ_MHZ (PK_BASE_FREQ_HZ / 1000000) - -/// Scale a time interval to be _closer_ to what was actually requested -/// base on the actual timebase frequency. -#define PK_INTERVAL_SCALE(interval) ((interval) + ((interval) >> __pk_timebase_rshift)) - -/// Convert a time in integral seconds to a time interval - overflows are -/// ignored. The application can redefine this macro. - -#ifndef PK_SECONDS -#define PK_SECONDS(s) ((PkInterval)(PK_BASE_FREQ_HZ * (s))) -#endif - -/// Convert a time in integral milliseconds to a time interval - overflows are -/// ignored, and a frequency evenly (or closely) divisible by 1000 is -/// assumed. The application can redefine this macro. - -#ifndef PK_MILLISECONDS -#define PK_MILLISECONDS(m) ( (PkInterval)(PK_BASE_FREQ_KHZ * (m)) ) -#endif - -/// Convert a time in integral microseconds to a time interval - overflows are -/// ignored, and a frequncy evenly (or closely) divisible by 1,000,000 is -/// assumed. The application can redefine this macro. - -#ifndef PK_MICROSECONDS -#define PK_MICROSECONDS(u) ( (PkInterval)(PK_BASE_FREQ_MHZ * (u)) ) -#endif - -/// Convert a time in integral nanoseconds to a time interval - overflows are -/// ignored, and a frequeyncy evenly (or closely) divisible by 1,000,000 is -/// assumed. The application can redefine this macro. - -#ifndef PK_NANOSECONDS -#define PK_NANOSECONDS(n) ( (PkInterval)( ( ((PK_BASE_FREQ_MHZ<<10)/1000) * (n) ) >> 10) ) -#endif - - -/// Enable PK application tracing (enabled by default) -#ifndef PK_TRACE_ENABLE -#define PK_TRACE_ENABLE 1 -#endif - -/// Enable PK kernel tracing (disabled by default) -#ifndef PK_KERNEL_TRACE_ENABLE -#define PK_KERNEL_TRACE_ENABLE 0 -#endif - -//Application trace macros -#if !PK_TRACE_ENABLE -#define PK_TRACE(...) -#define PK_TRACE_BIN(str, bufp, buf_size) -#else -#define PK_TRACE(...) PKTRACE(__VA_ARGS__) -#define PK_TRACE_BIN(str, bufp, buf_size) PKTRACE_BIN(str, bufp, buf_size) -#endif - - -//Kernel trace macros -#if !PK_KERNEL_TRACE_ENABLE -#define PK_KERN_TRACE(...) -#define PK_KERN_TRACE_ASM16(...) -#else -#define PK_KERN_TRACE(...) PK_TRACE(__VA_ARGS__) -#define PK_KERN_TRACE_ASM16(...) PK_TRACE_ASM16(__VA_ARGS__) -#endif /* PK_KERNEL_TRACE_ENABLE */ - - -/// Add a string to the trace buffer with an optional register holding a 16bit value -/// WARNING: This calls a c function which may clobber any of the volatile registers -#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) -#define PK_TRACE_ASM16(...) TRACE_ASM_HELPER16(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) -#else -#define PK_TRACE_ASM16(...) -#endif /* PK_TRACE_SUPPORT */ - -/// The following macros are helper macros for tracing. They should not be called -/// directly. -#define VARG_COUNT_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, N, ...) N -#define VARG_COUNT(...) VARG_COUNT_HELPER(, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) - -#ifdef __ASSEMBLER__ -#define TRACE_ASM_HELPER16_CALL(count, ...) TINY_TRACE_ASM ## count (__VA_ARGS__) -#define TRACE_ASM_HELPER16(count, ...) TRACE_ASM_HELPER16_CALL(count, __VA_ARGS__) - -#define TINY_TRACE_ASM0() .error "format string required" -#define TINY_TRACE_ASM1(str) \ - .tiny_trace_asm1 trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) -#define TINY_TRACE_ASM2(str, reg) \ - .tiny_trace_asm2 trace_ppe_hash(str, PK_TRACE_HASH_PREFIX), reg -#define TINY_TRACE_ASM3() .error "too many parameters" -#define TINY_TRACE_ASM4() .error "too many parameters" -#define TINY_TRACE_ASM5() .error "too many parameters" -#define TINY_TRACE_ASM6() .error "too many parameters" -#define TINY_TRACE_ASM7() .error "too many parameters" - -//TODO: add support for tracing more than 1 parameter and binary data in assembly - - .global pk_trace_tiny - - .macro .tiny_trace_asm1 hash16 - lis %r3, \hash16 - bl pk_trace_tiny - .endm - - .macro .tiny_trace_asm2 hash16, parm16 - clrlwi %r3, \parm16, 16 - oris %r3, %r3, \hash16 - bl pk_trace_tiny - .endm - -#endif /*__ASSEMBLER__*/ - - - -#ifndef __ASSEMBLER__ - -#include <stddef.h> -#include <stdint.h> - -/// The timebase frequency in Hz; A parameter to pk_initialize() -extern uint32_t __pk_timebase_frequency_hz; - -extern uint8_t __pk_timebase_rshift; - -/// The timebase frequency in KHz -extern uint32_t __pk_timebase_frequency_khz; - -/// The timebase frequency in Mhz -extern uint32_t __pk_timebase_frequency_mhz; - - -typedef unsigned long int PkAddress; - -typedef uint8_t PkThreadState; - -typedef uint8_t PkThreadPriority; - -typedef uint8_t PkThreadFlags; - -typedef uint32_t PkSemaphoreCount; - -typedef uint64_t PkTimebase; - -typedef PK_TIME_INTERVAL_TYPE PkInterval; - -#include "pk_port_types.h" - -typedef struct { - - /// A priority queue of threads pending on the semaphore. - PkThreadQueue pending_threads; - - /// The current semaphore count. - PkSemaphoreCount count; - - /// The maximum allowable count - for error checking. - PkSemaphoreCount max_count; - -} PkSemaphore; - - -/// Compile-time initialize a PkSemaphore structure -/// -/// This low-level macro creates a structure initializatin of an PkSemaphore -/// structure. This can be used for example to create compile-time initialized -/// arrays of semaphores. -#define PK_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ - {.pending_threads = 0, \ - .count = (_initial_count), \ - .max_count = (_max_count)} - - -/// Declare and initialize a semaphore -#define PK_SEMAPHORE(sem, initial_count, max_count) \ - PkSemaphore sem = PK_SEMAPHORE_INITIALIZATION(initial_count, max_count) - - -/// Trace macros for C functions -#define HASH_ARG_COMBO(str, arg) \ - ((((uint32_t)trace_ppe_hash(str, PK_TRACE_HASH_PREFIX)) << 16) | ((uint32_t)(arg) & 0x0000ffff)) - -#define PKTRACE0(...) pk_trace_tiny() //will fail at compile time - -#define PKTRACE1(str) \ - pk_trace_tiny((trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) << 16)) - -#define PKTRACE2(str, parm0) \ - ((sizeof(parm0) <= 2)? \ - pk_trace_tiny(HASH_ARG_COMBO(str, parm0)): \ - pk_trace_big(HASH_ARG_COMBO(str, 1), ((uint64_t)parm0) << 32, 0)) - -#define PKTRACE3(str, parm0, parm1) \ - pk_trace_big(HASH_ARG_COMBO(str, 2), ((((uint64_t)parm0) << 32) | parm1), 0) - -#define PKTRACE4(str, parm0, parm1, parm2) \ - pk_trace_big(HASH_ARG_COMBO(str, 3), ((((uint64_t)parm0) << 32) | parm1),\ - ((uint64_t)parm2) << 32 ) - -#define PKTRACE5(str, parm0, parm1, parm2, parm3) \ - pk_trace_big(HASH_ARG_COMBO(str, 4), ((((uint64_t)parm0) << 32) | parm1),\ - ((((uint64_t)parm2) << 32) | parm3) ) - -#define PKTRACE6(...) pk_trace_tiny() //will fail at compile time -#define PKTRACE7(...) pk_trace_tiny() //will fail at compile time - -#define PKTRACE_HELPER2(count, ...) PKTRACE ## count (__VA_ARGS__) -#define PKTRACE_HELPER(count, ...) PKTRACE_HELPER2(count, __VA_ARGS__) - -#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) -#define PKTRACE(...) PKTRACE_HELPER(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) -#define PKTRACE_BIN(str, bufp, buf_size) \ - pk_trace_binary(((buf_size < 255)? HASH_ARG_COMBO(str, buf_size): HASH_ARG_COMBO(str, 255)), bufp) -#else -#define PKTRACE(...) -#define PKTRACE_BIN(str, bufp, buf_size) -#endif //PK_TRACE_SUPPORT - - - -/// A generic doubly-linked list object -/// -/// This object functions both as a sentinel mode for a deque as well as a -/// pointer container for elements in deques. The PK API assumes that -/// queueable structures will be defined with an PkDeque structure as the -/// initial 'data member' of the structure. This allows a pointer to a queue -/// element to be cast to a pointer to an PkDeque and vice-versa. - -typedef struct PkDeque { - - /// Pointer to the head or the next element in a deque. - /// - /// When an PkDeque is used as the sentinel node for a queue, \a next - /// points to the head of the queue, and the condition (next == \<self\>) - /// indicates an empty PkDeque. By convention the condition (\a next == - /// 0) is used to indicate that a queue element is not enqueued. - struct PkDeque* next; - - /// Pointer to the tail or previous element in a deque. - /// - /// When a DQueue is used as the sentinel node for a queue, \a previous - /// points to the tail of the queue. - struct PkDeque* previous; - -} PkDeque; - - -typedef void (*PkTimerCallback)(void *); - -#define PK_TIMER_CALLBACK(callback) void callback(void *) - -struct PkTimer; - -/// The PK timer object - -typedef struct PkTimer { - - /// The time queue management pointers - /// - /// This pointer container is defined as the first element of the - /// structure to allow the PkTimer to be cast to an PkDeque and - /// vice-versa. - PkDeque deque; - - /// The absolute timeout of the timer. - PkTimebase timeout; - - /// The timer callback - /// - /// For PK thread timers used to implement Sleep and semaphore pend - /// timeouts this field is initialized to __pk_thread_timeout(). - PkTimerCallback callback; - - /// Private data passed to the callback. - /// - /// For PK thread timers used to implement Sleep and semaphore pend this - /// field is initialized to a pointer to the thread. - void *arg; - -} PkTimer; - - -// Threads - -typedef void (*PkThreadRoutine)(void *arg); - -#define PK_THREAD_ROUTINE(f) void f(void *arg); - -typedef struct { - - /// Stack pointer saved during context switches. Assembler code expects - /// this to always be at address offset 0 from the thread pointer. - PkAddress saved_stack_pointer; - - /// This is 1 past the last valid byte address of the thread stack. - /// Assembler code expects this to always be at address offset (sizeof - /// PkAddress) from the thread pointer. - PkAddress stack_limit; - - /// This is the original base of the stack. - /// Assembler code expects this to always be at address offset 2 * (sizeof - /// PkAddress) from the thread pointer. - PkAddress stack_base; - - /// If the thread is blocked on a semaphore, then this is the semaphore the - /// thread is blocked on. - PkSemaphore *semaphore; - - /// The thread priority. - PkThreadPriority priority; - - /// The thread state; See \ref pk_thread_states - PkThreadState state; - - /// Thread flags; See \ref pk_thread_flags - PkThreadFlags flags; - - /// The timer structure handles Sleep and blocking on a semaphore with - /// timeout. - PkTimer timer; - -} PkThread; - - -typedef void (*PkBhHandler)(void *); - -#define PK_BH_HANDLER(handler) void handler(void *) - -typedef struct { - - /// The bottom half queue management pointers - /// - /// This pointer container is defined as the first element of the - /// structure to allow the PkBottomHalf to be cast to a PkDeque and - /// vice-versa. - PkDeque deque; - - /// The bottom half handler - PkBhHandler bh_handler; - - /// Private data passed to the handler. - void *arg; - -} PkBottomHalf; - - -// Initialization APIs - -int -pk_initialize(PkAddress kernel_stack, - size_t kernel_stack_size, - PkTimebase initial_timebase, - uint32_t timebase_frequency_hz); - - -// Timebase APIs - -PkTimebase -pk_timebase_get(void); - - -// Timer APIs - -int -pk_timer_create(PkTimer *timer, - PkTimerCallback callback, - void *arg); - - -int -pk_timer_schedule(PkTimer *timer, - PkInterval interval); - -int -pk_timer_cancel(PkTimer *timer); - -int -pk_timer_info_get(PkTimer *timer, - PkTimebase *timeout, - int *active); - -// Thread APIs - -int -pk_thread_create(PkThread *thread, - PkThreadRoutine thread_routine, - void *arg, - PkAddress stack, - size_t stack_size, - PkThreadPriority priority); - -int -pk_start_threads(void); - -int -pk_thread_resume(PkThread *thread); - -int -pk_thread_suspend(PkThread *thread); - -int -pk_thread_delete(PkThread *thread); - -int -pk_complete(void); - -int -pk_sleep(PkInterval interval); - -int -pk_thread_info_get(PkThread *thread, - PkThreadState *state, - PkThreadPriority *priority, - int *runnable); - -int -pk_thread_priority_change(PkThread *thread, - PkThreadPriority new_priority, - PkThreadPriority *old_priority); - -int -pk_thread_at_priority(PkThreadPriority priority, - PkThread **thread); - -int -pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b); - - -// Semaphore APIs - -int -pk_semaphore_create(PkSemaphore *semaphore, - PkSemaphoreCount initial_count, - PkSemaphoreCount max_count); - -int -pk_semaphore_post(PkSemaphore *semaphore); - -int -pk_semaphore_pend(PkSemaphore *semaphore, - PkInterval timeout); - -int -pk_semaphore_release_all(PkSemaphore *semaphore); - - -int -pk_semaphore_info_get(PkSemaphore *semaphore, - PkSemaphoreCount *count, - int *pending); - -void -pk_semaphore_post_handler(void *arg, - PkIrqId irq); - -// Misc. APIs - -void -pk_halt() __attribute__ ((noreturn)); - -// Deque APIs - -int -pk_deque_sentinel_create(PkDeque *deque); - -#define PK_DEQUE_SENTINEL_INIT(dq_addr) \ -{\ - .next = dq_addr, \ - .previous = dq_addr \ -} - -#define PK_DEQUE_SENTINEL_STATIC_CREATE(deque) \ - PkDeque deque = PK_DEQUE_SENTINEL_INIT(&deque) - -int -pk_deque_element_create(PkDeque *element); - -#define PK_DEQUE_ELEMENT_INIT() \ -{\ - .next = 0, \ - .previous = 0 \ -} - -#define PK_DEQUE_ELEMENT_STATIC_CREATE(deque) \ - PkDeque deque = PK_DEQUE_ELEMENT_INIT() - -/// Check for an empty PkDeque -/// -/// \param deque The sentinel node of a deque -/// -/// \retval 0 The PkDeque is not empty -/// -/// \retval 1 The PkDeque is empty - -static inline int -pk_deque_is_empty(PkDeque *deque) -{ - return (deque == deque->next); -} - - -/// Check if an PkDeque element is currently enqueued -/// -/// \param element Typically the PkDeque object of a queable structure -/// -/// \retval 0 The element is not currently enqueued -/// -/// \retval 1 The element is currently enqueued - -static inline int -pk_deque_is_queued(PkDeque *element) -{ - return (element->next != 0); -} - - -/// Append an element to the tail of a deque (FIFO order) -/// -/// \param deque The sentinel node of a deque -/// -/// \param element Typically the PkDeque object of a queable structure -/// -/// It is an error to call this API on an element that is already enqueued, -/// but the API does not check for this error. - -static inline void -pk_deque_push_back(PkDeque *deque, PkDeque *element) -{ - deque->previous->next = element; - element->previous = deque->previous; - element->next = deque; - deque->previous = element; -} - - -/// Push an element at the head of a deque (LIFO order) -/// -/// \param deque The sentinel node of a deque -/// -/// \param element Typically the PkDeque object of a queable structure -/// -/// It is an error to call this API on an element that is already enqueued, -/// but the API does not check for this error. - -static inline void -pk_deque_push_front(PkDeque *deque, PkDeque *element) -{ - deque->next->previous = element; - element->next = deque->next; - element->previous = deque; - deque->next = element; -} - -/// Pop an element from the head of a deque -/// -/// \param deque The sentinel node of a deque -/// -/// \retval 0 The PkDeque was empty prior to the call -/// -/// \retval non-0 A pointer to the previous head of the deque, which has been -/// removed from the deque and marked as no longer queued. - -// The cast of 'head' is used to remove the 'volatile' attribute. - -static inline PkDeque * -pk_deque_pop_front(PkDeque *deque) -{ - PkDeque *head; - - if (pk_deque_is_empty(deque)) { - return 0; - } else { - head = (PkDeque *)(deque->next); - deque->next = head->next; - deque->next->previous = deque; - head->next = 0; - return head; - } -} - - -/// Remove a deque element from any position in the deque -/// -/// \param element Typically the PkDeque object of a queable structure -/// -/// It is an error to call this API on an element that is not currently -/// enqueued, but the API does not check for this error. - -static inline void -pk_deque_delete(PkDeque *element) -{ - element->previous->next = element->next; - element->next->previous = element->previous; - element->next = 0; -} - -// Bottom Half APIs - -extern PkDeque _pk_bh_queue; - -static inline void -pk_bh_schedule(PkBottomHalf *bottom_half) -{ - pk_deque_push_back(&_pk_bh_queue, (PkDeque *)bottom_half); -} - -#define PK_BH_INIT(_handler, _arg) \ -{\ - .deque = PK_DEQUE_ELEMENT_INIT(), \ - .bh_handler = _handler, \ - .arg = _arg \ -} - -#define PK_BH_STATIC_CREATE(bh_name, handler, arg) \ -PkBottomHalf bh_name = PK_BH_INIT(handler, arg) - - -//Trace function prototypes -void pk_trace_tiny(uint32_t i_parm); -void pk_trace_big(uint32_t i_hash_and_count, - uint64_t i_parm1, uint64_t i_parm2); -void pk_trace_binary(uint32_t i_hash_and_size, void* bufp); -void pk_trace_set_timebase(PkTimebase timebase); - - -/// Cast a pointer to another type, in a way that won't cause warnings - -#define PK_CAST_POINTER(t, p) ((t)((PkAddress)(p))) - -// Static Assert Macro for Compile time assertions. -// - This macro can be used both inside and outside of a function. -// - A value of false will cause the ASSERT to produce this error -// - This will show up on a compile fail as: -// <file>:<line> error: size of array '_static_assert' is negative -// - It would be trivial to use the macro to paste a more descriptive -// array name for each assert, but we will leave it like this for now. -#define PK_STATIC_ASSERT(cond) extern uint8_t _static_assert[(cond) ? 1 : -1] __attribute__ ((unused)) - -/// \page pk_errors PK API and Kernel Error Handling -/// -/// Error checking in the PK API consumes a significant amount of code space. -/// Approximately 20% of the object code in the PPC405 port is devoted to -/// error checking. Presumably a like amount of time overhead is also added to -/// PK API calls by this checking. -/// -/// API error checking can be disabled to save space and time in the kernel. -/// API errors can also be configured to cause kernel panics, allowing -/// applications to be coded without the overhead of error checking but still -/// providing an escape in the event of application errors or (unlikely) -/// hardware failures. The PK default is to check for API errors and kernel -/// invariants, and panic should errors occur. -/// -/// PK follows the Unix convention that a successful call of an API returns 0 -/// (PK_OK), but returns a negative code in the event of failure, or to -/// provide further information. The error codes are all defined as manifest -/// constants. -/// -/// Some negative codes returned by PK APIs are not considered errors. These -/// conditions are always checked, never cause a panic if they occur, and -/// their interpretation is always left to the application. See the detailed -/// documentation for each API for lists of error and non-error codes returned -/// by the API. -/// -/// There are three configuration options that control error handling in the -/// PK API and kernel: -/// -/// \c PK_ERROR_CHECK_API -/// -/// \arg \b 0 - No PK API error checking. APIs that potentially return error -/// codes will always return 0 (PK_OK) instead of an error code. Those -/// APIs that return negative codes that are not errors (see Table 1.5) -/// always return the negative non-error codes when appropriate. -/// -/// \arg \b 1 - (Default) All PK API errors are checked. The behavior in -/// the event of an error is defined by the configuration option -/// PK_ERROR_PANIC. -/// -/// \c PK_ERROR_CHECK_KERNEL -/// -/// \arg \b 0 - No kernel invariant error checking is done. -/// -/// \arg \b 1 - (Default) Selected kernel invariants are checked. The overhead -/// for these checks should be minimal. -/// -/// \c PK_ERROR_PANIC -/// -/// \arg \b 0 - PK API calls return negative error codes in the event of -/// errors. Note that PK kernel invariants always cause a panic if -/// violations occur. -/// -/// \arg \b 1 - (Default) In the event of errors PK APIs invoke PK_PANIC(code), -/// where code is a positive error code. Kernel invariant checks always -/// cause a panic if violations are detected. - -#endif /* __ASSEMBLER__ */ - -#endif /* __PK_API_H__ */ diff --git a/pk/kernel/pk_bh_core.c b/pk/kernel/pk_bh_core.c deleted file mode 100644 index 8a6181cb..00000000 --- a/pk/kernel/pk_bh_core.c +++ /dev/null @@ -1,29 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2015 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_bh_core.c -/// \brief PK bottom half APIs -/// -/// The entry points in this file are considered 'core' routines that will -/// always be present at runtime in any PK application. - -#include "pk.h" - -/// Statically initialize the bottom half queue -PK_DEQUE_SENTINEL_STATIC_CREATE(_pk_bh_queue); - -void _pk_process_bh(void) -{ - PkBottomHalf *bh; - while((bh = (PkBottomHalf*)pk_deque_pop_front(&_pk_bh_queue)) != 0) - { - bh->bh_handler(bh->arg); - } - return; -} - - -#undef __PK_THREAD_CORE_C__ diff --git a/pk/kernel/pk_core.c b/pk/kernel/pk_core.c deleted file mode 100644 index bfa6d6be..00000000 --- a/pk/kernel/pk_core.c +++ /dev/null @@ -1,79 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_core.c -/// \brief Core routines for the PK kernel. -/// -/// The entry points in this file are routines that are expected to be needed -/// at runtime by all PK applications. This file also serves as a place for -/// kernel global variables to be realized. - -#define __PK_CORE_C__ - -#include "pk.h" - -#if !PK_TIMER_SUPPORT - -/// If there is no timer support, then any call of the timer interrupt handler -/// is considered a fatal error. - -void -__pk_timer_handler() -{ - PK_PANIC(PK_NO_TIMER_SUPPORT); -} - -#endif /* PK_TIMER_SUPPORT */ - - -/// Initialize an PkDeque sentinel node -/// -/// \param deque The sentinel node of the deque -/// -/// PK has no way of knowing whether the \a deque is currently in use, so -/// this API must only be called on unitialized or otherwise unused sentinel -/// nodes. -/// -/// \retval 0 success -/// -/// \retval -PK_INVALID_DEQUE_SENTINEL The \a deque pointer was null - -int -pk_deque_sentinel_create(PkDeque *deque) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(deque == 0, PK_INVALID_DEQUE_SENTINEL); - } - - deque->next = deque->previous = deque; - return 0; -} - - -/// Initialize an PkDeque element -/// -/// \param element Typically the PkDeque object of a queable structure -/// -/// PK has no way of knowing whether the \a element is currently in use, so -/// this API must only be called on unitialized or otherwise unused deque -/// elements. -/// -/// \retval 0 success -/// -/// \retval -PK_INVALID_DEQUE_ELEMENT The \a element pointer was null - -int -pk_deque_element_create(PkDeque *element) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(element == 0, PK_INVALID_DEQUE_ELEMENT); - } - - element->next = 0; - return 0; -} - -#undef __PK_CORE_C__ diff --git a/pk/kernel/pk_debug_ptrs.c b/pk/kernel/pk_debug_ptrs.c deleted file mode 100644 index 0ed540f4..00000000 --- a/pk/kernel/pk_debug_ptrs.c +++ /dev/null @@ -1,53 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2015 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_debug_ptrs.c -/// \brief Defines a table of pointers to important kernel debug data. -/// -/// This table is placed in a special section named .debug_ptrs which can be -/// placed at a well-known memory location for tools to find. -/// - -#include "pk.h" -#include "pk_trace.h" -#include "pk_debug_ptrs.h" - -extern PkTimebase ppe42_64bit_timebase; - -#if PK_TRACE_SUPPORT -extern PkTraceBuffer g_pk_trace_buf; -#endif - -pk_debug_ptrs_t pk_debug_ptrs SECTION_ATTRIBUTE(".debug_ptrs") = -{ - .debug_ptrs_size = sizeof(pk_debug_ptrs), - .debug_ptrs_version = PK_DEBUG_PTRS_VERSION, - -#if PK_TRACE_SUPPORT - .debug_trace_ptr = &g_pk_trace_buf, - .debug_trace_size = sizeof(g_pk_trace_buf), -#else - .debug_trace_ptr = 0, - .debug_trace_size = 0, -#endif /* PK_TRACE_SUPPORT */ - -#if PK_THREAD_SUPPORT - .debug_thread_table_ptr = &__pk_priority_map, - .debug_thread_table_size = sizeof(__pk_priority_map), - .debug_thread_runq_ptr = (void*)&__pk_run_queue, - .debug_thread_runq_size = sizeof(__pk_run_queue), -#else - .debug_thread_table_ptr = 0, - .debug_thread_table_size = 0, - .debug_thread_runq_ptr = 0, - .debug_thread_runq_size = 0, -#endif /* PK_THREAD_SUPPORT */ - - .debug_timebase_ptr = &ppe42_64bit_timebase, - .debug_timebase_size = sizeof(ppe42_64bit_timebase), - -}; - diff --git a/pk/kernel/pk_debug_ptrs.h b/pk/kernel/pk_debug_ptrs.h deleted file mode 100644 index 62a36839..00000000 --- a/pk/kernel/pk_debug_ptrs.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef __PK_DEBUG_PTRS_H__ -#define __PK_DEBUG_PTRS_H__ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2015 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_debug_ptrs.h -/// \brief Structure for a table of pointers to kernel debug data -/// - -#define PK_DEBUG_PTRS_VERSION 1 - -typedef struct -{ - // The size and version of this structure - unsigned short debug_ptrs_size; - unsigned short debug_ptrs_version; - - // Trace buffer location and size - void* debug_trace_ptr; - unsigned long debug_trace_size; - - // Thread table location and size - void* debug_thread_table_ptr; - unsigned long debug_thread_table_size; - - // Thread run queue location and size - void* debug_thread_runq_ptr; - unsigned long debug_thread_runq_size; - - // Emulated timebase location and size - void* debug_timebase_ptr; - unsigned long debug_timebase_size; - -} pk_debug_ptrs_t; - -#endif /*__PK_DEBUG_PTRS_H__*/ diff --git a/pk/kernel/pk_init.c b/pk/kernel/pk_init.c deleted file mode 100644 index ebd6323a..00000000 --- a/pk/kernel/pk_init.c +++ /dev/null @@ -1,196 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_init.c -/// \brief PK initialization -/// -/// The entry points in this file are initialization routines - they are never -/// needed after PK initialization and their code space could be reclaimed by -/// the application after initialization if required. - -#include "pk.h" -#include "pk_trace.h" - -uint32_t __pk_timebase_frequency_hz; - -/// The timebase frequency is passed into PK during initialization. It cannot -/// be set statically because there is a requirement to support a frequency -/// that can change from one IPL to the next. On the 405, scaling of time -/// intervals is accomplished by doing a 32x32 bit multiplication which is -/// supported by the ppc405 instruction set. PPE42 does not support 32x32 bit -/// multiplication directly and some applications can not afford to use a -/// function call to emulate the operation. Instead we scale the time -/// interval by shifting the value X bits to the right and adding it to itself. -/// This can scale the value by 2, 1.5, 1.25, 1.125, etc. -/// -/// This is the right shift value. -/// NOTE: shifting by 0 gives a 2x scale factor, shifting by 32 gives a 1x -/// scale factor. -uint8_t __pk_timebase_rshift = 32; - -void pk_set_timebase_rshift(uint32_t timebase_freq_hz) -{ - //Use 1.0 scale if less than halfway between 1.0 and 1.25 - if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3))) - { - __pk_timebase_rshift = 32; - } - - //use 1.25 scale if less than halfway between 1.25 and 1.5 - else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3) + (PK_BASE_FREQ_HZ >> 2))) - { - __pk_timebase_rshift = 2; - } - //use 1.5 scale if less than halfway between 1.5 and 2.0 - else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 2) + (PK_BASE_FREQ_HZ >> 1))) - { - __pk_timebase_rshift = 1; - } - //use 2.0 scale if greater than 1.5 - else - { - __pk_timebase_rshift = 0; - } -} - -/// Initialize PK. -/// -/// \param kernel_stack A stack area for interrupt and bottom-half handlers. -/// -/// \param kernel_stack_size The size (in bytes) of the stack area for -/// interrupt and bottom-half handlers. -/// -/// \param initial_timebase The initial value of the PK timebase. -/// If the argument is given as the special value \c PK_TIMEBASE_CONTINUES, then the -/// timebase is not reset. -/// -/// \param timebase_frequency_hz The frequency of the PK timebase in Hz. -/// -/// This routine \e must be called before any other PK / routines, and \e -/// should be called before any interrupts are enabled. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_ARGUMENT_INIT A stack pointer is 0 or is given -/// a 0 size. -/// -/// \retval -PK_STACK_OVERFLOW One or both stacks are not large enough to -/// support a minimum context save in the event of an interrupt. - -// Note that PK does not rely on any static initialization of dynamic -// variables. In debugging sessions using RAM-resident PK images it is -// assumed that the processor may be reset at any time, so we always need to -// reset everything at initialization. - -int -pk_initialize(PkAddress kernel_stack, - size_t kernel_stack_size, - PkTimebase initial_timebase, - uint32_t timebase_frequency_hz) -{ - int rc; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((kernel_stack == 0) || - (kernel_stack_size == 0), - PK_INVALID_ARGUMENT_INIT); - } - - __pk_timebase_frequency_hz = timebase_frequency_hz; - - __pk_thread_machine_context_default = PK_THREAD_MACHINE_CONTEXT_DEFAULT; - - rc = __pk_stack_init(&kernel_stack, &kernel_stack_size); - if (rc) { - return rc; - } - - __pk_kernel_stack = kernel_stack; - __pk_kernel_stack_size = kernel_stack_size; - -#if PK_TIMER_SUPPORT - - // Initialize the time queue sentinel as a circular queue, set the next - // timeout and clear the cursor. - - pk_deque_sentinel_create((PkDeque*)&__pk_time_queue); - __pk_time_queue.cursor = 0; - __pk_time_queue.next_timeout = PK_TIMEBASE_MAX; - -#if PK_TRACE_SUPPORT -extern PkTimer g_pk_trace_timer; -extern PkTraceBuffer g_pk_trace_buf; - - //set the trace timebase HZ - g_pk_trace_buf.hz = timebase_frequency_hz; - - //set the shift adjustment to get us closer to the true - //timebase frequency (versus what was hardcoded) - pk_set_timebase_rshift(timebase_frequency_hz); - - if(initial_timebase != PK_TIMEBASE_CONTINUES) - { - //set the timebase ajdustment for trace synchronization - pk_trace_set_timebase(initial_timebase); - } - - // Schedule the timer that puts a 64bit timestamp in the trace buffer - // periodically. This allows us to use 32bit timestamps. - pk_timer_schedule(&g_pk_trace_timer, - PK_TRACE_TIMER_PERIOD); - -#endif /* PK_TRACE_SUPPORT */ - -#endif /* PK_TIMER_SUPPORT */ - -#if PK_THREAD_SUPPORT - - // Clear the priority map. The final entry [PK_THREADS] is for the idle - // thread. - - int i; - for (i = 0; i <= PK_THREADS; i++) { - __pk_priority_map[i] = 0; - } - - // Initialize the thread scheduler - - __pk_thread_queue_clear(&__pk_run_queue); - __pk_current_thread = 0; - __pk_next_thread = 0; - __pk_delayed_switch = 0; - -#endif /* PK_THREAD_SUPPORT */ - - return PK_OK; -} - - -/// Call the application main() -/// -/// __pk_main() is called from the bootloader. It's only purpose is to -/// provide a place for the PK_MAIN_HOOK to be called before main() is -/// called. - -void -__pk_main(int argc, char **argv) -{ - PK_MAIN_HOOK; - - int main(int argc, char **argv); - main(argc, argv); -} - - - - - - - - - diff --git a/pk/kernel/pk_kernel.h b/pk/kernel/pk_kernel.h deleted file mode 100644 index 85b028a2..00000000 --- a/pk/kernel/pk_kernel.h +++ /dev/null @@ -1,246 +0,0 @@ -#ifndef __PK_KERNEL_H__ -#define __PK_KERNEL_H__ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_kernel.h -/// \brief PK portable kernel (non-API) data and data structures -/// -/// \todo In theory, as long as the critical section entry/exit macros use GCC -/// memory barriers, we should be able to eliminate all of the 'volatile' -/// declarations in PK code. These have been added to the port, so -/// we should try it. - -#ifdef __PK_CORE_C__ -#define IF__PK_CORE_C__(x) x -#define UNLESS__PK_CORE_C__(x) -#else -#define IF__PK_CORE_C__(x) -#define UNLESS__PK_CORE_C__(x) x -#endif - -#if PK_MINIMIZE_KERNEL_CODE_SPACE -#define IF_PK_MINIMIZE_KERNEL_CODE_SPACE(x) x -#define UNLESS_PK_MINIMIZE_KERNEL_CODE_SPACE(x) -#else -#define IF_PK_MINIMIZE_KERNEL_CODE_SPACE(x) -#define UNLESS_PK_MINIMIZE_KERNEL_CODE_SPACE(x) x -#endif - - -#ifndef __ASSEMBLER__ - -/// This is the stack pointer saved when switching from a thread context to an -/// interrupt context. - -UNLESS__PK_CORE_C__(extern) -volatile -PkAddress __pk_saved_sp; - -/// The kernel stack; constant once defined by the call of -/// pk_initialize(). - -UNLESS__PK_CORE_C__(extern) -volatile -PkAddress __pk_kernel_stack; - -/// This is the run queue - the queue of mapped runnable tasks. -UNLESS__PK_CORE_C__(extern) -volatile -PkThreadQueue __pk_run_queue; - -/// This flag is set by \c __pk_schedule() if a new highest-priority thread -/// becomes runnable during an interrupt handler. The context switch will -/// take place at the end of interrupt processing, and the -/// interrupt handling code will clear the flag. - -UNLESS__PK_CORE_C__(extern) -volatile -int __pk_delayed_switch; - -/// The currently running thread, or NULL (0) to indicate the idle thread -/// -/// \a __pk_current_thread holds a pointer to the currently executing -/// thread. This pointer will be NULL (0) under the following conditions: -/// -/// - After pk_initialize() but prior to pk_start_threads() -/// -/// - After pk_start_threads(), when no threads are runnable. In this case -/// the NULL (0) value indicates that the PK idle thread is 'running'. -/// -/// - After pk_start_threads(), when the current (non-idle) thread has -/// completed or been deleted. -/// -/// If \a __pk_current_thread == 0 then there is no requirement to save any -/// register state on a context switch, either because the PK idle thread has -/// no permanent context, or because any thread context on the kernel stack is -/// associated with a deleted thread. -/// -/// If \a __pk_current_thread != 0 then \a __pk_current_thread is a pointer -/// to the currently executing thread. In an interrupt handler \a -/// pk_current_thread is a pointer to the thread whose context is saved on -/// the kernel stack. -UNLESS__PK_CORE_C__(extern) -volatile -PkThread* __pk_current_thread; - -/// The thread to switch to during the next context switch, or NULL (0). -/// -/// \a __pk_next_thread is computed by __pk_schedule(). \a -/// __pk_next_thread holds a pointer to the thread to switch to at the next -/// context switch. In a thread context the switch happens immediately if \a -/// __pk_next_thread == 0 or \a __pk_next_thread != \a __pk_current_thread. -/// In an interrupt context the check happens at the end of processing all -/// interrupts. -/// -/// \a __pk_next_thread may be NULL (0) under the following -/// conditions: -/// -/// - After pk_initialize() but prior to pk_start_threads(), assuming no -/// threads have been made runnable. -/// -/// - After pk_start_threads(), when no threads are runnable. In this case -/// the NULL (0) value indicates that the PK idle thread is the next thread -/// to 'run'. -/// -/// If \a __pk_next_thread == 0 then there is no requirement to restore -/// any register state on a context switch, because the PK idle thread has -/// no permanent context. -/// -/// If \a __pk_next_thread != 0 then \a __pk_next_thread is a pointer -/// to the thread whose context will be restored at the next context switch. -UNLESS__PK_CORE_C__(extern) -volatile -PkThread* __pk_next_thread; - -/// The priority of \a __pk_next_thread -/// -/// If \a __pk_next_thread == 0, the \a __pk_next_priority == PK_THREADS. -UNLESS__PK_CORE_C__(extern) -volatile -PkThreadPriority __pk_next_priority; - -/// This variable holds the default thread machine context for newly created -/// threads. The idle thread also uses this context. This variable is normally -/// constant after the call of \c pk_initialize(). - -UNLESS__PK_CORE_C__(extern) -volatile -PkMachineContext __pk_thread_machine_context_default; - - -/// The size of the kernel stack (bytes). - -UNLESS__PK_CORE_C__(extern) -volatile -size_t __pk_kernel_stack_size; - -/// This table maps priorities to threads, and contains PK_THREADS + 1 -/// entries. The final entry is for the idle thread and will always be null -/// after initizlization. - -UNLESS__PK_CORE_C__(extern) -volatile -PkThread* __pk_priority_map[PK_THREADS + 1]; - -/// The PK time queue structure -/// -/// This structure is defined for use by the kernel, however applications -/// could also use this structure to define their own time queues. - -typedef struct { - - /// A sentinel node for the time queue. - /// - /// The time queue is an PkDeque managed as a FIFO queue for queue - /// management purpose, although events time out in time order. - /// - /// This pointer container is defined as the first element of the - /// structure to allow the PkTimeQueue to be cast to an PkDeque. - PkDeque queue; - - /// The next timeout in absolute time. - PkTimebase next_timeout; - - /// A pointer to allow preemption of time queue processing - /// - /// If non-0, then this is the next timer in the time queue to handle, or - /// a pointer to the \a queue object indicating no more timers to handle. - /// - /// \a cursor != 0 implies that time queue handler is in the midst of - /// processing the time queue, but has enabled interrupt preemption for - /// processing a timer handler. This means that 1) if the timer pointed to - /// by \a cursor is deleted then the cursor must be assigned to the - /// next timer in the queue; and 2) if a new timer is scheduled then - /// activating the next timeout will be handled by the timer handler. - PkDeque* cursor; - -} PkTimeQueue; - -UNLESS__PK_CORE_C__(extern) -PkTimeQueue __pk_time_queue; - -/// Return a pointer to the PkThread object of the currently running thread, -/// or NULL (0) if PK is idle or has not been started. -/// -/// In this API the current thread is not volatile - it will never change -/// inside application code - thus the 'volatile' is cast away. The PK kernel -/// does not (must not) use this API. - -UNLESS__PK_CORE_C__(extern) -inline PkThread * -pk_current(void) -{ - return (PkThread *)__pk_current_thread; -} - -/// Schedule the next timeout in a machine-specific way. - -void -__pk_schedule_hardware_timeout(PkTimebase timeout); - -/// The thread timeout handler. Portable. - -PK_TIMER_CALLBACK(__pk_thread_timeout); - -/// Generic stack initialization. Portable. - -int -__pk_stack_init(PkAddress *stack, - size_t *size); - -/// Machine-specific thread context initialization. - -void -__pk_thread_context_initialize(PkThread *thread, - PkThreadRoutine thread_routine, - void *arg); - -/// Machine specific resumption of __pk_next_thread at __pk_next_priority -/// without saving the current context. -void -__pk_next_thread_resume(void); - -/// Schedule a timer in the time queue. Portable. -void -__pk_timer_schedule(PkTimer *timer); - -/// Remove a timer from the time queue. Portable. -int -__pk_timer_cancel(PkTimer *timer); - -void -__pk_schedule(void); - - -// Call the application main(). Portable. - -void -__pk_main(int argc, char **argv); - -#endif /* __ASSEMBLER__ */ - -#endif /* __PK_KERNEL_H__ */ diff --git a/pk/kernel/pk_macros.h b/pk/kernel/pk_macros.h deleted file mode 100644 index 45bfbac6..00000000 --- a/pk/kernel/pk_macros.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef __PK_MACROS_H__ -#define __PK_MACROS_H__ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_macros.h -/// \brief Boilerplate macros for PK - -/// This macro encapsulates error handling boilerplate for code that uses the -/// PK API-type error handling, for errors that do not occur in critical -/// sections. - -#define PK_ERROR(code) \ - do { \ - if (PK_ERROR_PANIC) { \ - PK_PANIC(code); \ - } else { \ - return -(code); \ - } \ - } while (0) - - -/// This macro encapsulates error handling boilerplate in the PK API -/// functions, for errors that do not occur in critical sections. - -#define PK_ERROR_IF(condition, code) \ - do { \ - if (condition) { \ - PK_ERROR(code); \ - } \ - } while (0) - - -/// This macro encapsulates error handling boilerplate in the PK API -/// functions, for errors that do not occur in critical sections and always -/// force a kernel panic, indicating a kernel or API bug. - -#define PK_PANIC_IF(condition, code) \ - do { \ - if (condition) { \ - PK_PANIC(code); \ - } \ - } while (0) - - -/// This macro encapsulates error handling boilerplate in the PK API -/// functions, for errors that do not occur in critical sections. -/// The error handling will only be enabled when PK_ERROR_CHECK_API -/// is enabled. - -#define PK_ERROR_IF_CHECK_API(condition, code) \ - do { \ - if (PK_ERROR_CHECK_API) { \ - PK_ERROR_IF(condition, code); \ - } \ - } while (0) - -/// This macro encapsulates error handling boilerplate in the PK API -/// functions, for errors that occur in critical sections. - -#define PK_ERROR_IF_CRITICAL(condition, code, context) \ - do { \ - if (condition) { \ - if (PK_ERROR_PANIC) { \ - PK_PANIC(code); \ - pk_critical_section_exit(context); \ - } else { \ - pk_critical_section_exit(context); \ - return -(code); \ - } \ - } \ - } while (0) - - -/// This is a general macro for errors that require cleanup before returning -/// the error code. - -#define PK_ERROR_IF_CLEANUP(condition, code, cleanup) \ - do { \ - if (condition) { \ - if (PK_ERROR_PANIC) { \ - PK_PANIC(code); \ - cleanup; \ - } else { \ - cleanup; \ - return -(code); \ - } \ - } \ - } while (0) - - - -/// Some PK APIs can only be called from thread contexts - these are APIs -/// that threads call on 'themselves'. - -#define PK_ERROR_UNLESS_THREAD_CONTEXT() \ - PK_ERROR_IF(!__pk_kernel_context_thread(), \ - PK_ILLEGAL_CONTEXT_THREAD_CONTEXT) - - -/// Some PK APIs must be called from an interrupt context only. - -#define PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT() \ - PK_ERROR_IF(!__pk_kernel_context_any_interrupt(), \ - PK_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT) - -#endif /* __PK_MACROS_H__ */ diff --git a/pk/kernel/pk_semaphore_core.c b/pk/kernel/pk_semaphore_core.c deleted file mode 100644 index 0e1e34d4..00000000 --- a/pk/kernel/pk_semaphore_core.c +++ /dev/null @@ -1,318 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_semaphore_core.c -/// \brief PK semaphore APIs -/// -/// The entry points in this file are considered 'core' routines that will -/// always be present at runtime in any PK application that enables -/// semaphores. - -#include "pk.h" - -/// Post a count to a semaphore -/// -/// \param semaphore A pointer to the semaphore -/// -/// If any thread is pending on the semaphore, the highest priority thread -/// will be made runnable and the internal count will remain 0. -/// -/// If no thread is pending on the semaphore then the internal count will be -/// incremented by 1, with overflow wrapping the internal count through 0. If -/// the \a max_count argument supplied when the semaphore was created is -/// non-zero and the new internal count is greater than the \a max_count, an -/// overflow error will be signalled. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_SEMAPHORE_AT_POST The \a semaphore is a null (0) pointer. -/// -/// \retval -PK_SEMAPHORE_OVERFLOW The \a max_count argument supplied when -/// the semaphore was created is non-zero and the new internal count is -/// greater than the \a max_count. - -int -pk_semaphore_post(PkSemaphore *semaphore) -{ - PkMachineContext ctx; - PkThreadPriority priority; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_POST); - } - - pk_critical_section_enter(&ctx); - - priority = __pk_thread_queue_min(&(semaphore->pending_threads)); - - if (priority != PK_IDLE_THREAD_PRIORITY) { - - __pk_thread_queue_delete(&(semaphore->pending_threads), priority); - __pk_thread_queue_insert(&__pk_run_queue, priority); - - PK_KERN_TRACE("THREAD_SEMAPHORE_POST(%d)", priority); - - __pk_schedule(); - - } else { - - semaphore->count++; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((semaphore->max_count > 0) && - (semaphore->count > semaphore->max_count), - PK_SEMAPHORE_OVERFLOW); - } - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Pend on a semaphore with timeout -/// -/// \param semaphore A pointer to the semaphore -/// -/// \param timeout A relative timeout in PK timebase ticks, including the -/// special values PK_NO_WAIT and PK_WAIT_FOREVER -/// -/// This API is normally called from threads, and can only be successfully -/// called from interupt handlers under special conditions. -/// -/// If the internal count of the \a semaphore is non-zero, the internal count -/// is decremented by one and execution of the caller continues. -/// -/// If the internal count of the \a semaphore is zero and the \a timeout is -/// PK_NO_WAIT (0) then the call returns immediately with the informational -/// code -PK_SEMAPHORE_PEND_NO_WAIT. -/// -/// If the internal count of the \a semaphore is zero and the \a timeout is -/// non-zero then a thread will block until either a semaphore count is -/// acquired or the relative timeout expires. If this condition occurs in a -/// call from an interrupt context or before threads have been started then -/// the call will fail with the error \c -PK_SEMAPHORE_PEND_WOULD_BLOCK. -/// -/// Once timed out the thread is removed from the semaphore pending queue and -/// made runnable, and the pk_semaphore_pend() operation will fail, even if -/// the semaphore count becomes available before the thread runs again. The -/// pk_semaphore_pend() API returns the informational code -/// -PK_SEMAPHORE_PEND_TIMED_OUT in this case. -/// -/// By convention, a timeout interval equal to the maximum possible value of -/// the \c PkInterval type is taken to mean "wait forever". A thread blocked -/// on a semaphore in this mode will never time out. PK provides this -/// constant as \c PK_WAIT_FOREVER. -/// -/// Return values other than PK_OK (0) are not necessarily errors; see \ref -/// pk_errors -/// -/// The following return codes are non-error codes: -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_SEMAPHORE_PEND_NO_WAIT timeout is set to PK_NO_WAIT -/// -/// \retval -PK_SEMAPHORE_PEND_TIMED_OUT The semaphore was not acquired -/// before the timeout expired. -/// -/// The following return codes are error codes: -/// -/// \retval -PK_INVALID_SEMAPHORE_AT_PEND The \a semaphore is a null (0) -/// pointer. -/// -/// \retval -PK_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an -/// interrupt context (or before threads have been started), the semaphore -/// internal count was 0 and a non-zero timeout was specified. - -// Note: Casting __pk_current_thread removes the 'volatile' attribute. - -int -pk_semaphore_pend(PkSemaphore *semaphore, - PkInterval timeout) -{ - PkMachineContext ctx; - PkThreadPriority priority; - PkThread *thread; - PkTimer *timer = 0; - PkInterval scaled_timeout = PK_INTERVAL_SCALE(timeout); - - int rc = PK_OK; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_PEND); - } - - pk_critical_section_enter(&ctx); - - if (semaphore->count != 0) { - - semaphore->count--; - - } else if (timeout == PK_NO_WAIT) { - - rc = -PK_SEMAPHORE_PEND_NO_WAIT; - - } else { - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(!__pk_kernel_context_thread(), - PK_SEMAPHORE_PEND_WOULD_BLOCK, - &ctx); - } - - thread = (PkThread *)__pk_current_thread; - priority = thread->priority; - - __pk_thread_queue_insert(&(semaphore->pending_threads), priority); - - thread->semaphore = semaphore; - thread->flags |= PK_THREAD_FLAG_SEMAPHORE_PEND; - - PK_KERN_TRACE("THREAD_SEMAPHORE_PEND(%d)", priority); - - if (timeout != PK_WAIT_FOREVER) { - timer = &(thread->timer); - timer->timeout = pk_timebase_get() + scaled_timeout; - __pk_timer_schedule(timer); - thread->flags |= PK_THREAD_FLAG_TIMER_PEND; - } - - __pk_thread_queue_delete(&__pk_run_queue, priority); - __pk_schedule(); - - thread->flags &= ~PK_THREAD_FLAG_SEMAPHORE_PEND; - - if (thread->flags & PK_THREAD_FLAG_TIMER_PEND) { - if (thread->flags & PK_THREAD_FLAG_TIMED_OUT) { - rc = -PK_SEMAPHORE_PEND_TIMED_OUT; - __pk_thread_queue_delete(&(semaphore->pending_threads), thread->priority); - } else { - __pk_timer_cancel(timer); - } - thread->flags &= - ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT); - } - } - - pk_critical_section_exit(&ctx); - - return rc; -} - - -/// Release all threads blocked on a semaphore -/// -/// \param semaphore A pointer to a semaphore -/// -/// This API is provided to allow an PK semaphore to be used as a thread -/// barrier. pk_semaphore_release_all() simultaneously unblocks all threads -/// (if any) currently pending on a semaphore. A semaphore to be used as a -/// thread barrier will typically be initialized with -/// pk_semaphore_create(\a sem, 0, 0), and sxx_semaphore_post() would never be -/// called on the \a sem. -/// -/// This API never modifies the \a count field of the semaphore; If any -/// threads are blocked on a semaphore the semaphore count is 0 by definition. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_SEMAPHORE_AT_RELEASE The \a semaphore is a null (0) -/// pointer. - -int -pk_semaphore_release_all(PkSemaphore* semaphore) -{ - PkMachineContext ctx; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_RELEASE); - } - - pk_critical_section_enter(&ctx); - - __pk_thread_queue_union(&__pk_run_queue, &(semaphore->pending_threads)); - __pk_thread_queue_clear(&(semaphore->pending_threads)); - __pk_schedule(); - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Get information about a semaphore. -/// -/// \param semaphore A pointer to the PkSemaphore to query -/// -/// \param count The value returned through this pointer is the current count -/// of the semaphore. The caller can set this parameter to the null pointer -/// (0) if this information is not required. -/// -/// \param pending The value returned through this pointer is the current -/// number of threads pending on the semaphore. The caller can set this -/// parameter to the null pointer (0) if this information is not required. -/// -/// The information returned by this API can only be guaranteed consistent if -/// the API is called from a critical section. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_SEMAPHORE_AT_INFO The \a semaphore is a null (0) -/// pointer. - -int -pk_semaphore_info_get(PkSemaphore* semaphore, - PkSemaphoreCount* count, - int* pending) - -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_INFO); - } - - if (count) { - *count = semaphore->count; - } - if (pending) { - *pending = __pk_thread_queue_count(&(semaphore->pending_threads)); - } - - return PK_OK; -} - - -/// An simple interrupt handler that posts to a semaphore. -/// -/// To implement basic event-driven blocking of a thread, install -/// pk_semaphore_post_handler() as the handler for an interrupt -/// and provide a pointer to the semaphore as the \a arg argument in -/// pk_irq_handler_set(). The semaphore should be initialized with -/// pk_semaphore_create(&sem, 0, 1). This handler simply disables (masks) -/// the interrupt, clears the status and calls pk_semaphore_post() on the -/// semaphore. -/// -/// Note that clearing the status in the interrupt controller as done here is -/// effectively a no-op for level-sensitive interrupts. In the level-sensitive -/// case any thread pending on the semaphore must reset the interrupt -/// condition in the device before re-enabling the interrupt. -#if 0 -void -pk_semaphore_post_handler(void *arg, PkIrqId irq, int priority) -{ - pk_irq_disable(irq); - pk_irq_status_clear(irq); - pk_semaphore_post((PkSemaphore *)arg); -} - -#endif diff --git a/pk/kernel/pk_semaphore_init.c b/pk/kernel/pk_semaphore_init.c deleted file mode 100644 index bed029da..00000000 --- a/pk/kernel/pk_semaphore_init.c +++ /dev/null @@ -1,82 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_semaphore_init.c -/// \brief PK semaphore API initialization routines -/// -/// The entry points in this file are routines that are typically used during -/// initialization, and their code space could be deallocated and recovered if -/// no longer needed by the application after initialization. - -#include "pk.h" - -/// Create (initialize) a semaphore -/// -/// \param semaphore A pointer to an PkSemaphore structure to initialize -/// -/// \param initial_count The initial count of the semaphore -/// -/// \param max_count The maximum count allowed in the semaphore, for error -/// checking -/// -/// Semaphores are created (initialized) by a call of \c -/// pk_semaphore_create(), using an application-provided instance of an \c -/// PkSemaphore structure. This structure \e is the semaphore, so the -/// application must never modify the structure if the semaphore is in use. -/// PK has no way to know if an \c PkSemaphore structure provided to -/// \c pk_semaphore_create() is safe to use as a semaphore, and will silently -/// modify whatever memory is provided. -/// -/// PK provides two simple overflow semantics based on the value of max_count -/// in the call of \c pk_semaphore_create(). -/// -/// If \a max_count = 0, then posting to the semaphore first increments the -/// internal count by 1. Overflows are ignored and will wrap the internal -/// count through 0. -/// -/// If \a max_count != 0, then posting to the semaphore first increments the -/// internal count by 1, wrapping through 0 in the event of overflow. If the -/// resulting count is greater than max_count, \c pk_semaphore_post() will -/// return the error \c -PK_SEMAPHORE_POST_OVERFLOW to the caller. -/// -/// In most applications it is probably best to use the \a max_count != 0 -/// semantics to trap programming errors, unless there is a specific -/// application where overflow is expected and ignorable. As a fine point of -/// the specification, a \a max_count of 0 is equivalent to a max_count of -/// 0xFFFFFFFF. -/// -/// Return values other then PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_SEMAPHORE_AT_CREATE The \a semaphore is a null (0) -/// pointer. -/// -/// \retval -PK_INVALID_ARGUMENT_SEMAPHORE The \a max_count is non-zero -/// and less than the \a initial_count. - -int -pk_semaphore_create(PkSemaphore *semaphore, - PkSemaphoreCount initial_count, - PkSemaphoreCount max_count) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_CREATE); - PK_ERROR_IF((max_count != 0) && (initial_count > max_count), - PK_INVALID_ARGUMENT_SEMAPHORE); - } - - __pk_thread_queue_clear(&(semaphore->pending_threads)); - semaphore->count = initial_count; - semaphore->max_count = max_count; - - return PK_OK; -} - - - - - diff --git a/pk/kernel/pk_stack_init.c b/pk/kernel/pk_stack_init.c deleted file mode 100644 index c3ddc90a..00000000 --- a/pk/kernel/pk_stack_init.c +++ /dev/null @@ -1,85 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_stack_init.c -/// \brief PK stack initialization -/// -/// The entry points in this file are initialization routines - they are never -/// needed after PK initialization and their code space could be reclaimed by -/// the application after initialization if required. -/// -/// This code was split out from "pk_init.c" because it may be needed in a -/// thread configuration if threads are being created dynamically. in an -/// interrupt-only configuration it is not needed after \c pk_initialize(). - -#include "pk.h" - -/// Initialize a stack area. -/// -/// \param stack A pointer to the smallest legal address of the stack. The -/// stack address is modified as the stack is aligned and initialized. -/// -/// \param size A pointer to the size of the stack (in bytes). The size is -/// modified as the stack is aligned and initialized. At exit this is the -/// final usable stack area size aligned to the size of the PK_STACK_TYPE. -/// -/// PK makes no assumptions about size or alignment of the area provided as a -/// stack, and carefully aligns and initializes the stack. Regardless of how -/// the stack grows, the \a stack parameter is considered to be the lowest -/// legal address of the stack. - -int -__pk_stack_init(PkAddress *stack, - size_t *size) -{ - PkAddress mask; - size_t excess, i, count; - PK_STACK_TYPE *p; - - if (PK_STACK_DIRECTION < 0) { - - // Stacks grow down. The initial stack pointer is set to just above - // the last allocated stack address. This is legal for pre-decrement - // stacks, otherwise the initial address is first brought into range - // before alignment. The stack is aligned downward, then the size is - // adjusted to a multiple of the stack type. Stacks are optionally - // prepatterned. Alignment is assumed to be a power of 2. - - *stack += *size; - - if (!PK_STACK_PRE_DECREMENT) { - *stack -= sizeof(PK_STACK_TYPE); - *size -= sizeof(PK_STACK_TYPE); - } - - mask = PK_STACK_ALIGNMENT - 1; - excess = *stack & mask; - *stack -= excess; - *size -= excess; - *size = (*size / sizeof(PK_STACK_TYPE)) * sizeof(PK_STACK_TYPE); - - if (PK_STACK_CHECK) { - p = (PK_STACK_TYPE *)(*stack); - count = *size / sizeof(PK_STACK_TYPE); - for (i = 0; i < count; i++) { - if (PK_STACK_PRE_DECREMENT) { - *(--p) = PK_STACK_PATTERN; - } else { - *(p--) = PK_STACK_PATTERN; - } - } - } - - __pk_stack_create_initial_frame(stack, size); - - } else { - - PK_PANIC(PK_UNIMPLEMENTED); - } - - return PK_OK; -} - diff --git a/pk/kernel/pk_thread.h b/pk/kernel/pk_thread.h deleted file mode 100644 index acc32525..00000000 --- a/pk/kernel/pk_thread.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef __PK_THREAD_H__ -#define __PK_THREAD_H__ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2015 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_thread.h -/// \brief Contains private declarations and definitions needed for threads -/// - -void -__pk_thread_map(PkThread* thread); - -void -__pk_thread_unmap(PkThread *thread); - - -// Interrupts must be disabled at entry. - -static inline int -__pk_thread_is_active(PkThread *thread) -{ - return ((thread->state != PK_THREAD_STATE_COMPLETED) && - (thread->state != PK_THREAD_STATE_DELETED)); -} - - -// Interrupts must be disabled at entry. - -static inline int -__pk_thread_is_mapped(PkThread *thread) -{ - return (thread->state == PK_THREAD_STATE_MAPPED); -} - - -// Interrupts must be disabled at entry. This is only called on mapped threads. - -static inline int -__pk_thread_is_runnable(PkThread *thread) -{ - return __pk_thread_queue_member(&__pk_run_queue, thread->priority); -} - - -// Interrupts must be disabled at entry. - -static inline PkThread* -__pk_thread_at_priority(PkThreadPriority priority) -{ - return (PkThread*)__pk_priority_map[priority]; -} - -#endif /* __PK_THREAD_H__ */ diff --git a/pk/kernel/pk_thread_core.c b/pk/kernel/pk_thread_core.c deleted file mode 100644 index 2966eb9b..00000000 --- a/pk/kernel/pk_thread_core.c +++ /dev/null @@ -1,573 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_thread_core.c -/// \brief PK thread APIs -/// -/// The entry points in this file are considered 'core' routines that will -/// always be present at runtime in any PK application that enables threads. - -#include "pk.h" -#include "pk_thread.h" - -#define __PK_THREAD_CORE_C__ - - -// This routine is only used locally. Interrupts must be disabled -// at entry. The caller must also have checked that the priority is free. -// This routine is only called on threads known to be in a suspended state, -// either PK_THREAD_STATE_SUSPENDED_RUNNABLE or -// PK_THREAD_STATE_SUSPENDED_BLOCKED. Mapping a runnable thread adds it to -// the run queue. Mapping a thread pending on a semaphore either takes the -// count and becomes runnable or adds the thread to the pending queue for the -// semaphore. Mapping a sleeping thread requires no further action -// here. Scheduling after the map must be handled by the caller. - -void -__pk_thread_map(PkThread* thread) -{ - PkThreadPriority priority; - - priority = thread->priority; - __pk_priority_map[priority] = thread; - - if (thread->state == PK_THREAD_STATE_SUSPENDED_RUNNABLE) { - - __pk_thread_queue_insert(&__pk_run_queue, priority); - - } else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) { - - if (thread->semaphore->count) { - - thread->semaphore->count--; - __pk_thread_queue_insert(&__pk_run_queue, priority); - - } else { - - __pk_thread_queue_insert(&(thread->semaphore->pending_threads), - priority); - } - } - - thread->state = PK_THREAD_STATE_MAPPED; - - if (PK_KERNEL_TRACE_ENABLE) { - if (__pk_thread_is_runnable(thread)) { - PK_KERN_TRACE("THREAD_MAPPED_RUNNABLE(%d)", priority); - } else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) { - PK_KERN_TRACE("THREAD_MAPPED_SEMAPHORE_PEND(%d)", priority); - } else { - PK_KERN_TRACE("THREAD_MAPPED_SLEEPING(%d)", priority); - } - } -} - - -// This routine is only used locally. Interrupts must be disabled -// at entry. This routine is only ever called on threads in the -// PK_THREAD_STATE_MAPPED. Unmapping a thread removes it from the priority -// map, the run queue and any semaphore pend, but does not cancel any -// timers. Scheduling must be handled by the code calling -// __pk_thread_unmap(). - -void -__pk_thread_unmap(PkThread *thread) -{ - PkThreadPriority priority; - - priority = thread->priority; - __pk_priority_map[priority] = 0; - - if (__pk_thread_is_runnable(thread)) { - - thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; - __pk_thread_queue_delete(&__pk_run_queue, priority); - - } else { - - thread->state = PK_THREAD_STATE_SUSPENDED_BLOCKED; - if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) { - __pk_thread_queue_delete(&(thread->semaphore->pending_threads), - priority); - } - } -} - - -// Schedule and run the highest-priority mapped runnable thread. -// -// The priority of the next thread to run is first computed. This may be -// PK_THREADS, indicating that the only thread to run is the idle thread. -// This will always cause (or defer) a 'context switch' to the idle thread. -// Otherwise, if the new thread is not equal to the current thread this will -// also cause (or defer) a context switch. Note that scheduling is defined in -// terms of priorities but actually implemented in terms of PkThread pointers. -// -// If we are not yet in thread mode we're done - threads will be started by -// pk_start_threads() later. If we're in thread context a context switch -// happens immediately. In an interrupt context the switch is deferred to the -// end of interrupt processing. - -void -__pk_schedule(void) -{ - __pk_next_priority = __pk_thread_queue_min(&__pk_run_queue); - __pk_next_thread = __pk_priority_map[__pk_next_priority]; - - if ((__pk_next_thread == 0) || - (__pk_next_thread != __pk_current_thread)) { - - if (__pk_kernel_mode_thread()) { - if (__pk_kernel_context_thread()) { - if (__pk_current_thread != 0) { - __pk_switch(); - } else { - __pk_next_thread_resume(); - } - } else { - __pk_delayed_switch = 1; - } - } - } -} - - -// This routine is only used locally. -// -// Completion and deletion are pretty much the same thing. Completion is -// simply self-deletion of the current thread (which is mapped by -// definition.) The complete/delete APIs have slightly different error -// conditions but are otherwise the same. -// -// Deleting a mapped thread first unmaps (suspends) the thread, which takes -// care of removing the thread from any semaphores it may be pending on. Then -// any outstanding timer is also cancelled. -// -// If the current thread is being deleted we install the idle thread as -// __pk_current_thread, so scheduling is forced and no context is saved on -// the context switch. -// -// Note that we do not create trace events for unmapped threads since the trace -// tag only encodes the priority, which may be in use by a mapped thread. - -void -__pk_thread_delete(PkThread *thread, PkThreadState final_state) -{ - PkMachineContext ctx; - int mapped; - - pk_critical_section_enter(&ctx); - - mapped = __pk_thread_is_mapped(thread); - - if (mapped) { - __pk_thread_unmap(thread); - } - - __pk_timer_cancel(&(thread->timer)); - thread->state = final_state; - - if (mapped) { - - if (PK_KERNEL_TRACE_ENABLE) { - if (final_state == PK_THREAD_STATE_DELETED) { - PK_KERN_TRACE("THREAD_DELETED(%d)", thread->priority); - } else { - PK_KERN_TRACE("THREAD_COMPLETED(%d)", thread->priority); - } - } - - if (thread == __pk_current_thread) { - __pk_current_thread = 0; - } - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); -} - - -// Generic thread timeout -// -// This routine is called as a timer callback either because a sleeping thread -// has timed out or a thread pending on a semaphore has timed out. If the -// thread is not already runnable then the the timeout flag is set, and if the -// thread is mapped it is scheduled. -// -// This implementation allows that a thread blocked on a timer may have been -// made runnable by some other mechanism, such as acquiring a semaphore. In -// order to provide an iteration-free implementation of -// pk_semaphore_release_all(), cancelling any semaphore timeouts is deferred -// until the thread runs again. -// -// Note that we do not create trace events for unmapped threads since the trace -// tag only encodes the priority, which may be in use by a mapped thread. - -void -__pk_thread_timeout(void *arg) -{ - PkMachineContext ctx; - PkThread *thread = (PkThread *)arg; - - pk_critical_section_enter(&ctx); - - switch (thread->state) { - - case PK_THREAD_STATE_MAPPED: - if (!__pk_thread_is_runnable(thread)) { - thread->flags |= PK_THREAD_FLAG_TIMED_OUT; - __pk_thread_queue_insert(&__pk_run_queue, thread->priority); - __pk_schedule(); - } - break; - - case PK_THREAD_STATE_SUSPENDED_RUNNABLE: - break; - - case PK_THREAD_STATE_SUSPENDED_BLOCKED: - thread->flags |= PK_THREAD_FLAG_TIMED_OUT; - thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; - break; - - default: - PK_PANIC(PK_THREAD_TIMEOUT_STATE); - } - - pk_critical_section_exit(&ctx); -} - - -// This routine serves as a container for the PK_START_THREADS_HOOK and -// actually starts threads. The helper routine __pk_call_pk_start_threads() -// arranges this routine to be called with interrupts disabled while running -// on the kernel stack. -// -// The reason for this roundabout is that we want to be able to run a hook -// routine (transparent to the application) that can hand over every last byte -// of free memory to "malloc()" - including the stack of main(). Since we -// always need to run on some stack, we chose to run the hook on the kernel -// stack. However to do this safely we need to make sure -// that no interrupts will happen during this time. When __pk_thread_resume() -// is finally called all stack-based context is lost but it doesn't matter at -// that point - it's a one-way street into thread execution. -// -// This is considered part of pk_start_threads() and so is also considered a -// 'core' routine. - -void -__pk_start_threads(void) -{ - PK_START_THREADS_HOOK; - - __pk_next_thread_resume(); - - PK_PANIC(PK_START_THREADS_RETURNED); -} - - -/// Start PK threads -/// -/// This routine starts the PK thread scheduler infrastructure. This routine -/// must be called after a call of \c pk_initialize(). This routine never -/// returns. Interrupt (+ timer) only configurations of PK need not call this -/// routine. -/// -/// Note: This tiny routine is considered a 'core' routine so that the -/// initialziation code can safely recover all 'init' code space before -/// starting threads. -/// -/// This routine typically does not return - any return value indicates an -/// error; see \ref pk_errors -/// -/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was called twice. - -int -pk_start_threads(void) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(__pk_kernel_mode_thread(), PK_ILLEGAL_CONTEXT_THREAD); - } - - __pk_call_pk_start_threads(); - - return 0; -} - - -/// Resume a suspended thread -/// -/// \param thread The thread to resume -/// -/// PK only allows one thread at a time to run at a given priority, and -/// implements the notion of a thread \e claiming a priority. A suspended -/// thread claims a priority when it is mapped by a call of -/// pk_thread_resume(). This API will succeed only if no other active thread -/// is currently mapped at the priority assigned to the thread. PK provides -/// the pk_thread_at_priority() API which allows an application-level -/// scheduler to correctly manage multiple threads running at the same -/// priority. -/// -/// If the thread was sleeping while suspended it remains asleep. However if -/// the sleep timer timed out while the thread was suspended it will be -/// resumed runnable. -/// -/// If the thread was blocked on a semaphore when it was suspended, then when -/// the thread is resumed it will attempt to reacquire the semaphore. -/// However, if the thread was blocked on a semaphore with timeout while -/// suspended and the timeout interval has passed, the thread will be resumed -/// runnable and see that the semaphore pend timed out. -/// -/// It is not an error to call pk_thread_resume() on a mapped -/// thread. However it is an error to call pk_thread_resume() on a completed -/// or deleted thread. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion, including calls on a \a thread that is -/// already mapped. -/// -/// \retval -PK_INVALID_THREAD_AT_RESUME1 The \a thread is a null (0) pointer. -/// -/// \retval -PK_INVALID_THREAD_AT_RESUME2 The \a thread is not active, -/// i.e. has completed or been deleted. -/// -/// \retval -PK_PRIORITY_IN_USE_AT_RESUME Another thread is already mapped at -/// the priority of the \a thread. - -int -pk_thread_resume(PkThread *thread) -{ - PkMachineContext ctx; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_RESUME1); - } - - pk_critical_section_enter(&ctx); - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(!__pk_thread_is_active(thread), - PK_INVALID_THREAD_AT_RESUME2, - &ctx); - } - - if (!__pk_thread_is_mapped(thread)) { - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(__pk_priority_map[thread->priority] != 0, - PK_PRIORITY_IN_USE_AT_RESUME, - &ctx); - } - __pk_thread_map(thread); - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Suspend a thread -/// -/// Any active thread can be suspended. A suspended thread 1) remains active -/// but will not be scheduled; 2) relinquishes its priority assignment, -/// allowing another thread to be resumed at the suspended thread's priority; -/// and 3) disassociates from any semaphore mutual exclusion it may have been -/// participating in. -/// -/// If a sleeping thread is suspended, the sleep timer remains active but a -/// timeout of the timer simply marks the thread as runnable, but does not -/// resume the thread. -/// -/// If a thread blocked on a semaphore is suspended, the thread no longer -/// participates in the semaphore mutual exclusion. If the thread is later -/// resumed it will attempt to acquire the semaphore again the next time it -/// runs (unless it was blocked with a timeout and the timeout has expired). -/// -/// If a thread blocked on a semaphore with timeout is suspended, the -/// semaphore timeout timer continues to run. If the timer times out while the -/// thread is suspended the thread is simply marked runnable. If the thread is -/// later resumed, the suspended call of \c pk_semaphore_pend() will return the -/// timeout code -PK_SEMAPHORE_PEND_TIMED_OUT. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion, including calls on a \a thread that is -/// already suspended. -/// -/// \retval -PK_INVALID_THREAD_AT_SUSPEND1 The \a thread is a null (0) pointer -/// -/// \retval -PK_INVALID_THREAD_AT_SUSPEND2 The \a thread is not active, -/// i.e. has completed or been deleted. - -int -pk_thread_suspend(PkThread *thread) -{ - PkMachineContext ctx; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((thread == 0), PK_INVALID_THREAD_AT_SUSPEND1); - } - - pk_critical_section_enter(&ctx); - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(!__pk_thread_is_active(thread), - PK_INVALID_THREAD_AT_SUSPEND2, - &ctx); - } - - if (__pk_thread_is_mapped(thread)) { - - PK_KERN_TRACE("THREAD_SUSPENDED(%d)", thread->priority); - __pk_thread_unmap(thread); - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Delete a thread -/// -/// Any active thread can be deleted. If a thread is deleted it is removed -/// from the run queue, deleted from the timer queue (if sleeping or blocked -/// on a semaphore with timeout), and deleted from the semaphore mutual -/// exclusion if blocked on a semaphore. The thread control block is then -/// marked as deleted. -/// -/// Once a thread has completed or been deleted the thread structure and -/// thread stack areas can be used for other purposes. -/// -/// \param thread The thread to delete -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors. If a -/// thread deletes itself this API does not return at all. -/// -/// \retval 0 Successful completion, including calls on a \a thread that has -/// completed or had already been deleted. -/// -/// -/// \retval -PK_INVALID_THREAD_AT_DELETE The \a thread is a null (0) pointer. - -int -pk_thread_delete(PkThread *thread) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_DELETE); - } - - __pk_thread_delete(thread, PK_THREAD_STATE_DELETED); - - return PK_OK; -} - - -/// Complete a thread -/// -/// If a thread ever returns from the subroutine defining the thread entry -/// point, the thread is removed from all PK kernel data structures and -/// marked completed. The thread routine can also use the API pk_complete() -/// to make this more explicit if desired. PK makes no distinction between -/// completed and deleted threads, but provides these indications for -/// the benefit of the application. -/// -/// Note that this API is only available from the current thread to mark its -/// own completion. -/// -/// Once a thread has completed or been deleted the thread structure and -/// thread stack areas can be used for other purposes. -/// -/// Any return value indicates an error; see \ref pk_errors. In the event of -/// a successful completion this API does not return to the caller, which is -/// always the thread context being completed. -/// -/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread -/// context. - -// Note: Casting __pk_current_thread removes the 'volatile' attribute. - -int -pk_complete(void) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_UNLESS_THREAD_CONTEXT(); - } - - __pk_thread_delete((PkThread *)__pk_current_thread, - PK_THREAD_STATE_COMPLETED); - - return PK_OK; -} - -/// Sleep a thread for an interval relative to the current time. -/// -/// \param interval A time interval relative to the current timebase. -/// -/// Threads can use this API to sleep for a time relative to the current -/// timebase. The absolute timeout is \c pk_timebase_get() + \a interval. -/// -/// Sleeping threads are not scheduled, although they maintain their -/// priorities. This differs from thread suspension, where the suspended -/// thread relinquishes its priority. When the sleep timer times out the -/// thread becomes runnable again, and will run as soon as it becomes the -/// highest-priority mapped runnable thread. -/// -/// Sleeping threads may also be later suspended. In this case the Sleep timer -/// continues to run, and if it times out before the thread is resumed the -/// thread will be immediately runnable when it is resumed. -/// -/// See the PK specification for a full discussion of how PK handles -/// scheduling events at absolute times "in the past". Briefly stated, if the -/// \a interval is 0 or is so small that the absolute time becomes a "past" -/// time before the Sleep is actually scheduled, the thread will Sleep for the -/// briefest possible period supported by the hardware. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion. -/// -/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread -/// context. - -int -pk_sleep(PkInterval interval) -{ - PkTimebase time; - PkMachineContext ctx; - PkThread *current; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_UNLESS_THREAD_CONTEXT(); - } - - time = pk_timebase_get() + PK_INTERVAL_SCALE(interval); - - pk_critical_section_enter(&ctx); - - current = (PkThread *)__pk_current_thread; - - current->timer.timeout = time; - __pk_timer_schedule(&(current->timer)); - - current->flags |= PK_THREAD_FLAG_TIMER_PEND; - - PK_KERN_TRACE("THREAD_SLEEP(%d)", current->priority); - - __pk_thread_queue_delete(&__pk_run_queue, current->priority); - __pk_schedule(); - - current->flags &= ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT); - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - -#undef __PK_THREAD_CORE_C__ diff --git a/pk/kernel/pk_thread_init.c b/pk/kernel/pk_thread_init.c deleted file mode 100644 index 97bce4c4..00000000 --- a/pk/kernel/pk_thread_init.c +++ /dev/null @@ -1,134 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_thread_init.c -/// \brief PK thread API initialization routines -/// -/// The entry points in this file are routines that are typically used during -/// initialization, and their code space could be deallocated and recovered if -/// no longer needed by the application after initialization. - -#include "pk.h" - -/// Create (initialize) a thread -/// -/// \param thread A pointer to an PkThread structure to initialize -/// -/// \param thread_routine The subroutine that implements the thread -/// -/// \param arg Private data to be passed as the argument to the thread -/// routine when it begins execution -/// -/// \param stack The stack space of the thread -/// -/// \param stack_size The size of the stack in bytes -/// -/// \param priority The initial priority of the thread -/// -/// The \a thread argument must be a pointer to an uninitialized or completed -/// or deleted thread. This \c PkThread structure \em is the thread, so this -/// memory area must not be modified by the application until the thread -/// completes or is deleted. PK can not tell if an PkThread structure is -/// currently in use as a thread control block.pk_thread_create() will -/// silently overwrite an PkThread structure that is currently in use. -/// -/// The stack area must be large enough to hold the dynamic stack requirements -/// of the entry point routine, and all subroutines and functions that might -/// be invoked on any path from the entry point. The stack must also always -/// be able to hold the thread context in the event the thread is preempted, -/// plus other critical context. PK aligns stack areas in machine-specific -/// ways, so that the actual stack area may reduced in size slightly if it is -/// not already aligned. -/// -/// Threads are created runnable but unmapped. A newly created thread will -/// not be eligible to run until a call of pk_thread_resume() targets the -/// thread. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_THREAD_AT_CREATE The \a thread is a null (0) pointer. -/// -/// \retval -PK_INVALID_ARGUMENT_THREAD1 the \a thread_routine is null (0) -/// -/// \retval -PK_INVALID_ARGUMENT_THREAD2 the \a priority is invalid, -/// -/// \retval -PK_INVALID_ARGUMENT_THREAD3 the stack area wraps around -/// the end of memory. -/// -/// \retval -PK_STACK_OVERFLOW The stack area at thread creation is smaller -/// than the minimum safe size. - -int -pk_thread_create(PkThread *thread, - PkThreadRoutine thread_routine, - void *arg, - PkAddress stack, - size_t stack_size, - PkThreadPriority priority) -{ - int rc; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CREATE); - PK_ERROR_IF((thread_routine == 0) || - (priority >= PK_THREADS), - PK_INVALID_ARGUMENT_THREAD1); - } - - rc = __pk_stack_init(&stack, &stack_size); - if (rc) { - return rc; - } - - thread->saved_stack_pointer = stack; - thread->stack_base = stack; - - if (PK_STACK_DIRECTION < 0) { - - thread->stack_limit = stack - stack_size; - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread->stack_limit > thread->stack_base, - PK_INVALID_ARGUMENT_THREAD2); - } - - } else { - - thread->stack_limit = stack + stack_size; - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread->stack_limit < thread->stack_base, - PK_INVALID_ARGUMENT_THREAD3); - } - } - - thread->semaphore = 0; - thread->priority = priority; - thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; - thread->flags = 0; - - pk_timer_create(&(thread->timer), - __pk_thread_timeout, - (void *)thread); - - __pk_thread_context_initialize(thread, thread_routine, arg); - - return rc; -} - - - - - - - - - - - - - - diff --git a/pk/kernel/pk_thread_util.c b/pk/kernel/pk_thread_util.c deleted file mode 100644 index bf2e21b7..00000000 --- a/pk/kernel/pk_thread_util.c +++ /dev/null @@ -1,291 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_thread_util.c -/// \brief PK thread utility APIs -/// -/// The entry points in this file are considered extra routines that will -/// only be included in a PK application that enables threads and uses at -/// least one of these interfaces. - -#include "pk.h" -#include "pk_thread.h" - -/// Get information about a thread. -/// -/// \param thread A pointer to the PkThread to query -/// -/// \param state The value returned through this pointer is the current state -/// of the thread; See \ref pk_thread_states. The caller can set this -/// parameter to the null pointer (0) if this information is not required. -/// -/// \param priority The value returned through this pointer is the current -/// priority of the thread. The caller can set this parameter to the null -/// pointer (0) if this information is not required. -/// -/// \param runnable The value returned through this pointer is 1 if the thread -/// is in state PK_THREAD_STATE_MAPPED and is currently in the run queue -/// (i.e., neither blocked on a semaphore nor sleeping), otherwise 0. The -/// caller can set this parameter to the null pointer (0) if this information -/// is not required. -/// -/// The information returned by this API can only be guaranteed consistent if -/// the API is called from a critical section. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_THREAD_AT_INFO The \a thread is a null (0) pointer. - -int -pk_thread_info_get(PkThread *thread, - PkThreadState *state, - PkThreadPriority *priority, - int *runnable) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_INFO); - } - - if (state) { - *state = thread->state; - } - if (priority) { - *priority = thread->priority; - } - if (runnable) { - *runnable = ((thread->state == PK_THREAD_STATE_MAPPED) && - __pk_thread_queue_member(&__pk_run_queue, - thread->priority)); - } - return PK_OK; -} - - -/// Change the priority of a thread. -/// -/// \param thread The thread whose priority will be changed -/// -/// \param new_priority The new priority of the thread -/// -/// \param old_priority The value returned through this pointer is the -/// old priority of the thread prior to the change. The caller can set -/// this parameter to the null pointer (0) if this information is not -/// required. -/// -/// Thread priorities can be changed by the \c pk_thread_priority_change() -/// API. This call will fail if the thread pointer is invalid or if the thread -/// is mapped and the new priority is currently in use. The call will succeed -/// even if the \a thread is suspended, completed or deleted. The -/// application-level scheduling algorithm is completely responsible for the -/// correctness of the application in the event of suspended, completed or -/// deleted threads. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion, including the redundant case of -/// attempting to change the priority of the thread to its current priority. -/// -/// \retval -PK_INVALID_THREAD_AT_CHANGE The \a thread is null (0) or -/// otherwise invalid. -/// -/// \retval -PK_INVALID_ARGUMENT_THREAD_CHANGE The \a new_priority is invalid. -/// -/// \retval -PK_PRIORITY_IN_USE_AT_CHANGE The \a thread is mapped and the \a -/// new_priority is currently in use by another thread. - -int -pk_thread_priority_change(PkThread *thread, - PkThreadPriority new_priority, - PkThreadPriority *old_priority) -{ - PkMachineContext ctx; - PkThreadPriority priority; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CHANGE); - PK_ERROR_IF(new_priority > PK_THREADS, - PK_INVALID_ARGUMENT_THREAD_CHANGE); - } - - pk_critical_section_enter(&ctx); - - priority = thread->priority; - - if (priority != new_priority) { - - if (!__pk_thread_is_mapped(thread)) { - - thread->priority = new_priority; - - } else { - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF_CRITICAL(__pk_priority_map[new_priority] != 0, - PK_PRIORITY_IN_USE_AT_CHANGE, - &ctx); - } - - __pk_thread_unmap(thread); - thread->priority = new_priority; - __pk_thread_map(thread); - __pk_schedule(); - } - } - - if (old_priority) { - *old_priority = priority; - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Return a pointer to the thread (if any) mapped at a given priority. -/// -/// \param priority The thread priority of interest -/// -/// \param thread The value returned through this pointer is a pointer to the -/// thread currently mapped at the given priority level. If no thread is -/// mapped, or if the \a priority is the priority of the idle thread, the -/// pointer returned will be null (0). -/// -/// The information returned by this API can only be guaranteed consistent if -/// the API is called from a critical section. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion. -/// -/// \retval -PK_INVALID_ARGUMENT_THREAD_PRIORITY The \a priority is invalid -/// or the \a thread parameter is null (0). - -int -pk_thread_at_priority(PkThreadPriority priority, - PkThread **thread) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((priority > PK_THREADS) || (thread == 0), - PK_INVALID_ARGUMENT_THREAD_PRIORITY); - } - - *thread = __pk_thread_at_priority(priority); - - return PK_OK; -} - - -/// Swap thread priorities -/// -/// \param thread_a A pointer to an initialized PkThread -/// -/// \param thread_b A pointer to an initialized PkThread -/// -/// This API swaps the priorities of \a thread_a and \a thread_b. The API is -/// provided to support general and efficient application-directed scheduling -/// algorithms. The requirements on the \a thread_a and \a thread_b arguments -/// are that they are valid pointers to initialized PkThread structures, that -/// the current thread priorities of both threads are legal, and that if a -/// thread is currently mapped, that the new thread priority is not otherwise -/// in use. -/// -/// The API does not require either thread to be mapped, or even to be active. -/// It is legal for one or both of the swap partners to be suspended, deleted -/// or completed threads. The application is completely responsible for the -/// correctness of scheduling algorithms that might operate on inactive or -/// suspended threads. -/// -/// The API does not change the mapped status of a thread. A thread will be -/// mapped after the call of pk_thread_priority_swap() if and only if it was -/// mapped prior to the call. If the new priority of a mapped thread is -/// currently in use (by a thread other than the swap partner), then the -/// PK_PRIORITY_IN_USE_AT_SWAP error is signalled and the swap does not take -/// place. This could only happen if the swap partner is not currently mapped. -/// -/// It is legal for a thread to swap its own priority with another thread. The -/// degenerate case that \a thread_a and \a thread_b are equal is also legal - -/// but has no effect. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion, including the redundant cases that do not -/// actually change priorities, or the cases that assign new priorities to -/// suspended, completed or deleted threads. -/// -/// \retval -PK_INVALID_THREAD_AT_SWAP1 One or both of \a thread_a and -/// \a thread_b is null (0) or otherwise invalid, -/// -/// \retval -PK_INVALID_THREAD_AT_SWAP2 the priorities of One or both of -/// \a thread_a and \a thread_b are invalid. -/// -/// \retval -PK_INVALID_ARGUMENT One or both of the priorities -/// of \a thread_a and \a thread_b is invalid. -/// -/// \retval -PK_PRIORITY_IN_USE_AT_SWAP Returned if a thread is mapped and the -/// new thread priority is currently in use by another thread (other than the -/// swap partner). - -int -pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b) -{ - PkMachineContext ctx; - PkThreadPriority priority_a, priority_b; - int mapped_a, mapped_b; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((thread_a == 0) || (thread_b == 0), - PK_INVALID_THREAD_AT_SWAP1); - } - - pk_critical_section_enter(&ctx); - - if (thread_a != thread_b) { - - mapped_a = __pk_thread_is_mapped(thread_a); - mapped_b = __pk_thread_is_mapped(thread_b); - priority_a = thread_a->priority; - priority_b = thread_b->priority; - - if (PK_ERROR_CHECK_API) { - int priority_in_use; - PK_ERROR_IF_CRITICAL((priority_a > PK_THREADS) || - (priority_b > PK_THREADS), - PK_INVALID_THREAD_AT_SWAP2, - &ctx); - priority_in_use = - (mapped_a && !mapped_b && - (__pk_thread_at_priority(priority_b) != 0)) || - (!mapped_a && mapped_b && - (__pk_thread_at_priority(priority_a) != 0)); - PK_ERROR_IF_CRITICAL(priority_in_use, - PK_PRIORITY_IN_USE_AT_SWAP, &ctx); - } - - if (mapped_a) { - __pk_thread_unmap(thread_a); - } - if (mapped_b) { - __pk_thread_unmap(thread_b); - } - thread_a->priority = priority_b; - thread_b->priority = priority_a; - if (mapped_a) { - __pk_thread_map(thread_a); - } - if (mapped_b) { - __pk_thread_map(thread_b); - } - __pk_schedule(); - } - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - diff --git a/pk/kernel/pk_timer_core.c b/pk/kernel/pk_timer_core.c deleted file mode 100644 index 59b9d628..00000000 --- a/pk/kernel/pk_timer_core.c +++ /dev/null @@ -1,401 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_timer_core.c -/// \brief PK portable kernel timer handler -/// -/// This file contains core routines that would be needed by any application -/// that requires PK timer support at runtime. -/// -/// PK implements a 'tickless' kernel - all events are scheduled at absolute -/// times of the PK timebase. This approach gives the application full -/// control over granularity of event scheduling. Scheduling in absolute time -/// opens up the possibility of scheduling events "in the past". PK -/// uniformly handles this case by scheduling "past" events to occur 1 -/// timebase tick in the future, so that timer callbacks are always run in the -/// expected interrupt context. -/// -/// PK implements the time queue as a simple unordered list of events, plus a -/// dedicated variable that holds the earliest timeout of any event in the -/// list. This is thought to be an appropriate data structure for the -/// following reasons: -/// -/// - PK applications will be small and will not schedule a large number of -/// events. Therefore the cost of scanning the list each time an event times -/// out is balanced against the cost of maintaining the list as a sorted data -/// structure each time an event is added or removed from the event queue. -/// -/// - PK applications may schedule and cancel many, many more events (safety -/// timeouts) than are ever allowed to expire. Events can be added and deleted -/// from the simple DEQUE very quickly since there is no sorting -/// overhead. -/// -/// Events are added to the queue simply by placing them at the end of the -/// queue. If the new event times out earlier than the previous earliest -/// event, the hardware timeout is rescheduled for the new event time. Events -/// are deleted from the queue (cancelled) simply by deleting them. Deletion -/// does not affect the hardware timeout, even if the deleted event would have -/// been the next to time out. It is not an error for the timer handler to -/// take a timer interrupt and find no events pending. Pending events can -/// also be rescheduled in place. -/// -/// When a timeout occurs the event list is scanned from the beginning, and -/// any event that has timed out has its callback processed. -/// Since event and callback processing take -/// time, the list is potentially scanned multiple times until there are no -/// more timed-out events in the list. -/// -/// Note that callbacks are not necessarily processed in time-order. In this -/// sense the PK time queue is like a traditional tick-based time queue in -/// that events are effectively lumped into groups of events that time out -/// together. In a tick-based kernel the 'lump' is the tick interval; here -/// the 'lump' is a variable interval that corresponds to the time it takes to -/// process the entire event list. -/// -/// Timer callbacks are typically run with interrupt preemption enabled. -/// Special callbacks may run without preemption. This is the only part of -/// the PK kernel where data structures of indeterminate size are processed. -/// During processing of the event list by the timer interrupt handler, the -/// consideration of each event always includes a window of preemptability. - -#define __PK_TIMER_CORE_C__ - -#include "pk.h" - -// Declare the timer bottom half handler -static PK_BH_HANDLER(__pk_timer_bh_handler); - -// Define the timer bottom half handler that the interrupt handler will -// schedule -PK_BH_STATIC_CREATE(pk_timer_bh, __pk_timer_bh_handler, 0); - - -// This routine is only used in this file, and will always be called in a -// critical section. - -static inline int -timer_active(PkTimer* timer) -{ - return pk_deque_is_queued((PkDeque*)timer); -} - - -// This is the kernel version of pk_timer_cancel(). -// -// This routine is used here and by thread and semaphore routines. -// External interrupts must be disabled at entry. -// -// If the timer is active, then there is a special case if we are going to -// delete the 'cursor' - that is the timer that __pk_timer_handler() is going -// to handle next. In this case we need to move the cursor to the next timer -// in the queue. -// -// Note that cancelling a timer does not cause a re-evaluation of the next -// timeout. This will happen naturally when the current timeout expires. - -int -__pk_timer_cancel(PkTimer *timer) -{ - int rc; - PkDeque* timer_deque = (PkDeque*)timer; - PkTimeQueue* tq = &__pk_time_queue; - - if (!timer_active(timer)) { - - rc = -PK_TIMER_NOT_ACTIVE; - - } else { - - if (timer_deque == tq->cursor) { - tq->cursor = tq->cursor->next; - } - pk_deque_delete(timer_deque); - rc = 0; - } - return rc; -} - - -// This is the kernel version of pk_timer_schedule(). -// -// This routine is used here and by thread and semaphore routines. -// interrupts must be disabled at entry. -// -// Unless the timer is already active it is enqueued in the doubly-linked -// timer list by inserting the timer at the end of the queue. Then the -// hardware timeout is scheduled if necessary. If the time queue 'cursor' != 0 -// we are in the midst of processing the time queue, and the end of time queue -// processing will schedule the next hardware timemout. - -void -__pk_timer_schedule(PkTimer* timer) -{ - PkTimeQueue* tq = &__pk_time_queue; - - if (!timer_active(timer)) { - pk_deque_push_back((PkDeque*)tq, (PkDeque*)timer); - } - - if (timer->timeout < tq->next_timeout) { - tq->next_timeout = timer->timeout; - if (tq->cursor == 0) { - __pk_schedule_hardware_timeout(tq->next_timeout); - } - } -} - - -// The tickless timer mechanism has timed out. Note that due to timer -// deletions and other factors, there may not actually be a timer in the queue -// that has timed out - but it doesn't matter (other than for efficiency). -// -// This routine must not be entered reentrantly. -// -// First, time out any timers that have expired. Timers in the queue are -// unordered, so we have to check every one. Since passing through the -// loop takes time, we may have to make multiple passes until we know -// that there are no timers in the queue that have already timed -// out. Note that it would also work to only go through the loop once and -// let the hardware scheduler take care of looping, but that would imply -// more overhead than the current implementation. -// -// On each pass through the loop tq->next_timeout computes the minimum timeout -// of events remaining in the queue. This is the only part of the kernel that -// searches a list of indefinite length. Kernel interrupt latency is mitigated -// by running this function as a bottom half. As such, interrupts are only -// disabled when explicitly requested. -// -// Because interrupt preemption is enabled during processing, and preempting -// handlers may invoke time queue operations, we need to establish a pointer -// to the next entry to be examined (tq->cursor) before enabling interupts. -// It's possible that this pointer will be changed by other interrupt handlers -// that cancel the timer pointed to by tq->cursor. -// -// The main loop iterates on the PkDeque form of the time queue, casting each -// element back up to the PkTimer as it is processed. - -static void -__pk_timer_bh_handler(void* arg) -{ - PkMachineContext ctx; - PkTimeQueue* tq; - PkTimebase now; - PkTimer* timer; - PkDeque* timer_deque; - PkTimerCallback callback; - - tq = &__pk_time_queue; - - // Check if we entered the function while it was running in another context. - if (PK_ERROR_CHECK_KERNEL) { - if (tq->cursor != 0) { - PK_PANIC(PK_TIMER_HANDLER_INVARIANT); - } - } - - pk_critical_section_enter(&ctx); - - while ((now = pk_timebase_get()) >= tq->next_timeout) { - tq->next_timeout = PK_TIMEBASE_MAX; - timer_deque = ((PkDeque*)tq)->next; - - // Iterate through the entire timer list, calling the callback of - // timed-out elements and finding the timer that will timeout next, - // which is stored in tq->next_timeout. - while (timer_deque != (PkDeque*)tq) { - - timer = (PkTimer*)timer_deque; - - // Setting this to a non-zero value indicates we are in the middle - // of processing the time queue. - tq->cursor = timer_deque->next; - - if (timer->timeout <= now) { - - // The timer timed out. It is removed from the queue. - // - // The callback may be made with interrupt preemption enabled - // or disabled. However to mitigate kernel interrupt latency - // we go ahead and open up to interrupts after the callback if - // the callback itself was not preemptible. - - pk_deque_delete(timer_deque); - - pk_critical_section_exit(&ctx); - - callback = timer->callback; - if (callback) { - callback(timer->arg); - } - - } else { - - // This timer has not timed out. Its timeout will simply - // participate in the computation of the next timeout. - tq->next_timeout = MIN(timer->timeout, tq->next_timeout); - pk_critical_section_exit(&ctx); - } - - timer_deque = tq->cursor; - pk_critical_section_enter(&ctx); - } - - // Time has passed since we checked the time. Loop back - // to check the time again and see if enough time has passed - // that the next timer has timed out too. - } - - pk_critical_section_exit(&ctx); - - // This marks that we are no longer processing the time queue - tq->cursor = 0; - - // Finally, reschedule the next timeout - __pk_schedule_hardware_timeout(tq->next_timeout); -} - - -void -__pk_timer_handler(void) -{ - //schedule the timer bottom half handler which - //is preemptible. - pk_bh_schedule(&pk_timer_bh); -} - - -/// Schedule a timer for an interval relative to the current time. -/// -/// \param timer The PkTimer to schedule. -/// -/// \param interval The timer will be scheduled to time out at the current -/// time (pk_timebase_get()) plus this \a interval. -/// -/// Once created with pk_timer_create() a timer can be \e scheduled, which -/// queues the timer in the kernel time queue. It is not an error to call \c -/// pk_timer_schedule() on a timer that is already scheduled in the time -/// queue - the timer is simply rescheduled with the new characteristics. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_TIMER_AT_SCHEDULE A a null (0) pointer was provided as -/// the \a timer argument. -/// - -int -pk_timer_schedule(PkTimer *timer, - PkInterval interval) -{ - PkMachineContext ctx; - PkTimebase timeout = pk_timebase_get() + PK_INTERVAL_SCALE(interval); - - pk_critical_section_enter(&ctx); - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_SCHEDULE); - } - - timer->timeout = timeout; - __pk_timer_schedule(timer); - - pk_critical_section_exit(&ctx); - - return PK_OK; -} - - -/// Cancel (dequeue) a timer. -/// -/// \param timer The PkTimer to cancel. -/// -/// Timers can be canceled at any time. It is never an error to call -/// pk_timer_cancel() on an PkTimer object after it is created. Memory used -/// by an PkTimer can be safely reused for another purpose after a successful -/// call ofpk_timer_cancel(). -/// -/// Return values other than PK_OK (0) are not necessarily errors; see \ref -/// pk_errors -/// -/// The following return codes are non-error codes: -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_TIMER_NOT_ACTIVE The \a timer is not currently scheduled, -/// i.e. it was never scheduled or has timed out. This code is returned for -/// information only and is not considered an error. -/// -/// The following return codes are error codes: -/// -/// \retval -PK_INVALID_TIMER_AT_CANCEL The \a timer is a null (0) pointer. -/// - -int -pk_timer_cancel(PkTimer *timer) -{ - PkMachineContext ctx; - int rc = PK_OK; - - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_CANCEL); - } - - pk_critical_section_enter(&ctx); - - rc = __pk_timer_cancel(timer); - - pk_critical_section_exit(&ctx); - - return rc; -} - - -/// Get information about a timer. -/// -/// \param timer The PkTimer to query -/// -/// \param timeout The API returns the absolute timeout of the timer through -/// this pointer. If the timer is active, this is the current timeout. If -/// the timer has timed out then this is the previous absolute timeout. If -/// the timer was never scheduled this will be 0. The caller can set this -/// parameter to the null pointer (0) if this information is not required. -/// -/// \param active If the value returned through this pointer is 1 then the -/// timer is active (currently scheduled), otherwise the value will be 0 -/// indicating an inactive timer. The caller can set this parameter to the -/// null pointer (0) if this information is not required. -/// -/// The information returned by this API can only be guaranteed consistent if -/// the API is called from a critical section. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_TIMER_AT_INFO The \a timer is a null (0) pointer. - -int -pk_timer_info_get(PkTimer *timer, - PkTimebase *timeout, - int *active) - -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_INFO); - } - - if (timeout) { - *timeout = timer->timeout; - } - if (active) { - *active = timer_active(timer); - } - - return PK_OK; -} - -#undef __PK_TIMER_CORE_C__ diff --git a/pk/kernel/pk_timer_init.c b/pk/kernel/pk_timer_init.c deleted file mode 100644 index 457f78c4..00000000 --- a/pk/kernel/pk_timer_init.c +++ /dev/null @@ -1,62 +0,0 @@ -//----------------------------------------------------------------------------- -// *! (C) Copyright International Business Machines Corp. 2014 -// *! All Rights Reserved -- Property of IBM -// *! *** IBM Confidential *** -//----------------------------------------------------------------------------- - -/// \file pk_timer_init.c -/// \brief PK timer initialization -/// -/// The entry points in this file might only be used during initialization of -/// the application. In this case the code space for these routines could be -/// recovered and reused after initialization. - -#include "pk.h" - - -/// Create (initialize) a timer. -/// -/// \param timer The PkTimer to initialize. -/// -/// \param callback The timer callback -/// -/// \param arg Private data provided to the callback. -/// -/// Once created with pk_timer_create() a timer can be scheduled with -/// pk_timer_schedule() or pk_timer_schedule_absolute(), which queues the -/// timer in the kernel time queue. Timers can be cancelled by a call of -/// pk_timer_cancel(). -/// -/// Timers created with pk_timer_create() are always run as -/// bottom-half handlers with interrupt preemption enabled. Timer callbacks are -/// free to enter critical sections if required, but must -/// always exit with interrupts enabled. -/// -/// Caution: PK has no way to know if an PkTimer structure provided to -/// pk_timer_create() is safe to use as a timer, and will silently modify -/// whatever memory is provided. -/// -/// Return values other then PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_TIMER_AT_CREATE The \a timer is a null (0) pointer. - -int -pk_timer_create(PkTimer *timer, - PkTimerCallback callback, - void *arg) -{ - if (PK_ERROR_CHECK_API) { - PK_ERROR_IF((timer == 0), PK_INVALID_TIMER_AT_CREATE); - } - - pk_deque_element_create((PkDeque*)timer); - timer->timeout = 0; - timer->callback = callback; - timer->arg = arg; - - return PK_OK; -} - - diff --git a/pk/kernel/pkkernelfiles.mk b/pk/kernel/pkkernelfiles.mk deleted file mode 100644 index df7872bd..00000000 --- a/pk/kernel/pkkernelfiles.mk +++ /dev/null @@ -1,32 +0,0 @@ -# @file pkkernelfiles.mk -# -# @brief mk for including architecture independent pk object files -# -# @page ChangeLogs Change Logs -# @section pkkernelfiles.mk -# @verbatim -# -# -# Change Log ****************************************************************** -# Flag Defect/Feature User Date Description -# ------ -------------- ---------- ------------ ----------- -# -# @endverbatim -# -########################################################################## -# Include -########################################################################## - - -########################################################################## -# Object Files -########################################################################## -PK-C-SOURCES = pk_core.c pk_init.c pk_stack_init.c pk_bh_core.c pk_debug_ptrs.c - -PK-TIMER-C-SOURCES += pk_timer_core.c pk_timer_init.c - -PK-THREAD-C-SOURCES += pk_thread_init.c pk_thread_core.c pk_thread_util.c \ - pk_semaphore_init.c pk_semaphore_core.c - -PK_OBJECTS += $(PK-C-SOURCES:.c=.o) - |