From 0a6dc36fb0c2cff9841a52081a5121f7646bbf66 Mon Sep 17 00:00:00 2001 From: Claus Michael Olsen Date: Mon, 9 Nov 2015 12:24:05 -0600 Subject: Initial checkin of pstate and ./pk support codes and creation of ./ppe dir. The pstate make files have been customized to successfully build the pstate image in the ./ekb environment. The following is required for a successful pstate image build: - Presence of ./ppe dir at ./chip/p9/procedures/ppe (Note, this is not the the same dir as the ./ppe [submodule] repository). - Presence of ./ppe/{include,lib,pk,tools} from the p9pman_mode (pm) repository, i.e. ./pm/p9_code/{include,lib} and ./pm/p9_code/ppe{pk,tools}. - The following files were modified, predominantly with the wrapping of *INDENT-OFF/ON* to get around beautifier fails and beautifier literally messing up assembly code sections in *.h files using the __ASSEMBLER__ directive. A separate email was sent out on 11/10/2015 detailing the specific problems. - Removed ./ppe/lib/ppc405lib Change-Id: Ifcd49d196982854cf83887bea36f8f6db96b59d9 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/21934 Tested-by: Jenkins Server Reviewed-by: Claus Michael Olsen Reviewed-by: Gregory S. Still Reviewed-by: Sachin Gupta Reviewed-by: Jennifer A. Stofer Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/28284 Tested-by: Jenkins Server --- import/chips/p9/procedures/ppe/pk/kernel/Makefile | 50 + import/chips/p9/procedures/ppe/pk/kernel/pk.h | 149 +++ import/chips/p9/procedures/ppe/pk/kernel/pk_api.h | 1050 ++++++++++++++++++++ .../chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c | 55 + import/chips/p9/procedures/ppe/pk/kernel/pk_core.c | 105 ++ .../p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c | 77 ++ .../p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h | 63 ++ import/chips/p9/procedures/ppe/pk/kernel/pk_init.c | 225 +++++ .../chips/p9/procedures/ppe/pk/kernel/pk_kernel.h | 271 +++++ .../chips/p9/procedures/ppe/pk/kernel/pk_macros.h | 134 +++ .../procedures/ppe/pk/kernel/pk_semaphore_core.c | 365 +++++++ .../procedures/ppe/pk/kernel/pk_semaphore_init.c | 107 ++ .../p9/procedures/ppe/pk/kernel/pk_stack_init.c | 119 +++ .../chips/p9/procedures/ppe/pk/kernel/pk_thread.h | 80 ++ .../p9/procedures/ppe/pk/kernel/pk_thread_core.c | 645 ++++++++++++ .../p9/procedures/ppe/pk/kernel/pk_thread_init.c | 168 ++++ .../p9/procedures/ppe/pk/kernel/pk_thread_util.c | 342 +++++++ .../p9/procedures/ppe/pk/kernel/pk_timer_core.c | 450 +++++++++ .../p9/procedures/ppe/pk/kernel/pk_timer_init.c | 87 ++ .../p9/procedures/ppe/pk/kernel/pkkernelfiles.mk | 56 ++ 20 files changed, 4598 insertions(+) create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/Makefile create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk.h create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_api.h create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_core.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_init.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c create mode 100644 import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk (limited to 'import/chips/p9/procedures/ppe/pk/kernel') diff --git a/import/chips/p9/procedures/ppe/pk/kernel/Makefile b/import/chips/p9/procedures/ppe/pk/kernel/Makefile new file mode 100644 index 00000000..9494348f --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/Makefile @@ -0,0 +1,50 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/kernel/Makefile $ +# +# OpenPOWER sbe Project +# +# Contributors Listed Below - COPYRIGHT 2015,2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# 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/import/chips/p9/procedures/ppe/pk/kernel/pk.h b/import/chips/p9/procedures/ppe/pk/kernel/pk.h new file mode 100644 index 00000000..7d063b75 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk.h @@ -0,0 +1,149 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk.h $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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 + #include +#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/import/chips/p9/procedures/ppe/pk/kernel/pk_api.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_api.h new file mode 100644 index 00000000..8d863562 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_api.h @@ -0,0 +1,1050 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_api.h $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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__ +// *INDENT-OFF* +#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 + +// *INDENT-ON* +#endif /*__ASSEMBLER__*/ + + + +#ifndef __ASSEMBLER__ + +#include +#include + +/// 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 == \) + /// 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: +// : 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/import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c new file mode 100644 index 00000000..734d529c --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c @@ -0,0 +1,55 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_core.c new file mode 100644 index 00000000..2f91d94e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_core.c @@ -0,0 +1,105 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_core.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c new file mode 100644 index 00000000..76a05508 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c @@ -0,0 +1,77 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h new file mode 100644 index 00000000..02ef948d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h @@ -0,0 +1,63 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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/import/chips/p9/procedures/ppe/pk/kernel/pk_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_init.c new file mode 100644 index 00000000..79669552 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_init.c @@ -0,0 +1,225 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_init.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h new file mode 100644 index 00000000..2afd184f --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h @@ -0,0 +1,271 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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/import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h new file mode 100644 index 00000000..61155679 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h @@ -0,0 +1,134 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c new file mode 100644 index 00000000..5b1e3374 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c @@ -0,0 +1,365 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c new file mode 100644 index 00000000..821acd62 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c @@ -0,0 +1,107 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c new file mode 100644 index 00000000..8df26c4c --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c @@ -0,0 +1,119 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h new file mode 100644 index 00000000..13b6a215 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h @@ -0,0 +1,80 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#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/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c new file mode 100644 index 00000000..c57ff970 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c @@ -0,0 +1,645 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c new file mode 100644 index 00000000..a0a8b956 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c @@ -0,0 +1,168 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c new file mode 100644 index 00000000..670a89a0 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c @@ -0,0 +1,342 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c new file mode 100644 index 00000000..2b812963 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c @@ -0,0 +1,450 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c new file mode 100644 index 00000000..595fb376 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c @@ -0,0 +1,87 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c $ */ +/* */ +/* OpenPOWER sbe Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (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/import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk b/import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk new file mode 100644 index 00000000..2a726b53 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk @@ -0,0 +1,56 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk $ +# +# OpenPOWER sbe Project +# +# Contributors Listed Below - COPYRIGHT 2015,2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @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) + -- cgit v1.2.1