diff options
Diffstat (limited to 'import/chips/p9/procedures/ppe')
59 files changed, 11212 insertions, 0 deletions
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 <stdint.h> + #include <stddef.h> +#endif /* __ASSEMBLER__ */ + +#ifndef __PK__ + #define __PK__ 1 +#endif + +/// The application environment specifies whether or not it will provide an +/// application configuration file, which must be named "pk_app_cfg.h". + +#ifndef USE_PK_APP_CFG_H + #define USE_PK_APP_CFG_H 0 +#endif + +#if USE_PK_APP_CFG_H + #include "pk_app_cfg.h" +#endif + +#include "pk_macros.h" +#include "pk_api.h" +#include "pk_port.h" +#include "pk_kernel.h" +//#include "pk_io.h" + +#ifndef __ASSEMBLER__ + +#define MIN(X, Y) \ + ({ \ + typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x < __y) ? __x : __y; }) + +#define MAX(X, Y) \ + ({ \ + typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x > __y) ? __x : __y; \ + }) + +/// \todo These don't require 32/64 bit versions, can always promote 32->64. + +#define FLOOR_LOG2_32(x) (32 - 1 - cntlz32(x)) +#define FLOOR_LOG2_64(x) (64 - 1 - cntlz64(x)) + +#define CEILING_LOG2(x) \ + ({ \ + uint64_t __x = (uint64_t)(x); \ + int __y; \ + __y = FLOOR_LOG2_64(__x); \ + if ((__x & (__x - 1)) != 0) { \ + __y++; \ + } \ + __y;}) + + +#define POW2_32(x) ((uint32_t)1 << (x)) +#define POW2_64(x) ((uint64_t)1 << (x)) + +/// Cast a pointer to another type +/// +/// This macro is necessary when casting a pointer to a longer integer type. +/// The pointer is first cast to the equivalent integer type 'unsigned long', +/// then cast to the final type. You can also use this to cast integers longer +/// than pointers back to pointers. + +#define CAST_POINTER(t, p) ((t)((unsigned long)(p))) + + +/// Create an alignment attribute. +#define ALIGNED_ATTRIBUTE(alignment) __attribute__ ((aligned (alignment))) + +/// Create a specific-section attribute +/// +/// Note that the section \a s here must be a string. Also note that data +/// specified to reside in specific sections must always be +/// initialized. Otherwise it confuses the linker which wants to put +/// uninitialized data into .bss sections. +/// +/// \code +/// +/// int foo SECTION_ATTRIBUTE(".noncacheable") = 0; +/// int bar[10] SECTION_ATTRIBUTE(".noncacheable") = {0}; +/// +/// \endcode +#define SECTION_ATTRIBUTE(s) __attribute__ ((section (s))) + +/// Create a 'used' attribute +/// +/// This is required for example to avoid "function unused" warnings when a +/// function is declared static but only referenced by inline assembler: +/// +/// \code +/// +/// static USED_ATTRIBUTE void +/// _checkstop(void* arg, PkIrqId irq, int priority) +/// { +/// PK_PANIC(VALIDATION_CHECKSTOP); +/// } +/// +/// PK_IRQ_FAST2FULL(_validationCheckstopHandler, _checkstop); +/// +/// \endcode +#define USED_ATTRIBUTE __attribute__ ((used)) + +#endif /* __ASSEMBLER__ */ + +#endif /* __PK_H__ */ diff --git a/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 <stddef.h> +#include <stdint.h> + +/// The timebase frequency in Hz; A parameter to pk_initialize() +extern uint32_t __pk_timebase_frequency_hz; + +extern uint8_t __pk_timebase_rshift; + +/// The timebase frequency in KHz +extern uint32_t __pk_timebase_frequency_khz; + +/// The timebase frequency in Mhz +extern uint32_t __pk_timebase_frequency_mhz; + + +typedef unsigned long int PkAddress; + +typedef uint8_t PkThreadState; + +typedef uint8_t PkThreadPriority; + +typedef uint8_t PkThreadFlags; + +typedef uint32_t PkSemaphoreCount; + +typedef uint64_t PkTimebase; + +typedef PK_TIME_INTERVAL_TYPE PkInterval; + +#include "pk_port_types.h" + +typedef struct +{ + + /// A priority queue of threads pending on the semaphore. + PkThreadQueue pending_threads; + + /// The current semaphore count. + PkSemaphoreCount count; + + /// The maximum allowable count - for error checking. + PkSemaphoreCount max_count; + +} PkSemaphore; + + +/// Compile-time initialize a PkSemaphore structure +/// +/// This low-level macro creates a structure initializatin of an PkSemaphore +/// structure. This can be used for example to create compile-time initialized +/// arrays of semaphores. +#define PK_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ + {.pending_threads = 0, \ + .count = (_initial_count), \ + .max_count = (_max_count)} + + +/// Declare and initialize a semaphore +#define PK_SEMAPHORE(sem, initial_count, max_count) \ + PkSemaphore sem = PK_SEMAPHORE_INITIALIZATION(initial_count, max_count) + + +/// Trace macros for C functions +#define HASH_ARG_COMBO(str, arg) \ + ((((uint32_t)trace_ppe_hash(str, PK_TRACE_HASH_PREFIX)) << 16) | ((uint32_t)(arg) & 0x0000ffff)) + +#define PKTRACE0(...) pk_trace_tiny() //will fail at compile time + +#define PKTRACE1(str) \ + pk_trace_tiny((trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) << 16)) + +#define PKTRACE2(str, parm0) \ + ((sizeof(parm0) <= 2)? \ + pk_trace_tiny(HASH_ARG_COMBO(str, parm0)): \ + pk_trace_big(HASH_ARG_COMBO(str, 1), ((uint64_t)parm0) << 32, 0)) + +#define PKTRACE3(str, parm0, parm1) \ + pk_trace_big(HASH_ARG_COMBO(str, 2), ((((uint64_t)parm0) << 32) | parm1), 0) + +#define PKTRACE4(str, parm0, parm1, parm2) \ + pk_trace_big(HASH_ARG_COMBO(str, 3), ((((uint64_t)parm0) << 32) | parm1),\ + ((uint64_t)parm2) << 32 ) + +#define PKTRACE5(str, parm0, parm1, parm2, parm3) \ + pk_trace_big(HASH_ARG_COMBO(str, 4), ((((uint64_t)parm0) << 32) | parm1),\ + ((((uint64_t)parm2) << 32) | parm3) ) + +#define PKTRACE6(...) pk_trace_tiny() //will fail at compile time +#define PKTRACE7(...) pk_trace_tiny() //will fail at compile time + +#define PKTRACE_HELPER2(count, ...) PKTRACE ## count (__VA_ARGS__) +#define PKTRACE_HELPER(count, ...) PKTRACE_HELPER2(count, __VA_ARGS__) + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) +#define PKTRACE(...) PKTRACE_HELPER(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) +#define PKTRACE_BIN(str, bufp, buf_size) \ + pk_trace_binary(((buf_size < 255)? HASH_ARG_COMBO(str, buf_size): HASH_ARG_COMBO(str, 255)), bufp) +#else +#define PKTRACE(...) +#define PKTRACE_BIN(str, bufp, buf_size) +#endif //PK_TRACE_SUPPORT + + + +/// A generic doubly-linked list object +/// +/// This object functions both as a sentinel mode for a deque as well as a +/// pointer container for elements in deques. The PK API assumes that +/// queueable structures will be defined with an PkDeque structure as the +/// initial 'data member' of the structure. This allows a pointer to a queue +/// element to be cast to a pointer to an PkDeque and vice-versa. + +typedef struct PkDeque +{ + + /// Pointer to the head or the next element in a deque. + /// + /// When an PkDeque is used as the sentinel node for a queue, \a next + /// points to the head of the queue, and the condition (next == \<self\>) + /// indicates an empty PkDeque. By convention the condition (\a next == + /// 0) is used to indicate that a queue element is not enqueued. + struct PkDeque* next; + + /// Pointer to the tail or previous element in a deque. + /// + /// When a DQueue is used as the sentinel node for a queue, \a previous + /// points to the tail of the queue. + struct PkDeque* previous; + +} PkDeque; + + +typedef void (*PkTimerCallback)(void*); + +#define PK_TIMER_CALLBACK(callback) void callback(void *) + +struct PkTimer; + +/// The PK timer object + +typedef struct PkTimer +{ + + /// The time queue management pointers + /// + /// This pointer container is defined as the first element of the + /// structure to allow the PkTimer to be cast to an PkDeque and + /// vice-versa. + PkDeque deque; + + /// The absolute timeout of the timer. + PkTimebase timeout; + + /// The timer callback + /// + /// For PK thread timers used to implement Sleep and semaphore pend + /// timeouts this field is initialized to __pk_thread_timeout(). + PkTimerCallback callback; + + /// Private data passed to the callback. + /// + /// For PK thread timers used to implement Sleep and semaphore pend this + /// field is initialized to a pointer to the thread. + void* arg; + +} PkTimer; + + +// Threads + +typedef void (*PkThreadRoutine)(void* arg); + +#define PK_THREAD_ROUTINE(f) void f(void *arg); + +typedef struct +{ + + /// Stack pointer saved during context switches. Assembler code expects + /// this to always be at address offset 0 from the thread pointer. + PkAddress saved_stack_pointer; + + /// This is 1 past the last valid byte address of the thread stack. + /// Assembler code expects this to always be at address offset (sizeof + /// PkAddress) from the thread pointer. + PkAddress stack_limit; + + /// This is the original base of the stack. + /// Assembler code expects this to always be at address offset 2 * (sizeof + /// PkAddress) from the thread pointer. + PkAddress stack_base; + + /// If the thread is blocked on a semaphore, then this is the semaphore the + /// thread is blocked on. + PkSemaphore* semaphore; + + /// The thread priority. + PkThreadPriority priority; + + /// The thread state; See \ref pk_thread_states + PkThreadState state; + + /// Thread flags; See \ref pk_thread_flags + PkThreadFlags flags; + + /// The timer structure handles Sleep and blocking on a semaphore with + /// timeout. + PkTimer timer; + +} PkThread; + + +typedef void (*PkBhHandler)(void*); + +#define PK_BH_HANDLER(handler) void handler(void *) + +typedef struct +{ + + /// The bottom half queue management pointers + /// + /// This pointer container is defined as the first element of the + /// structure to allow the PkBottomHalf to be cast to a PkDeque and + /// vice-versa. + PkDeque deque; + + /// The bottom half handler + PkBhHandler bh_handler; + + /// Private data passed to the handler. + void* arg; + +} PkBottomHalf; + + +// Initialization APIs + +int +pk_initialize(PkAddress kernel_stack, + size_t kernel_stack_size, + PkTimebase initial_timebase, + uint32_t timebase_frequency_hz); + + +// Timebase APIs + +PkTimebase +pk_timebase_get(void); + + +// Timer APIs + +int +pk_timer_create(PkTimer* timer, + PkTimerCallback callback, + void* arg); + + +int +pk_timer_schedule(PkTimer* timer, + PkInterval interval); + +int +pk_timer_cancel(PkTimer* timer); + +int +pk_timer_info_get(PkTimer* timer, + PkTimebase* timeout, + int* active); + +// Thread APIs + +int +pk_thread_create(PkThread* thread, + PkThreadRoutine thread_routine, + void* arg, + PkAddress stack, + size_t stack_size, + PkThreadPriority priority); + +int +pk_start_threads(void); + +int +pk_thread_resume(PkThread* thread); + +int +pk_thread_suspend(PkThread* thread); + +int +pk_thread_delete(PkThread* thread); + +int +pk_complete(void); + +int +pk_sleep(PkInterval interval); + +int +pk_thread_info_get(PkThread* thread, + PkThreadState* state, + PkThreadPriority* priority, + int* runnable); + +int +pk_thread_priority_change(PkThread* thread, + PkThreadPriority new_priority, + PkThreadPriority* old_priority); + +int +pk_thread_at_priority(PkThreadPriority priority, + PkThread** thread); + +int +pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b); + + +// Semaphore APIs + +int +pk_semaphore_create(PkSemaphore* semaphore, + PkSemaphoreCount initial_count, + PkSemaphoreCount max_count); + +int +pk_semaphore_post(PkSemaphore* semaphore); + +int +pk_semaphore_pend(PkSemaphore* semaphore, + PkInterval timeout); + +int +pk_semaphore_release_all(PkSemaphore* semaphore); + + +int +pk_semaphore_info_get(PkSemaphore* semaphore, + PkSemaphoreCount* count, + int* pending); + +void +pk_semaphore_post_handler(void* arg, + PkIrqId irq); + +// Misc. APIs + +void +pk_halt() __attribute__ ((noreturn)); + +// Deque APIs + +int +pk_deque_sentinel_create(PkDeque* deque); + +#define PK_DEQUE_SENTINEL_INIT(dq_addr) \ + {\ + .next = dq_addr, \ + .previous = dq_addr \ + } + +#define PK_DEQUE_SENTINEL_STATIC_CREATE(deque) \ + PkDeque deque = PK_DEQUE_SENTINEL_INIT(&deque) + +int +pk_deque_element_create(PkDeque* element); + +#define PK_DEQUE_ELEMENT_INIT() \ + {\ + .next = 0, \ + .previous = 0 \ + } + +#define PK_DEQUE_ELEMENT_STATIC_CREATE(deque) \ + PkDeque deque = PK_DEQUE_ELEMENT_INIT() + +/// Check for an empty PkDeque +/// +/// \param deque The sentinel node of a deque +/// +/// \retval 0 The PkDeque is not empty +/// +/// \retval 1 The PkDeque is empty + +static inline int +pk_deque_is_empty(PkDeque* deque) +{ + return (deque == deque->next); +} + + +/// Check if an PkDeque element is currently enqueued +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// \retval 0 The element is not currently enqueued +/// +/// \retval 1 The element is currently enqueued + +static inline int +pk_deque_is_queued(PkDeque* element) +{ + return (element->next != 0); +} + + +/// Append an element to the tail of a deque (FIFO order) +/// +/// \param deque The sentinel node of a deque +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// It is an error to call this API on an element that is already enqueued, +/// but the API does not check for this error. + +static inline void +pk_deque_push_back(PkDeque* deque, PkDeque* element) +{ + deque->previous->next = element; + element->previous = deque->previous; + element->next = deque; + deque->previous = element; +} + + +/// Push an element at the head of a deque (LIFO order) +/// +/// \param deque The sentinel node of a deque +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// It is an error to call this API on an element that is already enqueued, +/// but the API does not check for this error. + +static inline void +pk_deque_push_front(PkDeque* deque, PkDeque* element) +{ + deque->next->previous = element; + element->next = deque->next; + element->previous = deque; + deque->next = element; +} + +/// Pop an element from the head of a deque +/// +/// \param deque The sentinel node of a deque +/// +/// \retval 0 The PkDeque was empty prior to the call +/// +/// \retval non-0 A pointer to the previous head of the deque, which has been +/// removed from the deque and marked as no longer queued. + +// The cast of 'head' is used to remove the 'volatile' attribute. + +static inline PkDeque* +pk_deque_pop_front(PkDeque* deque) +{ + PkDeque* head; + + if (pk_deque_is_empty(deque)) + { + return 0; + } + else + { + head = (PkDeque*)(deque->next); + deque->next = head->next; + deque->next->previous = deque; + head->next = 0; + return head; + } +} + + +/// Remove a deque element from any position in the deque +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// It is an error to call this API on an element that is not currently +/// enqueued, but the API does not check for this error. + +static inline void +pk_deque_delete(PkDeque* element) +{ + element->previous->next = element->next; + element->next->previous = element->previous; + element->next = 0; +} + +// Bottom Half APIs + +extern PkDeque _pk_bh_queue; + +static inline void +pk_bh_schedule(PkBottomHalf* bottom_half) +{ + pk_deque_push_back(&_pk_bh_queue, (PkDeque*)bottom_half); +} + +#define PK_BH_INIT(_handler, _arg) \ + {\ + .deque = PK_DEQUE_ELEMENT_INIT(), \ + .bh_handler = _handler, \ + .arg = _arg \ + } + +#define PK_BH_STATIC_CREATE(bh_name, handler, arg) \ + PkBottomHalf bh_name = PK_BH_INIT(handler, arg) + + +//Trace function prototypes +void pk_trace_tiny(uint32_t i_parm); +void pk_trace_big(uint32_t i_hash_and_count, + uint64_t i_parm1, uint64_t i_parm2); +void pk_trace_binary(uint32_t i_hash_and_size, void* bufp); +void pk_trace_set_timebase(PkTimebase timebase); + + +/// Cast a pointer to another type, in a way that won't cause warnings + +#define PK_CAST_POINTER(t, p) ((t)((PkAddress)(p))) + +// Static Assert Macro for Compile time assertions. +// - This macro can be used both inside and outside of a function. +// - A value of false will cause the ASSERT to produce this error +// - This will show up on a compile fail as: +// <file>:<line> error: size of array '_static_assert' is negative +// - It would be trivial to use the macro to paste a more descriptive +// array name for each assert, but we will leave it like this for now. +#define PK_STATIC_ASSERT(cond) extern uint8_t _static_assert[(cond) ? 1 : -1] __attribute__ ((unused)) + +/// \page pk_errors PK API and Kernel Error Handling +/// +/// Error checking in the PK API consumes a significant amount of code space. +/// Approximately 20% of the object code in the PPC405 port is devoted to +/// error checking. Presumably a like amount of time overhead is also added to +/// PK API calls by this checking. +/// +/// API error checking can be disabled to save space and time in the kernel. +/// API errors can also be configured to cause kernel panics, allowing +/// applications to be coded without the overhead of error checking but still +/// providing an escape in the event of application errors or (unlikely) +/// hardware failures. The PK default is to check for API errors and kernel +/// invariants, and panic should errors occur. +/// +/// PK follows the Unix convention that a successful call of an API returns 0 +/// (PK_OK), but returns a negative code in the event of failure, or to +/// provide further information. The error codes are all defined as manifest +/// constants. +/// +/// Some negative codes returned by PK APIs are not considered errors. These +/// conditions are always checked, never cause a panic if they occur, and +/// their interpretation is always left to the application. See the detailed +/// documentation for each API for lists of error and non-error codes returned +/// by the API. +/// +/// There are three configuration options that control error handling in the +/// PK API and kernel: +/// +/// \c PK_ERROR_CHECK_API +/// +/// \arg \b 0 - No PK API error checking. APIs that potentially return error +/// codes will always return 0 (PK_OK) instead of an error code. Those +/// APIs that return negative codes that are not errors (see Table 1.5) +/// always return the negative non-error codes when appropriate. +/// +/// \arg \b 1 - (Default) All PK API errors are checked. The behavior in +/// the event of an error is defined by the configuration option +/// PK_ERROR_PANIC. +/// +/// \c PK_ERROR_CHECK_KERNEL +/// +/// \arg \b 0 - No kernel invariant error checking is done. +/// +/// \arg \b 1 - (Default) Selected kernel invariants are checked. The overhead +/// for these checks should be minimal. +/// +/// \c PK_ERROR_PANIC +/// +/// \arg \b 0 - PK API calls return negative error codes in the event of +/// errors. Note that PK kernel invariants always cause a panic if +/// violations occur. +/// +/// \arg \b 1 - (Default) In the event of errors PK APIs invoke PK_PANIC(code), +/// where code is a positive error code. Kernel invariant checks always +/// cause a panic if violations are detected. + +#endif /* __ASSEMBLER__ */ + +#endif /* __PK_API_H__ */ diff --git a/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) + diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/Makefile b/import/chips/p9/procedures/ppe/pk/ppe42/Makefile new file mode 100644 index 00000000..60f8d7f8 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/Makefile @@ -0,0 +1,50 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe42/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 pkppe42files.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +PPE42_OBJECTS += ${PPE42-TIMER-C-SOURCES:.c=.o} ${PPE42-TIMER-S-SOURCES:.S=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +PPE42_OBJECTS += ${PPE42-THREAD-C-SOURCES:.c=.o} ${PPE42-THREAD-S-SOURCES:.S=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(PPE42_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/ppe42/div64.S b/import/chips/p9/procedures/ppe/pk/ppe42/div64.S new file mode 100644 index 00000000..cf19fa6e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/div64.S @@ -0,0 +1,272 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/div64.S $ */ +/* */ +/* 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 div64.S +/// \brief Unsigned 64/64 bit division +/// +/// This is IBM code, originally part of OS Open. The code has been slightly +/// modified from its original form, both to be compatible with PK and to +/// change the function prototype slightly. +/// +/// The code was provided by Matt Tyrlik in Raleigh. + +/* @#START#@ +** +** PSCN (Power Service and Control Network) +** Cage Controller OS Open Code +** +** (C) Copyright International Business Machines Corporation 2002 +** All Rights Reserved +** Licensed Material - Program Property of I B M +** Refer to copyright instructions: Form G120-2083 +** +** Module: +** div64.s +** +** Description: +** Divide 64 bit unsigned values on 32 bit CPU +** div64(uint64_t dividen, uint64_t divisor, +** uint64_t *quotient, uint64_t *remainder) +** +** Original source from: +** "The PowerPC Compiler Writer's Guide", pp62-65 by +** Steve Hoxey, Faraydon Karim, Bill Hay, Hank Warray, +** published by Warthman Associates, 240 Hamilton Avenue, +** Palo Alto, CA 94301, USA, 1996 for IBM. +** ISBN 0-9649654-0-2. +** +** This version checks for divisor equal to zero. +** +** Environment: +** OS Open (XCOFF) +** +** Linkage: +** AIX 4.3.3 +** +** @author +** Thomas Richter +** +** History: +** Date Author Description +** ----------------------------------------------------------------------------- +** 23-Sep-02 Richter Created +** +** @#END#@*/ + + .nolist +#include "pk.h" + .list + + .global_function __ppe42_udiv64 + + /* + ** Code comment notation: + ** msw = most-significant (high-order) word, i.e. bits 0..31 + ** lsw = least-significant (low-order) word, i.e. bits 32..63 + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** + ** R3:R4 = Input parameter, dividend. + ** R5:R6 = Input parameter, divisor. + ** R7 = Output parameter, pointer to quotient. + ** R8 = Output parameter, pointer to remainder. + ** + ** Pointer arguments point to a uint64_t. + ** + ** Division is achieved using a shift/rotate/substract algorithsm + ** described above. + ** The registers are used as follows: + ** R3:R4 = dividend (upper 32bits:lower 32bits) + ** R5:R6 = divisor (upper 32bits:lower 32bits) + ** + ** R7:R8 = temporary 64 bit register (upper 32bits:lower 32bits) + ** count the number of leading 0s in the dividend + ** + ** Here is the description from the book. The dividend is placed + ** in the low order part of a 4 (32bit) register sequence named + ** tmp-high:tmp-low:dividend-high:dividend:low or tmp:dvd for short. + ** + ** Each iteration includes the following steps: + ** 1. Shift tmp:dvd by one bit to the left. + ** 2. Subtract the divisor from tmp. This is a 64 bit operation. + ** 3. If result is greater than or equal, place result in tmp and + ** set the low order bit of dividend + ** 4. If result is negative, do not modify tmp and + ** clear the low order bit of dividend + ** 5. If the number of iterations is less than the width of the + ** dividend, goto step 1. + ** + ** Now the algorithm can be improved by reducing the number of + ** iterations to be executed. + ** 1. Calculate the leading zeroes of the dividend. + ** 2. Calculate the leading zeroes of the divisor. + ** 3. Calculate the significant ones of the dividend. + ** 4. Calculate the significant ones of the divisor. + ** + ** Initial tmp := dvd >> (dvd.SD - dvs.SD) + ** Initial dvd := dvd << (dvd.LZ + dvs.SD) + ** Loops: dvd.SD - dvs.SD. + ** + ** Warning: Special care must be taken if dvd.LZ == dvs.LZ. The code + ** below does so by reducing the number of dvs.SD by one. This leads + ** to the loop being executed 1 more time than really necessary, + ** but avoids to check for the case when dvd.LZ == dvs.LZ. + ** This case (dvd.LZ == dvs.LZ) only checks for the number of leading + ** zeroes, but does not check if dividend is really greater than the + ** divisor. + ** Consider 16/17, both have an LZ value of 59. The code sets dvs.LZ + ** 60. This resutls in dvs.SD to 4, thus one iteration after which + ** tmp is the remainder 16. + */ + +__ppe42_udiv64: // PK + + /* push R30 & R31 onto the stack */ + stwu r1, -16(r1) + stvd r30, 8(r1) + + /* Save result pointers on volatile spare registers */ + ori r31, r8, 0 /* Save remainder address */ + ori r30, r7, 0 /* Save quotient address */ + + /* count the number of leading 0s in the dividend */ + cmpwi cr0, r3, 0 /* dvd.msw == 0? */ + cntlzw r0, r3 /* R0 = dvd.msw.LZ */ + cntlzw r9, r4 /* R9 = dvd.lsw.LZ */ + bne cr0, lab1 /* if(dvd.msw == 0) dvd.LZ = dvd.msw.LZ */ + addi r0, r9, 32 /* dvd.LZ = dvd.lsw.LZ + 32 */ +lab1: + /* count the number of leading 0s in the divisor */ + cmpwi cr0, r5, 0 /* dvd.msw == 0? */ + cntlzw r9, r5 /* R9 = dvs.msw.LZ */ + cntlzw r10, r6 /* R10 = dvs.lsw.LZ */ + bne cr0, lab2 /* if(dvs.msw == 0) dvs.LZ = dvs.msw.LZ */ + cmpwi cr0, r6, 0 /* dvd.lsw == 0? */ + beq cr0, lab10 /* dvs.msw == 0 */ + addi r9, r10, 32 /* dvs.LZ = dvs.lsw.LZ + 32 */ + +lab2: + /* Determine shift amounts to minimize the number of iterations */ + cmpw cr0, r0, r9 /* Compare dvd.LZ to dvs.LZ */ + subfic r10, r0, 64 /* R10 = dvd.SD */ + bgt cr0, lab9 /* if(dvs > dvd) quotient = 0 */ + addi r9, r9, 1 /* See comment above. ++dvs.LZ (or --dvs.SD) */ + subfic r9, r9, 64 /* R9 = dvs.SD */ + add r0, r0, r9 /* (dvd.LZ + dvs.SD) = left shift of dvd for */ + /* initial dvd */ + subf r9, r9, r10 /* (dvd.SD - dvs.SD) = right shift of dvd for */ + /* initial tmp */ + mtctr r9 /* Number of iterations = dvd.SD - dvs.SD */ + + /* R7:R8 = R3:R4 >> R9 */ + cmpwi cr0, r9, 32 /* compare R9 to 32 */ + addi r7, r9, -32 + blt cr0, lab3 /* if(R9 < 32) jump to lab3 */ + srw r8, r3, r7 /* tmp.lsw = dvd.msw >> (R9 - 32) */ + addi r7, r0, 0 /* tmp.msw = 0 */ + b lab4 + +lab3: + srw r8, r4, r9 /* R8 = dvd.lsw >> R9 */ + subfic r7, r9, 32 + slw r7,r3,r7 /* R7 = dvd.msw << 32 - R9 */ + or r8, r8,r7 /* tmp.lsw = R8 | R7 */ + srw r7,r3,r9 /* tmp.msw = dvd.msw >> R9 */ +lab4: + /* R3:R4 = R3:R4 << R0 */ + cmpwi cr0, r0, 32 /* Compare R0 to 32 */ + addic r9, r0, -32 + blt cr0, lab5 /* if(R0 < 32) jump to lab5 */ + slw r3, r4, r9 /* dvd.msw = dvd.lsw << R9 */ + addi r4, r0, 0 /* dvd.lsw = 0 */ + b lab6 + +lab5: + slw r3, r3, r0 /* r3 = dvd.msw << r0 */ + subfic r9, r0, 32 + srw r9, r4, r9 /* r9 = dvd.lsw >> 32 - r0 */ + or r3, r3, r9 /* dvd.msw = r3 | r9 */ + slw r4, r4, r0 /* dvd.lsw = dvd.lsw << r0 */ +lab6: + /* Restoring division shift and subtract loop */ + addi r10, r0, -1 /* r10 = -1 */ + addic r7, r7, 0 /* Clear carry bit before loop starts */ +lab7: + /* + ** tmp:dvd is considered one large register + ** each portion is shifted left 1 bit by adding it to itself + ** adde sums the carry from the previous and creates a new carry + */ + adde r4, r4, r4 /* Shift dvd.lsw left 1 bit */ + adde r3, r3, r3 /* Shift dvd.msw to left 1 bit */ + adde r8, r8, r8 /* Shift tmp.lsw to left 1 bit */ + adde r7, r7, r7 /* Shift tmp.msw to left 1 bit */ + subfc r0, r6, r8 /* tmp.lsw - dvs.lsw */ + subfe. r9, r5, r7 /* tmp.msw - dvs.msw */ + blt cr0, lab8 /* if(result < 0) clear carry bit */ + or r8, r0, r0 /* Move lsw */ + or r7, r9, r9 /* Move msw */ + addic r0, r10, 1 /* Set carry bit */ + +lab8: + bdnz lab7 + + /* Write quotient and remainder */ + adde r4, r4, r4 /* quo.lsw (lsb = CA) */ + adde r3, r3, r3 /* quo.msw (lsb from lsw) */ + stw r4, 4(r30) + stw r3, 0(r30) + stw r8, 4(r31) /* rem.lsw */ + stw r7, 0(r31) /* rem.msw */ + b lab11 + +lab9: + /* Qoutient is 0, divisor > dividend */ + addi r0, r0, 0 + stw r3, 0(r31) /* Store remainder */ + stw r4, 4(r31) + stw r0, 0(r30) /* Set quotient to zero */ + stw r0, 4(r30) + b lab11 + +lab10: + /* Divisor is 0 */ + addi r0, r0, -1 + stw r0, 0(r31) /* Set remainder to zero */ + stw r0, 4(r31) + stw r0, 0(r30) /* Set quotient to zero */ + stw r0, 4(r30) + +lab11: + //pop r30 & r31 from stack + lvd r30, 8(r1) + lwz r1, 0(r1) + blr + .epilogue __ppe42_udiv64 diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h b/import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h new file mode 100644 index 00000000..7a368d4e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h @@ -0,0 +1,65 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.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_PORT_TYPES_H__ +#define __PK_PORT_TYPES_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_port_types.h +/// \brief Type definitions required by the PK port. +/// +/// \todo GCC provides a portable version of cntlzw called __builtin_clz(). +/// We should make the PK priority queues portable by using this facility. +/// +/// \todo I think that if more of the port-dependent types were moved here, we +/// could break the circular dependencies in some of the header inclusion and +/// simplify the way the PK/port/chip headers are included. + +/// An PkIrqId is an integer in the range of valid interrupts defined by the +/// interrupt controller. + +typedef uint8_t PkIrqId; + +/// PK requires the port to define the type PkThreadQueue, which is a +/// priority queue (where 0 is the highest priority). This queue must be able +/// to handle PK_THREADS + 1 priorities (the last for the idle thread). The +/// port must also define methods for clearing, insertion, deletion and min +/// (with assumed legal priorities). The min operation returns PK_THREADS if +/// the queue is empty. (Or a queue could be initialized with the PK_THREADS +/// entry always present - PK code never tries to delete the idle thread from +/// a thread queue). +/// +/// These queues are used both for the run queue and the pending queue +/// associated with every semaphore. +/// +/// On PPE42 with 32 threads (implied), this is a job for a uint32_t and +/// cntlzw(). + +typedef uint32_t PkThreadQueue; + +#endif /* __PK_PORT_TYPES_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk b/import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk new file mode 100644 index 00000000..9f2b9f75 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk @@ -0,0 +1,70 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.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 pkppe42files.mk +# +# @brief mk for including ppe42 object files +# +# @page ChangeLogs Change Logs +# @section pkppe42files.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Include Files +########################################################################## + + + +########################################################################## +# Object Files +########################################################################## +PPE42-C-SOURCES = ppe42_core.c \ + ppe42_init.c \ + ppe42_irq_core.c\ + ppe42_gcc.c\ + ppe42_scom.c + +PPE42-S-SOURCES = ppe42_boot.S \ + ppe42_exceptions.S\ + div64.S\ + ppe42_timebase.S + +PPE42-TIMER-C-SOURCES = +PPE42-TIMER-S-SOURCES = + +PPE42-THREAD-C-SOURCES += +PPE42-THREAD-S-SOURCES += ppe42_thread_init.S + + +PPE42_OBJECTS += $(PPE42-C-SOURCES:.c=.o) $(PPE42-S-SOURCES:.S=.o) + + + diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h new file mode 100644 index 00000000..bc747dd4 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h @@ -0,0 +1,841 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42.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 __PPE42_H__ +#define __PPE42_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42.h +/// \brief PPE42 port header for PK + +// Macros to define where declared code is actually compiled + +#ifdef __PPE42_C__ + #define IF__PPE42_CORE_C__(x) x + #define UNLESS__PPE42_CORE_C__(x) +#else + #define IF__PPE42_CORE_C__(x) + #define UNLESS__PPE42_CORE_C__(x) x +#endif + +#ifdef __PPE42_IRQ_CORE_C__ + #define IF__PPE42_IRQ_CORE_C__(x) x + #define UNLESS__PPE42_IRQ_CORE_C__(x) +#else + #define IF__PPE42_IRQ_CORE_C__(x) + #define UNLESS__PPE42_IRQ_CORE_C__(x) x +#endif + +#ifdef HWMACRO_GPE + #include "gpe.h" +#elif defined(HWMACRO_STD) + #include "std.h" +#elif defined(HWMACRO_PPE) + #include "ppe.h" +#else + #error "Macro Type not specified. Are you building from the correct directory?" +#endif + + +#include "ppe42_asm.h" +#include "ppe42_gcc.h" +#include "ppe42_spr.h" +#include "ppe42_msr.h" + + +///start + +/// The synchronization macros defined here all create a compiler +/// memory barrier that will cause GCC to flush/invalidate all memory data +/// held in registers before the macro. This is consistent with other systems, +/// e.g., the PowerPC Linux kernel, and is the safest way to define these +/// macros. + + +// Condition register fields + +#define CR_LT(n) (0x80000000u >> (4 * (n))) +#define CR_GT(n) (0x40000000u >> (4 * (n))) +#define CR_EQ(n) (0x20000000u >> (4 * (n))) +#define CR_SO(n) (0x10000000u >> (4 * (n))) + + +#ifndef __ASSEMBLER__ + +#include "stdint.h" + +/// ssize_t is defined explictly rather than bringing in all of <unistd.h> +#ifndef __ssize_t_defined + #define __ssize_t_defined + typedef int ssize_t; +#endif + +/// A memory barrier +#define barrier() asm volatile ("" : : : "memory") + +/// Ensure In-order Execution of Input/Output +#define eieio() asm volatile ("sync" : : : "memory") + +/// Memory barrier +#define sync() asm volatile ("sync" : : : "memory") + +/// Instruction barrier +#define isync() asm volatile ("sync" : : : "memory") + +/// CouNT Leading Zeros Word +#define cntlzw(x) \ + ({uint32_t __x = (x); \ + uint32_t __lzw; \ + asm volatile ("cntlzw %0, %1" : "=r" (__lzw) : "r" (__x)); \ + __lzw;}) + +/// CouNT Leading Zeros : uint32_t +static inline int +cntlz32(uint32_t x) +{ + return cntlzw(x); +} + +/// CouNT Leading Zeros : uint64_t +static inline int +cntlz64(uint64_t x) +{ + if (x > 0xffffffff) + { + return cntlz32(x >> 32); + } + else + { + return 32 + cntlz32(x); + } +} + + +/// 32-bit population count +static inline int +popcount32(uint32_t x) +{ + return __builtin_popcount(x); +} + + +/// 64-bit population count +static inline int +popcount64(uint64_t x) +{ + return __builtin_popcountll(x); +} + + +// NB: Normally we wouldn't like to force coercion inside a macro because it +// can mask programming errors, but for the MMIO macros the addresses are +// typically manifest constants or 32-bit unsigned integer expressions so we +// embed the coercion to avoid warnings. + +/// 8-bit MMIO Write +#define out8(addr, data) \ + do {*(volatile uint8_t *)(addr) = (data);} while(0) + +/// 8-bit MMIO Read +#define in8(addr) \ + ({uint8_t __data = *(volatile uint8_t *)(addr); __data;}) + +/// 16-bit MMIO Write +#define out16(addr, data) \ + do {*(volatile uint16_t *)(addr) = (data);} while(0) + +/// 16-bit MMIO Read +#define in16(addr) \ + ({uint16_t __data = *(volatile uint16_t *)(addr); __data;}) + +/// 32-bit MMIO Write +#define out32(addr, data) \ + do {*(volatile uint32_t *)(addr) = (data);} while(0) + +/// 32-bit MMIO Read +#define in32(addr) \ + ({uint32_t __data = *(volatile uint32_t *)(addr); __data;}) + +#ifdef HWMACRO_GPE + +/// 64-bit MMIO Write +#define out64(addr, data) \ + do { \ + uint64_t __data = (data); \ + volatile uint32_t *__addr_hi = (uint32_t *)(addr); \ + volatile uint32_t *__addr_lo = __addr_hi + 1; \ + *__addr_hi = (__data >> 32); \ + *__addr_lo = (__data & 0xffffffff); \ + } while(0) + +#else /* standard PPE's require a 64 bit write */ + +/// 64-bit MMIO Write +#define out64(addr, data) \ + {\ + uint64_t __d = (data); \ + uint32_t* __a = (uint32_t*)(addr); \ + asm volatile \ + (\ + "stvd %1, %0 \n" \ + : "=o"(*__a) \ + : "r"(__d) \ + ); \ + } + +#endif /* HWMACRO_GPE */ + +#ifdef HWMACRO_GPE +/// 64-bit MMIO Read +#define in64(addr) \ + ({ \ + uint64_t __data; \ + volatile uint32_t *__addr_hi = (uint32_t *)(addr); \ + volatile uint32_t *__addr_lo = __addr_hi + 1; \ + __data = *__addr_hi; \ + __data = (__data << 32) | *__addr_lo; \ + __data;}) + +#else /* Standard PPE's require a 64 bit read */ + +#define in64(addr) \ + ({\ + uint64_t __d; \ + uint32_t* __a = (uint32_t*)(addr); \ + asm volatile \ + (\ + "lvd %0, %1 \n" \ + :"=r"(__d) \ + :"o"(*__a) \ + ); \ + __d; \ + }) + +#endif /* HWMACRO_GPE */ + +#endif /* __ASSEMBLER__ */ + +#include "ppe42_irq.h" + +#ifndef __ASSEMBLER__ + +/// Store revision information as a (global) string constant +#define REVISION_STRING(symbol, rev) const char* symbol = rev; + +#else // __ASSEMBLER__ +// *INDENT-OFF* + +/// Store revision information as a global string constant + .macro .revision_string, symbol:req, rev:req + .pushsection .rodata + .balign 4 + .global \symbol +\symbol\(): + .asciz "\rev" + .balign 4 + .popsection + .endm + +// *INDENT-ON* +#endif // __ASSEMBLER__ + + + +#include "ppe42_context.h" + +// PPE42 stack characteristics for PK. The pre-pattern pattern is selected +// to be easily recognizable yet be an illegal instruction. + +#define PK_STACK_DIRECTION -1 +#define PK_STACK_PRE_DECREMENT 1 +#define PK_STACK_ALIGNMENT 8 +#define PK_STACK_TYPE unsigned int +#define PK_STACK_PATTERN 0x03abcdef + +// Kernel data structure offsets for assembler code + +#define PK_THREAD_OFFSET_SAVED_STACK_POINTER 0 +#define PK_THREAD_OFFSET_STACK_LIMIT 4 +#define PK_THREAD_OFFSET_STACK_BASE 8 + +// PK boot loader panic codes + +#define PPE42_BOOT_VECTORS_NOT_ALIGNED 0x00405001 + +// Interrupt handler panic codes + +#define PPE42_DEFAULT_IRQ_HANDLER 0x00405010 +#define PPE42_DEFAULT_SPECIAL_HANDLER 0x00405011 +#define PPE42_PHANTOM_INTERRUPT 0x00405012 +#define PPE42_PROGRAM_HALT 0x00405013 + + +// Exception handling invariant panic codes + +#define PPE42_IRQ_FULL_EXIT_INVARIANT 0x00405020 +#define PPE42_IRQ_FAST2FULL_INVARIANT 0x00405021 + + +// API error panic codes + + +// Application-overrideable definitions + +/// The default thread machine context has MSR[CE], MSR[EE] and MSR[ME] set, +/// and all other MSR bits cleared. +/// +/// The default definition allows external and machine check exceptions. This +/// definition can be overriden by the application. + +#ifndef PK_THREAD_MACHINE_CONTEXT_DEFAULT +#define PK_THREAD_MACHINE_CONTEXT_DEFAULT \ + (MSR_UIE | MSR_EE | MSR_ME) + +#endif + + +#ifndef __ASSEMBLER__ + +/// The PK kernel default panic sequence for C code +/// +/// By default a kernel panic from C code forces external debug mode then +/// generates a \c trap instruction followed by the error code. The \a code +/// argument must be a compile-time integer immediate. This definition can be +/// overriden by the application. +/// +/// The OCC may be running in internal debug mode for various reasons, and +/// TRAP-ing in internal debug mode would lead to an infinite loop in the +/// default Program Interrupt handler - which itself would be a TRAP (since +/// that's the default implementation of PK_PANIC(). Therefore by default +/// the panic is implemented as a special code sequence that forces the core +/// into external debug mode before issuing a TRAP which will halt the core. +/// To preserve the state we use the special global variables +/// __pk_panic_save_dbcr0 and __pk_panic_save_r3 defined in ppe42_core.c. +/// The original value of DBCR0 is destroyed, but can be recovered from the +/// global. In the end %r3 is reloaded from temporary storage and will be +/// unchanged at the halt. +/// +/// Note that there is a small chance that an interrupt will fire and +/// interrupt this code before the halt - in general there is no way around +/// this. +/// +/// The Simics environment does not model Debug events correctly. It executes +/// the TRAP as an illegal instruction and branches to the Program Interrupt +/// handler, destroying the contents of SRR0 and SRR1. Therefore we always +/// insert a special Simics magic breakpoint (which is an effective NOP) +/// before the hardware trap. The special-form magic instruction is +/// recognized by our Simics support scripts which decode the kernel state and +/// try to help the user interpret what happened based on the TRAP code. + +#ifndef PK_PANIC + +/*#define PK_PANIC(code) \ + do { \ + barrier(); \ + asm volatile ("stw %r3, __pk_panic_save_r3@sda21(0)"); \ + asm volatile ("mfdbcr0 %r3"); \ + asm volatile ("stw %r3, __pk_panic_save_dbcr0@sda21(0)"); \ + asm volatile ("lwz %r3, __pk_panic_dbcr0@sda21(0)"); \ + asm volatile ("mtdbcr0 %r3"); \ + asm volatile ("isync"); \ + asm volatile ("lwz %r3, __pk_panic_save_r3@sda21(0)"); \ + asm volatile ("rlwimi 1,1,0,0,0"); \ + asm volatile ("trap"); \ + asm volatile (".long %0" : : "i" (code)); \ + } while (0) +*/ +#define PK_PANIC(code) \ + do { \ + barrier(); \ + asm volatile ("b ."); \ + } while (0) + +// These variables are used by the PK_PANIC() definition above to save and +// restore state. __pk_panic_dbcr0 is the value loaded into DBCR0 to force +// traps to halt the OCC and freeze the timers. + +//#ifdef __PPE42_CORE_C__ +//uint32_t __pk_panic_save_r3; +//uint32_t __pk_panic_save_dbcr0; +//uint32_t __pk_panic_dbcr0 = DBCR0_EDM | DBCR0_TDE | DBCR0_FT; +//#endif + +#endif // PK_PANIC + +/// This is the Simics 'magic breakpoint' instruction. +/// +/// Note that this form does not include a memory barrier, as doing so might +/// change the semantics of the program. There is an alternative form +/// SIMICS_MAGIC_BREAKPOINT_BARRIER that does include a barrier. + +//#define SIMICS_MAGIC_BREAKPOINT asm volatile ("rlwimi 0,0,0,0,0") + +/// This is the Simics 'magic breakpoint' instruction including a memory +/// barrier. +/// +/// Note that the memory barrier guarantees that all variables held in +/// registers are flushed to memory before the breakpoint, however this might +/// change the semantics of the program. There is an alternative form of +/// SIMICS_MAGIC_BREAKPOINT that does not include a barrier. If the idea is +/// to use the breakpoint for tracing code execution in Simics, the barrier +/// form may be preferred so that variable values will be visible in memory. + +/*#define SIMICS_MAGIC_BREAKPOINT_BARRIER \ + asm volatile ("rlwimi 0,0,0,0,0" : : : "memory") +*/ + +#else // __ASSEMBLER__ +// *INDENT-OFF* + +/// This is the Simics 'magic breakpoint' instruction. An assembler macro +/// form is also provided for use within macros. + +//#define SIMICS_MAGIC_BREAKPOINT rlwimi 0,0,0,0,0 + +// .macro _simics_magic_breakpoint +// rlwimi 0,0,0,0,0 +// .endm + +/// The PK kernel panic default panic sequence for assembler code +/// +/// By default a kernel panic from assembler forces external debug mode then +/// generates a \c trap instruction followed by the error code. The \a code +/// argument must be a compile-time integer immediate. This definition can be +/// overriden by the application. +/// +/// See the comments for the non-ASSEMBLER version for further details. Note +/// that the code space reserved for exception handlers is only 8 +/// instructions, so in the assembler context we don't save DBCR0 as doing so +/// would require 10. + +#ifndef PK_PANIC + +#define PK_PANIC(code) _pk_panic code + + .macro _pk_panic, code + b . + .endm + +#endif // PK_PANIC + +// *INDENT-ON* +#endif // __ASSEMBLER__ + + +// Application-overridible definitions for the PK boot loader + +/// In order to enable the default kernel panic (a trap) to halt the machine, +/// the Debug Control Register 0 (DBCR0) is initialized in externel debug +/// mode, with the Trap Debug Event enabled so that the trap will not cause a +/// program exception, and the FT bit set so that the timers will freeze. +/// This definition can be overridden by the application. +/// +/// NB: It is expected that a reliable production system will redefine all of +/// the 'panic' macros and the default DBCR0 setup. + +#ifndef PPE42_DBCR_INITIAL +#define PPE42_DBCR_INITIAL DBCR_TRAP +#endif + +/// This is the value of the MSR used during initialization. Once PK threads +/// are started (with \c pk_start_threads()), all machine contexts derive +/// from the default thread context \c +/// PK_THREAD_MACHINE_CONTEXT_DEFAULT. This definition can be overriden by +/// the application. +/// +/// The default is to enable machine checks only. + +#ifndef PPE42_MSR_INITIAL +#define PPE42_MSR_INITIAL MSR_ME +#endif + +/// The \a argc argument passed to \c main(). This definition can be overriden +/// by the application. + +#ifndef PPE42_ARGC_INITIAL +#define PPE42_ARGC_INITIAL 0 +#endif + +/// The \a argv argument passed to \c main(). This definition can be overriden +/// by the application. + +#ifndef PPE42_ARGV_INITIAL +#define PPE42_ARGV_INITIAL 0 +#endif + +/// Optionally trap the reset for the debugger, which means that the PPE42 +/// will simply spin at the symbol \c __reset_trap after a chip reset. Set R0 +/// to a non-zero value in the debugger to continue execution. This definition +/// can be overriden by the application. + +#ifndef PPE42_RESET_TRAP +#define PPE42_RESET_TRAP 0 +#endif + +#ifndef __ASSEMBLER__ + +/// The PPE42 PK machine context is simply the MSR, a 32-bit integer. + +typedef uint32_t PkMachineContext; + +/// Disable interrupts and return the current +/// context. +/// +/// \param context A pointer to an PkMachineContext, this is the context that +/// existed before interrupts were disabled. Typically this +/// context is restored at the end of a critical section. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_INTERRUPT An illegal priority was specified. + +UNLESS__PPE42_CORE_C__(extern) +inline int +pk_interrupt_disable(PkMachineContext* context) +{ +*context = mfmsr(); + +wrteei(0); + +return PK_OK; +} + +/// Set the machine context. +/// +/// \param context A pointer to an PkMachineContext +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_CONTEXT_SET A null pointer was provided as +/// the \a context argument or an illegal machine context was specified. + +UNLESS__PPE42_CORE_C__(extern) +inline int +pk_machine_context_set(PkMachineContext* context) +{ +if (PK_ERROR_CHECK_API) +{ +PK_ERROR_IF(context == 0, PK_INVALID_ARGUMENT_CONTEXT_SET); +} + +mtmsr(*context); + +return PK_OK; +} + +/// Get the machine context. +/// +/// \param context A pointer to an PkMachineContext. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_CONTEXT_GET A null pointer was provided as +/// the \a context argument. + +UNLESS__PPE42_CORE_C__(extern) +inline int +pk_machine_context_get(PkMachineContext* context) +{ +if (PK_ERROR_CHECK_API) +{ +PK_ERROR_IF(context == 0, PK_INVALID_ARGUMENT_CONTEXT_GET); +} + +*context = mfmsr(); + +return PK_OK; +} + +extern void __ctx_switch(); +/// The PK context switch for the PPE kernel +// There is no protected mode in PPE42 so just call kernel code +#define __pk_switch() __ctx_switch() + + +/// In the PowerPC EABI all initial stack frames require 8 bytes - the 4 bytes +/// at the SP are zeroed to indicate the end of the stack, and the 4 bytes +/// behind the SP are for the initial subroutine's LR. + +static inline void +__pk_stack_create_initial_frame(PkAddress* stack, size_t* size) \ +{ +*stack -= 8; +* size -= 8; +* ((PK_STACK_TYPE*)(*stack)) = 0; +} + +/// The PK Kernel Context for PPE42 +/// +/// The PK portable kernel does not define how the kernel keeps track of +/// whether PK is running, interrupt levels, and other debug +/// information. Instead it defines an API that the port must provide to the +/// portable kernel. +/// +/// In the PPE42 port, the kernel context is maintained in SPRG0. This +/// 32-bit value is treated as 6 distinct fields as indicated in the structure +/// definition. +typedef union +{ + +uint32_t value; + +struct +{ + +/// A flag indicating that PK is in thread mode after a call of +/// pk_start_threads(). +unsigned thread_mode : 1; + +/// If this field is non-zero then PK is processing an interrupt +/// and the \c irq field will contain the PkIrqId of the interrupt +/// that kicked off interrupt processing. +unsigned processing_interrupt : 1; + +/// The priority of the currently running thread. In an interrupt +/// context, this is the priority of the thread that was interrupted. +unsigned thread_priority : 6; + +/// This bit tracks whether the current context can be discarded or +/// if the context must be saved. If the processor takes an interrupt +/// and this bit is set, then the current context will be discarded. +/// This bit is set at the end of handling an interrupt and prior +/// to entering the wait enabled state. +unsigned discard_ctx : 1; + +/// The PkIrqId of the currently running (or last run) handler. If +/// \c processing_interrupt is set, then this is the +/// PkIrqId of the IRQ that is currently executing. +unsigned irq : 7; + +/// Each PPE application will define (or not) the interpretation of +/// this field. Since SPRG0 is saved and restored during during thread +/// context switches, this field can be used to record the progress of +/// individual threads. The kernel and/or application will provide +/// APIs or macros to read and write this field. +unsigned app_specific : 16; + +} fields; + +} __PkKernelContext; + +// These APIs are provided for applications to get and set the app_specific +// field of the kernel context which is held in sprg0. + +static inline uint16_t ppe42_app_ctx_get(void) +{ +__PkKernelContext __ctx; +__ctx.value = mfspr(SPRN_SPRG0); +return __ctx.fields.app_specific; +} + +static inline void ppe42_app_ctx_set(uint16_t app_ctx) +{ +PkMachineContext mctx; +__PkKernelContext __ctx; +mctx = mfmsr(); +wrteei(0); +__ctx.value = mfspr(SPRN_SPRG0); +__ctx.fields.app_specific = app_ctx; +mtspr(SPRN_SPRG0, __ctx.value); +mtmsr(mctx); +} + +// These APIs are provided to the PK portable kernel by the port. + +/// PK threads have been started by a call of pk_start_threads(). + +#define __pk_kernel_mode_thread() \ + ({ \ + __PkKernelContext __ctx; \ + __ctx.value = mfspr(SPRN_SPRG0); \ + __ctx.fields.thread_mode;}) + + +/// PK is executing in a thread context (not an interrupt handler). + +#define __pk_kernel_context_thread() \ + ({ \ + __PkKernelContext __ctx; \ + __ctx.value = mfspr(SPRN_SPRG0); \ + __ctx.fields.thread_mode && !__ctx.fields.processing_interrupt;}) + + +/// PK is executing an interrupt handler of any priority. + +#define __pk_kernel_context_any_interrupt() \ + ({ \ + __PkKernelContext __ctx; \ + __ctx.value = mfspr(SPRN_SPRG0); \ + __ctx.fields.processing_interrupt;}) + + +// PK requires the port to define the type PkThreadQueue, which is a +// priority queue (where 0 is the highest priority). This queue must be able +// to handle PK_THREADS + 1 priorities (the last for the idle thread) The +// port must also define methods for clearing, insertion, deletion and min +// (with assumed legal priorities). The min operation returns PK_THREADS if +// the queue is empty (or a queue could be initialized with that entry always +// present - PK code never tries to delete the idle thread from a thread +// queue). +// +// These queues are used both for the run queue and the pending queue +// associated with every semaphore. +// +// On PPE42 with 32 threads (implied), this is a job for a uint32_t and +// cntlzw(). + +static inline void +__pk_thread_queue_clear(volatile PkThreadQueue* queue) +{ +*queue = 0; +} + +static inline void +__pk_thread_queue_insert(volatile PkThreadQueue* queue, PkThreadPriority priority) +{ +*queue |= (0x80000000u >> priority); +} + +static inline void +__pk_thread_queue_delete(volatile PkThreadQueue* queue, PkThreadPriority priority) +{ +*queue &= ~(0x80000000u >> priority); +} + +static inline PkThreadPriority +__pk_thread_queue_min(volatile PkThreadQueue* queue) +{ +return cntlzw(*queue); +} + +static inline int +__pk_thread_queue_member(volatile PkThreadQueue* queue, PkThreadPriority priority) +{ +return ((*queue >> (31 - priority)) & 1); +} + +static inline void +__pk_thread_queue_union(volatile PkThreadQueue* queue0, +volatile PkThreadQueue* queue1) +{ +*queue0 |= *queue1; +} + +static inline int +__pk_thread_queue_count(volatile PkThreadQueue* queue) +{ +return __builtin_popcount(*queue); +} + + +/// This macro is used to call __pk_start_threads() using the kernel stack, +/// in a critical section. + +#define __pk_call_pk_start_threads() \ + do { \ + PkMachineContext ctx; \ + pk_critical_section_enter(&ctx); \ + asm volatile ("mr 1, %0; mtlr %1; blrl" : : \ + "r" (__pk_kernel_stack), \ + "r" (__pk_start_threads)); \ + PK_PANIC(PK_START_THREADS_RETURNED); \ + } while (0) + + +#endif /* __ASSEMBLER__ */ + +/// The __PkKernelContext 'thread_mode' bit as a flag + +#define PPE42_THREAD_MODE 0x8000 +#define PPE42_PROC_IRQ 0x4000 +#define PPE42_DISCARD_CTX 0x0080 + +#define PPE42_THREAD_MODE_BIT 0 +#define PPE42_PROC_IRQ_BIT 1 +#define PPE42_DISCARD_CTX_BIT 8 + +#ifndef __ASSEMBLER__ + +/// Code breakpoints for PPE42 +/// +/// This macro inserts a special PPE42-only breakpoint into the object code +/// at the place the macro invocation appears. This facility is designed for +/// VBU/VPO procedure debugging. This type of breakpoint may not be required +/// on real hardware as we will then have the full power of RISCWatch, gdb, +/// etc. Once inserted into the code, code breakpoints can be enabled or +/// disabled by manipulating the global variable _code_breakpoint_enable, +/// which defaults to 1. +/// +/// The code breakpoint is implemented as a setup routine and a teardown +/// routine, executed in an critical section. The actual break +/// will occur at the address of the call of the teardown routine, in the +/// context of the calling code. The setup routine saves the state of DBCR0/1 +/// and IAC4, then programs the DBCR for an external debug mode, IAC4 +/// breakpoint. The IAC4 breakpoint is set for the address of the call of the +/// teardown routine. The teardown routine simply restores the state of the +/// debug registers that existed before the code breakpoint. +/// +/// Once hit, restarting from the break requires clearing IAC4 and restarting +/// instructions: +/// +/// \code +/// +/// putspr pu.occ iac4 0 +/// cipinstruct pu.occ start +/// +/// \endcode +/// +/// The above restart processes is also encapsulated as the p8_tclEcmd +/// procedure 'unbreakOcc'. +/// +/// In code built for the Simics environment (i.e., with the preprocessor +/// macro SIMICS_ENVIRONMENT=1) this macro simply expands into +/// SIMICS_MAGIC_BREAKPOINT, and simulation can be continued from the break as +/// normal. This Simics magic breakpoint is also under the control of +/// _code_breakpoint_enable. In code not built with SIMICS_ENVIROMENT=1, note +/// that the CODE_BREAKPOINT is ignored by the Simics PPE42 model as it does +/// not model debug events. + +//void +//_code_breakpoint_prologue(void); + +//void +//_code_breakpoint_epilogue(void); + +//extern uint32_t _code_breakpoint_enable; + +#endif // __ASSEMBLER__ + + +#endif /* __PPE42_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h new file mode 100644 index 00000000..6f3923aa --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h @@ -0,0 +1,634 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.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 __PPE42_ASM_H__ +#define __PPE42_ASM_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_asm.h +/// \brief Generic assembler macros for 32-bit PPE42 + +// Doxygen is confused by assembler; the best I know how to make it +// work is to put all of the documentation at the beginning like below +// and effectively comment out the code using Doxygen cond/endcond. + +/// \page ppe42_asm Generic assembler macros for 32-bit PPE42 +/// +/// +/// \section _lxzi _l<w,h,b>zi - Load register and Zero from Immediate address +/// +/// These macros encapsulate the 2-instruction sequence required to +/// load from a 32-bit immediate address. +/// +/// \arg \c dreg A register to receive the load data. +/// \arg \c areg A register to hold the immediate address. This can \e +/// not be register 0. Note that if \a areg != \a dreg +/// then \a areg will contain the address at the end of +/// the macro sequence. +/// \arg \c addr A 32-bit immediate address, which may be either an +/// absolute or relocatable expression. +/// +/// Forms: +/// +/// \b _lbzi \a dreg, \a areg, \a addr - Load Byte and Zero from Immediate address \n +/// \b _lhzi \a dreg, \a areg, \a addr - Load Halfword and Zero from Immediate address \n +/// \b _lwzi \a dreg, \a areg, \a addr - Load Word and Zero from Immediate address \n +/// +/// +/// \section _stxi _st<w,h,b>i - STore register to Immediate address +/// +/// These macros encapsulate the 2-instruction sequence required to +/// store to a 32-bit immediate address. +/// +/// \arg \c dreg The register to store. +/// \arg \c areg A register to hold the immediate address. This can \e +/// not be register 0, and can not be the same as \a dreg. +/// Note that \a areg will contain the address at the end of +/// the macro sequence. +/// \arg \c addr A 32-bit immediate address, which may be either an +/// absolute or relocatable expression. +/// +/// Forms: +/// +/// \b _stbi \a dreg, \a areg, \a addr - STore Byte to Immediate address \n +/// \b _sthi \a dreg, \a areg, \a addr - STore Halfword to Immediate address \n +/// \b _stwi \a dreg, \a areg, \a addr - STore Word to Immediate address \n +/// +/// +/// \section _lstzsd _<l,st><w,h,b><z>sd - Load/STore register from/to Small Data area +/// +/// These macros encapulate the small data area relocations for access +/// to storage in the small data sections .sbss, .sdata, .sbss2 and +/// .sdata2. Use of these macros implies small data area support in +/// the compile environment (for variables shared between compiled and +/// assembled code) and initialization code that sets up the small data +/// area registers R13 (and optionally R2). +/// +/// The relocations generated by this macro will work for both SVR4 ABI +/// and EABI environments. In particular, for EABI environments +/// the link editor will insert offsets to either R13 or R2 depending +/// on the section of the symbol. +/// +/// \arg \c dreg The register to load or store. +/// \arg \c addr A 32-bit immediate address, assumed to be a +/// relocatable address in one of the small data sections. +/// +/// Forms: +/// +/// \b _lbzsd \a dreg, \a addr - Load Byte and Zero from Small Data area \n +/// \b _lhzsd \a dreg, \a addr - Load Halfword and Zero from Small Data area \n +/// \b _lwzsd \a dreg, \a addr - Load Word and Zero from Small Data area \n +/// \b _stbsd \a dreg, \a addr - STore Byte to Small Data area \n +/// \b _sthsd \a dreg, \a addr - STore Halfword to Small Data area \n +/// \b _stwsd \a dreg, \a addr - STore Word to Small Data area \n +/// +/// +/// \section _liw _liw<a> - Load Immediate Word (Absolute) +/// +/// These macros encapsulate the two instructions required to load a +/// 32-bit immediate value into a register. If the immediate is an +/// absolute expression, then the \c 'a' form may be able to optimize +/// to a single instruction depending on whether only the high- or +/// low-order bits of the immediate are non-zero. +/// +/// Forms: +/// +/// \b _liw \a rd, \a imm - Load register \a rd with the 32-bit immediate \a imm \n +/// \b _liwa \a rd, \a imm - Load register \a rd with the 32-bit absolute immediate \a imm \n +/// +/// +/// \section _oriwa _oriwa - OR Immediate Word Absolute +/// +/// This macro encapsulates the logical OR of a 32-bit immediate with a +/// register. The immediate value must be an absolute expression. +/// +/// The PowerPC has instructions for OR-ing 16-bit immediates into the +/// upper (\c oris) and lower (\c ori) portions of a register. This +/// macro optimizes the generated code based on which bits (if any) of +/// the absolte immediate are non-zero. +/// +/// This special macro is only provided for the OR function. For other +/// logical operations and recording forms it is necessary in general +/// to first load the 32-bit immediate into a register (e.g., with \c +/// _liwa) then perform the logical operation. +/// +/// \arg \c rd The destination register; at the end will contain \c rs +/// OR \a imm +/// \arg \c rs The source register. +/// \arg \c imm 32-bit absolute expression. +/// +/// Forms: +/// +/// \b _oriwa \a rd, \a rs, \a imm - \a rd gets \a rs OR \a imm \n +/// +/// +/// \section _incr64_fast - 64-bit increment for fast interrupt handlers +/// +/// This macros implements 64-bit counter update in fast interrupt handlers +/// which are forbidden from using the carry-bit in the XER (without +/// saving/restoring it.) +/// +/// \arg \c rs Scratch register +/// \arg \c ra Register containing the counter address at entry +/// +/// \a rs and \a ra must be unique. At the end of the macro the count +/// is updated to memory and \a ra is unmodified. +/// +/// +/// \section _setclear_bits Set/Clear/Copy Bits from Immediate Positions +/// +/// There are situations where it is easier/faster to clear individual bits +/// and bit fields, set bits or copy fields, based on immediate bit numbers +/// and locations, rather than loading masks, since setting up a mask +/// requires 2 instruction in general, whereas these macros generate a single +/// instruction. +/// +/// \arg \c rd - The destination register +/// \arg \c rs - The source register +/// \arg \c n - An immediate size of a bit field, in the range 0 to 32 +/// \arg \c b - An immediate big-endian bit number in the range 0 to 31 +/// +/// Forms: +/// +/// \b _clrfield \a rd, \a rs, \a n, \a b - Clear an \a n bit field from \a rs +/// to \a rd starting from bit \a b \n +/// \b _clrbit \a rd, \a rs, \a b - Clear bit \a b \n +/// \b _setbit \a rd, \a rs, \a b - Set bit \a b \n +/// \b _copyfield \a rd, \a rs, \a n, \a b - Copy an n-bit field from \a rs to +/// \a rd starting from bit \a b \n +/// +/// +/// \section pseudo_ops Assembler Pseudo-Ops Macros +/// +/// Several macros define new 'pseudo-ops'. +/// +/// \subsection cache_align .cache_align +/// +/// The \c .cache_align pseudo-op is used to force alignment on a +/// cache-line boundary. It requires a preprocessor symbol definition for +/// \c LOG_CACHE_LINE_SIZE +/// +/// Forms: +/// +/// \b .cache_align \n +/// +/// +/// \subsection global_function Local and Global Functions +/// +/// The \c .function and \c .global_function pseudo-ops define function +/// symbols in the \c .text section. +/// +/// Forms: +/// +/// \b .function \a symbol - Define a local function \a symbol \n +/// \b .global_function \a symbol - Define a global function \a symbol \n +/// +/// +/// \subsection epilogue .epilogue +/// +/// The \c .epilogue pseudo-op adds size and type information for +/// functions defined in assembler. +/// +/// \arg \c symbol - Assembler epilogue for the function \a symbol. +/// +/// Forms: +/// +/// \b .epilogue \a symbol \n +/// +/// +/// \cond + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + + +### **************************************************************************** +### _l<b,h,w>zi +### _st<b,h,w>i +### **************************************************************************** + + .macro _lbzi dreg, areg, addr + lis \areg, \addr@ha + .ifc \areg, \dreg + lbz \dreg, \addr@l(\areg) + .else + lbzu \dreg, \addr@l(\areg) + .endif + .endm + + .macro _lhzi dreg, areg, addr + lis \areg, \addr@ha + .ifc \areg, \dreg + lhz \dreg, \addr@l(\areg) + .else + lhzu \dreg, \addr@l(\areg) + .endif + .endm + + .macro _lwzi dreg, areg, addr + lis \areg, \addr@ha + .ifc \areg, \dreg + lwz \dreg, \addr@l(\areg) + .else + lwzu \dreg, \addr@l(\areg) + .endif + .endm + + .macro _stbi dreg, areg, addr + .ifc \areg, \dreg + .err + .endif + lis \areg, \addr@ha + stbu \dreg, \addr@l(\areg) + .endm + + .macro _sthi dreg, areg, addr + .ifc \areg, \dreg + .err + .endif + lis \areg, \addr@ha + sthu \dreg, \addr@l(\areg) + .endm + + .macro _stwi dreg, areg, addr + .ifc \areg, \dreg + .err + .endif + lis \areg, \addr@ha + stwu \dreg, \addr@l(\areg) + .endm + + +### **************************************************************************** +### _l<b,h,w>zsd +### _st<b,h,w>sd +### **************************************************************************** + + .macro _lbzsd dreg, addr + lbz \dreg, \addr@sda21(0) + .endm + + .macro _lhzsd dreg, addr + lhz \dreg, \addr@sda21(0) + .endm + + .macro _lwzsd dreg, addr + lwz \dreg, \addr@sda21(0) + .endm + + .macro _stbsd dreg, addr + stb \dreg, \addr@sda21(0) + .endm + + .macro _sthsd dreg, addr + sth \dreg, \addr@sda21(0) + .endm + + .macro _stwsd dreg, addr + stw \dreg, \addr@sda21(0) + .endm + + +### **************************************************************************** +### _liw<a> +### _oriwa +### **************************************************************************** + + .macro _liw rd, imm + lis \rd, \imm@h + ori \rd, \rd, \imm@l + .endm + + .macro _liwa rd, imm + .if (\imm & 0xffff0000) + lis \rd, \imm@h + .if (\imm & 0xffff) + ori \rd, \rd, \imm@l + .endif + .else + li \rd, \imm@l + .endif + .endm + + .macro _oriwa rd, rs, imm + .if (\imm & 0xffff0000) + oris \rd, \rs, \imm@h + .if (\imm & 0xffff) + ori \rd, \rd, \imm@l + .endif + .else + ori \rd, \rs, \imm@l + .endif + .endm + +### **************************************************************************** +### _incr64_fast +### **************************************************************************** + + .macro _incr64_fast, rs:req, ra:req + + lwz \rs, 4(\ra) + addi \rs, \rs, 1 + cmpwi \rs, 0 + stw \rs, 4(\ra) + bne 233643278f + + lwz \rs, 0(\ra) + addi \rs, \rs, 1 + stw \rs, 0(\ra) +233643278: + + .endm + +### **************************************************************************** +### _clrfield +### _clrbit +### _setbit +### _copyfield +### **************************************************************************** + + .macro _clrfield, rd, rs, n, b + rlwinm \rd, \rs, 0, (\b + \n) & 0x1f, (\b - 1) & 0x1f + .endm + + .macro _clrbit, rd, rs, b + _clrfield \rd, \rs, 1, \b + .endm + + .macro _setbit, rd, rs, b + .ifle \b - 15 + oris \rd, \rs, 1 << (15 - \b) + .else + ori \rd, \rs, 1 << (31 - \b) + .endif + .endm + + .macro _copyfield, rd, rs, n, b + rlwimi \rd, \rs, 0, \b , (\b + \n - 1) + .endm + +### **************************************************************************** +### .cache_align +### .<global_>function +### .epilogue +### **************************************************************************** + + .set _log_cache_line_size, LOG_CACHE_LINE_SIZE + + .macro .cache_align + .align _log_cache_line_size + .endm + + .macro .function symbol + .text + .align 2 + .endm + + .macro .global_function symbol + .text + .align 2 + .global \symbol + .endm + + .macro .epilogue symbol + .type \symbol, @function + .size \symbol, . - \symbol + .endm + +### *************************************************************************** +### 64-bit macros +### *************************************************************************** + +### *************************************************************************** +### Using symbols for register names makes the code more readable and allows +### us to do register arithmetic within macros. +### *************************************************************************** + +.equiv r0, 0 +.equiv r1, 1 +.equiv sp, 1 +.equiv r3, 3 +.equiv r4, 4 +.equiv r5, 5 +.equiv r6, 6 +.equiv r7, 7 +.equiv r8, 8 +.equiv r9, 9 +.equiv r10, 10 + +.equiv r28, 28 +.equiv r29, 29 +.equiv r30, 30 +.equiv r31, 31 + +.equiv d3, 3 +.equiv d4, 4 +.equiv d5, 5 +.equiv d6, 6 +.equiv d7, 7 +.equiv d8, 8 +.equiv d9, 9 +.equiv d10, 10 +.equiv d28, 28 +.equiv d29, 29 +.equiv d30, 30 +.equiv d31, 31 + +### *************************************************************************** +### Load virtual doubleword generic. Load a virtual doubleword from a relocatable +### address expression. If the optional RA is specified, the address remains in +### RA. +### *************************************************************************** +.macro _lvdg DT:req addr:req RA=-1 + .if \RA == -1 + lis \DT, (\addr)@ha + lvd \DT, (\addr)@l(\DT) + .else + lis \RA, (\addr)@ha + lvdu \DT, (\addr)@l(\RA) + .endif +.endm + +### *************************************************************************** +### Load virtual doubleword from a relocatable small data area address +### *************************************************************************** +.macro _lvdsd DT:req addr:req + lvd \DT, (\addr)@sda21(0) +.endm + +### *************************************************************************** +### Store virtual doubleword generic. Store a virtual doubleword based on a +### relocatable address expression. The address remains in RA. +### *************************************************************************** +.macro _stvdg DS:req addr:req RA:req + lis \RA, (\addr)@ha + stvdu \DS, (\addr)@l(\RA) +.endm + +### *************************************************************************** +### Store virtual doubleword to a relocatable small data address expression +### *************************************************************************** +.macro _stvdsd DS:req addr:req + stvd \DS, (\addr)@sda21(0) +.endm + +### *************************************************************************** +### Load virtual doubleword absolute. Set DT to an absolute 64-bit constant +### *************************************************************************** +.macro _lvda DT, cvalue + lwa (\DT + 1)%32, (\cvalue) & 0x00000000ffffffff + lwa \DT, (\cvalue) >> 32 +.endm + +### *************************************************************************** +### +### 64-bit arithmetic macros +### +### *************************************************************************** + +.macro check_overlap2 DA, DB + .if ((\DA - \DB) % 32) == 1 || ((\DA - \DB) % 32) == -1 + .error "virtual doubleword registers must be identical or non-overlapping" + .endif +.endm + +.macro check_overlap3 DA, DB, DC + check_overlap2 \DA, \DB + check_overlap2 \DA, \DC + check_overlap2 \DB, \DC +.endm + +### *************************************************************************** +### Add virtual doubleword carrying +### *************************************************************************** +.macro _addvdc DT, DA, DB + check_overlap3 \DT, \DA, \DB + addc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + adde \DT, \DA, \DB +.endm + +### *************************************************************************** +### Add virtual doubleword to signed 16-bit immediate carrying +### *************************************************************************** +.macro _addvdic DT, DA, SI + .if \DA == 31 + .error "d31 for addend register is not supported" + .endif + check_overlap2 \DT, \DA + addi (\DT+1)%32, \DA+1, SI + addze \DT, \DA +.endm + +### *************************************************************************** +### Add virtual doubleword to unsigned word carrying +### *************************************************************************** +.macro _addvdwuc DT, DA, RB + check_overlap2 \DT, \DA + addc (\DT+1)%32, (\DA+1)%32, \RB + addze \DT, \DA +.endm + +### *************************************************************************** +### Subtract virtual doubleword carrying +### *************************************************************************** +.macro _subvdc DT, DA, DB + check_overlap3 \DT, \DA, \DB + subfc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + subfe \DT, \DA, \DB +.endm + +### *************************************************************************** +### +### 64-bit logic macros +### +### *************************************************************************** + +### *************************************************************************** +### AND virtual doubleword +### *************************************************************************** +.macro _andvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + and (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + and \DT, \DA, \DB +.endm + +### *************************************************************************** +### ANDC virtual doubleword +### *************************************************************************** +.macro _andcvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + andc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + andc \DT, \DA, \DB +.endm + +### *************************************************************************** +### EQV virtual doubleword +### *************************************************************************** +.macro _eqvvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + eqv (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + eqv \DT, \DA, \DB +.endm + +### *************************************************************************** +### OR virtual doubleword +### *************************************************************************** +.macro _orvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + or (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + or \DT, \DA, \DB +.endm + +### *************************************************************************** +### ORC virtual doubleword +### *************************************************************************** +.macro _orcvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + orc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + orc \DT, \DA, \DB +.endm + +### *************************************************************************** +### XOR virtual doubleword +### *************************************************************************** +.macro _xorvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + xor (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + xor \DT, \DA, \DB +.endm + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +/// \endcond + +// Local Variables: +// mode:asm +// End: + +#endif /* __PPE42_ASM_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S new file mode 100644 index 00000000..f51b1c2a --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S @@ -0,0 +1,193 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S $ */ +/* */ +/* 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 ppe42_boot.S +/// \brief PK bootloader for PPE42 + + .nolist +#include "pk.h" + .list + +### PK Bootloader for PPE42 +### +### This is the basic restart initialization of the processor. +### Parts of this code were derived from examples in the IBM OSopen +### OpenBIOS for the 405GP written by James Burke. +### +### This code does not really do very much, just makes sure that there will +### be a reasonable state in the machine when control is turned over to +### the PK application. Any core setup that requires SPR access will be done +### here. All other setup is expected to take place in system-specific +### routines. +### +### From the PowerPC 405-S Embedded Core User's manual: +### +### "In general, the contents of SPRs are undefined after a core, chip or +### system reset. Some SPRs retain the content they had before the reset +### occurred." +### +### Registers fully reset: +### DBCR1 - Data compares disabled +### DCWR - Data cache write-through disabled +### ESR - No exception syndromes +### MSR - No exceptions/interrupts are allowed +### +### Registers partially reset: +### CCR0 = 0x00700000 - Sets ICU and DCU PLB Priority +### DBCR0 [EDM] = 0 - External debug mode disabled +### [RST] = 0 - No reset action +### DBSR [MRR] = x - x indicates most recent reset action +### SGR = 0xffffffff - Storage is guarded +### TCR [WRC] = 0 - Watchdog timer reset disabled +### TSR [WRS] = x - x is a copy of TCR[WRC] Watchdog reset status +### [PIS] = x - undefined + + .global_function __pk_boot + .global __reset_trap + +__pk_boot: + + ## Trap the reset for the debugger. Set R0 to a non-zero value in the + ## debugger to continue. + + .if PPE42_RESET_TRAP + li %r0, 0 +__reset_trap: + cmpwi %r0, 0 + beq __reset_trap + .endif + + ## Set up PowerPC EABI constant registers. These registers are never + ## again touched by the PK kernel or the application (if they are + ## behaving). + + _liw %r2, _SDA2_BASE_ + _liw %r13, _SDA_BASE_ + + ## Clear the timer control register. This masks all timer interrupts. + + li %r3, 0 + mttcr %r3 + + ## The stack pointer is initialized for use by the remainder of the + ## initialization, including the application main(). The linker script + ## defines the initial stack area. + ## + ## Stacks are always 8-byte aligned. A '0' is stored at the + ## stack pointer to indicate the end of the stack chain. Stack frames + ## always consist of at least 8 bytes - the backchain pointer and the + ## slot above the backchain pointer for the callee's LR. + + _liw %r1, _PK_INITIAL_STACK + _clrfield %r1, %r1, 3, 29 # 8-byte align + li %r3, 0 + stwu %r3, -8(%r1) + + ## SPRG0 (__PkKernelContext) is initialized to 0 + ## indicating that the PK kernel is not in thread mode, and no + ## interrupts are active. + + li %r3, 0 + mtsprg0 %r3 + + ## Set up the initial value of Debug Control Register 0. Note that + ## DBCR1 is specified to be cleared at reset. VBU simulation requested + ## an option that this register not be modified so that they could + ## completely control debug behavior from reset of the PPE42. + +#ifndef NO_INIT_DBCR0 + _liwa %r3, PPE42_DBCR_INITIAL + mtdbcr %r3 +#endif + + ## The exception vector prefix is set - it must be 512 byte aligned. + ## NOTE: for PPE42, the IVPR is read only, but can be changed through scoms + + #_liw %r3, __vectors + #andi. %r4, %r3, 0x01ff + #beq 1f + #_pk_panic PPE42_BOOT_VECTORS_NOT_ALIGNED +#1: + #mtivpr %r3 + #sync + + ## The MSR to be used during the rest of intialization is + ## established. This MSR should NOT enable + ## interrupts, but could enable machine check exceptions. + + _liwa %r3, PPE42_MSR_INITIAL + mtmsr %r3 + sync + +#ifdef PK_BOOT_FROM_ROM + + ## NB: I don't think the old linker scripts were necessarily the most + ## optimal. We need to revisit this if we actually do ROM boots in PK + ## Version 2. Not sure the comments are correct. + + ## Data is copied from the initial ROM image to the RAM. The + ## address symbols are defined in the linker command file. The linker + ## will have zeroed this area in the ROM image. + + liw %r3, __pk_ram_lma - 4 # src + liw %r4, __pk_ram_vma - 4 # dest + liw %r5, __pk_ram_size + liw %r6, 2 + srw %r5, %r5, %r6 # Number of word transfers + mtctr %r5 + +copy_loop: + lwzu %r5, 4(%r3) + stwu %r5, 4(%r4) + bdnz copy_loop + +#endif /* PK_BOOT_FROM_ROM */ + + + ## Call the system setup code. + + bl __ppe42_system_setup + + ## Call the application. If for some reason we return from + ## the call of the application we call an alternate entry point of the + ## idle thread. + ## + ## An initial argc/argv can be passed into main(). argc is expected to + ## be a 32-bit immediate integer, and argv is expected to be a 32-bit + ## absolute or relocatable expression. + + _liwa %r3, PPE42_ARGC_INITIAL + _liw %r4, PPE42_ARGV_INITIAL + bl __pk_main + + b __pk_idle_thread_from_bootloader + + .epilogue __pk_boot + diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h new file mode 100644 index 00000000..cb06196d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h @@ -0,0 +1,126 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.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 __PPE42_CACHE_H__ +#define __PPE42_CACHE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_cache.h +/// \brief PowerPC-lite (PPE) cache management header for PK +/// +/// The data cache flush/invalidate macros defined here create a compiler +/// memory barrier that will cause GCC to flush/invalidate all memory data +/// held in registers before the macro. + +#ifndef __ASSEMBLER__ + +/// Determine cache-alignment of a pointer or byte-count +#define cache_aligned(x) \ + ((((unsigned long)(x)) & (POW2_32(LOG_CACHE_LINE_SIZE) - 1)) == 0) + +/// Cache-align a pointer or byte count. If the 'direction' is <= 0 then we +/// round down, else round up. +#define cache_align(x, direction) \ + ({ \ + unsigned long __x = (unsigned long)(x); \ + unsigned long __r; \ + if ((direction) <= 0) { \ + __r = __x & ~(((unsigned long)CACHE_LINE_SIZE) - 1); \ + } else { \ + if (__x % CACHE_LINE_SIZE) { \ + __r = __x + (CACHE_LINE_SIZE - (__x % CACHE_LINE_SIZE)); \ + } \ + } \ + (void *)__r; \ + }) + +/// Data Cache Block Flush +#define dcbf(p) asm volatile ("dcbf 0, %0" : : "r" (p) : "memory") + +/// Data Cache Block Touch +#define dcbt(p) asm volatile ("dcbt 0, %0" : : "r" (p) : "memory") + +/// Data Cache Block Invalidate (Privileged) +#define dcbi(p) asm volatile ("dcbi 0, %0" : : "r" (p) : "memory") + +void +dcache_invalidate_all(void); + +void +dcache_flush_all(void); + +void +dcache_invalidate(void* p, size_t bytes); + +void +dcache_flush(void* p, size_t bytes); + +/// Invalidate a line in the D-cache +/// +/// \param p An address withing the cache line to be invalidated. +/// +/// The dcache_invalidate_line() API is used to invalidate a single cache line +/// containing the address \a p. Note that invalidation is a destructive +/// operation that may cause the loss of information. It is the caller's +/// responsibility to insure that no useful data is inadverdently invalidated. +/// D-cache invalidation is more-or-less a no-op for data either not in the +/// cache or marked as non-cacheable. +/// +/// This API always issues a sync() after the invalidation. + +static inline void +dcache_invalidate_line(void* p) +{ + dcbi(p); + sync(); +} + +/// Flush and invalidate a line from the D-cache +/// +/// \param p An address within the cache line to be flushed. +/// +/// The dcache_flush_line() API can be used as a shortcut to flush and +/// invalidate a single cache line. Note that flushing is not a destructive +/// operation in the sense that no information is lost, however the caller +/// must make sure that the entirity of the data to be flushed is contained in +/// the line that includes the address \a p. D-cache flush is more-or-less a +/// no-op for data either not in the cache or marked as non-cacheable. +/// +/// This API always issues a sync() after the flush. + +static inline void +dcache_flush_line(void* p) +{ + dcbf(p); + sync(); +} + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_CAHE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h new file mode 100644 index 00000000..e54f255d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h @@ -0,0 +1,228 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.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 __PPE42_CONTEXT_H__ +#define __PPE42_CONTEXT_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_context.h +/// \brief PPE42 Machine and Thread context for PK + +/// \page ppe42_machine_context PPE42 Assembler Macros for PK Machine +/// Context (Critical Sections) +/// +/// \section _pk_enter_critical \b _pk_critical_section_enter/exit +/// +/// These macro encapsulates the instruction sequences required to enter and +/// exit critical sections, along with the machine context save for later +/// exiting the critical section. +/// +/// \arg \c ctxreg A register that will hold (holds) the machine context (MSR) +/// prior to entering the critical section (to be restored) for \c +/// _pk_critical_section_enter (\c _pk_critical_section_exit). +/// +/// \arg \c scrreg A scratch register required for the computation of +/// \c _pk_critical_section_enter. +/// +/// Forms: +/// +/// \b _pk_critical_section_enter \a priority, \a ctxreg, \a scrreg - Enter a +/// critical section \n +/// \b _pk_critical_section_exit \a ctxreg - Exit a critical section + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + + .set _msr_ee_bit, MSR_EE_BIT + + .macro _pk_critical_section_enter ctxreg, scrreg + mfmsr \ctxreg + wrteei 0 + .endm + + .macro _pk_critical_section_exit ctxreg + mtmsr \ctxreg + .endm + +// **************************************************************************** +// PK context save/restore macros for 32-bit Embedded PowerPC +// **************************************************************************** + +// All stack frames are 8-byte aligned in conformance with the EABI. PK +// never saves or restores GPR2 or GPR13. GPR13 is constant in (E)ABI +// applications - the base of the read-write small data area. GPR2 is +// system-reserved in ABI applications, and is the base for read-only small data +// in EABI applications. + +// USPRG0 holds the __PkKernelContext structure (defined in ppe42.h) that +// represents the current kernel context. The layout is as follows: +// +// Bits Meaning +// ============== +// 0 The 'thread_mode' flag +// 1 The 'processing_interrupt" flag +// 2:7 The thread priority of the running thread +// 8 The 'discard_ctx' flag +// 9:15 The IRQ currently being processed +// 16:31 The application specific data +// +// When PK is initialized USPRG0 is initialized to 0. When thread-mode is +// entered (by pk_start_threads()) bit 0 is set to 1. If desired, +// once initialized (with pk_initialize()) PK can simply +// handle interrupts, reverting back to the non-thread-mode idle loop when +// there's nothing to do. +// + + ## ------------------------------------------------------------ + ## Unused registers for embedded PPE42` + ## ------------------------------------------------------------ + + ## Registers GPR2 and GPR13 are never saved or restored. In ABI and + ## EABI applications these registers are constant. + + .set UNUSED_GPR2, 0x2 # Dedicated; EABI read-only small data area + .set UNUSED_GPR13, 0xd # Dedicated; (E)ABI read-write small data area + + ## ------------------------------------------------------------ + ## The PK context layout for Embedded PPE42 + ## ------------------------------------------------------------ + + .set PK_CTX_GPR1, 0x00 # Dedicated; Stack pointer + .set PK_CTX_LINKAGE, 0x04 # Slot for handler to store LR + .set PK_CTX_GPR3, 0x08 # Volatile; Parameter; Return Value + .set PK_CTX_GPR4, 0x0c # Volatile; Parameter + .set PK_CTX_GPR5, 0x10 # Volatile; Parameter + .set PK_CTX_GPR6, 0x14 # Volatile; Parameter + .set PK_CTX_CR, 0x18 # Condition register + .set PK_CTX_LR, 0x1c # Link register + + .set PK_CTX_GPR7, 0x20 # Volatile; Parameter + .set PK_CTX_GPR8, 0x24 # Volatile; Parameter + .set PK_CTX_GPR9, 0x28 # Volatile; Parameter + .set PK_CTX_GPR10, 0x2c # Volatile; Parameter + .set PK_CTX_GPR28, 0x30 # Non-volatile + .set PK_CTX_GPR29, 0x34 # Non-volatile + .set PK_CTX_GPR30, 0x38 # Non-volatile + .set PK_CTX_GPR31, 0x3c # Non-volatile + + .set PK_CTX_XER, 0x40 # Fixed-point exception register + .set PK_CTX_CTR, 0x44 # Count register + .set PK_CTX_SRR0, 0x48 # Save/restore register 0 + .set PK_CTX_SRR1, 0x4c # Save/restore register 1 + .set PK_CTX_GPR0, 0x50 # Volatile; Language specific + .set PK_CTX_KERNEL_CTX, 0x54 # Saved __PkKernelContext for IRQ + + .set PK_CTX_SIZE, 0x58 # Must be 8-byte aligned + + ## ------------------------------------------------------------ + ## Push the interrupted context if necessary + ## + ## This macro saves off some context in preparation for calling + ## the pk_ctx_check_discard routine. This is an attempt to use + ## the 32 byte cache more efficiently. + ## + ## 8 Instructions + ## ------------------------------------------------------------ + ## + + .macro _pk_ctx_push_as_needed branch_addr:req + + stwu %r1, -PK_CTX_SIZE(%r1) + stvd %d3, PK_CTX_GPR3(%r1) + mfcr %r3 + mflr %r4 + stvd %d3, PK_CTX_CR(%r1) + _liw %r3, \branch_addr + b ctx_check_discard + .endm + + + ## ------------------------------------------------------------ + ## update the kernel context in response to an interrupt. + ## ------------------------------------------------------------ + + ## The kernel context is updated with the currently active + ## IRQ in bits 9:15. + + .macro _update_kernel_context irqreg, ctxreg + rlwimi \ctxreg, \irqreg, 16, 9, 15 //set the irq # + oris \ctxreg, \ctxreg, 0x4000 //set the 'processing_interrupt' flag + mtsprg0 \ctxreg + +#if PK_KERNEL_TRACE_ENABLE + mr %r31, \irqreg + srwi \ctxreg, \ctxreg, 16 + PK_KERN_TRACE_ASM16("INTERRUPT_CONTEXT(0x%04x)", \ctxreg) + mr \irqreg, %r31 +#endif + + .endm +// *INDENT-ON* + +#else /* __ASSEMBLER__ */ + +/// PK thread context layout as a C structure. +/// +/// This is the structure of the stack area pointed to by +/// thread->saved_stack_pointer when a thread is fully context-switched out. + +typedef struct +{ + uint32_t r1; + uint32_t linkage; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t cr; + uint32_t lr; + + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r28; + uint32_t r29; + uint32_t r30; + uint32_t r31; + + uint32_t xer; + uint32_t ctr; + uint32_t srr0; + uint32_t srr1; + uint32_t r0; + uint32_t sprg0; + +} PkThreadContext; + + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_CONTEXT_H__ */ + + diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c new file mode 100644 index 00000000..ec8aec6d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c @@ -0,0 +1,157 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_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 ppe42_core.c +/// \brief The final bits of PK runtime code required to complete the PPE42 +/// port. +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present during runtime in any PK application. + +#define __PPE42_CORE_C__ + +#include "pk.h" + +typedef union +{ + uint64_t value; + struct + { + uint32_t dec_start; + uint32_t dec_change_tag; + }; +} ppe42_timebase_data_t; + +ppe42_timebase_data_t ppe42_tb_data = {0}; +PkTimebase ppe42_64bit_timebase = 0; + + +#if PK_TIMER_SUPPORT + +// The tickless kernel timer mechanism for PPE42 +// +// This routine must be called from a critical section. +// +// Tickless timeouts are provided by programming the PIT timer based on when +// the next timeout will occur. If the timeout is for the end of time there's +// nothing to do - PK does not use auto-reload mode so no more PIT interrupts +// will be arriving. Otherwise, if the timeout is longer than the 32-bit PIT +// timer can handle, we simply schedule the timeout for 2**32 - 1 and +// __pk_timer_handler() will keep rescheduling it until it finally occurs. +// If the \a timeout is in the past, we schedule the PIT interrupt for 1 tick +// in the future in accordance with the PK specification. + +#ifndef APPCFG_USE_EXT_TIMEBASE +void +__pk_schedule_hardware_timeout(PkTimebase timeout) +{ + PkTimebase now; + uint32_t new_dec; + uint32_t dec; + + if (timeout != PK_TIMEBASE_MAX) + { + + now = pk_timebase_get(); + + if (timeout <= now) + { + new_dec = 1; + } + else if ((timeout - now) > 0xffff0000) + { + new_dec = 0xffff0000; + } + else + { + new_dec = timeout - now; + } + + //read and write the DEC back-to-back so that we lose as little time + //as possible + dec = mfspr(SPRN_DEC); + mtspr(SPRN_DEC, new_dec); + + //update our 64bit accumulator with how much time has advanced since + //we last changed it. + ppe42_64bit_timebase += ppe42_tb_data.dec_start - dec; + + //update our start time so we know how much time has advanced since + //this update of the accumulator + ppe42_tb_data.dec_start = new_dec; + ppe42_tb_data.dec_change_tag++; + } +} + +#else + +void +__pk_schedule_hardware_timeout(PkTimebase timeout) +{ + PkTimebase now; + PkTimebase diff; + uint32_t new_dec; + + if (timeout != PK_TIMEBASE_MAX) + { + + now = pk_timebase_get(); + + //update our 64bit accumulator with the current snapshot + ppe42_64bit_timebase = now; + + if (timeout <= now) + { + new_dec = 1; + } + else + { + diff = (timeout - now); + + if (diff > 0xfffffffful) + { + new_dec = 0xffffffff; + } + else + { + new_dec = diff; + } + } + + mtspr(SPRN_DEC, new_dec); + + } +} + +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#endif /* PK_TIMER_SUPPORT */ + +#undef __PPE42_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S new file mode 100644 index 00000000..9c89284c --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S @@ -0,0 +1,525 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S $ */ +/* */ +/* 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 ppe42_exceptions.S +/// \brief PPE42 exception vector area. +/// +/// \cond + + .nolist +#include "pk.h" + .list + +## declare and initializes global variables that hold external irq config data +## Each PPE macro type (GPE, CME, and SBE) will have it's own implementation of this macro +## defined in (gpe, cme, sbe)_common.h + .hwmacro_irq_cfg_bitmaps + +### **************************************************************************** +### .vectors - This section contains all ppe42 exception vectors +### +### **************************************************************************** + + .section .vectors, "ax", @progbits + + .global __vectors + +__vectors: + + ############################################################ + # 0x0000 : Machine Check + ############################################################ + + ### Unmaskable interrupts (including program interrupts) are promoted + ### to machine check interrupts if MSR[UIE] = 0 and MSR[ME] = 1. + ### If the machine check was caused by a program interrupt it + ### will be forwarded to the program exception handler. +__machine_check: + + PPE42_MACHINE_CHECK_HANDLER + + ############################################################ + # 0x0040 : System Reset + ############################################################ + .global __system_reset + .org __vectors + 0x0040 +__system_reset: + b __pk_boot + + ############################################################ + # 0x0060 : Data Storage Interrupt + ############################################################ + + .org __vectors + 0x0060 +__data_storage: + + PPE42_DATA_STORAGE_HANDLER + + ############################################################ + # 0x0080 : Instruction Storage Interrupt + ############################################################ + + .org __vectors + 0x0080 +__instruction_storage: + + PPE42_INSTRUCTION_STORAGE_HANDLER + + + ############################################################ + # 0x00A0 : External Interrupt + ############################################################ + + .org __vectors + 0x00A0 +__external_interrupt_vector: + _pk_ctx_push_as_needed __get_ext_irq + + ############################################################ + # 0x00C0 : Alignment Exception + ############################################################ + + .org __vectors + 0x00C0 +__alignment_exception: + + PPE42_ALIGNMENT_HANDLER + + + ############################################################ + # 0x00E0 : Program Interrupt + ############################################################ + + .org __vectors + 0x00E0 + + ### Program exceptions are utilized for emulating the system call + ### instruction (0x44000002) which is used for doing context + ### switches between threads. They can also be used by the code + ### to signal an exception in an error scenario. +__program_exception: + _pk_ctx_push_as_needed program_exception_handler + + + ############################################################ + # 0x0100 : DEC Interrupts + ############################################################ + + .org __vectors + 0x0100 +__dec_interrupt: + _pk_ctx_push_as_needed dec_handler + + ############################################################ + # 0x0120 : FIT Interrupts + ############################################################ + .org __vectors + 0x0120 +__fit_interrupt: + + _pk_ctx_push_as_needed fit_handler + + ############################################################ + # 0x0140 : Watchdog Interrupts + ############################################################ + .org __vectors + 0x0140 +__watchdog_interrupt: + + _pk_ctx_push_as_needed watchdog_handler + + +### **************************************************************************** +### The rest of the code in this file doesn't have to be placed anywhere +### special, so just place it in the .text section. +### **************************************************************************** + + .section .text, "ax", @progbits + + + ## The idle thread has no permanent register context. The idle thread + ## entry point is re-entered whenever the idle thread is scheduled. + + .global __pk_idle_thread + .global __pk_idle_thread_from_bootloader + +__pk_idle_thread: + + ## The idle thread 'uses' the kernel stack. Any register context + ## pushed here is redundant and is wiped out/ignored every time the + ## idle thread is re-scheduled. + + ## The idle thread simply establishes a default machine context and + ## enters the wait-enable state. The idle thread is always entered + ## with interrupts disabled. + ## + ## The kernel context is initialized to indicate that the idle thread + ## is running - the idle thread priority is PK_THREADS, the + ## 'thread-mode' bit is asserted and so is the 'discard-ctx" bit. + ## In addition, the previous kernel context is stored in the lower + ## 16 bits. + ## + ## This loop can also be called from the PK bootloader if main() + ## returns - in which case we don't muck with the SPRG0 or the stack + ## pointer. + mfsprg0 %r3 + srwi %r3, %r3, 16 + oris %r3, %r3, (PK_THREADS << 8) | PPE42_THREAD_MODE | PPE42_DISCARD_CTX + mtsprg0 %r3 + _lwzsd %r1, __pk_kernel_stack + +__pk_idle_thread_from_bootloader: + + PK_KERN_TRACE_ASM16("ENTER_IDLE_STATE") + + _lwzsd %r3, __pk_thread_machine_context_default + _oriwa %r3, %r3, MSR_WE + mtmsr %r3 + b . + + ## pk_halt() is implemented on the ppe42 by writing a value of 0x3 to + ## the RST field of the DBCR. + .global pk_halt +pk_halt: + lis %r31, 0x3000 + mtdbcr %r31 + .long 0 + + +dec_handler: + + ## The portable timer handler of PK is a full-mode handler with the prototype: + ## void (*pk_timer_handler)(void). + ## + ## To support the portable specification, the kernel clears the + ## interrupt by writing the DIS back into the TSR before calling the + ## handler. The timer handler does not take any arguments. + + li %r4, PPE42_IRQ_DEC + _update_kernel_context %r4, %r3 + + _liwa %r3, TSR_DIS + mttsr %r3 + + bl __pk_timer_handler + b check_for_ext_interrupt + +program_exception_handler: + _pk_panic PPE42_ILLEGAL_INSTRUCTION + + .global __pk_next_thread_resume +__pk_next_thread_resume: + + _lwzsd %r3, __pk_next_thread + _stwsd %r3, __pk_current_thread + + ## Enter the wait enabled state if the thread pointer is null + bwz %r3, __pk_idle_thread + + ## switch to the new thread stack + lwz %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3) + + ## load sprg0 from the stack and update the thread priority + ## in case it changed. +restore_and_update_sprg0: + _lbzsd %r31, __pk_next_priority + + PK_KERN_TRACE_ASM16("RESUME_THREAD(%d)", %r31) + + lwz %r3, PK_CTX_KERNEL_CTX(%r1) + rlwimi %r3, %r31, 24, 2, 7 + mtsprg0 %r3 + + b ctx_pop + +fit_handler: + + ## The FIT handler is user defined. By + ## convention the kernel clears the interrupt by writing the FIS back + ## into the TSR. + + li %r4, PPE42_IRQ_FIT + + _update_kernel_context %r4, %r3 + + _lwzsd %r3, __ppe42_fit_arg + + _liwa %r6, TSR_FIS + mttsr %r6 + + _lwzsd %r6, __ppe42_fit_routine + mtlr %r6 + blrl + + b check_for_ext_interrupt + +watchdog_handler: + ## Watchdog setup is described in the PK Specification. + ## The kernel clears TSR[WIS] prior to calling the handler. + + li %r4, PPE42_IRQ_WATCHDOG + + _update_kernel_context %r4, %r3 + + _liwa %r6, TSR_WIS + mttsr %r6 + + _lwzsd %r6, __ppe42_watchdog_routine + mtlr %r6 + blrl + + b check_for_ext_interrupt + + + ## Check if we can disard the interrupted context. + ## This routine expects r3, r4, lr, and cr to already be pushed. + ## It also expects r3 to hold the address of the function to jump + ## to after the interrupted context has been pushed (if necessary). + + .align 5 +ctx_check_discard: + + ## Prepare to jump to the branch function that was passed in + mtlr %r3 + + ## Check if the DISCARD_CTX bit is set in the kernel context + mfsprg0 %r3 + bb0wi %r3, PPE42_DISCARD_CTX_BIT, ctx_continue_push + +ctx_discard: + ## DISCARD_CTX bit was set. Discard stack and branch to interrupt + ## handler code + addi %r1, %r1, PK_CTX_SIZE + blr + + ## DISCARD_CTX bit was not set. Continue saving full context. + ## (r3, r4, lr, and cr have already been saved for us) and + ## r3 contains the interrupted kernel context + + .global __ctx_switch +__ctx_switch: + stwu %r1, -PK_CTX_SIZE(%r1) + stvd %d3, PK_CTX_GPR3(%r1) + mfcr %r3 + mflr %r4 + stvd %d3, PK_CTX_CR(%r1) + _liw %r3 __pk_next_thread_resume + mtlr %r3 + ## emulate what interrupt would do + mtsrr0 %r4 + mfmsr %r3 + mtsrr1 %r3 + + ## ctx_continue_push expects r3 to be value of sprg0 + mfsprg0 %r3 + +ctx_continue_push: + + stvd %d5, PK_CTX_GPR5(%r1) + stvd %d7, PK_CTX_GPR7(%r1) + stvd %d9, PK_CTX_GPR9(%r1) + stvd %d28, PK_CTX_GPR28(%r1) + stvd %d30, PK_CTX_GPR30(%r1) + mfxer %r5 + mfctr %r6 + stvd %d5, PK_CTX_XER(%r1) + mfsrr0 %r7 + mfsrr1 %r8 + stvd %d7, PK_CTX_SRR0(%r1) + stw %r0, PK_CTX_GPR0(%r1) + stw %r3, PK_CTX_KERNEL_CTX(%r1) + + ## If the 'processing interrupt' bit is set then we were already + ## using the kernel stack and don't need to modify or save the current + ## stack pointer. + bb1wi %r3, PPE42_PROC_IRQ_BIT, ctx_push_completed + + ## load the pointer to the current thread control block + _lwzsd %r4, __pk_current_thread + + ## don't save the stack pointer in the thread control block + ## if the current thread was the idle thread (null pointer) + bwz %r4, switch_to_kernel_stack + + ## we interrupted a bonafide thread, so save off the stack + ## pointer + stw %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r4) + +switch_to_kernel_stack: + _stwsd %r1, __pk_saved_sp + _lwzsd %r1, __pk_kernel_stack + +ctx_push_completed: + blr + +__get_ext_irq: + + ## Entry invariants: + ## 1. external interupts are disabled; + ## 2. previous context has ben saved off + ## 3. r3 contains the kernel context + ## 4. r1 points to the kernel stack + + ## This is HW Macro specific code that is responsible for finding the + ## IRQ # and storing it in r4 (phantom IRQ's are assigned a value of EXTERNAL_IRQS). + + hwmacro_get_ext_irq + + ## An active or phantom IRQ was found. + ## R3 has the context of the interrupted thread or bottom half + ## R4 has the IRQ number. + ## The IRQ is converted into a pointer to an 8-byte handler + ## structure, and the handler is dispatched. The call is made with the + ## parameters: + + ## R3 = private data ptr + ## R4 = irq + +call_external_irq_handler: + + _update_kernel_context %r4, %r3 + slwi %r3, %r4, 3 //multiply the irq# by 8 + _liw %r6, __ppe42_irq_handlers + lwzx %r5, %r6, %r3 + addi %r3, %r3, 4 + lwzx %r3, %r6, %r3 + mtlr %r5 + blrl + + ## Once the interrupt handler returns, check if any interrupts are + ## waiting and handle them now. + +check_for_ext_interrupt: + + ## Set the CTX_DISCARD bit in the kernel context so that if there is + ## an interrupt it will not bother saving the full context. + mfsprg0 %r31 + oris %r31, %r31, PPE42_DISCARD_CTX + mtsprg0 %r31 + + ###### Enable/Disable External Interrupts ##### + wrteei 1 + wrteei 0 + + ## If we made it this far, there must not be any interrupts pending. + ## If bottom half processing was interrupted we need to restore it +check_interrupted_bh: + + ## If the thread ID is 33 then the bottom half handler was interrupted + ## and needs to be restored. + extrwi %r4, %r31, 6, 2 + cmpwi %r4, 33 + beq ctx_pop_with_sprg0 + +check_for_bh: + ## if the bottom half queue is pointing to itself then the queue is + ## empty and there are no bottom halves that need processing. + _lwzsd %r4, _pk_bh_queue + lwz %r5, 0(%r4) + cmplwbeq %r4, %r5, restore_interrupted_sp + +process_bottom_halves: + ## Clear the CTX_DISCARD bit so that interrupted bottom half context + ## will be saved in case an interrupt occurs after this point. Also + ## set the thread ID to 33 so that we know to restore the bottom half + ## context that was interrupted. + rlwinm %r3, %r31, 0, 9, 1 //clear thread id + discard bit + oris %r3, %r3, 0x2100 //set thread id to 33 + mtsprg0 %r3 //set bottom half context + + ## branch to a C function that processes bottom halves + wrteei 1 + bl _pk_process_bh + wrteei 0 + + ## restore the previous kernel context (with discard bit set) + mtsprg0 %r31 + +restore_interrupted_sp: + ## restore the interrupted thread stack pointer + _lwzsd %r1, __pk_saved_sp + + ## If we are not in thread mode (i.e., we took an interrupt in an + ## interupt-only configuration of PK or after pk_initialize() but + ## before pk_start_threads) simply pop the context and RFI - in this + ## case we'll most likely be returning to main() or the non-thread-mode + ## idle thread. + +check_thread_mode: + bb0wi %r31, PPE42_THREAD_MODE_BIT, ctx_pop_with_sprg0 + + ## Check if external interrupt activated a delayed context switch. The + ## C-level code has taken care of the scheduling decisions - we simply + ## need to implement them here. +check_for_ctx_switch: + + _lwzsd %r3, __pk_delayed_switch + bwz %r3, check_for_idle_thread + + ## Clear the delayed switch flag and go to the context switch code to + ## finish the switch. + + li %r3, 0 + _stwsd %r3, __pk_delayed_switch + + b __pk_next_thread_resume + + ## check if we should switch to the wait enabled state (idle) +check_for_idle_thread: + _lwzsd %r3, __pk_current_thread + bwz %r3, __pk_idle_thread + +ctx_pop_with_sprg0: + ## we must ensure that interrupts are disabled while restoring context + ## + ## restore sprg0 from the saved context + lwz %r0, PK_CTX_KERNEL_CTX(%r1) + mtsprg0 %r0 + +#if PK_KERNEL_TRACE_ENABLE + srwi %r0, %r0, 16 + PK_KERN_TRACE_ASM16("RESUME_CONTEXT(0x%04x)", %r0) +#endif + +ctx_pop: + lwz %r0, PK_CTX_GPR0(%r1) + lvd %d7, PK_CTX_SRR0(%r1) + mtsrr1 %r8 + mtsrr0 %r7 + lvd %d5, PK_CTX_XER(%r1) + mtctr %r6 + mtxer %r5 + lvd %d30, PK_CTX_GPR30(%r1) + lvd %d28, PK_CTX_GPR28(%r1) + lvd %d9, PK_CTX_GPR9(%r1) + lvd %d7, PK_CTX_GPR7(%r1) + lvd %d5, PK_CTX_GPR5(%r1) + lvd %d3, PK_CTX_CR(%r1) + mtlr %r4 + mtcr0 %r3 + lvd %d3, PK_CTX_GPR3(%r1) + addi %r1, %r1, PK_CTX_SIZE + + rfi + +/// \endcond diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c new file mode 100644 index 00000000..9b7aa8a8 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c @@ -0,0 +1,369 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.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 ppe42_gcc.h +/// \brief 32-bit PowerPC functions expected by GCC +/// +/// GCC expects certain built-in functions to be defined in the environment. +/// Since PK applications are statically linked, we must define these +/// functions ourselves to avoid a static link with the GCC libraries, which +/// would legaly require us to distribute (at least) the binary forms of PK +/// applications. +/// +/// We obviously had to look at some GCC library code to understand the +/// specifications of these routines. However, all of the code here is new - +/// no structure definitions or lines of executable code were copied from the +/// GCC sources. + +#include "pk.h" +#include "ppe42_gcc.h" + +/// A 64-bit logical right shift. +/// +/// Note that shifts with negative shift counts or shifts with shift counts +/// longer than 63 bits are undefined. + +uint64_t +__lshrdi3(uint64_t x, int i) +{ + Uint64 input, result; + + if (i == 0) + { + return x; + } + + input.value = x; + + if (i >= 32) + { + result.word[0] = 0; + result.word[1] = input.word[0] >> (i - 32); + } + else + { + result.word[0] = input.word[0] >> i; + result.word[1] = (input.word[1] >> i) | (input.word[0] << (32 - i)); + } + + return result.value; +} + + +/// A 64 bit arithmetic left shift. +/// +/// Note that shifts with negative shift counts or shifts with shift counts +/// longer than 63 bits are undefined. + +uint64_t +__ashldi3(uint64_t x, int i) +{ + Uint64 input, result; + + if (i == 0) + { + return x; + } + + input.value = x; + + if (i >= 32) + { + result.word[1] = 0; + result.word[0] = input.word[1] << (i - 32); + } + else + { + result.word[1] = input.word[1] << i; + result.word[0] = (input.word[0] << i) | (input.word[1] >> (32 - i)); + } + + return result.value ; + +} + + +/// A 64 bit arithmetic right shift. +/// +/// Note that shifts with negative shift counts or shifts with shift counts +/// longer than 63 bits are undefined. + +uint64_t +__ashrdi3(uint64_t x, int i) +{ + Int64 input, result; + + if (i == 0) + { + return x; + } + + input.value = x; + + if (i >= 32) + { + result.word[0] = input.word[0] >> 31; + result.word[1] = input.word[0] >> (i - 32); + } + else + { + result.word[0] = input.word[0] >> i; + result.word[1] = + (((uint32_t)input.word[1]) >> i) | + (input.word[0] << (32 - i)); + } + + return result.value ; + +} + + +/// 32-bit Population count + +// This is a well-known divide-and-conquer algorithm, e.g. look on Wikipedia +// under "Hamming Weight". The idea is to compute sums of adjacent bit +// segments in parallel, in place. + +int +__popcountsi2(uint32_t x) +{ + uint32_t m1 = 0x55555555; + uint32_t m2 = 0x33333333; + uint32_t m4 = 0x0f0f0f0f; + x -= (x >> 1) & m1; /* Sum pairs of bits */ + x = (x & m2) + ((x >> 2) & m2);/* Sum 4-bit segments */ + x = (x + (x >> 4)) & m4; /* Sum 8-bit segments */ + x += x >> 8; /* Sum 16-bit segments */ + return (x + (x >> 16)) & 0x3f; /* Final sum */ +} + + +/// 64-bit Population count + +int +__popcountdi2(uint64_t x) +{ + return __popcountsi2(x >> 32) + __popcountsi2(x & 0xffffffff); +} + + +// 64-bit divides +// +// For the unsigned case, note that divide by 0 returns quotient = remainder = +// 0. +// +// For the signed case, in general we perform the division on the absolute +// values and fix the signs of the quotient and remainder at the end. +// +// For the signed case, the convention in other libraries seems to be to +// ignore the case of the most-negative integer. Although it seems "wrong" to +// return the wrong answer when the right answer can be easily computed, in +// the interest of code size we follow the convention here and ignore the most +// negative integer. +// +// The assembler routine __ppe42_udiv64() assembles to ??? bytes. The full C +// routine __ppc_sdiv64 compiles to ??? bytes with the most-negative checks, +// but only ??? bytes as configured here. + +// For the signed cases, we need to handle the special case that the dividend +// or divisor is the most negative integer. +// +// If the dividend is the most negative integer, then dividing this integer by +// -1 would overflow as a positive quotient, so we set quotient and remainder +// to 0 in this case. For divide by 1, the quotient is the most negative +// integer. Otherwise we adjust the dividend by the absolute value of the +// divisor, then fix up the quotient later by adding or subtracting 1. +// +// If the divisor is the most negative integer, then the quotient is always 0 +// unless the dividend is also the most negative integer, in which case the +// quotient is 1 and the remainder is 0. +// + +uint64_t +__udivdi3(uint64_t u, uint64_t v) +{ + uint64_t quotient, remainder; + + __ppe42_udiv64(u, v, "ient, &remainder); + return quotient; +} + + +uint64_t +__umoddi3(uint64_t u, uint64_t v) +{ + uint64_t quotient, remainder; + + __ppe42_udiv64(u, v, "ient, &remainder); + return remainder; +} + + +#if 0 + #define INT64_T_MIN ((int64_t)(0x8000000000000000ull)) +#endif + +void +__ppe42_sdiv64(int64_t u, int64_t v, + int64_t* quotient, int64_t* remainder) +{ + int q_negate, r_negate; + uint64_t uu, uv; +#if 0 + int fixup = 0; +#endif + + q_negate = (u < 0) ^ (v < 0); + r_negate = (u < 0); + uu = (u < 0 ? -u : u); + uv = (v < 0 ? -v : v); + +#if 0 + + if (u == INT64_T_MIN) + { + if (v == -1) + { + *quotient = 0; + *remainder = 0; + return; + } + else if (v == 1) + { + *quotient = INT64_T_MIN; + *remainder = 0; + return; + } + else if (v == INT64_T_MIN) + { + *quotient = 1; + *remainder = 0; + return; + } + else + { + fixup = 1; + u += (v < 0 ? -v : v); + } + } + else if (v == INT64_T_MIN) + { + *quotient = 0; + *remainder = u; + return; + } + +#endif + + __ppe42_udiv64(uu, uv, (uint64_t*)quotient, (uint64_t*)remainder); + +#if 0 + + if (fixup) + { + *quotient += 1; + } + +#endif + + if (q_negate) + { + *quotient = -(*quotient); + } + + if (r_negate) + { + *remainder = -(*remainder); + } +} + + +int64_t +__divdi3(int64_t u, int64_t v) +{ + int64_t quotient, remainder; + + __ppe42_sdiv64(u, v, "ient, &remainder); + return quotient; +} + + +int64_t +__moddi3(int64_t u, int64_t v) +{ + int64_t quotient, remainder; + + __ppe42_sdiv64(u, v, "ient, &remainder); + return remainder; +} + + +/// 64-bit unsigned compare as a function, returning 0 (<), 1 (==) or 2 (>). + +int +__ucmpdi2(uint64_t i_a, uint64_t i_b) +{ + Uint64 a, b; + int rv; + + a.value = i_a; + b.value = i_b; + + if (a.word[0] < b.word[0]) + { + rv = 0; + } + else if (a.word[0] > b.word[0]) + { + rv = 2; + } + else if (a.word[1] < b.word[1]) + { + rv = 0; + } + else if (a.word[1] > b.word[1]) + { + rv = 2; + } + else + { + rv = 1; + } + + return rv; +} + + + + + + + + diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h new file mode 100644 index 00000000..7b9d37e1 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h @@ -0,0 +1,98 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.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 __PPE42_GCC_H__ +#define __PPE42_GCC_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_gcc.h +/// \brief 32-bit functions expected by GCC + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +/// A 64-bit unsigned integer type + +typedef union +{ + uint64_t value; + uint32_t word[2]; +} Uint64; + +/// A 64-bit signed integer type + +typedef union +{ + int64_t value; + int32_t word[2]; +} Int64; + +uint64_t +__lshrdi3(uint64_t x, int i); + +uint64_t +__ashldi3(uint64_t x, int i); + +uint64_t +__ashrdi3(uint64_t x, int i); + +int +__popcountsi2(uint32_t x); + +int +__popcountdi2(uint64_t x); + +/// Unsigned 64/64 bit divide, returning quotient and remainder via pointers. + +void +__ppe42_udiv64(uint64_t u, uint64_t v, uint64_t* q, uint64_t* r); + +/// Signed 64/64 bit divide, returning quotient and remainder via pointers. + +void +__ppe42_sdiv64(int64_t u, int64_t v, int64_t* q, int64_t* r); + +uint64_t +__udivdi3(uint64_t u, uint64_t v); + +int64_t +__divdi3(int64_t u, int64_t v); + +int64_t +__moddi3(int64_t u, int64_t v); + +uint64_t +__umoddi3(uint64_t u, uint64_t v); + +int +__ucmpdi2(uint64_t a, uint64_t b); + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_GCC_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c new file mode 100644 index 00000000..f81b5863 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c @@ -0,0 +1,102 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_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 ppe42_init.c +/// \brief PPE42 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" +#include "pk_trace.h" + +// Note that __ppe42_system_setup() is called from the PK bootloader early +// in the initialization, at a point before the aplication has enabled +// interrupts. + +// This function is expected to be defined by the macro specific code (GPE, CME, SBE) +void __hwmacro_setup(void); + + +void +__ppe42_system_setup() +{ + //Only do this if the application hasn't provided a static table definition +#ifndef STATIC_IRQ_TABLE + PkIrqId irq; + + // Initialize the interrupt vectors. + for (irq = 0; irq < EXTERNAL_IRQS; irq++) + { + __ppe42_irq_handlers[irq].handler = __ppe42_default_irq_handler; + } + + //NOTE: EXTERNAL_IRQS is the phantom interrupt assigned irq + __ppe42_irq_handlers[irq].handler = __ppe42_phantom_irq_handler; + + // Initialize special interrupt handlers + + __ppe42_fit_routine = __ppe42_default_irq_handler; + __ppe42_fit_arg = 0; + + __ppe42_watchdog_routine = __ppe42_default_irq_handler; + __ppe42_watchdog_arg = 0; + + /* + __ppe42_debug_routine = __ppe42_default_irq_handler; + __ppe42_debug_arg = 0; + */ +#endif /*STATIC_IRQ_TABLE*/ + + //Clear all status bits in the TSR + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + +#ifdef APPCFG_USE_EXT_TIMEBASE + //Enable the DEC interrupt and configure it to use the external dec_timer signal + mtspr(SPRN_TCR, TCR_DIE | TCR_DS); +#else + //Enable the DEC interrupt and configure it to use the internal clock signal + mtspr(SPRN_TCR, TCR_DIE); +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#if PK_TIMER_SUPPORT +#if PK_TRACE_SUPPORT + extern PkTraceBuffer g_pk_trace_buf; + //set the ppe instance id + g_pk_trace_buf.instance_id = (uint16_t)(mfspr(SPRN_PIR) & PIR_PPE_INSTANCE_MASK); +#endif /* PK_TRACE_SUPPORT */ +#endif /* PK_TIMER_SUPPORT */ + + //call macro-specific setup + __hwmacro_setup(); +} + + diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h new file mode 100644 index 00000000..f6f1964a --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h @@ -0,0 +1,243 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.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 __PPE42_IRQ_H__ +#define __PPE42_IRQ_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_irq.h +/// \brief PPE42 interrupt handling for PK +/// +/// Interrupt handling protocols and interrupt controller programming are +/// inherently non-portable, however PK defines APIs that may be useful among +/// different machines. +/// + + +// Define pseudo-IRQ numbers for PPE42 built-in interrupts. These numbers +// will appear in bits 16:23 of SPRG0 (__PkKernelContext) when the handlers +// are active + +#define PPE42_EXC_MACHINE_CHECK 0x50 +#define PPE42_EXC_DATA_STORAGE 0x53 +#define PPE42_EXC_INSTRUCTION_STORAGE 0x54 +#define PPE42_EXC_ALIGNMENT 0x56 +#define PPE42_EXC_PROGRAM 0x57 +#define PPE42_IRQ_DEC 0x58 +#define PPE42_IRQ_FIT 0x59 +#define PPE42_IRQ_WATCHDOG 0x5A + + +// Unhandled exceptions default to a kernel panic, but the application can +// override these definition. Note that the exception area only allocates 32 +// bytes (8 instructions) to an unhandled exception, so any redefinition +// would most likely be a branch to an application-defined handler. + +#ifndef PPE42_MACHINE_CHECK_HANDLER + #define PPE42_MACHINE_CHECK_HANDLER PK_PANIC(0x0200) +#endif + +#ifndef PPE42_DATA_STORAGE_HANDLER + #define PPE42_DATA_STORAGE_HANDLER PK_PANIC(0x0300) +#endif + +#ifndef PPE42_INSTRUCTION_STORAGE_HANDLER + #define PPE42_INSTRUCTION_STORAGE_HANDLER PK_PANIC(0x0400) +#endif + +#ifndef PPE42_ALIGNMENT_HANDLER + #define PPE42_ALIGNMENT_HANDLER PK_PANIC(0x0600) +#endif + + +//////////////////////////////////////////////////////////////////////////// +// PK API +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +/// An IRQ handler takes 2 arguments: +/// \arg \c arg - Private handler data installed by \c ssx_irq_setup() or +/// \c ssx_irq_handler_set(). +/// \arg \c irq - The IRQ id; to enable a generic handler to manipulate +/// its own interrupt status . + +typedef void (*PkIrqHandler)(void* arg, PkIrqId irq); + +/// Declare a subroutine as an IRQ handler + +#define PK_IRQ_HANDLER(f) void f(void* arg, PkIrqId irq) + +int pk_irq_setup(PkIrqId irq, + int polarity, + int trigger); + +int pk_irq_handler_set(PkIrqId irq, + PkIrqHandler handler, + void* arg); + +void pk_irq_enable(PkIrqId irq); +void pk_irq_disable(PkIrqId irq); +void pk_irq_statusclear(PkIrqId irq); + +PK_IRQ_HANDLER(__ppe42_default_irq_handler); +PK_IRQ_HANDLER(__ppe42_phantom_irq_handler); + + +int +ppe42_fit_setup(int tcr_fp, PkIrqHandler handler, void* arg); + + +/// The address of the optional FIT interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +PkIrqHandler __ppe42_fit_routine; + + +/// The private data of the optional FIT interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +void* __ppe42_fit_arg; + + +int +ppe42_watchdog_setup(int tcr_wp, int tcr_wrc, + PkIrqHandler handler, void* arg); + + +/// The address of the optional Watchdog interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +PkIrqHandler __ppe42_watchdog_routine; + + +/// The private data of the optional Watchdog interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +void* __ppe42_watchdog_arg; + + +int +ppe42_debug_setup(PkIrqHandler handler, void* arg); + + +/// The address of the optional Debug interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +PkIrqHandler __ppe42_debug_routine; + + +/// The private data of the optional Watchdog interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +void* __ppe42_debug_arg; + +#endif /* __ASSEMBLER__ */ + +// It's hard to be portable and get all of the definitions and headers in the +// correct order. We need to bring in the system IRQ header here. + +#ifdef HWMACRO_GPE + #include "gpe_irq.h" +#else + #ifdef HWMACRO_STD + #include "std_irq.h" + #endif +#endif + +/// \page ppe42_irq_macros_page PPE42 PK IRQ Assembler Macros +/// +/// + +#ifndef __ASSEMBLER__ + + +/// This structure holds the interrupt handler routine addresses and private +/// data. Assembler code assumes the given structure layout, so any changes +/// to this structure will need to be reflected down into the interrupt +/// dispatch assembler code. + +typedef struct +{ + PkIrqHandler handler; + void* arg; +} Ppe42IrqHandler; + + +#ifdef STATIC_IRQ_TABLE + +#define IRQ_HANDLER(func, arg) \ + {func, arg}, + +#define IRQ_HANDLER_DEFAULT \ + {__ppe42_default_irq_handler, 0}, + +#define EXTERNAL_IRQ_TABLE_END \ + {__ppe42_phantom_irq_handler, 0}\ + }; + +#define EXTERNAL_IRQ_TABLE_START \ + Ppe42IrqHandler __ppe42_irq_handlers[EXTERNAL_IRQS + 1] = \ + { + +#else + +#define EXTERNAL_IRQ_TABLE_START + +#define IRQ_HANDLER(func, arg) + +#define IRQ_HANDLER_DEFAULT + +#define EXTERNAL_IRQ_TABLE_END + +#endif /*STATIC_IRQ_TABLE*/ + +/// Interrupt handlers for real (implemented interrupts) plus one for the phantom interrupt handler +extern Ppe42IrqHandler __ppe42_irq_handlers[EXTERNAL_IRQS + 1]; + + +/// The 'phantom interrupt' handler +/// +/// A 'phantom' interrupt occurs when the interrupt handling code in the +/// kernel is entered, but no interrupt is found pending in the controller. +/// This is considered a serious bug, as it indictates a short window +/// condition where a level-sensitive interrupt has been asserted and then +/// quickly deasserted before it can be handled. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +Ppe42IrqHandler __ppe42_phantom_irq; + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_IRQ_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c new file mode 100644 index 00000000..7e369a02 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c @@ -0,0 +1,71 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_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 ppe42_irq_core.c +/// \brief Core IRQ routines required of any PPE42 configuration of PK +/// +/// This file is mostly only a placeholder - where 'extern inline' API +/// functions and 'extern' variables are realized. A couple of default +/// handlers are also installed here. The entry points in this file are +/// considered 'core' routines that will always be present at runtime in any +/// PK application. + +#define __PPE42_IRQ_CORE_C__ + +#include "pk.h" + +#ifndef STATIC_IRQ_TABLE + Ppe42IrqHandler __ppe42_irq_handlers[EXTERNAL_IRQS + 1]; +#endif + +/// This function is installed by default for interrupts not explicitly set up +/// by the application. These interrupts should never fire. + +void +__ppe42_default_irq_handler(void* arg, PkIrqId irq) +{ + PK_PANIC(PPE42_DEFAULT_IRQ_HANDLER); +} + + +/// This function is installed by default to handle the case that the +/// interrupt dispatch code is entered in response to an external +/// interrupt, but no interrupt is found pending in the interrupt +/// controller. This should never happen, as it would indicate that a +/// 'glitch' occurred on the external interrupt input +/// to the PPE42 core. + +void __ppe42_phantom_irq_handler(void* arg, PkIrqId irq) +{ + PK_PANIC(PPE42_PHANTOM_INTERRUPT); +} + + +#undef __PPE42_IRQ_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h new file mode 100644 index 00000000..8660f20d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h @@ -0,0 +1,112 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.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 __PPE42_MSR_H__ +#define __PPE42_MSR_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_msr.h +/// \brief Everything related to the PPE42 Machine State Register +/// +/// All of the macros defined here that \e modify the MSR create a compiler +/// memory barrier that will cause GCC to flush/invalidate all memory data +/// held in registers before the macro. This is consistent with other systems, +/// e.g., the PowerPC Linux kernel, and is the safest way to define these +/// macros as it guarantess for example that kernel data structure updates +/// have completed before exiting a critical section. + +#define MSR_SEM 0x7f000000 /* SIB Error Mask */ +#define MSR_IS0 0x00800000 /* Instance-Specific Field 0 */ +#define MSR_SIBRC 0x00700000 /* Last SIB return code */ +#define MSR_LP 0x00080000 /* Low Priority */ +#define MSR_WE 0x00040000 /* Wait State Enable */ +#define MSR_IS1 0x00020000 /* Instance-Specific Field 1 */ +#define MSR_UIE 0x00010000 /* Unmaskable Interrupt Enable */ +#define MSR_EE 0x00008000 /* External Interrupt Enable */ +#define MSR_ME 0x00001000 /* Machine Check Exception Enable */ +#define MSR_IPE 0x00000100 /* Imprecise Mode Enable */ +#define MSR_SIBRCA 0x000000ff /* SIB Return Code Accumulator */ + +//#define MSR_CE_BIT 14 +#define MSR_EE_BIT 16 +//#define MSR_IR_BIT 26 +//#define MSR_DR_BIT 27 + + +#define MSR_SEM_START_BIT 1 +#define MSR_SEM_LEN 7 +#define MSR_SIBRC_START_BIT 9 +#define MSR_SIBRC_LEN 3 + + +#ifndef __ASSEMBLER__ + +/// Move From MSR + +#define mfmsr() \ + ({uint32_t __msr; \ + asm volatile ("mfmsr %0" : "=r" (__msr)); \ + __msr;}) + + +/// Move to MSR + +#define mtmsr(value) \ + asm volatile ("mtmsr %0" : : "r" (value) : "memory") + + +/// Read-Modify-Write the MSR with OR (Set MSR bits). This operation is only +/// guaranteed atomic in a critical section. + +#define or_msr(x) \ + mtmsr(mfmsr() | (x)) + + +/// Read-Modify-Write the MSR with AND complement (Clear MSR bits). This +/// operation is only guaranteed atomic in a critical section. + +#define andc_msr(x) \ + mtmsr(mfmsr() & ~(x)) + + +/// Write MSR[EE] with an immediate value (0/1) +/// +/// Note that the immediate value \a i must be a compile-time constant. + +#define wrteei(i) \ + asm volatile ("wrteei %0" : : "i" (i) : "memory") + + +/// Write MSR[EE] from the EE bit of another MSR + +#define wrtee(other_msr) \ + asm volatile ("wrtee %0" : : "r" (other_msr) : "memory") + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_MSR_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c new file mode 100755 index 00000000..1dffcef4 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c @@ -0,0 +1,103 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.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 ppe42_scom.c +/// \brief Lowest level PK SCOM definitions. +/// +/// Currently these SCOM functions are only optimized for functionality, not +/// speed. Speed optimization will be done when we have full compiler support +/// for the low-level stvd and lvd SCOM OPs. +/// +/// A FAPI-lite SCOM can call these PK SCOM functions. +/// +/// Comment: +/// - No need to poll for SCOM completion, nor return error code of SCOM fails. +/// A SCOM fail will cause the GPE to hang if configured to do so. But do we +/// necessarily have to do this? Wouldn't a gentle recovery from a SCOM fail +/// be preferred? + +#include "pk.h" +#include "ppe42_scom.h" +#include "ppe42_msr.h" + + +uint32_t putscom_abs(const uint32_t i_address, uint64_t i_data) +{ + + // Perform the Store Virtual Double instruction + PPE_STVD(i_address, i_data); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); + +} + +uint32_t _putscom( uint32_t i_chiplet_id, uint32_t i_address, uint64_t i_data) +{ + + // Perform the Store Virtual Double Index instruction + PPE_STVDX(i_chiplet_id, i_address, i_data); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); + +} + +uint32_t getscom_abs( const uint32_t i_address, uint64_t* o_data) +{ + uint64_t temp; + // Perform the Load Virtual Double instruction + PPE_LVD(i_address, temp); + PPE_STVD(o_data, temp); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); +} + + +uint32_t _getscom( const uint32_t i_chiplet_id, const uint32_t i_address, uint64_t* o_data) +{ + uint64_t temp; + // Perform the Load Virtual Double Index instruction + PPE_LVDX(i_chiplet_id, i_address, temp); + PPE_STVD(o_data, temp); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); + +} diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h new file mode 100755 index 00000000..d3a8d2a7 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h @@ -0,0 +1,158 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.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 */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_scom.h +/// \brief Include file for PK SCOMs +/// + +#ifndef __PK_SCOM_H__ +#define __PK_SCOM_H__ + +/// SCOM operations return non-zero error codes that may or may not indicate +/// an actual error, depending on which SCOM is begin accessed. This error +/// code will appear in the MSR[SIBRC] field, bits[9:11] right after the +/// SCOM OP returns. The error code value increases with the severity of the +/// error. +#define PCB_ERROR_NONE 0 +#define PCB_ERROR_RESOURCE_OCCUPIED 1 +#define PCB_ERROR_CHIPLET_OFFLINE 2 +#define PCB_ERROR_PARTIAL_GOOD 3 +#define PCB_ERROR_ADDRESS_ERROR 4 +#define PCB_ERROR_CLOCK_ERROR 5 +#define PCB_ERROR_PACKET_ERROR 6 +#define PCB_ERROR_TIMEOUT 7 + +#ifdef __cplusplus +extern "C" { +#endif + + +/// PPE Load Virtual Double operation +#define PPE_LVD(_m_address, _m_data) \ + asm volatile \ + ( \ + "lvd %[data], 0(%[address]) \n" \ + : [data]"=r"(_m_data) \ + : [address]"b"(_m_address) \ + ); + + +// PPE Store Virtual Double operation +#define PPE_STVD(_m_address, _m_data) \ + asm volatile \ + ( \ + "stvd %[data], 0(%[address]) \n" \ + : [data]"=&r"(_m_data) \ + : "[data]"(_m_data), \ + [address]"b"(_m_address) \ + : "memory" \ + ); + +/// PPE Load Virtual Double Indexed operation +#define PPE_LVDX(_m_base, _m_offset, _m_data) \ + asm volatile \ + ( \ + "lvdx %[data], %[base], %[offset] \n" \ + : [data]"=r"(_m_data) \ + : [base]"b"(_m_base), \ + [offset]"r"(_m_offset) \ + ); + + +// PPE Store Virtual Double Indexed operation +#define PPE_STVDX(_m_base, _m_offset, _m_data) \ + asm volatile \ + ( \ + "stvdx %[data], %[base], %[offset] \n" \ + : [data]"=&r"(_m_data) \ + : "[data]"(_m_data), \ + [base]"b"(_m_base), \ + [offset]"r"(_m_offset) \ + : "memory" \ + ); + +#define PPE_MFMSR(_m_data) \ + asm volatile \ + ( \ + "mfmsr %[data] \n" \ + : [data]"=&r"(*_m_data) \ + : "[data]"(*_m_data) \ + ); + +/// @brief putscom with absolute address +/// @param [in] i_address Fully formed SCOM address +/// @param [in] i_data Pointer to uint64_t data to be written. A pointer is used +/// to optimize the underlying hardware execution +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts +uint32_t putscom_abs(const uint32_t i_address, uint64_t i_data); + +/// @brief getscom with absolute address +/// @param [in] i_address Fully formed SCOM address +/// @param [in] *o_data Pointer to uint64_t data read +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts + +uint32_t getscom_abs( const uint32_t i_address, uint64_t* o_data); + +/// @brief Implementation of PPE putscom functionality +/// @param [in] i_chiplet Chiplet ID (@todo Should only be right justified) +/// @param [in] i_address Base SCOM address +/// @param [in] i_data Pointer to uint64_t data to be written. A pointer is used +/// to optimize the underlying hardware execution +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts +uint32_t _putscom( const uint32_t i_chiplet, const uint32_t i_address, uint64_t i_data); + + +/// @brief Implementation of PPE getscom functionality +/// @param [in] i_chiplet Chiplet ID (@todo Should only be right justified) +/// @param [in] i_address Base SCOM address +/// @param [in] i_data Pointer to uint64_t data read +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts +uint32_t _getscom( uint32_t i_chiplet, uint32_t i_address, uint64_t* o_data); + +extern inline uint32_t putscom(const uint32_t i_chiplet, const uint32_t i_address, uint64_t i_data) +{ + return _putscom(i_chiplet, i_address, i_data); +} + + +extern inline uint32_t getscom(const uint32_t i_chiplet, const uint32_t i_address, uint64_t* o_data) +{ + return _getscom(i_chiplet, i_address, o_data); +} + +#ifdef __cplusplus +} // extern C +#endif + +#endif // __PK_SCOM_H__ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h new file mode 100644 index 00000000..82fed011 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h @@ -0,0 +1,201 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.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 __PPE42_SPR_H__ +#define __PPE42_SPR_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_spr.h +/// \brief Everything related to PPE42-specific SPRs + +/// \defgroup ppe42_sprs PPE42 SPRs +/// +/// These are the documented SPRs of the PPE42. Most of these SPRs are +/// available in RISCWatch and eCmd using the defined names (minus SPRN_). In +/// some cases RISCWatch/eCMD use different names, which appear in square +/// brackets in the brief comments for each register. RISCWatch/eCMD also +/// allow CR, MSR and IAR (Instruction Address Register) to be accessed as +/// SPRs. +/// +/// @{ + +#define SPRN_XER 0x001 /// Fixed-point exception register +#define SPRN_LR 0x008 /// Link register +#define SPRN_CTR 0x009 /// Count register +#define SPRN_DEC 0x016 /// Decrementer +#define SPRN_SRR0 0x01a /// Save/restore register 0 +#define SPRN_SRR1 0x01b /// Save/restore register 1 +#define SPRN_EDR 0x03d /// Error Data Register +#define SPRN_ISR 0x03e /// Interrupt Status Register +#define SPRN_IVPR 0x03f /// Interrupt Vector Prefix Register +#define SPRN_SPRG0 0x110 /// SPR general register 0 +#define SPRN_PIR 0x11e /// Processor Identification Register +#define SPRN_PVR 0x11f /// Processor version register +#define SPRN_DBCR 0x134 /// Debug Control Register +#define SPRN_DACR 0x13c /// Debug Address Compare Register +#define SPRN_TSR 0x150 /// Timer Status Register +#define SPRN_TCR 0x154 /// Timer Control Register + +/* DBCR - Debug Control Register */ + +#define DBCR_RST 0x30000000 /* Reset: 01=Soft Reset, 10=Hard Reset, 11=Halt */ +#define DBCR_TRAP 0x01000000 /* Trap Instruction Enable */ +#define DBCR_IACE 0x00800000 /* Instruction Address Compare Enable */ +#define DBCR_DACE 0x000c0000 /* Data Address Compare Enable: 01=store, 10=load, 11=both */ + +/* TCR - Timer Control Register */ + +#define TCR_WP_MASK 0xc0000000 /* Watchdog timer select bits */ +#define TCR_WP_0 0x00000000 /* WDT uses timer 0 */ +#define TCR_WP_1 0x40000000 /* WDT uses timer 1 */ +#define TCR_WP_2 0x80000000 /* WDT uses timer 2 */ +#define TCR_WP_3 0xc0000000 /* WDT uses timer 3 */ +#define TCR_WRC_MASK 0x30000000 /* Watchdog Reset Control mask */ +#define TCR_WRC_NONE 0x00000000 /* WDT results in no action */ +#define TCR_WRC_SOFT 0x10000000 /* WDT results in Soft reset */ +#define TCR_WRC_HARD 0x20000000 /* WDT results in Hard reset */ +#define TCR_WRC_HALT 0x30000000 /* WDT results in Halt */ +#define TCR_WIE 0x08000000 /* Watchdog Interrupt Enable */ +#define TCR_DIE 0x04000000 /* Decrementer Interrupt Enable */ +#define TCR_FP_MASK 0x03000000 /* FIT Timer Select bits*/ +#define TCR_FP_0 0x00000000 /* FIT uses timer 0 */ +#define TCR_FP_1 0x01000000 /* FIT uses timer 1 */ +#define TCR_FP_2 0x02000000 /* FIT uses timer 2 */ +#define TCR_FP_3 0x03000000 /* FIT uses timer 3 */ +#define TCR_FIE 0x00800000 /* FIT Interrupt Enable */ +#define TCR_DS 0x00400000 /* Decrementer timer select: 0=every cycle, 1=use dec_timer input signal */ + +#ifndef __ASSEMBLER__ + +typedef union +{ + uint32_t value; + struct + { + unsigned int wp : 2; + unsigned int wrc : 2; + unsigned int wie : 1; + unsigned int die : 1; + unsigned int fp : 2; + unsigned int fie : 1; + unsigned int ds : 1; + unsigned int reserved : 22; + } fields; +} Ppe42TCR; + +#endif /* __ASSEMBLER__ */ + +/* TSR - Timer Status Register */ + +#define TSR_ENW 0x80000000 /* Enable Next Watchdog */ +#define TSR_WIS 0x40000000 /* Watchdog Interrupt Status */ +#define TSR_WRS_MASK 0x30000000 /* Watchdog Reset Status */ +#define TSR_WRS_NONE 0x00000000 /* No watchdog reset has occurred */ +#define TSR_WRS_SOFT 0x10000000 /* Soft reset was forced by the watchdog */ +#define TSR_WRS_HARD 0x20000000 /* Hard reset was forced by the watchdog */ +#define TSR_WRS_HALT 0x30000000 /* Halt was forced by the watchdog */ +#define TSR_DIS 0x08000000 /* Decrementer Interrupt Status */ +#define TSR_FIS 0x04000000 /* FIT Interrupt Status */ + +/* PIR - Processor Identification Register */ +#define PIR_PPE_TYPE_MASK 0x000000E0 +#define PIR_PPE_TYPE_GPE 0x00000020 +#define PIR_PPE_TYPE_CME 0x00000040 +#define PIR_PPE_INSTANCE_MASK 0x0000001F + +#ifndef __ASSEMBLER__ + +/// Move From SPR +/// +/// Note that \a sprn must be a compile-time constant. + +#define mfspr(sprn) \ + ({uint32_t __value; \ + asm volatile ("mfspr %0, %1" : "=r" (__value) : "i" (sprn)); \ + __value;}) + + +/// Move to SPR +/// +/// Note that \a sprn must be a compile-time constant. + +#define mtspr(sprn, value) \ + ({uint32_t __value = (value); \ + asm volatile ("mtspr %0, %1" : : "i" (sprn), "r" (__value)); \ + }) + + +/// Read-Modify-Write an SPR with OR (Set SPR bits) +/// +/// Note that \a sprn must be a compile-time constant. This operation is only +/// guaranteed atomic in a critical section. + +#define or_spr(sprn, x) \ + mtspr(sprn, mfspr(sprn) | (x)) + + +/// Read-Modify-Write an SPR with AND complement (Clear SPR bits) +/// +/// Note that \a sprn must be a compile-time constant. This operation is only +/// guaranteed atomic in a critical section. + +#define andc_spr(sprn, x) \ + mtspr(sprn, mfspr(sprn) & ~(x)) + +#endif /* __ASSEMBLER__ */ + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + + /// \cond + + // Use this macro to define new mt<spr> and mf<spr> instructions that + // may not exist in the assembler. + + .macro _sprinstrs, name, num + .macro mt\name, reg + mtspr \num, \reg + .endm + .macro mf\name, reg + mfspr \reg, \num + .endm + .endm + + _sprinstrs dbcr, SPRN_DBCR + _sprinstrs tcr, SPRN_TCR + _sprinstrs tsr, SPRN_TSR + _sprinstrs sprg0, SPRN_SPRG0 + _sprinstrs ivpr, SPRN_IVPR + _sprinstrs dec, SPRN_DEC + + /// \endcond + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_SPR_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S new file mode 100644 index 00000000..1bed7882 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S @@ -0,0 +1,134 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S $ +# +# 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 ppe42_thread_init.S +/// \brief PPE42-specific thread initialization +/// +/// 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. + + .nolist +#include "pk.h" + .list + +/// \fn void __pk_thread_context_initialize(PkThread *thread, PkThreadRoutine thread_routine, void *private) +/// \brief Create the initial thread context on the stack +/// +/// The non-reserved GPRs are prepatterned with 0x0000\<rn\>\<rn\> where \<rn\> is +/// the register number (as decimal). The initial context is set up with the +/// thread running in the default machine context, and when the thread is +/// switched in it will begin executing at the entry point of the thread +/// routine with the \c private parameter in R3. The LR is initialized such +/// that when the thread returns, it will return to the entry point of \c +/// pk_complete(). +#ifdef DOXYGEN_ONLY +void +__pk_thread_context_initialize(PkThread *thread, + PkThreadRoutine thread_routine, + void *private); +#endif +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \cond + + .global_function __pk_thread_context_initialize + +__pk_thread_context_initialize: + + ## R3 = thread (param) + ## R4 = thread_routine (param) + ## R5 = private (param) + ## R6 = thread stack pointer (computed) + ## R7 = scratch + + .macro _gpr_init, prefix, reg, val + li %r7, \val + stw %r7, \prefix\reg(%r6) + .endm + + ## Initialize volatile context on the thread stack. The CR is cleared, + ## the LR = pk_complete(), R3 has the private parameter. + + lwz %r6, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3) + + stwu %r6, -PK_CTX_SIZE(%r6) + + li %r7, 0 + stw %r7, PK_CTX_CR(%r6) + + _liw %r7, pk_complete + stw %r7, PK_CTX_LR(%r6) + + stw %r5, PK_CTX_GPR3(%r6) + + _gpr_init PK_CTX_GPR, 4, 0x0404 + _gpr_init PK_CTX_GPR, 5, 0x0505 + _gpr_init PK_CTX_GPR, 6, 0x0606 + + ## XER and CTR are clear, SRR0 = thread_routine, SRR1 = default machine + ## context. + + li %r7, 0 + stw %r7, PK_CTX_XER(%r6) + stw %r7, PK_CTX_CTR(%r6) + + stw %r4, PK_CTX_SRR0(%r6) + + _lwzsd %r7, __pk_thread_machine_context_default + stw %r7, PK_CTX_SRR1(%r6) + + _gpr_init PK_CTX_GPR, 0, 0x0000 + _gpr_init PK_CTX_GPR, 7, 0x0707 + _gpr_init PK_CTX_GPR, 8, 0x0808 + _gpr_init PK_CTX_GPR, 9, 0x0909 + _gpr_init PK_CTX_GPR, 10, 0x1010 + + ## Initialize the non-volatile context on the thread stack. + + _gpr_init PK_CTX_GPR, 28, 0x2828 + _gpr_init PK_CTX_GPR, 29, 0x2929 + _gpr_init PK_CTX_GPR, 30, 0x3030 + _gpr_init PK_CTX_GPR, 31, 0x3131 + + ## Initialize the kernel context on the thread stack. + ## Note: Thread priority is set later each time the thread is + ## resumed. + + lis %r7, PPE42_THREAD_MODE + stw %r7, PK_CTX_KERNEL_CTX(%r6) + + ## Initialization is done - the stack pointer is stored back in the + ## thread. + + stw %r6, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3) + blr + + .epilogue __pk_thread_context_initialize + +/// \endcond diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S new file mode 100644 index 00000000..cc40bce4 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S @@ -0,0 +1,140 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S $ */ +/* */ +/* 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 ppe42_timebase.S +/// \brief PPE42-specific 64 bit timebase emulation +/// + .nolist +#include "pk.h" + .list + +/// \fn PkTimebase pk_timebase_get(void) +/// \brief Returns a 64 bit timebase +/// +#ifdef DOXYGEN_ONLY +PkTimebase +pk_timebase_get(void); +#endif +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \cond + .global ppe42_64bit_timebase + .global ppe42_tb_data + .global_function pk_timebase_get + +#ifndef APPCFG_USE_EXT_TIMEBASE + + /// Note that it is ok to use this function in a fast interrupt + /// context + .align 5 + .global_function pk_timebase32_get +pk_timebase32_get: + + //load the decrementer start time and change tag + lvd %r4, ppe42_tb_data@sda21(0) + + //load the lower 32 bits of the 64bit timebase accumulator + lwz %r3, ppe42_64bit_timebase+4@sda21(0) + + //load the current decrementer value + mfdec %r0 + + //load the change tag again (should already be in the cache) + lwz %r6, ppe42_tb_data+4@sda21(0) + + //loop until the change tag is the same (typically should be same) + cmplwbne %r5, %r6, pk_timebase32_get + + //calculate how much time has passed since the decrementer was started and store in r6 + subf %r5, %r0, %r4 + + //add the 32bit difference to our 32bit timebase accumulator + add %r3, %r5, %r3 + + blr + + +/// Use the DEC for our timebase until we have a real timebase register (uses +/// 9 instructions). +/// Note: It is not ok to use this function in a fast interrupt context due to +/// its use of r7 + .align 5 +pk_timebase_get: + + //load the decrementer start time and change tag + lvd %r5, ppe42_tb_data@sda21(0) + + //load 64bit timebase accumulator + lvd %r3, ppe42_64bit_timebase@sda21(0) + + + //load the current decrementer value + mfdec %r0 + + //load the change tag again (should already be in the cache) + lwz %r7, ppe42_tb_data+4@sda21(0) + + //loop until the change tag is the same + cmplwbne %r6, %r7, pk_timebase_get + + //calculate how much time has passed since the decrementer was started and store in r6 + subf %r6, %r0, %r5 + + //add the 32bit difference to the 64bit timebase accumulator + addc %r4, %r6, %r4 + addze %r3, %r3 + + blr + +//enable this once we have a local timebase register in the model +#else + +// use the local timebase register to keep more accurate time with just 6 instructions +// in the common case and 7 otherwise. + .align 5 +pk_timebase_get: + + //load the 64bit timebase accumulator + lvd r3, ppe42_64bit_timebase@sda21(0) + + //read the local timebase register (2 instructions) + _pk_timebase32_get r5, r5 + + //increment the upper 32 bits if the lower 32 bits have flipped + cmplwbge r5, r4, update_lower_32 + + //increment the upper 32 bits + addi r3, r3, 1 + +update_lower_32: + //replace the lower 32bits with what we read from the local timebase register + mr r4, r5 + + blr +#endif /* APPCFG_USE_EXT_TIMEBASE */ +/// \endcond diff --git a/import/chips/p9/procedures/ppe/pk/std/Makefile b/import/chips/p9/procedures/ppe/pk/std/Makefile new file mode 100644 index 00000000..3f484e61 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/Makefile @@ -0,0 +1,74 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/std/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 compiles all of the PK code required for the STD (standard PPE) port +# of PK. See the "pk.mk" file in this directory. + +#all generated files from this makefile will end up in obj/$(IMAGE_NAME)/pk +export SUB_OBJDIR = /pk + +include img_defs.mk +include pkstdfiles.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +STD_OBJECTS += ${STD-TIMER-C-SOURCES:.c=.o} ${STD-TIMER-S-SOURCES:.S=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +STD_OBJECTS += ${STD-THREAD-C-SOURCES:.c=.o} ${STD-THREAD-S-SOURCES:.S=.o} +endif + +ifeq "$(STD_ASYNC_SUPPORT)" "1" +STD_OBJECTS += ${STD-ASYNC-C-SOURCES:.c=.o} ${STD-ASYNC-S-SOURCES:.S=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(STD_OBJECTS)) + +libpk.a: kernel ppe42 trace std + $(AR) crs $(OBJDIR)/libpk.a $(OBJDIR)/*.o + +.PHONY: clean std kernel ppe42 trace +std: $(OBJS) + +trace: + $(MAKE) -I $(IMAGE_SRCDIR) -C ../trace + +kernel: + $(MAKE) -I $(IMAGE_SRCDIR) -C ../kernel + +ppe42: + $(MAKE) -I $(IMAGE_SRCDIR) -C ../ppe42 + + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +clean: + rm -fr $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif diff --git a/import/chips/p9/procedures/ppe/pk/std/pk_port.h b/import/chips/p9/procedures/ppe/pk/std/pk_port.h new file mode 100644 index 00000000..d214ba1f --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/pk_port.h @@ -0,0 +1,41 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/pk_port.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_PORT_H__ +#define __PK_PORT_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_port.h +/// \brief The top-level standard PPE environment header for PK. + +#define HWMACRO_STD + +#include "ppe42.h" +#include "std_timebase.h" + +#endif /* __PK_PORT_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk b/import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk new file mode 100644 index 00000000..71b8521f --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk @@ -0,0 +1,57 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/std/pkstdfiles.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 pkstdfiles.mk +# +# @brief mk for including std object files +# +# @page ChangeLogs Change Logs +# @section pkstdfiles.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Object Files +########################################################################## + +STD-C-SOURCES = std_init.c std_irq_init.c +STD-S-SOURCES = + +STD-TIMER-C-SOURCES = +STD-TIMER-S-SOURCES = + +STD-THREAD-C-SOURCES = +STD-THREAD-S-SOURCES = + +STD-ASYNC-C-SOURCES = +STD-ASYNC-S-SOURCES = + +STD_OBJECTS += $(STD-C-SOURCES:.c=.o) $(STD-S-SOURCES:.S=.o) + diff --git a/import/chips/p9/procedures/ppe/pk/std/std.h b/import/chips/p9/procedures/ppe/pk/std/std.h new file mode 100644 index 00000000..61b554bd --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std.h @@ -0,0 +1,44 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std.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 __STD_H__ +#define __STD_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pgp.h +/// \brief The STD environment for PK. + +#ifndef HWMACRO_STD + #define HWMACRO_STD + #include "ppe42.h" +#endif + +#include "std_register_addresses.h" +#include "std_common.h" + +#endif /* __STD_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_common.h b/import/chips/p9/procedures/ppe/pk/std/std_common.h new file mode 100644 index 00000000..358be9f1 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_common.h @@ -0,0 +1,80 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_common.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 __STD_COMMON_H__ +#define __STD_COMMON_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file cme_common.h +/// \brief Common header for standard PPE's +/// + +#ifndef __ASSEMBLER__ + #include <stdint.h> +#endif + +//#include "cmehw_interrupts.h" +#include "pk.h" +#include "std_irq_config.h" + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* +/// This macro contains standard PPE code for determining what IRQ caused the +/// external exception handler to be invoked by the PPE + +/// Check for interrupts pending in the interrupt status register while the IRQ +/// is computed. The IRQ is expected to be stored in r4. If no IRQ is +/// pending then load the phantom irq # (EXTERNAL_IRQS). +/// +/// r1, r2, r3, and r13 must not be modified. All other registers may be used. +/// + .macro hwmacro_get_ext_irq + + _lvdg d5, STD_LCL_EISTR #load the 64bit interrupt status into d5 + cntlzw r4, r5 + cmpwible r4, 31, call_external_irq_handler #branch if irq is lt or eq to 31 + + ## No IRQ pending in r5. Try r6. + ## Note: irq # will be 64 (phantom irq) if no bits were set in either register + + cntlzw r4, r6 + addi r4, r4, 32 + + .endm + +/// Redirect the .hwmacro_irq_cfg_bitmaps macro to call our standard PPE implementation +/// This is called from the ppe42_exceptions.S file. + .macro .hwmacro_irq_cfg_bitmaps + .std_irq_cfg_bitmaps + .endm + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_COMMON_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_init.c b/import/chips/p9/procedures/ppe/pk/std/std_init.c new file mode 100644 index 00000000..d2ea8c0b --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_init.c @@ -0,0 +1,74 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_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 std_init.c +/// \brief PK initialization for a standard PPE. +/// +/// The entry points in this routine are used during initialization. This +/// code space can be deallocated and reassigned after application +/// initialization if required. + +#include "pk.h" + +/// Standard PPE environment initial setup. +/// +/// This is setup common to all standard PPE Macro applications. This setup takes place +/// during boot, before main() is called. + +void +__hwmacro_setup(void) +{ + //mask all interrupts + out64(STD_LCL_EIMR_OR, 0xffffffffffffffffull); + + //Set all interrupts to active low, level sensitive by default + out64(STD_LCL_EIPR_CLR, 0xffffffffffffffffull); + out64(STD_LCL_EITR_CLR, 0xffffffffffffffffull); + + //set up the configured type + out64(STD_LCL_EITR_OR, g_ext_irqs_type); + + //set up the configured polarity + out64(STD_LCL_EIPR_OR, g_ext_irqs_polarity); + + //clear the status of all active-high interrupts (has no affect on + //level sensitive interrupts) + out64(STD_LCL_EISR_CLR, g_ext_irqs_polarity); + + //clear the status of all active-low interrupts (has no affect on + //level sensitive interrupts) + out64(STD_LCL_EISR_OR, ~g_ext_irqs_polarity); + + //unmask the interrupts that are to be enabled by default + out64(STD_LCL_EIMR_CLR, g_ext_irqs_enable); + + //wait for the last operation to complete + sync(); +} diff --git a/import/chips/p9/procedures/ppe/pk/std/std_irq.h b/import/chips/p9/procedures/ppe/pk/std/std_irq.h new file mode 100644 index 00000000..fb71ae09 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_irq.h @@ -0,0 +1,113 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_irq.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 __STD_IRQ_H__ +#define __STD_IRQ_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_irq.h +/// \brief Standard PPE Externnal Interrupt handling for PK +/// +/// The standard PPE interrupt controller supports a maximum of 64 interrupts with +/// simple OR combining of the interrupt signals. +/// +/// The standard PPE interrupt controller allows interrupt status to be set directly by +/// software. It contains a 'mask' register, unlike most 405 interrupt +/// controllers that have an 'enable' register. The standard PPE mask and status +/// registers also have atomic CLR/OR function so that it is never necessary +/// to enter a critical section to enable/disable/clear interrupts and +/// interrupt status. + +#include "std_common.h" +#include "std_register_addresses.h" +#include "ppe42.h" + +#ifndef __ASSEMBLER__ + +/// Enable an interrupt by clearing the mask bit. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_enable(PkIrqId irq) +{ + out64(STD_LCL_EIMR_CLR, STD_IRQ_MASK64(irq)); +} + + +/// Disable an interrupt by setting the mask bit. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_disable(PkIrqId irq) +{ + out64(STD_LCL_EIMR_OR, STD_IRQ_MASK64(irq)); +} + + +/// Clear interrupt status with an CLR mask. Only meaningful for +/// edge-triggered interrupts. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_status_clear(PkIrqId irq) +{ + out64(STD_LCL_EISR_CLR, STD_IRQ_MASK64(irq)); +} + + +/// Get IRQ status as a 0 or non-0 integer + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline int +pk_irq_status_get(PkIrqId irq) +{ + return (in64(STD_LCL_EISR) & STD_IRQ_MASK64(irq)) != 0; +} + + +/// Set or clear interrupt status explicitly. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_status_set(PkIrqId irq, int value) +{ + if (value) + { + out64(STD_LCL_EISR_OR, STD_IRQ_MASK64(irq)); + } + else + { + out64(STD_LCL_EISR_CLR, STD_IRQ_MASK64(irq)); + } +} + + +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_IRQ_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_irq_config.h b/import/chips/p9/procedures/ppe/pk/std/std_irq_config.h new file mode 100644 index 00000000..66b8b018 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_irq_config.h @@ -0,0 +1,169 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_irq_config.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 __STD_IRQ_CONFIG_H__ +#define __STD_IRQ_CONFIG_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_irq_config.h +/// \brief Contains data and macros pertaining to external interrupt +/// configuration for a standard PPE. +/// + +#include "pk_app_cfg.h" + +/// This constant is used to define the size of the table of interrupt handler +/// structures as well as a limit for error checking. +#define EXTERNAL_IRQS 64 + +// Standard interrupt type values (level or edge) +#define STD_IRQ_TYPE_LEVEL 0 +#define STD_IRQ_TYPE_EDGE 1 + +// Standard interrupt polarity values (high or low, rising falling) +#define STD_IRQ_POLARITY_LO 0 +#define STD_IRQ_POLARITY_FALLING 0 +#define STD_IRQ_POLARITY_HI 1 +#define STD_IRQ_POLARITY_RISING 1 + +// Standard interrupt mask values (masked or enabled) +#define STD_IRQ_MASKED 0 +#define STD_IRQ_ENABLED 1 + +// Fail to compile if the application does not define this +#ifndef APPCFG_EXT_IRQS_CONFIG + #error "APPCFG_EXT_IRQS_CONFIG must be defined in pk_app_cfg.h" +#endif + +// Fail to compile if the application does not define this +#ifndef APPCFG_IRQ_INVALID_MASK + #error "APPCFG_IRQ_INVALID_MASK must be defined in pk_app_cfg.h" +#endif + +#ifndef __ASSEMBLER__ + +/// This expression recognizes only those IRQ numbers that have named +/// (non-reserved) interrupts in the standard PPE interrupt controller. +#define STD_IRQ_VALID(irq) \ + ({unsigned __irq = (unsigned)(irq); \ + ((__irq < EXTERNAL_IRQS) && \ + ((STD_IRQ_MASK64(__irq) & \ + APPCFG_IRQ_INVALID_MASK) == 0));}) + +/// This is a 64-bit mask, with big-endian bit 'irq' set. +#define STD_IRQ_MASK64(irq) (0x8000000000000000ull >> (irq)) + +#else + +//Untyped assembler version of STD_IRQ_MASK64 +#define STD_IRQ_MASK64(irq) (0x8000000000000000 >> (irq)) + +#endif /* __ASSEMBLER__ */ + +#ifndef __ASSEMBLER__ + /// These globals are statically initialized elsewhere + extern uint64_t g_ext_irqs_type; + extern uint64_t g_ext_irqs_valid; + extern uint64_t g_ext_irqs_polarity; + extern uint64_t g_ext_irqs_enable; +#endif + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* +/// These macros aid in the initialization of the external interrupt globals. I would +/// prefer to use CPP macros, but they don't support recursive macros which I use to +/// convert the variable number of interrupts that a processor can control into static +/// bitmaps used by __hwmacro_setup() at runtime. + + + //helper macro for setting up the irq configuration bitmaps for a standard PPE + .macro .std_irq_config irq_num=-1 irq_type=-1 irq_polarity=-1 irq_mask=-1 parms:vararg + .if (( \irq_num == -1 ) && ( \irq_type == -1 ) && ( \irq_polarity == -1 ) && ( \irq_mask == -1 )) +#.if ( .ext_irqs_defd != .ext_irqs_valid ) +#.error "###### .std_irq_config: Missing configuration for one or more interrupts ######" +#.endif + + .section .sdata + .align 3 + .global g_ext_irqs_type + .global g_ext_irqs_polarity + .global g_ext_irqs_enable + g_ext_irqs_polarity: + .quad .ext_irqs_polarity + g_ext_irqs_type: + .quad .ext_irqs_type + g_ext_irqs_enable: + .quad .ext_irqs_enable + .else + .if (( \irq_num < 0 ) || ( \irq_num > (EXTERNAL_IRQS - 1))) + .error "###### .std_irq_config: invalid irq number \irq_num ######" + .elseif ((.ext_irqs_valid & (1 << ( EXTERNAL_IRQS - 1 - \irq_num ))) == 0 ) + .error "###### .std_irq_config: Attempt to configure invalid irq number \irq_num ######" + .elseif (.ext_irqs_defd & (1 << ( EXTERNAL_IRQS - 1 - \irq_num ))) + .error "###### .std_irq_config: duplicate definition for irq \irq_num ######" + .else + .ext_irqs_defd = .ext_irqs_defd | (1 << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .if (( \irq_type < 0 ) || ( \irq_type > 1 )) + .error "###### .std_irq_config: invalid/unspecified irq type \irq_type for irq \irq_num ######" + .else + .ext_irqs_type = .ext_irqs_type | ( \irq_type << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .if (( \irq_polarity < 0 ) || ( \irq_polarity > 1 )) + .error "###### .std_irq_config: invalid/unspecified irq polarity ( \irq_polarity ) for irq \irq_num ######" + .else + .ext_irqs_polarity = .ext_irqs_polarity | ( \irq_polarity << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .if (( \irq_mask < 0 ) || ( \irq_mask > 1 )) + .error "###### .std_irq_config: invalid/unspecified irq mask ( \irq_mask ) for irq \irq_num ######" + .else + .ext_irqs_enable = .ext_irqs_enable | ( \irq_mask << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .std_irq_config \parms + .endif + .endm + + //Top level macro for generating interrupt configuration globals for a standard PPE + .macro .std_irq_cfg_bitmaps + .ext_irqs_valid = ~(APPCFG_IRQ_INVALID_MASK) + .ext_irqs_type = 0 + .ext_irqs_polarity = 0 + .ext_irqs_enable = 0 + .irq_mask = 0 + .ext_irqs_defd = 0 + .std_irq_config APPCFG_EXT_IRQS_CONFIG + .endm + +// *INDENT-ON* +#endif /*__ASSEMBLER__*/ + +#endif /*__STD_IRQ_CONFIG_H__*/ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_irq_init.c b/import/chips/p9/procedures/ppe/pk/std/std_irq_init.c new file mode 100644 index 00000000..1115c4bb --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_irq_init.c @@ -0,0 +1,137 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_irq_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. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_irq_init.c +/// \brief Standard PPE IRQ initialization code for PK +/// +/// The entry points in this file are initialization routines that could be +/// eliminated/deallocated by the application to free up storage if they are +/// no longer needed after initialization. + +#include "pk.h" + +/// Define the polarity and trigger condition for an interrupt. +/// +/// It is up to the application to take care of any side effects that may +/// occur from programming or reprogramming the interrupt controller. For +/// example, changing edge/level sensitivity or active level may set or clear +/// interrupt status in the controller. +/// +/// Note that PK allows this API to be called from any context, and changes +/// to the interrupt controller are made from a critical section. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_IRQ_SETUP One or more arguments are invalid, +/// including an invalid \a irq, or invalid \a polarity or \a trigger parameters. + +int +pk_irq_setup(PkIrqId irq, + int polarity, + int trigger) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(!STD_IRQ_VALID(irq) || + !((polarity == PK_IRQ_POLARITY_ACTIVE_HIGH) || + (polarity == PK_IRQ_POLARITY_ACTIVE_LOW)) || + !((trigger == PK_IRQ_TRIGGER_LEVEL_SENSITIVE) || + (trigger == PK_IRQ_TRIGGER_EDGE_SENSITIVE)), + PK_INVALID_ARGUMENT_IRQ_SETUP); + } + + pk_critical_section_enter(&ctx); + + if (polarity == PK_IRQ_POLARITY_ACTIVE_HIGH) + { + out64(STD_LCL_EIPR_OR, STD_IRQ_MASK64(irq)); + } + else + { + out64(STD_LCL_EIPR_CLR, STD_IRQ_MASK64(irq)); + } + + if (trigger == PK_IRQ_TRIGGER_EDGE_SENSITIVE) + { + out64(STD_LCL_EITR_OR, STD_IRQ_MASK64(irq)); + } + else + { + out64(STD_LCL_EITR_CLR, STD_IRQ_MASK64(irq)); + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// (Re)define the IRQ handler and priority for an interrupt. +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// Note that PK allows this API to be called from any context, and changes +/// to the interrupt controller are made from a critical section. +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_IRQ_HANDLER One or more arguments are +/// invalid, including an invalid \a irq, a null (0) \a handler, +/// or invalid \a priority. + +int +pk_irq_handler_set(PkIrqId irq, + PkIrqHandler handler, + void* arg) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(!STD_IRQ_VALID(irq) || + (handler == 0), + PK_INVALID_ARGUMENT_IRQ_HANDLER); + } + + pk_critical_section_enter(&ctx); + + __ppe42_irq_handlers[irq].handler = handler; + __ppe42_irq_handlers[irq].arg = arg; + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + + diff --git a/import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h b/import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h new file mode 100644 index 00000000..1a6b56fc --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h @@ -0,0 +1,73 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_register_addresses.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 __STD_REGISTER_ADDRESSES_H__ +#define __STD_REGISTER_ADDRESSES_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_register_addresses.h +/// \brief Symbolic addresses for a standard PPE + +#include "pk.h" + +// Define the base address for the external interrupt controller registers +// This can be overridden in the pk_app_cfg.h file +#ifndef STD_EIC_BASE + #define STD_EIC_BASE 0xC0000000 +#endif + +// Define the base address for the PPE mode registers +// This can be overridden in the pk_app_cfg.h file +#ifndef STD_PMR_BASE + #define STD_PMR_BASE 0xC0000100 +#endif + +// Note: This list only contains registers that are needed by PK. If +// an application requires other registers, it should define them +// elsewhere (i.e., cme_register_addresses.h) +#define STD_LCL_EISR (STD_EIC_BASE + 0x0000) +#define STD_LCL_EISR_OR (STD_EIC_BASE + 0x0010) +#define STD_LCL_EISR_CLR (STD_EIC_BASE + 0x0018) +#define STD_LCL_EIMR (STD_EIC_BASE + 0x0020) +#define STD_LCL_EIMR_OR (STD_EIC_BASE + 0x0030) +#define STD_LCL_EIMR_CLR (STD_EIC_BASE + 0x0038) +#define STD_LCL_EIPR (STD_EIC_BASE + 0x0040) +#define STD_LCL_EIPR_OR (STD_EIC_BASE + 0x0050) +#define STD_LCL_EIPR_CLR (STD_EIC_BASE + 0x0058) +#define STD_LCL_EITR (STD_EIC_BASE + 0x0060) +#define STD_LCL_EITR_OR (STD_EIC_BASE + 0x0070) +#define STD_LCL_EITR_CLR (STD_EIC_BASE + 0x0078) +#define STD_LCL_EISTR (STD_EIC_BASE + 0x0080) +#define STD_LCL_EINR (STD_EIC_BASE + 0x00a0) + +#define STD_LCL_TSEL (STD_PMR_BASE + 0x0000) +#define STD_LCL_TBR (STD_PMR_BASE + 0x0040) + +#endif // __STD_REGISTER_ADDRESSES_H__ + diff --git a/import/chips/p9/procedures/ppe/pk/std/std_timebase.h b/import/chips/p9/procedures/ppe/pk/std/std_timebase.h new file mode 100644 index 00000000..f3f07448 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_timebase.h @@ -0,0 +1,66 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_timebase.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 __STD_TIMEBASE_H__ +#define __STD_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_timebase.h +/// \brief support for using the standard PPE 32 bit timebase register +/// +/// Each standard PPE has it's own timebase register that runs at a constant +/// frequency. + +#include "pk.h" + +#ifndef __ASSEMBLER__ + +#ifndef APPCFG_USE_EXT_TIMEBASE +static inline +uint32_t pk_timebase32_get(void) +{ + return (uint32_t)((in64(STD_LCL_TBR)) >> 32); +} + +#else +//assembly function is defined in ppe42_timebase.S +uint32_t pk_timebase32_get(void); + +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#else + +.macro _pk_timebase32_get rT, rA +lis \rA, STD_LCL_TBR@ha +lvd \rT, STD_LCL_TBR@l(\rA) +.endm + +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_TIMEBASE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/trace/Makefile b/import/chips/p9/procedures/ppe/pk/trace/Makefile new file mode 100644 index 00000000..846076a9 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/Makefile @@ -0,0 +1,50 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/trace/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 pktracefiles.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +PKTRACE_OBJECTS += ${PKTRACE-TIMER-C-SOURCES:.c=.o} ${PKTRACE-TIMER-S-SOURCES:.S=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +PKTRACE_OBJECTS += ${PKTRACE-THREAD-C-SOURCES:.c=.o} ${PKTRACE-THREAD-S-SOURCES:.S=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(PKTRACE_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/trace/pk_trace.h b/import/chips/p9/procedures/ppe/pk/trace/pk_trace.h new file mode 100644 index 00000000..8d699476 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace.h @@ -0,0 +1,305 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace.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_TRACE_H__ +#define __PK_TRACE_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_trace.h +/// \brief Macros and declarations for the PK Firmware Tracing Facility. +/// + +#include <stdint.h> + +#define PK_TRACE_VERSION 2 + +#ifndef PK_TRACE_SZ + #define PK_TRACE_SZ 256 +#endif + +//Fail compilation if size is not a power of 2 +#if ((PK_TRACE_SZ - 1) & PK_TRACE_SZ) + #error "PK_TRACE_SZ is not a power of two!!!" +#endif + +//Fail compilation if size is smaller than 64 bytes +#if (PK_TRACE_SZ < 64) + #error "PK_TRACE_SZ must be at least 64 bytes!!!" +#endif + +//Mask for calculating offsets into the trace circular buffer +#define PK_TRACE_CB_MASK (PK_TRACE_SZ - 1) + +#define STRINGIFY_HELPER(x) #x +#define STRINGIFY(x) STRINGIFY_HELPER(x) + +#define PPE_IMG_STRING STRINGIFY(IMAGE_NAME) + +#ifdef PK_TRACE_HASH_PREFIX + #if (PK_TRACE_HASH_PREFIX > 0xffff) + #error PK_TRACE_HASH_PREFIX must be defined as a 16 bit constant value + #endif +#endif //PK_TRACE_HASH_PREFIX + +//This provides a 128ns tick (assuming a 32ns clock period) +//and 4 different format values +#define PK_TRACE_TS_BITS 30 + +#define PK_TRACE_FORMAT_BITS (32 - PK_TRACE_TS_BITS) + +#define PK_TRACE_TS_MASK (0xfffffffful << PK_TRACE_FORMAT_BITS) +#define PK_TRACE_FORMAT_MASK (~PK_TRACE_TS_MASK) + +#define PK_GET_TRACE_FORMAT(w32) (PK_TRACE_FORMAT_MASK & w32) +#define PK_GET_TRACE_TIME(w32) (PK_TRACE_TS_MASK & w32) + +//Set the trace timer period to be the maximum +//32 bit time minus 2 seconds (assuming a 32ns tick) +//This allows for up to 1 second of interrupt latency + +//1 second for PK_TRACE_MTBT while only requiring a trace +//every 135 seconds in order to maintain the 64bit timebase. +#define PK_TRACE_TIMER_PERIOD (0xfffffffful - 62500000) + +//The Maximum Time Between Traces. In order to reduce the time that interrupts +//are disabled for tracing, reading of the time stamp is not done atomically +//with alocating an entry in the circular buffer. This means that the +//timestamps might not appear in order in the trace buffer. This is a +//problem because our calculation of the 64 bit timebase uses the unsigned +//difference of the current 32bit timestamp and the previous one and if they +//are out of order it will result in a very large difference. To solve this +//problem, any time that the parser code sees a very large difference (larger +//than PK_TRACE_MTBT) it will treat it as a negative number. +#define PK_TRACE_MTBT (0xfffffffful - 31250000) + +#define PK_TRACE_MAX_PARMS 4 + +//This is the maximum number of bytes allowed to be traced in a binary trace +//entry. +//The trace version needs to change if this changes. +#define PK_TRACE_MAX_BINARY 256 + +//clip the largest binary trace according to the trace buffer size. +//(The trace version does not need to change if this changes as long +// as it remains less than PK_TRACE_MAX_BINARY) +#if (PK_TRACE_SZ <= 256) + #define PK_TRACE_CLIPPED_BINARY_SZ PK_TRACE_SZ / 2 +#else + #define PK_TRACE_CLIPPED_BINARY_SZ PK_TRACE_MAX_BINARY +#endif + +//Trace formats that are supported +typedef enum +{ + PK_TRACE_FORMAT_EMPTY, + PK_TRACE_FORMAT_TINY, + PK_TRACE_FORMAT_BIG, + PK_TRACE_FORMAT_BINARY, +} PkTraceFormat; //pk_trace_format_t; + +//This combines the timestamp and the format bits into a +//single 32 bit word. +typedef union +{ + struct + { + uint32_t timestamp : + PK_TRACE_TS_BITS; + uint32_t format : + PK_TRACE_FORMAT_BITS; + }; + uint32_t word32; +} PkTraceTime; //pk_trace_time_t; + +//PK trace uses a 16 bit string format hash value +typedef uint16_t PkTraceHash; //pk_trace_hash_t; + +//The constant 16 bit hash value is combined with a +//16 bit parameter value when doing a tiny trace +typedef union +{ + struct + { + PkTraceHash string_id; + uint16_t parm; + }; + uint32_t word32; +} PkTraceTinyParms; //pk_trace_tiny_parms_t; + +//A tiny trace fits within a single 8 byte word. This includes +//the timestamp, format bits, hash id, and a 16 bit parameter. +typedef union +{ + struct + { + PkTraceTinyParms parms; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceTiny; //pk_trace_tiny_t; + +//Larger traces that require a 32 bit parameter or more than one +//parameter use the big trace format. The number of parms and +//the 'complete' flag are combined with the hash id. 'complete' +//is set to 0 initially and set to one only after all of the trace +//data has been written. +typedef union +{ + struct + { + PkTraceHash string_id; + uint8_t complete; + uint8_t num_parms; + }; + uint32_t word32; +} PkTraceBigParms; //pk_trace_big_parms_t; + +typedef union +{ + struct + { + PkTraceBigParms parms; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceBig; //pk_trace_big_t; + +//Binary traces are handled in a similar fashion to big traces, except +//that instead of having a number of parameters, we have number of bytes. +typedef union +{ + struct + { + PkTraceHash string_id; + uint8_t complete; + uint8_t num_bytes; + }; + uint32_t word32; +} PkTraceBinaryParms; //pk_trace_binary_parms_t; + +typedef union +{ + struct + { + PkTraceBinaryParms parms; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceBinary; //pk_trace_binary_t; + +//This is a generic structure that can be used to retrieve data +//for tiny, big, and binary formatted entries. +typedef union +{ + struct + { + PkTraceHash string_id; + union + { + uint16_t parm16; + struct + { + uint8_t complete; + uint8_t bytes_or_parms_count; + }; + }; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceGeneric; //pk_trace_generic_t; + +//This is a format that might be used in the future for tracing +//a 64 bit timestamp so that we don't fill up the buffer with periodic +//timer traces. It is not currently used. +#if 0 +typedef union +{ + struct + { + uint32_t upper32; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceTime64; //pk_trace_time64_t; +#endif + +//It would probably be more accurate to call this a footer since it +//actually resides at the highest address of each trace entry. These eight +//bytes contain information that allow us to walk the trace buffer from the +//most recent entry to the oldest entry. +typedef union +{ + PkTraceGeneric generic; + PkTraceBinary binary; + PkTraceBig big; + PkTraceTiny small; +} PkTraceEntryFooter; //pk_trace_entry_header_t; + + +//This is the data that is updated (in the buffer header) every time we add +//a new entry to the buffer. +typedef union +{ + struct + { + uint32_t tbu32; + uint32_t offset; + }; + uint64_t word64; +} PkTraceState; //pk_trace_state_t; + +#define PK_TRACE_IMG_STR_SZ 16 + +//Header data for the trace buffer that is used for parsing the data. +//Note: pk_trace_state_t contains a uint64_t which is required to be +//placed on an 8-byte boundary according to the EABI Spec. This also +//causes cb to start on an 8-byte boundary. +typedef struct +{ + //these values are needed by the parser + uint16_t version; + uint16_t rsvd; + char image_str[PK_TRACE_IMG_STR_SZ]; + uint16_t instance_id; + uint16_t partial_trace_hash; + uint16_t hash_prefix; + uint16_t size; + uint32_t max_time_change; + uint32_t hz; + uint32_t pad; + uint64_t time_adj64; + + //updated with each new trace entry + PkTraceState state; + + //circular trace buffer + uint8_t cb[PK_TRACE_SZ]; +} PkTraceBuffer; //pk_trace_buffer_t; + +extern PkTraceBuffer g_pk_trace_buf; + +#endif /* __PK_TRACE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c new file mode 100644 index 00000000..42df103d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c @@ -0,0 +1,117 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.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_trace_big.c +/// \brief PK Trace function that supports up to four 32-bit parameters +/// +/// The pk_trace_big function is only called (via some macro magic) if the +/// caller passes in a single parameter (not including the format string) +/// that is larger than 16 bits to the PK_TRACE(...) macro. +/// + +#include "pk.h" +#include "pk_trace.h" + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) +void pk_trace_big(uint32_t i_hash_and_count, + uint64_t i_parm1, uint64_t i_parm2) +{ + PkTraceBig footer; + PkTraceBig* footer_ptr; + PkTraceState state; + uint64_t* ptr64; + uint64_t tb64; + PkMachineContext ctx; + uint32_t parm_size; + uint32_t cur_offset; + uint32_t footer_offset; + + //fill in the footer data + tb64 = pk_timebase_get(); + footer.parms.word32 = i_hash_and_count; //this has the parm count and hash + state.tbu32 = tb64 >> 32; + footer.time_format.word32 = tb64 & 0x00000000ffffffffull; + footer.time_format.format = PK_TRACE_FORMAT_BIG; + + //round up to 8 byte boundary + if(footer.parms.num_parms <= 2) + { + parm_size = 8; + } + else + { + parm_size = 16; + } + + //*****The following operations must be done atomically***** + pk_critical_section_enter(&ctx); + + //load in the offset in the cb for the entry we are adding + cur_offset = g_pk_trace_buf.state.offset; + + //Find the offset for the footer (at the end of the entry) + footer_offset = cur_offset + parm_size; + + //calculate the address of the footer + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[footer_offset & PK_TRACE_CB_MASK]; + + //calculate the offset for the next entry in the cb + state.offset = footer_offset + sizeof(PkTraceBig); + + //update the cb state (tbu and offset) + g_pk_trace_buf.state.word64 = state.word64; + + //write the data to the circular buffer including the + //timesamp, string hash, and 16bit parameter + *ptr64 = footer.word64; + + //*******************exit the critical section*************** + pk_critical_section_exit(&ctx); + + + //write parm values to the circular buffer + footer_ptr = (PkTraceBig*)ptr64; + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[cur_offset & PK_TRACE_CB_MASK]; + *ptr64 = i_parm1; + + if(parm_size > 8) + { + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[(cur_offset + 8) & PK_TRACE_CB_MASK]; + *ptr64 = i_parm2; + } + + //Mark the trace entry update as being completed + footer_ptr->parms.complete = 1; + +} + +#endif + + diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c new file mode 100644 index 00000000..5bc63179 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c @@ -0,0 +1,115 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.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_trace_binary.c +/// \brief PK Trace function for dumping memory contents +/// +/// The pk_trace_binary function is called by the PK_TRACE_BINARY() macro. +/// + + +#include "pk.h" +#include "pk_trace.h" + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) +void pk_trace_binary(uint32_t i_hash_and_size, void* bufp) +{ + PkTraceBinary footer; + PkTraceBinary* footer_ptr; + PkTraceState state; + uint64_t* ptr64; + uint64_t tb64; + PkMachineContext ctx; + uint32_t data_size; + uint32_t cb_offset; + uint32_t footer_offset; + uint8_t* dest; + uint8_t* src; + uint32_t index; + + //fill in the footer data + tb64 = pk_timebase_get(); + footer.parms.word32 = i_hash_and_size; //this has the size and hash + state.tbu32 = tb64 >> 32; + footer.time_format.word32 = tb64 & 0x00000000ffffffffull; + footer.time_format.format = PK_TRACE_FORMAT_BINARY; + + //round up to 8 byte boundary + data_size = (footer.parms.num_bytes + 7) & ~0x00000007ul; + + //limit data size + if(data_size > PK_TRACE_CLIPPED_BINARY_SZ) + { + data_size = PK_TRACE_CLIPPED_BINARY_SZ; + } + + //*****The following operations must be done atomically***** + pk_critical_section_enter(&ctx); + + //load in the offset in the cb for the entry we are adding + cb_offset = g_pk_trace_buf.state.offset; + + //Find the offset for the footer (at the end of the entry) + footer_offset = cb_offset + data_size; + + //calculate the address of the footer + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[footer_offset & PK_TRACE_CB_MASK]; + + //calculate the offset for the next entry in the cb + state.offset = footer_offset + sizeof(PkTraceBinary); + + //update the cb state (tbu and offset) + g_pk_trace_buf.state.word64 = state.word64; + + //write the footer data to the circular buffer including the + //timesamp, string hash and data size + *ptr64 = footer.word64; + + //*******************exit the critical section*************** + pk_critical_section_exit(&ctx); + + //write data to the circular buffer + for(src = bufp, index = 0; + index < data_size; + index++) + { + dest = &g_pk_trace_buf.cb[(cb_offset + index) & PK_TRACE_CB_MASK]; + *dest = *(src++); + } + + //Mark the trace entry update as being completed + footer_ptr = (PkTraceBinary*)ptr64; + footer_ptr->parms.complete = 1; + +} + +#endif + + diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c new file mode 100644 index 00000000..4669a56b --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c @@ -0,0 +1,144 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace_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_trace_core.c +/// \brief PK Trace core data and code. +/// +/// This file includes the minimal code/data required to do minimal tracing. +/// This includes the periodic timer initialization and the pk_trace_tiny +/// function. The pk_trace_tiny function is called by the PK_TRACE() macro +/// when there is one or less parameters (not including the format string) +/// and the parameter size is 16 bits or smaller. +/// + +#include "pk.h" +#include "pk_trace.h" + +void pk_trace_timer_callback(void* arg); + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) + +//Static initialization of the trace timer +PkTimer g_pk_trace_timer = +{ + .deque = PK_DEQUE_ELEMENT_INIT(), + .timeout = 0, + .callback = pk_trace_timer_callback, + .arg = 0, +}; + +//Static initialization of the pk trace buffer +PkTraceBuffer g_pk_trace_buf = +{ + .version = PK_TRACE_VERSION, + .image_str = PPE_IMG_STRING, + .hash_prefix = PK_TRACE_HASH_PREFIX, + .partial_trace_hash = trace_ppe_hash("PARTIAL TRACE ENTRY. HASH_ID = %d", PK_TRACE_HASH_PREFIX), + .size = PK_TRACE_SZ, + .max_time_change = PK_TRACE_MTBT, + .hz = 500000000, //default value. Actual value is set in pk_init.c + .time_adj64 = 0, + .state.word64 = 0, + .cb = {0} +}; + +//Needed for buffer extraction in simics for now +PkTraceBuffer* g_pk_trace_buf_ptr = &g_pk_trace_buf; + +// Creates an 8 byte entry in the trace buffer that includes a timestamp, +// a format string hash value and a 16 bit parameter. +// +// i_parm has the hash value combined with the 16 bit parameter +void pk_trace_tiny(uint32_t i_parm) +{ + PkTraceTiny footer; + PkTraceState state; + uint64_t* ptr64; + uint64_t tb64; + PkMachineContext ctx; + + //fill in the footer data + footer.parms.word32 = i_parm; + tb64 = pk_timebase_get(); + state.tbu32 = tb64 >> 32; + footer.time_format.word32 = tb64 & 0x00000000ffffffffull; + + footer.time_format.format = PK_TRACE_FORMAT_TINY; + + //The following operations must be done atomically + pk_critical_section_enter(&ctx); + + //load the current byte count and calculate the address for this + //entry in the cb + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[g_pk_trace_buf.state.offset & PK_TRACE_CB_MASK]; + + //calculate the offset for the next entry in the cb + state.offset = g_pk_trace_buf.state.offset + sizeof(PkTraceTiny); + + //update the cb state (tbu and offset) + g_pk_trace_buf.state.word64 = state.word64; + + //write the data to the circular buffer including the + //timesamp, string hash, and 16bit parameter + *ptr64 = footer.word64; + + //exit the critical section + pk_critical_section_exit(&ctx); +} + + +// This function is called periodically in order to ensure that the max ticks +// between trace entries is no more than what will fit inside a 32bit value. +#ifndef PK_TRACE_TIMER_OUTPUT + #define PK_TRACE_TIMER_OUTPUT 1 +#endif +void pk_trace_timer_callback(void* arg) +{ +#if PK_TRACE_TIMER_OUTPUT + // guarantee at least one trace before the lower 32bit timebase flips + PK_TRACE("PERIODIC TIMESTAMPING TRACE"); +#endif + // restart the timer + pk_timer_schedule(&g_pk_trace_timer, + PK_TRACE_TIMER_PERIOD); +} + +// Use this function to synchronize the timebase between multiple PPEs. +// PPE A can send PPE B it's current timebase and then PPE B can set that +// as the current timebase for tracing purposes. It can also be used +// to set the current time to 0. This function changes the timebase for +// all entries that are currently in the trace buffer. Setting the current +// timebase to 0 will cause previous traces to have very large timestamps. +void pk_trace_set_timebase(PkTimebase timebase) +{ + g_pk_trace_buf.time_adj64 = timebase - pk_timebase_get(); +} + +#endif diff --git a/import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk b/import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk new file mode 100644 index 00000000..760b553e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk @@ -0,0 +1,63 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/trace/pktracefiles.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 pkppe42files.mk +# +# @brief mk for including ppe42 object files +# +# @page ChangeLogs Change Logs +# @section pkppe42files.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Include Files +########################################################################## + + + +########################################################################## +# Object Files +########################################################################## +PKTRACE-C-SOURCES = pk_trace_core.c pk_trace_big.c pk_trace_binary.c + +PKTRACE-S-SOURCES = + +PKTRACE-TIMER-C-SOURCES = +PKTRACE-TIMER-S-SOURCES = + +PKTRACE-THREAD-C-SOURCES += +PKTRACE-THREAD-S-SOURCES += + + +PKTRACE_OBJECTS += $(PKTRACE-C-SOURCES:.c=.o) $(PKTRACE-S-SOURCES:.S=.o) + + + |