summaryrefslogtreecommitdiffstats
path: root/pk
diff options
context:
space:
mode:
authorGlenn Miles <milesg@us.ibm.com>2015-02-18 13:54:50 -0600
committerDerk Rembold <rembold@de.ibm.com>2015-02-19 09:45:49 -0600
commitf7a56090b73768f8fec063e41aba12662ee59a45 (patch)
treed2373137870269e93d215ba65319ad1af4b131c3 /pk
parentd84a1393ddec82dda67207a6f21edb5153877b5a (diff)
downloadtalos-sbe-f7a56090b73768f8fec063e41aba12662ee59a45.tar.gz
talos-sbe-f7a56090b73768f8fec063e41aba12662ee59a45.zip
Seed the files in the pk directory
Change-Id: I03398098e6625f0e06e4a96769b03002a1c71d35 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/15813 Reviewed-by: Glenn R. Miles <milesg@us.ibm.com> Reviewed-by: Derk Rembold <rembold@de.ibm.com> Tested-by: Derk Rembold <rembold@de.ibm.com>
Diffstat (limited to 'pk')
-rw-r--r--pk/cme/Makefile50
-rw-r--r--pk/cme/cme.h22
-rw-r--r--pk/cme/cme_common.h57
-rw-r--r--pk/cme/cme_init.c25
-rw-r--r--pk/cme/pk_port.h16
-rw-r--r--pk/cme/pkcmefiles.mk33
-rw-r--r--pk/gpe/Makefile50
-rw-r--r--pk/gpe/gpe.h35
-rw-r--r--pk/gpe/gpe_common.h62
-rw-r--r--pk/gpe/gpe_init.c123
-rw-r--r--pk/gpe/gpe_irq.h247
-rw-r--r--pk/gpe/gpe_irq_init.c108
-rw-r--r--pk/gpe/pk_port.h16
-rw-r--r--pk/gpe/pkgpefiles.mk33
-rw-r--r--pk/kernel/Makefile26
-rw-r--r--pk/kernel/pk.h125
-rw-r--r--pk/kernel/pk_api.h1010
-rw-r--r--pk/kernel/pk_core.c79
-rw-r--r--pk/kernel/pk_init.c162
-rw-r--r--pk/kernel/pk_kernel.h266
-rw-r--r--pk/kernel/pk_macros.h110
-rw-r--r--pk/kernel/pk_semaphore_core.c328
-rw-r--r--pk/kernel/pk_semaphore_init.c82
-rw-r--r--pk/kernel/pk_stack_init.c85
-rw-r--r--pk/kernel/pk_thread_core.c939
-rw-r--r--pk/kernel/pk_thread_init.c137
-rw-r--r--pk/kernel/pk_timer_core.c444
-rw-r--r--pk/kernel/pk_timer_init.c122
-rw-r--r--pk/kernel/pkkernelfiles.mk32
-rw-r--r--pk/ppe/Makefile50
-rw-r--r--pk/ppe/pk_port.h16
-rw-r--r--pk/ppe/pkppefiles.mk33
-rw-r--r--pk/ppe/ppe.h22
-rw-r--r--pk/ppe/ppe_common.h60
-rw-r--r--pk/ppe/ppe_init.c25
-rw-r--r--pk/ppe42/Makefile26
-rw-r--r--pk/ppe42/div64.S261
-rw-r--r--pk/ppe42/pk_port_types.h41
-rw-r--r--pk/ppe42/pkppe42files.mk45
-rw-r--r--pk/ppe42/ppe42.h795
-rw-r--r--pk/ppe42/ppe42_asm.h420
-rw-r--r--pk/ppe42/ppe42_boot.S169
-rw-r--r--pk/ppe42/ppe42_cache.h102
-rw-r--r--pk/ppe42/ppe42_context.h455
-rw-r--r--pk/ppe42/ppe42_core.c199
-rw-r--r--pk/ppe42/ppe42_exceptions.S580
-rw-r--r--pk/ppe42/ppe42_gcc.c305
-rw-r--r--pk/ppe42/ppe42_gcc.h72
-rw-r--r--pk/ppe42/ppe42_init.c75
-rw-r--r--pk/ppe42/ppe42_irq.h326
-rw-r--r--pk/ppe42/ppe42_irq_core.c47
-rw-r--r--pk/ppe42/ppe42_msr.h81
-rw-r--r--pk/ppe42/ppe42_scom.c241
-rw-r--r--pk/ppe42/ppe42_scom.h87
-rw-r--r--pk/ppe42/ppe42_spr.h173
-rw-r--r--pk/ppe42/ppe42_thread_init.S108
-rw-r--r--pk/ppe42/ppe42_timebase.S82
-rw-r--r--pk/trace/Makefile26
-rw-r--r--pk/trace/pk_trace.h276
-rw-r--r--pk/trace/pk_trace_big.c92
-rw-r--r--pk/trace/pk_trace_binary.c91
-rw-r--r--pk/trace/pk_trace_core.c152
-rw-r--r--pk/trace/pktracefiles.mk39
63 files changed, 10396 insertions, 0 deletions
diff --git a/pk/cme/Makefile b/pk/cme/Makefile
new file mode 100644
index 00000000..60c82c87
--- /dev/null
+++ b/pk/cme/Makefile
@@ -0,0 +1,50 @@
+# This Makefile compiles all of the PK code required for the CME 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 pkcmefiles.mk
+
+ifeq "$(PK_TIMER_SUPPORT)" "1"
+CME_OBJECTS += ${CME-TIMER-C-SOURCES:.c=.o} ${CME-TIMER-S-SOURCES:.S=.o}
+endif
+
+ifeq "$(PK_THREAD_SUPPORT)" "1"
+CME_OBJECTS += ${CME-THREAD-C-SOURCES:.c=.o} ${CME-THREAD-S-SOURCES:.S=.o}
+endif
+
+ifeq "$(CME_ASYNC_SUPPORT)" "1"
+CME_OBJECTS += ${CME-ASYNC-C-SOURCES:.c=.o} ${CME-ASYNC-S-SOURCES:.S=.o}
+endif
+
+OBJS := $(addprefix $(OBJDIR)/, $(CME_OBJECTS))
+
+libpk.a: kernel ppe42 trace cme
+ $(AR) crs $(OBJDIR)/libpk.a $(OBJDIR)/*.o
+
+.PHONY: clean cme kernel ppe42 trace
+cme: $(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/pk/cme/cme.h b/pk/cme/cme.h
new file mode 100644
index 00000000..d789ff3b
--- /dev/null
+++ b/pk/cme/cme.h
@@ -0,0 +1,22 @@
+#ifndef __CME_H__
+#define __CME_H__
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pgp.h
+/// \brief The CME environment for PK.
+
+// This is a 'circular' reference in PK, but included here to simplify PGAS
+// programming.
+
+#ifndef HWMACRO_CME
+#define HWMACRO_CME
+#include "ppe42.h"
+#endif
+
+#include "cme_common.h"
+
+#endif /* __CME_H__ */
diff --git a/pk/cme/cme_common.h b/pk/cme/cme_common.h
new file mode 100644
index 00000000..23eb59ff
--- /dev/null
+++ b/pk/cme/cme_common.h
@@ -0,0 +1,57 @@
+#ifndef __CME_COMMON_H__
+#define __CME_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 CME
+///
+/// This header is maintained as part of the PK port for CME, but needs to be
+/// physically present in the PMX area to allow dropping PMX code as a whole
+/// to other teams.
+
+// -*- WARNING: This file is maintained as part of PK. Do not edit in -*-
+// -*- the PMX area as your edits will be lost. -*-
+
+#ifndef __ASSEMBLER__
+#include <stdint.h>
+#endif
+
+/// This constant is used to define the size of the table of interrupt handler
+/// structures as well as a limit for error checking.
+/// NOTE: This can be specific to each PPE type (SBE, CME, GPE)
+#define EXTERNAL_IRQS 64
+
+#ifdef __ASSEMBLER__
+/// This macro contains CME specific code for determining what IRQ caused the
+/// external exception handler to be invoked by the PPE
+
+/// Load noncritical status 0 and the handler array base address. Check
+/// for interrupts pending in status register 0 while the IRQ is
+/// computed. The IRQ is expected to be stored in r3.
+ .macro hwmacro_get_ext_irq
+
+#_lwzi %r4, %r4, OCB_ONISR0
+ cntlzw %r3, %r4
+ cmpwible %r3, 31, external_irq_found #branch if irq is lt or eq to 31
+
+ ## No IRQ pending in interrupt set 0. Try set 1.
+
+#_lwzi %r4, %r4, OCB_ONISR1
+ cntlzw %r3, %r4
+ addi %r3, %r3, 32
+
+ .endm
+
+/// Redirect the .hwmacro_irq_cfg_bitmaps macro to call our cme specific implementation
+/// This is called from the ppe42_exceptions.S file.
+ .macro .hwmacro_irq_cfg_bitmaps
+ .endm
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __CME_COMMON_H__ */
diff --git a/pk/cme/cme_init.c b/pk/cme/cme_init.c
new file mode 100644
index 00000000..3a0c3910
--- /dev/null
+++ b/pk/cme/cme_init.c
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file cme_init.c
+/// \brief PK initialization for CME
+///
+/// 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"
+
+/// CME environment initial setup.
+///
+/// This is setup common to all CME HW Macro applications. This setup takes place
+/// during boot, before main() is called.
+
+void
+__hwmacro_setup(void)
+{
+ //async_initialize();
+}
diff --git a/pk/cme/pk_port.h b/pk/cme/pk_port.h
new file mode 100644
index 00000000..bf750d13
--- /dev/null
+++ b/pk/cme/pk_port.h
@@ -0,0 +1,16 @@
+#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 CME environment header for PK.
+
+#define HWMACRO_CME
+
+#include "ppe42.h"
+
+#endif /* __PK_PORT_H__ */
diff --git a/pk/cme/pkcmefiles.mk b/pk/cme/pkcmefiles.mk
new file mode 100644
index 00000000..66b0e4fb
--- /dev/null
+++ b/pk/cme/pkcmefiles.mk
@@ -0,0 +1,33 @@
+# @file pkcmefiles.mk
+#
+# @brief mk for including cme object files
+#
+# @page ChangeLogs Change Logs
+# @section pkcmefiles.mk
+# @verbatim
+#
+#
+# Change Log ******************************************************************
+# Flag Defect/Feature User Date Description
+# ------ -------------- ---------- ------------ -----------
+#
+# @endverbatim
+#
+##########################################################################
+# Object Files
+##########################################################################
+
+CME-C-SOURCES = cme_init.c
+CME-S-SOURCES =
+
+CME-TIMER-C-SOURCES =
+CME-TIMER-S-SOURCES =
+
+CME-THREAD-C-SOURCES =
+CME-THREAD-S-SOURCES =
+
+CME-ASYNC-C-SOURCES =
+CME-ASYNC-S-SOURCES =
+
+CME_OBJECTS += $(CME-C-SOURCES:.c=.o) $(CME-S-SOURCES:.S=.o)
+
diff --git a/pk/gpe/Makefile b/pk/gpe/Makefile
new file mode 100644
index 00000000..324107dc
--- /dev/null
+++ b/pk/gpe/Makefile
@@ -0,0 +1,50 @@
+# This Makefile compiles all of the PK code required for the GPE 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 pkgpefiles.mk
+
+ifeq "$(PK_TIMER_SUPPORT)" "1"
+GPE_OBJECTS += ${GPE-TIMER-C-SOURCES:.c=.o} ${GPE-TIMER-S-SOURCES:.S=.o}
+endif
+
+ifeq "$(PK_THREAD_SUPPORT)" "1"
+GPE_OBJECTS += ${GPE-THREAD-C-SOURCES:.c=.o} ${GPE-THREAD-S-SOURCES:.S=.o}
+endif
+
+ifeq "$(GPE_ASYNC_SUPPORT)" "1"
+GPE_OBJECTS += ${GPE-ASYNC-C-SOURCES:.c=.o} ${GPE-ASYNC-S-SOURCES:.S=.o}
+endif
+
+OBJS := $(addprefix $(OBJDIR)/, $(GPE_OBJECTS))
+
+libpk.a: kernel ppe42 trace gpe
+ $(AR) crs $(OBJDIR)/libpk.a $(OBJDIR)/*.o
+
+.PHONY: clean gpe kernel ppe42 trace
+gpe: $(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/pk/gpe/gpe.h b/pk/gpe/gpe.h
new file mode 100644
index 00000000..3b8e634e
--- /dev/null
+++ b/pk/gpe/gpe.h
@@ -0,0 +1,35 @@
+#ifndef __GPE_H__
+#define __GPE_H__
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pgp.h
+/// \brief The GPE environment for PK.
+
+// This is a 'circular' reference in PK, but included here to simplify PGAS
+// programming.
+
+#ifndef HWMACRO_GPE
+#define HWMACRO_GPE
+#include "ppe42.h"
+#endif
+
+#include "ocb_register_addresses.h"
+#include "gpe_common.h"
+
+/*
+#include "pcbs_register_addresses.h"
+#include "pcbs_firmware_registers.h"
+
+#include "tod_register_addresses.h"
+#include "tod_firmware_registers.h"
+
+#include "plb_arbiter_register_addresses.h"
+#include "plb_arbiter_firmware_registers.h"
+
+*/
+
+#endif /* __GPE_H__ */
diff --git a/pk/gpe/gpe_common.h b/pk/gpe/gpe_common.h
new file mode 100644
index 00000000..05ec4464
--- /dev/null
+++ b/pk/gpe/gpe_common.h
@@ -0,0 +1,62 @@
+#ifndef __GPE_COMMON_H__
+#define __GPE_COMMON_H__
+
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_common.h
+/// \brief Common header for GPE
+///
+/// This header is maintained as part of the PK port for GPE, but needs to be
+/// physically present in the PMX area to allow dropping PMX code as a whole
+/// to other teams.
+
+// -*- WARNING: This file is maintained as part of PK. Do not edit in -*-
+// -*- the PMX area as your edits will be lost. -*-
+
+#ifndef __ASSEMBLER__
+#include <stdint.h>
+#endif
+
+#include "occhw_common.h"
+
+/// Each GPE instance has it's own interrupt status register these macros
+/// are added for convenience in accessing the correct register
+#define GPE_GISR0(instance_id) (OCB_G0ISR0 + (instance_id * 8))
+#define GPE_GISR1(instance_id) (OCB_G0ISR1 + (instance_id * 8))
+
+#ifdef __ASSEMBLER__
+/// This macro contains GPE specific code for determining what IRQ caused the
+/// external exception handler to be invoked by the PPE
+
+/// Check for interrupts pending in status register 0 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).
+ .macro hwmacro_get_ext_irq
+
+ _lwzi %r3, %r3, GPE_GISR0(APPCFG_OCC_INSTANCE_ID)
+ cntlzw %r4, %r3
+ cmpwible %r4, 31, external_irq_found #branch if irq is lt or eq to 31
+
+ ## No IRQ pending in interrupt set 0. Try set 1.
+ ## Note: irq # will be 64 (EXTERNAL_IRQS) if no bits were set in either register
+
+ _lwzi %r3, %r3, GPE_GISR1(APPCFG_OCC_INSTANCE_ID)
+ cntlzw %r4, %r3
+ addi %r4, %r4, 32
+
+ .endm
+
+
+/// Redirect the .hwmacro_irq_cfg_bitmaps macro to call our macro that is common for both
+/// GPE's and the 405 inside the OCC complex. This is called from the ppe42_exceptions.S
+/// file.
+ .macro .hwmacro_irq_cfg_bitmaps
+ .occhw_irq_cfg_bitmaps
+ .endm
+#endif /* __ASSEMBLER__ */
+
+#endif /* __GPE_COMMON_H__ */
diff --git a/pk/gpe/gpe_init.c b/pk/gpe/gpe_init.c
new file mode 100644
index 00000000..3801a2bd
--- /dev/null
+++ b/pk/gpe/gpe_init.c
@@ -0,0 +1,123 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_init.c
+/// \brief PK initialization for GPE
+///
+/// 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"
+#include "ocb_register_addresses.h"
+
+/// GPE environment initial setup.
+///
+/// This is setup common to all GPE HW Macro applications. This setup takes place
+/// during boot, before main() is called.
+
+void
+__hwmacro_setup(void)
+{
+ uint64_t oirrA;
+ uint64_t oirrB;
+ uint64_t oirrC;
+ uint64_t owned_actual;
+ uint64_t reverse_polarity;
+
+ //verify that this code is running on the correct GPE instance (one time check)
+ if((mfspr(SPRN_PIR) & PIR_PPE_INSTANCE_MASK) != APPCFG_OCC_INSTANCE_ID)
+ {
+ //APPCFG_OCC_INSTANCE_ID does not match actual instance ID!
+ PK_PANIC(OCCHW_INSTANCE_MISMATCH);
+ }
+
+#if (APPCFG_OCC_INSTANCE_ID == OCCHW_IRQ_ROUTE_OWNER)
+ //If this instance is the owner of the interrupt routting registers
+ //then write the routing registers for all OCC interrupts.
+ //This instance must be the first instance to run within the OCC
+ //This will be done while all external interrupts are masked.
+ PKTRACE("Initializing External Interrupt Routing Registers");
+ out32(OCB_OIMR0_OR, 0xffffffff);
+ out32(OCB_OIMR1_OR, 0xffffffff);
+ out32(OCB_OIRR0A, (uint32_t)(g_ext_irqs_routeA >> 32));
+ out32(OCB_OIRR1A, (uint32_t)g_ext_irqs_routeA);
+ out32(OCB_OIRR0B, (uint32_t)(g_ext_irqs_routeB >> 32));
+ out32(OCB_OIRR1B, (uint32_t)g_ext_irqs_routeB);
+ out32(OCB_OIRR0C, (uint32_t)(g_ext_irqs_routeC >> 32));
+ out32(OCB_OIRR1C, (uint32_t)g_ext_irqs_routeC);
+#endif
+
+ //Determine from the routing registers which irqs are owned by this instance
+ //NOTE: If a bit is not set in the routeA register, it is not owned by a GPE
+
+ oirrA = ((uint64_t)in32(OCB_OIRR0A)) << 32;
+ oirrA |= in32(OCB_OIRR1A);
+ oirrB = ((uint64_t)in32(OCB_OIRR0B)) << 32;
+ oirrB |= in32(OCB_OIRR1B);
+ oirrC = ((uint64_t)in32(OCB_OIRR0C)) << 32;
+ oirrC |= in32(OCB_OIRR1C);
+
+ //All interrupts routed to a GPE will have a bit set in routeA
+ owned_actual = oirrA;
+
+ //wittle it down by bits in the routeB register
+#if APPCFG_OCC_INSTANCE_ID & 0x2
+ owned_actual &= oirrB;
+#else
+ owned_actual &= ~oirrB;
+#endif
+
+ //wittle it down further by bits in the routeC register
+#if APPCFG_OCC_INSTANCE_ID & 0x1
+ owned_actual &= oirrC;
+#else
+ owned_actual &= ~oirrC;
+#endif
+
+ //Panic if we don't own the irqs we were expecting
+ //NOTE: we don't panic if we are given more IRQ's than expected
+ if((owned_actual & g_ext_irqs_owned) != g_ext_irqs_owned)
+ {
+ //IRQ's were not routed to us correctly.
+ PK_PANIC(OCC_IRQ_ROUTING_ERROR);
+ }
+
+ //Mask all external interrupts owned by this instance
+ //(even the ones given to us that we weren't expecting)
+ out32(OCB_OIMR0_OR, (uint32_t)(owned_actual >> 32));
+ out32(OCB_OIMR1_OR, (uint32_t)owned_actual);
+
+ //Set the interrupt type for all interrupts owned by this instance
+ out32(OCB_OITR0_CLR, (uint32_t)(g_ext_irqs_owned >> 32));
+ out32(OCB_OITR1_CLR, (uint32_t)g_ext_irqs_owned);
+ out32(OCB_OITR0_OR, (uint32_t)(g_ext_irqs_type >> 32));
+ out32(OCB_OITR1_OR, (uint32_t)g_ext_irqs_type);
+
+ //Set the interrupt polarity for all interrupts owned by this instance
+ out32(OCB_OIEPR0_CLR, (uint32_t)(g_ext_irqs_owned >> 32));
+ out32(OCB_OIEPR1_CLR, (uint32_t)g_ext_irqs_owned);
+ out32(OCB_OIEPR0_OR, (uint32_t)(g_ext_irqs_polarity >> 32));
+ out32(OCB_OIEPR1_OR, (uint32_t)g_ext_irqs_polarity);
+
+ //clear the status of all external interrupts owned by this instance
+ out32(OCB_OISR0_CLR, ((uint32_t)(g_ext_irqs_owned >> 32)));
+ out32(OCB_OISR1_CLR, ((uint32_t)g_ext_irqs_owned));
+
+ //set the status for interrupts that have reverse polarity
+ reverse_polarity = ~g_ext_irqs_polarity & g_ext_irqs_owned;
+ out32(OCB_OISR0_OR, ((uint32_t)(reverse_polarity >> 32)));
+ out32(OCB_OISR1_OR, ((uint32_t)reverse_polarity));
+
+ //Unmask the interrupts owned by this instance that are to be enabled by default
+ out32(OCB_OIMR0_CLR, (uint32_t)(g_ext_irqs_enable >> 32));
+ out32(OCB_OIMR1_CLR, (uint32_t)g_ext_irqs_enable);
+
+ //Wait for the last out32 operation to complete
+ sync();
+
+ //async_initialize();
+}
diff --git a/pk/gpe/gpe_irq.h b/pk/gpe/gpe_irq.h
new file mode 100644
index 00000000..3b018916
--- /dev/null
+++ b/pk/gpe/gpe_irq.h
@@ -0,0 +1,247 @@
+#ifndef __GPE_IRQ_H__
+#define __GPE_IRQ_H__
+
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file occhw_irq.h
+/// \brief GPE-OCCHW Interrupt handling for PK
+///
+/// The OCCHW interrupt controller supports a maximum of 64 interrupts, split
+/// into 2 x 32-bit non-cascaded interrupt controllers with simple OR
+/// combining of the interrupt signals.
+///
+/// The OCB interrupt controller allows interrupt status to be set directly by
+/// software, as well as providing a mode that causes an enabled pending
+/// interrupt to trigger an Unconditional Debug Event. The OCB interrupt
+/// controller contains a 'mask' register, unlike other 405 interrupt
+/// controllers that have an 'enable' register. The OCCHW 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 "occhw_common.h"
+#include "ocb_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)
+{
+ out32(OCCHW_OIMR_CLR(irq), OCCHW_IRQ_MASK32(irq));
+}
+
+
+/// Disable an interrupt by setting the mask bit.
+
+UNLESS__PPE42_IRQ_CORE_C__(extern)
+inline void
+pk_irq_disable(PkIrqId irq)
+{
+ out32(OCCHW_OIMR_OR(irq), OCCHW_IRQ_MASK32(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)
+{
+ out32(OCCHW_OISR_CLR(irq), OCCHW_IRQ_MASK32(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 (in32(OCCHW_OISR(irq)) & OCCHW_IRQ_MASK32(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) {
+ out32(OCCHW_OISR_OR(irq), OCCHW_IRQ_MASK32(irq));
+ } else {
+ out32(OCCHW_OISR_CLR(irq), OCCHW_IRQ_MASK32(irq));
+ }
+}
+
+
+#endif /* __ASSEMBLER__ */
+
+/// \page occhw_irq_macros OCCHW IRQ API Assembler Macros
+///
+/// These macros encapsulate the PK API for the OCCHW interrupt
+/// controller. These macros require 2 scratch registers in addition to the \c
+/// irq parameter register passed into the handler from PK interrupt
+/// dispatch. These macros also modify CR0.
+///
+/// \arg \c rirq A register that holds the \c irq parameter passed to
+/// the handler from PK interrupt dispatch. This register is not
+/// modified.
+/// \arg \c rmask A scratch register - At the end of macro execution this
+/// register contains the 32-bit mask form of the irq.
+///
+/// \arg \c raddr A scratch register - At the end of macro execution this
+/// register holds the address of the interrupt
+/// controller facility that implements the action.
+///
+/// \arg \c imm An immediate (0/non-0) value for certain macros.
+///
+/// Forms:
+///
+/// \b _pk_irq_enable \a rirq, \a rmask, \a raddr - Enable an \c irq. \n
+/// \b _pk_irq_disable \a rirq, \a rmask, \a raddr - Disable an \c irq. \n
+/// \b _pk_irq_status_clear \a rirq, \a rmask, \a raddr - Clear \c irq
+/// interrupt status. \n
+/// \b _pk_irq_status_set \a rirq, \a rmask, \a raddr, \a imm - Set \c irq status
+/// with an immediate (0/non-0) value. \n
+///
+/// \todo Once the logic design is locked down, revisit whether these macros
+/// (and C-code versions) can be implemented without branching. This could be
+/// done in theory by converting bit 26 into the byte offset between addresses
+/// in interupt controller 0 and interrupt controller 1 - assuming the
+/// distances are all the same power-of-two.
+///
+/// \cond
+
+// IRQ numbers are in the range 0..63. IRQs are converted to the 32-bit
+// residue used to compute the mask. CR0 is set as a test of IRQ > 32 - the
+// register \c raddr is used as scratch for these computations. Hopefully the
+// local labels 888 and 999 are unique enough.
+
+// Register names must be compared as strings - e.g., %r0 is not
+// a symbol, it is converted to "0" by the assembler.
+
+#ifdef __ASSEMBLER__
+
+ .macro .two_unique, ra, rb
+ .ifnc \ra, \rb
+ .exitm
+ .endif
+ .error "Both register arguments must be unique"
+ .endm
+
+
+ .macro .three_unique, ra, rb, rc
+ .ifnc \ra, \rb
+ .ifnc \rb, \rc
+ .ifnc \ra, \rc
+ .exitm
+ .endif
+ .endif
+ .endif
+ .error "All three register arguments must be unique"
+ .endm
+
+
+ .macro _occhw_irq_or_mask, rirq:req, rmask:req
+ .two_unique \rirq, \rmask
+ lis \rmask, 0x8000
+ srw \rmask, \rmask, \rirq
+ .endm
+
+ .macro _occhw_irq_clr_mask, rirq:req, rmask:req
+ .two_unique \rirq, \rmask
+ _occhw_irq_or_mask \rirq, \rmask
+ .endm
+
+
+ .macro _pk_irq_enable, rirq:req, rmask:req, raddr:req
+ .three_unique \rirq, \rmask, \raddr
+
+ andi. \raddr, \rirq, 0x20
+ clrlwi \raddr, \rirq, 27
+ _occhw_irq_clr_mask \raddr, \rmask
+ bne- 888f
+ _stwi \rmask, \raddr, OCB_OIMR0_CLR
+ b 999f
+888:
+ _stwi \rmask, \raddr, OCB_OIMR1_CLR
+999:
+ eieio
+ .endm
+
+
+ .macro _pk_irq_disable, rirq:req, rmask:req, raddr:req
+ .three_unique \rirq, \rmask, \raddr
+
+ andi. \raddr, \rirq, 0x20
+ clrlwi \raddr, \rirq, 27
+ _occhw_irq_or_mask \raddr, \rmask
+ bne- 888f
+ _stwi \rmask, \raddr, OCB_OIMR0_OR
+ b 999f
+888:
+ _stwi \rmask, \raddr, OCB_OIMR1_OR
+999:
+ eieio
+ .endm
+
+
+ .macro _pk_irq_status_clear, rirq:req, rmask:req, raddr:req
+ .three_unique \rirq, \rmask, \raddr
+
+ andi. \raddr, \rirq, 0x20
+ clrlwi \raddr, \rirq, 27
+ _occhw_irq_clr_mask \raddr, \rmask
+ bne- 888f
+ _stwi \rmask, \raddr, OCB_OISR0_CLR
+ b 999f
+888:
+ _stwi \rmask, \raddr, OCB_OISR1_CLR
+999:
+ eieio
+ .endm
+
+
+ .macro _pk_irq_status_set, rirq:req, rmask:req, raddr:req, imm:req
+ .three_unique \rirq, \rmask, \raddr
+
+ andi. \raddr, \rirq, 0x20
+ clrlwi \raddr, \rirq, 27
+
+ .if \imm
+ _occhw_irq_or_mask \raddr, \rmask
+ bne- 888f
+ _stwi \rmask, \raddr, OCB_OISR0_OR
+ b 999f
+888:
+ _stwi \rmask, \raddr, OCB_OISR1_OR
+
+ .else
+
+ _occhw_irq_clr_mask \raddr, \rmask
+ bne- 888f
+ _stwi \rmask, \raddr, OCB_OISR0_CLR
+ b 999f
+888:
+ _stwi \rmask, \raddr, OCB_OISR1_CLR
+ .endif
+
+999:
+ eieio
+ .endm
+
+#endif /* __ASSEMBLER__ */
+
+/// \endcond
+
+#endif /* __GPE_IRQ_H__ */
diff --git a/pk/gpe/gpe_irq_init.c b/pk/gpe/gpe_irq_init.c
new file mode 100644
index 00000000..078d2fa7
--- /dev/null
+++ b/pk/gpe/gpe_irq_init.c
@@ -0,0 +1,108 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2015
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file gpe_irq_init.c
+/// \brief OCCHW IRQ initialization code for PK
+///
+/// The entry points in this file are initialization rotines 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(!OCCHW_IRQ_VALID(irq) ||
+ !OCCHW_IRQ_OWNED(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) {
+ out32(OCCHW_OIEPR_OR(irq), OCCHW_IRQ_MASK32(irq));
+ } else {
+ out32(OCCHW_OIEPR_CLR(irq), OCCHW_IRQ_MASK32(irq));
+ }
+
+ if (trigger == PK_IRQ_TRIGGER_EDGE_SENSITIVE) {
+ out32(OCCHW_OITR_OR(irq), OCCHW_IRQ_MASK32(irq));
+ } else {
+ out32(OCCHW_OITR_CLR(irq), OCCHW_IRQ_MASK32(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(!OCCHW_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/pk/gpe/pk_port.h b/pk/gpe/pk_port.h
new file mode 100644
index 00000000..887c123e
--- /dev/null
+++ b/pk/gpe/pk_port.h
@@ -0,0 +1,16 @@
+#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 GPE environment header for PK.
+
+#define HWMACRO_GPE
+
+#include "ppe42.h"
+
+#endif /* __PK_PORT_H__ */
diff --git a/pk/gpe/pkgpefiles.mk b/pk/gpe/pkgpefiles.mk
new file mode 100644
index 00000000..8c33aa86
--- /dev/null
+++ b/pk/gpe/pkgpefiles.mk
@@ -0,0 +1,33 @@
+# @file pkgpefiles.mk
+#
+# @brief mk for including gpe object files
+#
+# @page ChangeLogs Change Logs
+# @section pkgpefiles.mk
+# @verbatim
+#
+#
+# Change Log ******************************************************************
+# Flag Defect/Feature User Date Description
+# ------ -------------- ---------- ------------ -----------
+#
+# @endverbatim
+#
+##########################################################################
+# Object Files
+##########################################################################
+
+GPE-C-SOURCES = gpe_init.c gpe_irq_init.c
+GPE-S-SOURCES =
+
+GPE-TIMER-C-SOURCES =
+GPE-TIMER-S-SOURCES =
+
+GPE-THREAD-C-SOURCES =
+GPE-THREAD-S-SOURCES =
+
+GPE-ASYNC-C-SOURCES =
+GPE-ASYNC-S-SOURCES =
+
+GPE_OBJECTS += $(GPE-C-SOURCES:.c=.o) $(GPE-S-SOURCES:.S=.o)
+
diff --git a/pk/kernel/Makefile b/pk/kernel/Makefile
new file mode 100644
index 00000000..3fad153e
--- /dev/null
+++ b/pk/kernel/Makefile
@@ -0,0 +1,26 @@
+# This Makefile is designed to be invoked with the -I argument set to
+# the location of the "pk.mk" for the build
+
+include img_defs.mk
+include pkkernelfiles.mk
+
+ifeq "$(PK_TIMER_SUPPORT)" "1"
+PK_OBJECTS += ${PK-TIMER-C-SOURCES:.c=.o}
+endif
+
+ifeq "$(PK_THREAD_SUPPORT)" "1"
+PK_OBJECTS += ${PK-THREAD-C-SOURCES:.c=.o}
+endif
+
+OBJS := $(addprefix $(OBJDIR)/, $(PK_OBJECTS))
+
+all: $(OBJS)
+
+$(OBJS) $(OBJS:.o=.d): | $(OBJDIR)
+
+$(OBJDIR):
+ mkdir -p $(OBJDIR)
+
+ifneq ($(MAKECMDGOALS),clean)
+include $(OBJS:.o=.d)
+endif
diff --git a/pk/kernel/pk.h b/pk/kernel/pk.h
new file mode 100644
index 00000000..2efcdf11
--- /dev/null
+++ b/pk/kernel/pk.h
@@ -0,0 +1,125 @@
+#ifndef __PK_H__
+#define __PK_H__
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk.h
+/// \brief The combined header of the PK kernel.
+///
+/// This header will be included in any C or assembler source file that
+/// requires any of the PK API. All headers defined by PK and co-compiled
+/// code should be protected such that they can be included without error into
+/// assembly.
+
+#ifndef __ASSEMBLER__
+#include <stdint.h>
+#include <stddef.h>
+#endif /* __ASSEMBLER__ */
+
+#ifndef __PK__
+#define __PK__ 1
+#endif
+
+/// The application environment specifies whether or not it will provide an
+/// application configuration file, which must be named "pk_app_cfg.h".
+
+#ifndef USE_PK_APP_CFG_H
+#define USE_PK_APP_CFG_H 0
+#endif
+
+#if USE_PK_APP_CFG_H
+#include "pk_app_cfg.h"
+#endif
+
+#include "pk_macros.h"
+#include "pk_api.h"
+#include "pk_port.h"
+#include "pk_kernel.h"
+//#include "pk_io.h"
+
+#ifndef __ASSEMBLER__
+
+#define MIN(X, Y) \
+ ({ \
+ typeof (X) __x = (X); \
+ typeof (Y) __y = (Y); \
+ (__x < __y) ? __x : __y; })
+
+#define MAX(X, Y) \
+ ({ \
+ typeof (X) __x = (X); \
+ typeof (Y) __y = (Y); \
+ (__x > __y) ? __x : __y; \
+ })
+
+/// \todo These don't require 32/64 bit versions, can always promote 32->64.
+
+#define FLOOR_LOG2_32(x) (32 - 1 - cntlz32(x))
+#define FLOOR_LOG2_64(x) (64 - 1 - cntlz64(x))
+
+#define CEILING_LOG2(x) \
+ ({ \
+ uint64_t __x = (uint64_t)(x); \
+ int __y; \
+ __y = FLOOR_LOG2_64(__x); \
+ if ((__x & (__x - 1)) != 0) { \
+ __y++; \
+ } \
+ __y;})
+
+
+#define POW2_32(x) ((uint32_t)1 << (x))
+#define POW2_64(x) ((uint64_t)1 << (x))
+
+/// Cast a pointer to another type
+///
+/// This macro is necessary when casting a pointer to a longer integer type.
+/// The pointer is first cast to the equivalent integer type 'unsigned long',
+/// then cast to the final type. You can also use this to cast integers longer
+/// than pointers back to pointers.
+
+#define CAST_POINTER(t, p) ((t)((unsigned long)(p)))
+
+
+/// Create an alignment attribute.
+#define ALIGNED_ATTRIBUTE(alignment) __attribute__ ((aligned (alignment)))
+
+/// Create a specific-section attribute
+///
+/// Note that the section \a s here must be a string. Also note that data
+/// specified to reside in specific sections must always be
+/// initialized. Otherwise it confuses the linker which wants to put
+/// uninitialized data into .bss sections.
+///
+/// \code
+///
+/// int foo SECTION_ATTRIBUTE(".noncacheable") = 0;
+/// int bar[10] SECTION_ATTRIBUTE(".noncacheable") = {0};
+///
+/// \endcode
+#define SECTION_ATTRIBUTE(s) __attribute__ ((section (s)))
+
+/// Create a 'used' attribute
+///
+/// This is required for example to avoid "function unused" warnings when a
+/// function is declared static but only referenced by inline assembler:
+///
+/// \code
+///
+/// static USED_ATTRIBUTE void
+/// _checkstop(void* arg, PkIrqId irq, int priority)
+/// {
+/// PK_PANIC(VALIDATION_CHECKSTOP);
+/// }
+///
+/// PK_IRQ_FAST2FULL(_validationCheckstopHandler, _checkstop);
+///
+/// \endcode
+#define USED_ATTRIBUTE __attribute__ ((used))
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PK_H__ */
diff --git a/pk/kernel/pk_api.h b/pk/kernel/pk_api.h
new file mode 100644
index 00000000..b0e7419c
--- /dev/null
+++ b/pk/kernel/pk_api.h
@@ -0,0 +1,1010 @@
+#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
+
+
+/// \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
+/// noncritical 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
+/// noncritical interrupt stack, with noncritical 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
+
+/// 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_timebase_frequency_hz * (PkInterval)(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_timebase_frequency_khz * (PkInterval)(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_timebase_frequency_mhz * (PkInterval)(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_timebase_frequency_mhz * (PkInterval)(n)) / 1000))
+#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_TRACE_THREAD_SLEEP(priority)
+#define PK_TRACE_THREAD_WAKEUP(priority)
+#define PK_TRACE_THREAD_SEMAPHORE_PEND(priority)
+#define PK_TRACE_THREAD_SEMAPHORE_POST(priority)
+#define PK_TRACE_THREAD_SEMAPHORE_TIMEOUT(priority)
+#define PK_TRACE_THREAD_SUSPENDED(priority)
+#define PK_TRACE_THREAD_DELETED(priority)
+#define PK_TRACE_THREAD_COMPLETED(priority)
+#define PK_TRACE_THREAD_MAPPED_RUNNABLE(priority)
+#define PK_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority)
+#define PK_TRACE_THREAD_MAPPED_SLEEPING(priority)
+
+#else
+
+#define PK_TRACE_THREAD_SLEEP(priority) PKTRACE("THREAD_SLEEP(%d)", priority)
+#define PK_TRACE_THREAD_WAKEUP(priority) PKTRACE("THREAD_WAKEUP(%d)", priority)
+#define PK_TRACE_THREAD_SEMAPHORE_PEND(priority) PKTRACE("SEMAPHORE_PEND(%d)", priority)
+#define PK_TRACE_THREAD_SEMAPHORE_POST(priority) PKTRACE("SEMAPHORE_POST(%d)", priority)
+#define PK_TRACE_THREAD_SEMAPHORE_TIMEOUT(priority) PKTRACE("SEMAPHORE_TIMEOUT(%d)", priority)
+#define PK_TRACE_THREAD_SUSPENDED(priority) PKTRACE("THREAD_SUSPENDED(%d)", priority)
+#define PK_TRACE_THREAD_DELETED(priority) PKTRACE("THREAD_DELETED(%d)", priority)
+#define PK_TRACE_THREAD_COMPLETED(priority) PKTRACE("THREAD_COMPLETED(%d)", priority)
+#define PK_TRACE_THREAD_MAPPED_RUNNABLE(priority) PKTRACE("THREAD_MAPPED_RUNNABLE(%d)", priority)
+#define PK_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority) PKTRACE("THREAD_MAPPED_SEMAPHORE_PEND(%d)", priority)
+#define PK_TRACE_THREAD_MAPPED_SLEEPING(priority) PKTRACE("THREAD_MAPPED_SLEEPING(%d)", priority)
+#endif /* PK_KERNEL_TRACE_ENABLE */
+
+
+/// Add a string to the trace buffer with an optional register holding a 16bit value
+/// WARNING: This calls a c function which may clobber any of the volatile registers
+#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT)
+#define PK_TRACE_ASM16(...) TRACE_ASM_HELPER16(VARG_COUNT(__VA_ARGS__), __VA_ARGS__)
+#else
+#define PK_TRACE_ASM16(...)
+#endif /* PK_TRACE_SUPPORT */
+
+/// The following macros are helper macros for tracing. They should not be called
+/// directly.
+#define VARG_COUNT_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, N, ...) N
+#define VARG_COUNT(...) VARG_COUNT_HELPER(, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
+
+#ifdef __ASSEMBLER__
+#define TRACE_ASM_HELPER16_CALL(count, ...) TINY_TRACE_ASM ## count (__VA_ARGS__)
+#define TRACE_ASM_HELPER16(count, ...) TRACE_ASM_HELPER16_CALL(count, __VA_ARGS__)
+
+#define TINY_TRACE_ASM0() .error "format string required"
+#define TINY_TRACE_ASM1(str) \
+ .tiny_trace_asm1 trace_adal_hash(str, PK_TRACE_HASH_PREFIX)
+#define TINY_TRACE_ASM2(str, reg) \
+ .tiny_trace_asm2 trace_adal_hash(str, PK_TRACE_HASH_PREFIX), reg
+#define TINY_TRACE_ASM3() .error "too many parameters"
+#define TINY_TRACE_ASM4() .error "too many parameters"
+#define TINY_TRACE_ASM5() .error "too many parameters"
+#define TINY_TRACE_ASM6() .error "too many parameters"
+#define TINY_TRACE_ASM7() .error "too many parameters"
+
+//TODO: add support for tracing more than 1 parameter and binary data in assembly
+
+ .global pk_trace_tiny
+
+ .macro .tiny_trace_asm1 hash16
+ lis %r3, \hash16
+ bl pk_trace_tiny
+ .endm
+
+ .macro .tiny_trace_asm2 hash16, parm16
+ clrlwi %r3, \parm16, 16
+ oris %r3, %r3, \hash16
+ bl pk_trace_tiny
+ .endm
+
+#endif /*__ASSEMBLER__*/
+
+
+
+#ifndef __ASSEMBLER__
+
+#include <stddef.h>
+#include <stdint.h>
+
+/// The timebase frequency in Hz; A parameter to pk_initialize()
+extern uint32_t __pk_timebase_frequency_hz;
+
+/// 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 an 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_adal_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_adal_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 period
+ ///
+ /// If not 0, then this is a periodic timer and it will be automatically
+ /// rescheduled in absolute time from the previous timeout.
+ PkInterval period;
+
+ /// 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;
+
+ /// Options for timer processing; See \ref pk_timer_options
+ uint8_t options;
+
+} PkTimer;
+
+/// \defgroup pk_timer_options PK Timer Options
+/// @{
+
+/// Allow interrupt preemption during the callback
+///
+/// This is the normal mode for PkTimer objects scheduled by PK kernal
+/// mechanisms. The timer callbacks effectively run as if inside a
+/// highest-priority thread, allowing other interrupts to preempt them.
+#define PK_TIMER_CALLBACK_PREEMPTIBLE 0x1
+
+/// @}
+
+
+// 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;
+
+
+// Initialization APIs
+
+int
+pk_initialize(PkAddress noncritical_stack,
+ size_t noncritical_stack_size,
+ PkTimebase initial_timebase,
+ uint32_t timebase_frequency_hz);
+
+
+// Timebase APIs
+
+PkTimebase
+pk_timebase_get(void);
+
+void
+pk_timebase_set(PkTimebase timebase);
+
+// Interrupt preemption APIs
+
+int
+pk_interrupt_preemption_enable(void);
+
+int
+pk_interrupt_preemption_disable(void);
+
+// Timer APIs
+
+int
+pk_timer_create(PkTimer *timer,
+ PkTimerCallback callback,
+ void *arg);
+
+int
+pk_timer_create_nonpreemptible(PkTimer *timer,
+ PkTimerCallback callback,
+ void *arg);
+
+int
+pk_timer_schedule_absolute(PkTimer *timer,
+ PkTimebase time,
+ PkInterval period);
+
+int
+pk_timer_schedule(PkTimer *timer,
+ PkInterval interval,
+ PkInterval period);
+
+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_absolute(PkTimebase time);
+
+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;
+}
+
+
+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);
+
+/// Cast a pointer to another type, in a way that won't cause warnings
+
+#define PK_CAST_POINTER(t, p) ((t)((PkAddress)(p)))
+
+// Static Assert Macro for Compile time assertions.
+// - This macro can be used both inside and outside of a function.
+// - A value of false will cause the ASSERT to produce this error
+// - This will show up on a compile fail as:
+// <file>:<line> error: size of array '_static_assert' is negative
+// - It would be trivial to use the macro to paste a more descriptive
+// array name for each assert, but we will leave it like this for now.
+#define PK_STATIC_ASSERT(cond) extern uint8_t _static_assert[(cond) ? 1 : -1] __attribute__ ((unused))
+
+/// \page pk_errors PK API and Kernel Error Handling
+///
+/// Error checking in the PK API consumes a significant amount of code space.
+/// Approximately 20% of the object code in the PPC405 port is devoted to
+/// error checking. Presumably a like amount of time overhead is also added to
+/// PK API calls by this checking.
+///
+/// API error checking can be disabled to save space and time in the kernel.
+/// API errors can also be configured to cause kernel panics, allowing
+/// applications to be coded without the overhead of error checking but still
+/// providing an escape in the event of application errors or (unlikely)
+/// hardware failures. The PK default is to check for API errors and kernel
+/// invariants, and panic should errors occur.
+///
+/// PK follows the Unix convention that a successful call of an API returns 0
+/// (PK_OK), but returns a negative code in the event of failure, or to
+/// provide further information. The error codes are all defined as manifest
+/// constants.
+///
+/// Some negative codes returned by PK APIs are not considered errors. These
+/// conditions are always checked, never cause a panic if they occur, and
+/// their interpretation is always left to the application. See the detailed
+/// documentation for each API for lists of error and non-error codes returned
+/// by the API.
+///
+/// There are three configuration options that control error handling in the
+/// PK API and kernel:
+///
+/// \c PK_ERROR_CHECK_API
+///
+/// \arg \b 0 - No PK API error checking. APIs that potentially return error
+/// codes will always return 0 (PK_OK) instead of an error code. Those
+/// APIs that return negative codes that are not errors (see Table 1.5)
+/// always return the negative non-error codes when appropriate.
+///
+/// \arg \b 1 - (Default) All PK API errors are checked. The behavior in
+/// the event of an error is defined by the configuration option
+/// PK_ERROR_PANIC.
+///
+/// \c PK_ERROR_CHECK_KERNEL
+///
+/// \arg \b 0 - No kernel invariant error checking is done.
+///
+/// \arg \b 1 - (Default) Selected kernel invariants are checked. The overhead
+/// for these checks should be minimal.
+///
+/// \c PK_ERROR_PANIC
+///
+/// \arg \b 0 - PK API calls return negative error codes in the event of
+/// errors. Note that PK kernel invariants always cause a panic if
+/// violations occur.
+///
+/// \arg \b 1 - (Default) In the event of errors PK APIs invoke PK_PANIC(code),
+/// where code is a positive error code. Kernel invariant checks always
+/// cause a panic if violations are detected.
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PK_API_H__ */
diff --git a/pk/kernel/pk_core.c b/pk/kernel/pk_core.c
new file mode 100644
index 00000000..bfa6d6be
--- /dev/null
+++ b/pk/kernel/pk_core.c
@@ -0,0 +1,79 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_core.c
+/// \brief Core routines for the PK kernel.
+///
+/// The entry points in this file are routines that are expected to be needed
+/// at runtime by all PK applications. This file also serves as a place for
+/// kernel global variables to be realized.
+
+#define __PK_CORE_C__
+
+#include "pk.h"
+
+#if !PK_TIMER_SUPPORT
+
+/// If there is no timer support, then any call of the timer interrupt handler
+/// is considered a fatal error.
+
+void
+__pk_timer_handler()
+{
+ PK_PANIC(PK_NO_TIMER_SUPPORT);
+}
+
+#endif /* PK_TIMER_SUPPORT */
+
+
+/// Initialize an PkDeque sentinel node
+///
+/// \param deque The sentinel node of the deque
+///
+/// PK has no way of knowing whether the \a deque is currently in use, so
+/// this API must only be called on unitialized or otherwise unused sentinel
+/// nodes.
+///
+/// \retval 0 success
+///
+/// \retval -PK_INVALID_DEQUE_SENTINEL The \a deque pointer was null
+
+int
+pk_deque_sentinel_create(PkDeque *deque)
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(deque == 0, PK_INVALID_DEQUE_SENTINEL);
+ }
+
+ deque->next = deque->previous = deque;
+ return 0;
+}
+
+
+/// Initialize an PkDeque element
+///
+/// \param element Typically the PkDeque object of a queable structure
+///
+/// PK has no way of knowing whether the \a element is currently in use, so
+/// this API must only be called on unitialized or otherwise unused deque
+/// elements.
+///
+/// \retval 0 success
+///
+/// \retval -PK_INVALID_DEQUE_ELEMENT The \a element pointer was null
+
+int
+pk_deque_element_create(PkDeque *element)
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(element == 0, PK_INVALID_DEQUE_ELEMENT);
+ }
+
+ element->next = 0;
+ return 0;
+}
+
+#undef __PK_CORE_C__
diff --git a/pk/kernel/pk_init.c b/pk/kernel/pk_init.c
new file mode 100644
index 00000000..9174e51d
--- /dev/null
+++ b/pk/kernel/pk_init.c
@@ -0,0 +1,162 @@
+//-----------------------------------------------------------------------------
+// *! (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;
+uint32_t __pk_timebase_frequency_khz;
+uint32_t __pk_timebase_frequency_mhz;
+
+/// Initialize PK.
+///
+/// \param noncritical_stack A stack area for noncritical interrupt handlers.
+///
+/// \param noncritical_stack_size The size (in bytes) of the stack area for
+/// noncritical interrupt handlers.
+///
+/// \param critical_stack A stack area for critical interrupt handlers.
+///
+/// \param critical_stack_size The size (in bytes) of the stack area for
+/// critical interrupt handlers.
+///
+/// \param initial_timebase The initial value of the PK timebase. If this
+/// argument is given as the special value \c PK_TIMEBASE_CONTINUE, 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 noncritical_stack,
+ size_t noncritical_stack_size,
+ PkTimebase initial_timebase,
+ uint32_t timebase_frequency_hz)
+{
+ int rc;
+
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF((noncritical_stack == 0) ||
+ (noncritical_stack_size == 0),
+ PK_INVALID_ARGUMENT_INIT);
+ }
+
+ if (initial_timebase != PK_TIMEBASE_CONTINUES) {
+ pk_timebase_set(initial_timebase);
+ }
+
+ __pk_timebase_frequency_hz = timebase_frequency_hz;
+ __pk_timebase_frequency_khz = timebase_frequency_hz / 1000;
+ __pk_timebase_frequency_mhz = timebase_frequency_hz / 1000000;
+
+ __pk_thread_machine_context_default = PK_THREAD_MACHINE_CONTEXT_DEFAULT;
+
+ rc = __pk_stack_init(&noncritical_stack, &noncritical_stack_size);
+ if (rc) {
+ return rc;
+ }
+
+ __pk_noncritical_stack = noncritical_stack;
+ __pk_noncritical_stack_size = noncritical_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;
+
+ // 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,
+ 0);
+
+ //set the trace timebase HZ
+ g_pk_trace_buf.hz = timebase_frequency_hz;
+
+ //TODO: set the ppe instance id (for CME's)
+
+#endif /* PK_TRACE_SUPPORT */
+
+#endif /* PK_TIMER_SUPPORT */
+
+#if PK_THREAD_SUPPORT
+
+ // Clear the priority map. The final entry [PK_THREADS] is for the idle
+ // thread.
+
+ int i;
+ for (i = 0; i <= PK_THREADS; i++) {
+ __pk_priority_map[i] = 0;
+ }
+
+ // Initialize the thread scheduler
+
+ __pk_thread_queue_clear(&__pk_run_queue);
+ __pk_current_thread = 0;
+ __pk_next_thread = 0;
+ __pk_delayed_switch = 0;
+
+#endif /* PK_THREAD_SUPPORT */
+
+ return PK_OK;
+}
+
+
+/// Call the application main()
+///
+/// __pk_main() is called from the bootloader. It's only purpose is to
+/// provide a place for the PK_MAIN_HOOK to be called before main() is
+/// called.
+
+void
+__pk_main(int argc, char **argv)
+{
+ PK_MAIN_HOOK;
+
+ int main(int argc, char **argv);
+ main(argc, argv);
+}
+
+
+
+
+
+
+
+
+
diff --git a/pk/kernel/pk_kernel.h b/pk/kernel/pk_kernel.h
new file mode 100644
index 00000000..7e88b828
--- /dev/null
+++ b/pk/kernel/pk_kernel.h
@@ -0,0 +1,266 @@
+#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 or
+/// non-critical interrupt context to a full-mode critical interrupt context.
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+PkAddress __pk_saved_sp_critical;
+
+/// The critical interrupt stack; constant once defined by the call of
+/// pk_initialize().
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+PkAddress __pk_critical_stack;
+
+/// This is the stack pointer saved when switching from a thread context to a
+/// full-mode non-critical interrupt context.
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+PkAddress __pk_saved_sp_noncritical;
+
+/// The non-critical interrupt stack; constant once defined by the call of
+/// pk_initialize().
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+PkAddress __pk_noncritical_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 non-critical 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 noncritical stack (bytes).
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+size_t __pk_noncritical_stack_size;
+
+/// The size of the critical stack (bytes).
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+size_t __pk_critical_stack_size;
+
+/// This table maps priorities to threads, and contains PK_THREADS + 1
+/// entries. The final entry is for the idle thread and will always be null
+/// after initizlization.
+
+UNLESS__PK_CORE_C__(extern)
+volatile
+PkThread* __pk_priority_map[PK_THREADS + 1];
+
+/// The PK time queue structure
+///
+/// This structure is defined for use by the kernel, however applications
+/// could also use this structure to define their own time queues.
+
+typedef struct {
+
+ /// A sentinel node for the time queue.
+ ///
+ /// The time queue is an PkDeque managed as a FIFO queue for queue
+ /// management purpose, although events time out in time order.
+ ///
+ /// This pointer container is defined as the first element of the
+ /// structure to allow the PkTimeQueue to be cast to an PkDeque.
+ PkDeque queue;
+
+ /// The next timeout in absolute time.
+ PkTimebase next_timeout;
+
+ /// A pointer to allow preemption of time queue processing
+ ///
+ /// If non-0, then this is the next timer in the time queue to handle, or
+ /// a pointer to the \a queue object indicating no more timers to handle.
+ ///
+ /// \a cursor != 0 implies that time queue handler is in the midst of
+ /// processing the time queue, but has enabled interrupt preemption for
+ /// processing a timer handler. This means that 1) if the timer pointed to
+ /// by \a cursor is deleted then the cursor must be assigned to the
+ /// next timer in the queue; and 2) if a new timer is scheduled then
+ /// activating the next timeout will be handled by the timer handler.
+ PkDeque* cursor;
+
+} PkTimeQueue;
+
+UNLESS__PK_CORE_C__(extern)
+PkTimeQueue __pk_time_queue;
+
+/// Return a pointer to the PkThread object of the currently running thread,
+/// or NULL (0) if PK is idle or has not been started.
+///
+/// In this API the current thread is not volatile - it will never change
+/// inside application code - thus the 'volatile' is cast away. The PK kernel
+/// does not (must not) use this API.
+
+UNLESS__PK_CORE_C__(extern)
+inline PkThread *
+pk_current(void)
+{
+ return (PkThread *)__pk_current_thread;
+}
+
+/// Schedule the next timeout in a machine-specific way.
+
+void
+__pk_schedule_hardware_timeout(PkTimebase timeout);
+
+/// The thread timeout handler. Portable.
+
+PK_TIMER_CALLBACK(__pk_thread_timeout);
+
+/// Generic stack initialization. Portable.
+
+int
+__pk_stack_init(PkAddress *stack,
+ size_t *size);
+
+/// Machine-specific thread context initialization.
+
+void
+__pk_thread_context_initialize(PkThread *thread,
+ PkThreadRoutine thread_routine,
+ void *arg);
+
+/// Machine specific resumption of __pk_next_thread at __pk_next_priority
+/// without saving the current context.
+void
+__pk_next_thread_resume(void);
+
+/// Schedule a timer in the time queue. Portable.
+void
+__pk_timer_schedule(PkTimer *timer);
+
+/// Remove a timer from the time queue. Portable.
+int
+__pk_timer_cancel(PkTimer *timer);
+
+void
+__pk_schedule(void);
+
+
+// Call the application main(). Portable.
+
+void
+__pk_main(int argc, char **argv);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PK_KERNEL_H__ */
diff --git a/pk/kernel/pk_macros.h b/pk/kernel/pk_macros.h
new file mode 100644
index 00000000..45bfbac6
--- /dev/null
+++ b/pk/kernel/pk_macros.h
@@ -0,0 +1,110 @@
+#ifndef __PK_MACROS_H__
+#define __PK_MACROS_H__
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_macros.h
+/// \brief Boilerplate macros for PK
+
+/// This macro encapsulates error handling boilerplate for code that uses the
+/// PK API-type error handling, for errors that do not occur in critical
+/// sections.
+
+#define PK_ERROR(code) \
+ do { \
+ if (PK_ERROR_PANIC) { \
+ PK_PANIC(code); \
+ } else { \
+ return -(code); \
+ } \
+ } while (0)
+
+
+/// This macro encapsulates error handling boilerplate in the PK API
+/// functions, for errors that do not occur in critical sections.
+
+#define PK_ERROR_IF(condition, code) \
+ do { \
+ if (condition) { \
+ PK_ERROR(code); \
+ } \
+ } while (0)
+
+
+/// This macro encapsulates error handling boilerplate in the PK API
+/// functions, for errors that do not occur in critical sections and always
+/// force a kernel panic, indicating a kernel or API bug.
+
+#define PK_PANIC_IF(condition, code) \
+ do { \
+ if (condition) { \
+ PK_PANIC(code); \
+ } \
+ } while (0)
+
+
+/// This macro encapsulates error handling boilerplate in the PK API
+/// functions, for errors that do not occur in critical sections.
+/// The error handling will only be enabled when PK_ERROR_CHECK_API
+/// is enabled.
+
+#define PK_ERROR_IF_CHECK_API(condition, code) \
+ do { \
+ if (PK_ERROR_CHECK_API) { \
+ PK_ERROR_IF(condition, code); \
+ } \
+ } while (0)
+
+/// This macro encapsulates error handling boilerplate in the PK API
+/// functions, for errors that occur in critical sections.
+
+#define PK_ERROR_IF_CRITICAL(condition, code, context) \
+ do { \
+ if (condition) { \
+ if (PK_ERROR_PANIC) { \
+ PK_PANIC(code); \
+ pk_critical_section_exit(context); \
+ } else { \
+ pk_critical_section_exit(context); \
+ return -(code); \
+ } \
+ } \
+ } while (0)
+
+
+/// This is a general macro for errors that require cleanup before returning
+/// the error code.
+
+#define PK_ERROR_IF_CLEANUP(condition, code, cleanup) \
+ do { \
+ if (condition) { \
+ if (PK_ERROR_PANIC) { \
+ PK_PANIC(code); \
+ cleanup; \
+ } else { \
+ cleanup; \
+ return -(code); \
+ } \
+ } \
+ } while (0)
+
+
+
+/// Some PK APIs can only be called from thread contexts - these are APIs
+/// that threads call on 'themselves'.
+
+#define PK_ERROR_UNLESS_THREAD_CONTEXT() \
+ PK_ERROR_IF(!__pk_kernel_context_thread(), \
+ PK_ILLEGAL_CONTEXT_THREAD_CONTEXT)
+
+
+/// Some PK APIs must be called from an interrupt context only.
+
+#define PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT() \
+ PK_ERROR_IF(!__pk_kernel_context_any_interrupt(), \
+ PK_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT)
+
+#endif /* __PK_MACROS_H__ */
diff --git a/pk/kernel/pk_semaphore_core.c b/pk/kernel/pk_semaphore_core.c
new file mode 100644
index 00000000..9079ff01
--- /dev/null
+++ b/pk/kernel/pk_semaphore_core.c
@@ -0,0 +1,328 @@
+//-----------------------------------------------------------------------------
+// *! (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_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \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_TRACE_THREAD_SEMAPHORE_POST(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_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \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;
+ 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_TRACE_THREAD_SEMAPHORE_PEND(priority);
+
+ if (timeout != PK_WAIT_FOREVER) {
+ timer = &(thread->timer);
+ timer->timeout = pk_timebase_get() + 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_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \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. Since the
+/// implementation of this API does not require a critical section, it is not
+/// an error to call this API from a critical interrupt context.
+///
+/// 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 a non-critical 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_full(void *arg, PkIrqId irq, int priority)
+{
+ pk_irq_disable(irq);
+ pk_irq_status_clear(irq);
+ pk_semaphore_post((PkSemaphore *)arg);
+}
+
+PK_IRQ_FAST2FULL(pk_semaphore_post_handler, pk_semaphore_post_handler_full);
+#endif
diff --git a/pk/kernel/pk_semaphore_init.c b/pk/kernel/pk_semaphore_init.c
new file mode 100644
index 00000000..bed029da
--- /dev/null
+++ b/pk/kernel/pk_semaphore_init.c
@@ -0,0 +1,82 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_semaphore_init.c
+/// \brief PK semaphore API initialization routines
+///
+/// The entry points in this file are routines that are typically used during
+/// initialization, and their code space could be deallocated and recovered if
+/// no longer needed by the application after initialization.
+
+#include "pk.h"
+
+/// Create (initialize) a semaphore
+///
+/// \param semaphore A pointer to an PkSemaphore structure to initialize
+///
+/// \param initial_count The initial count of the semaphore
+///
+/// \param max_count The maximum count allowed in the semaphore, for error
+/// checking
+///
+/// Semaphores are created (initialized) by a call of \c
+/// pk_semaphore_create(), using an application-provided instance of an \c
+/// PkSemaphore structure. This structure \e is the semaphore, so the
+/// application must never modify the structure if the semaphore is in use.
+/// PK has no way to know if an \c PkSemaphore structure provided to
+/// \c pk_semaphore_create() is safe to use as a semaphore, and will silently
+/// modify whatever memory is provided.
+///
+/// PK provides two simple overflow semantics based on the value of max_count
+/// in the call of \c pk_semaphore_create().
+///
+/// If \a max_count = 0, then posting to the semaphore first increments the
+/// internal count by 1. Overflows are ignored and will wrap the internal
+/// count through 0.
+///
+/// If \a max_count != 0, then posting to the semaphore first increments the
+/// internal count by 1, wrapping through 0 in the event of overflow. If the
+/// resulting count is greater than max_count, \c pk_semaphore_post() will
+/// return the error \c -PK_SEMAPHORE_POST_OVERFLOW to the caller.
+///
+/// In most applications it is probably best to use the \a max_count != 0
+/// semantics to trap programming errors, unless there is a specific
+/// application where overflow is expected and ignorable. As a fine point of
+/// the specification, a \a max_count of 0 is equivalent to a max_count of
+/// 0xFFFFFFFF.
+///
+/// Return values other then PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -PK_INVALID_SEMAPHORE_AT_CREATE The \a semaphore is a null (0)
+/// pointer.
+///
+/// \retval -PK_INVALID_ARGUMENT_SEMAPHORE The \a max_count is non-zero
+/// and less than the \a initial_count.
+
+int
+pk_semaphore_create(PkSemaphore *semaphore,
+ PkSemaphoreCount initial_count,
+ PkSemaphoreCount max_count)
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_CREATE);
+ PK_ERROR_IF((max_count != 0) && (initial_count > max_count),
+ PK_INVALID_ARGUMENT_SEMAPHORE);
+ }
+
+ __pk_thread_queue_clear(&(semaphore->pending_threads));
+ semaphore->count = initial_count;
+ semaphore->max_count = max_count;
+
+ return PK_OK;
+}
+
+
+
+
+
diff --git a/pk/kernel/pk_stack_init.c b/pk/kernel/pk_stack_init.c
new file mode 100644
index 00000000..c3ddc90a
--- /dev/null
+++ b/pk/kernel/pk_stack_init.c
@@ -0,0 +1,85 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_stack_init.c
+/// \brief PK stack initialization
+///
+/// The entry points in this file are initialization routines - they are never
+/// needed after PK initialization and their code space could be reclaimed by
+/// the application after initialization if required.
+///
+/// This code was split out from "pk_init.c" because it may be needed in a
+/// thread configuration if threads are being created dynamically. in an
+/// interrupt-only configuration it is not needed after \c pk_initialize().
+
+#include "pk.h"
+
+/// Initialize a stack area.
+///
+/// \param stack A pointer to the smallest legal address of the stack. The
+/// stack address is modified as the stack is aligned and initialized.
+///
+/// \param size A pointer to the size of the stack (in bytes). The size is
+/// modified as the stack is aligned and initialized. At exit this is the
+/// final usable stack area size aligned to the size of the PK_STACK_TYPE.
+///
+/// PK makes no assumptions about size or alignment of the area provided as a
+/// stack, and carefully aligns and initializes the stack. Regardless of how
+/// the stack grows, the \a stack parameter is considered to be the lowest
+/// legal address of the stack.
+
+int
+__pk_stack_init(PkAddress *stack,
+ size_t *size)
+{
+ PkAddress mask;
+ size_t excess, i, count;
+ PK_STACK_TYPE *p;
+
+ if (PK_STACK_DIRECTION < 0) {
+
+ // Stacks grow down. The initial stack pointer is set to just above
+ // the last allocated stack address. This is legal for pre-decrement
+ // stacks, otherwise the initial address is first brought into range
+ // before alignment. The stack is aligned downward, then the size is
+ // adjusted to a multiple of the stack type. Stacks are optionally
+ // prepatterned. Alignment is assumed to be a power of 2.
+
+ *stack += *size;
+
+ if (!PK_STACK_PRE_DECREMENT) {
+ *stack -= sizeof(PK_STACK_TYPE);
+ *size -= sizeof(PK_STACK_TYPE);
+ }
+
+ mask = PK_STACK_ALIGNMENT - 1;
+ excess = *stack & mask;
+ *stack -= excess;
+ *size -= excess;
+ *size = (*size / sizeof(PK_STACK_TYPE)) * sizeof(PK_STACK_TYPE);
+
+ if (PK_STACK_CHECK) {
+ p = (PK_STACK_TYPE *)(*stack);
+ count = *size / sizeof(PK_STACK_TYPE);
+ for (i = 0; i < count; i++) {
+ if (PK_STACK_PRE_DECREMENT) {
+ *(--p) = PK_STACK_PATTERN;
+ } else {
+ *(p--) = PK_STACK_PATTERN;
+ }
+ }
+ }
+
+ __pk_stack_create_initial_frame(stack, size);
+
+ } else {
+
+ PK_PANIC(PK_UNIMPLEMENTED);
+ }
+
+ return PK_OK;
+}
+
diff --git a/pk/kernel/pk_thread_core.c b/pk/kernel/pk_thread_core.c
new file mode 100644
index 00000000..539b3321
--- /dev/null
+++ b/pk/kernel/pk_thread_core.c
@@ -0,0 +1,939 @@
+//-----------------------------------------------------------------------------
+// *! (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"
+
+#define __PK_THREAD_CORE_C__
+
+
+// This routine is only used locally. Noncritical 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));
+}
+
+
+// This routine is only used locally. Noncritical interrupts must be disabled
+// at entry.
+
+static inline int
+__pk_thread_is_mapped(PkThread *thread)
+{
+ return (thread->state == PK_THREAD_STATE_MAPPED);
+}
+
+
+// This routine is only used locally. Noncritical 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);
+}
+
+
+// This routine is only used locally. Noncritical interrupts must be disabled
+// at entry.
+
+static inline PkThread*
+__pk_thread_at_priority(PkThreadPriority priority)
+{
+ return (PkThread*)__pk_priority_map[priority];
+}
+
+
+// This routine is only used locally. Noncritical 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_TRACE_THREAD_MAPPED_RUNNABLE(priority);
+ } else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) {
+ PK_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority);
+ } else {
+ PK_TRACE_THREAD_MAPPED_SLEEPING(priority);
+ }
+ }
+}
+
+
+// This routine is only used locally. Noncritical 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_TRACE_THREAD_DELETED(thread->priority);
+ } else {
+ PK_TRACE_THREAD_COMPLETED(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.
+//
+// __pk_thread_timeout() is currenly the only timer interrupt called from a
+// critical section.
+//
+// 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)
+{
+ PkThread *thread = (PkThread *)arg;
+
+ 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);
+ }
+}
+
+
+// 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 noncritical interrupt 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
+// noncritical interrupt 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_ILLEGAL_CONTEXT_THREAD The API was called
+/// from a critical interrupt context.
+///
+/// \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_ILLEGAL_CONTEXT_THREAD The API was called from a critical
+/// interrupt context.
+///
+/// \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_TRACE_THREAD_SUSPENDED(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_ILLEGAL_CONTEXT_THREAD The API was called from a critical
+/// interrupt context.
+///
+/// \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 until an absolute time
+///
+/// \param time An absolute time as measured by the PK timebase
+///
+/// Threads can use this API to sleep until an absolute time. 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 time is in the past, 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.
+
+// Note: Casting __pk_current_thread removes the 'volatile' attribute.
+
+int
+pk_sleep_absolute(PkTimebase time)
+{
+ PkMachineContext ctx;
+ PkThread *current;
+
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_UNLESS_THREAD_CONTEXT();
+ }
+
+ 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_TRACE_THREAD_SLEEP(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;
+}
+
+/// 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)
+{
+ return pk_sleep_absolute(pk_timebase_get() + interval);
+}
+
+
+/// 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. Since the
+/// implementation of this API does not enforce a critical section, it is not
+/// an error to call this API from a critical interrupt context.
+///
+/// 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_ILLEGAL_CONTEXT_THREAD the API was called from a critical
+/// interrupt context.
+///
+/// \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. Since the
+/// implementation of this API does not require a critical section, it is not
+/// an error to call this API from a critical interrupt context.
+///
+/// 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_ILLEGAL_CONTEXT_THREAD the API was called from a critical
+/// interrupt context.
+///
+/// \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;
+}
+
+
+#undef __PK_THREAD_CORE_C__
diff --git a/pk/kernel/pk_thread_init.c b/pk/kernel/pk_thread_init.c
new file mode 100644
index 00000000..686f3512
--- /dev/null
+++ b/pk/kernel/pk_thread_init.c
@@ -0,0 +1,137 @@
+//-----------------------------------------------------------------------------
+// *! (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_ILLEGAL_CONTEXT The API was called from a critical interrupt
+/// context.
+///
+/// \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_nonpreemptible(&(thread->timer),
+ __pk_thread_timeout,
+ (void *)thread);
+
+ __pk_thread_context_initialize(thread, thread_routine, arg);
+
+ return rc;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pk/kernel/pk_timer_core.c b/pk/kernel/pk_timer_core.c
new file mode 100644
index 00000000..abc83bad
--- /dev/null
+++ b/pk/kernel/pk_timer_core.c
@@ -0,0 +1,444 @@
+//-----------------------------------------------------------------------------
+// *! (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 noncritical 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 is rescheduled if necessary (periodic events)
+/// and its callback is 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"
+
+// This routine is only used in this file, and will always be called in
+// 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.
+// Noncritical 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.
+// Noncritical 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).
+//
+// Noncritical interrupts are (must be) disabled at entry, and this invariant
+// is checked. 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 callbacks with interrupts disabled either during or after the
+// call for timed out events, and also after every check for events that have
+// not timed out.
+//
+// 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.
+
+void
+__pk_timer_handler()
+{
+ PkTimeQueue* tq;
+ PkTimebase now;
+ PkTimer* timer;
+ PkDeque* timer_deque;
+ PkTimerCallback callback;
+
+ tq = &__pk_time_queue;
+
+ if (PK_ERROR_CHECK_KERNEL) {
+ if (tq->cursor != 0) {
+ PK_PANIC(PK_TIMER_HANDLER_INVARIANT);
+ }
+ }
+
+ while ((now = pk_timebase_get()) >= tq->next_timeout) {
+
+ tq->next_timeout = PK_TIMEBASE_MAX;
+ timer_deque = ((PkDeque*)tq)->next;
+
+ while (timer_deque != (PkDeque*)tq) {
+
+ timer = (PkTimer*)timer_deque;
+ tq->cursor = timer_deque->next;
+
+ if (timer->timeout <= now) {
+
+ // The timer timed out. It is removed from the queue unless
+ // it is a peridic timer that needs to be rescheduled. We do
+ // rescheduling here in the critical section to correctly
+ // handle timers whose callbacks may cancel the timer. The
+ // timer is rescheduled in absolute time.
+ //
+ // 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.
+
+ if (timer->period == 0) {
+ pk_deque_delete(timer_deque);
+ } else {
+ timer->timeout += timer->period;
+ tq->next_timeout = MIN(timer->timeout, tq->next_timeout);
+ }
+
+ callback = timer->callback;
+ if (callback) {
+ if (timer->options & PK_TIMER_CALLBACK_PREEMPTIBLE) {
+ pk_interrupt_preemption_enable();
+ callback(timer->arg);
+ } else {
+ callback(timer->arg);
+ pk_interrupt_preemption_enable();
+ }
+ }
+ pk_interrupt_preemption_disable();
+
+ } else {
+
+ // This timer has not timed out. Its timeout will simply
+ // participate in the computation of the next timeout. For
+ // interrupt latency reasons we always allow a period of
+ // interrupt preemption.
+
+ tq->next_timeout = MIN(timer->timeout, tq->next_timeout);
+ pk_interrupt_preemption_enable();
+ pk_interrupt_preemption_disable();
+ }
+
+ timer_deque = tq->cursor;
+ }
+ }
+
+ tq->cursor = 0;
+
+ // Finally, reschedule the next timeout
+
+ __pk_schedule_hardware_timeout(tq->next_timeout);
+}
+
+
+/// Schedule a timer in absolute time.
+///
+/// \param timer The PkTimer to schedule.
+///
+/// \param timeout The timer will be scheduled to time out at this absolute
+/// time. Note that if the \a timeout is less than the current time then the
+/// timer will be scheduled at a minimum timeout in the future and the
+/// callback will be executed in an interrupt context.
+///
+/// \param period If non-0, then when the timer times out it will rescheduled
+/// to time out again at the absolute time equal to the last timeout time plus
+/// the \a period. By convention a \a period of 0 indicates a one-shot
+/// timer that is not rescheduled.
+///
+/// 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
+/// 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.
+///
+/// \retval -PK_ILLEGAL_CONTEXT_TIMER The call was made from a critical
+/// interrupt context.
+
+int
+pk_timer_schedule_absolute(PkTimer *timer,
+ PkTimebase timeout,
+ PkInterval period)
+
+{
+ PkMachineContext ctx;
+
+ pk_critical_section_enter(&ctx);
+
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_SCHEDULE);
+// PK_ERROR_IF(__pk_kernel_context_critical_interrupt(),
+// PK_ILLEGAL_CONTEXT_TIMER);
+ }
+
+ timer->timeout = timeout;
+ timer->period = period;
+ __pk_timer_schedule(timer);
+
+ pk_critical_section_exit(&ctx);
+
+ return PK_OK;
+}
+
+
+/// 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.
+///
+/// \param period If non-0, then when the timer times out it will rescheduled
+/// to time out again at the absolute time equal to the last timeout time plus
+/// the \a period. By convention a \a period of 0 indicates a one-shot
+/// timer that is not rescheduled.
+///
+/// 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.
+///
+/// \retval -PK_ILLEGAL_CONTEXT_TIMER The call was made from a critical
+/// interrupt context.
+
+int
+pk_timer_schedule(PkTimer *timer,
+ PkInterval interval,
+ PkInterval period)
+{
+ return pk_timer_schedule_absolute(timer,
+ pk_timebase_get() + interval,
+ period);
+}
+
+
+/// 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.
+///
+/// \retval -PK_ILLEGAL_CONTEXT_TIMER The call was made from a critical
+/// interrupt context.
+///
+
+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. Since the
+/// implementation of this API does not require a critical section, it is not
+/// an error to call this API from a critical interrupt context.
+///
+/// Return values other than PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -PK_INVALID_TIMER_AT_INFO The \a timer is a null (0) pointer.
+
+int
+pk_timer_info_get(PkTimer *timer,
+ PkTimebase *timeout,
+ int *active)
+
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_INFO);
+ }
+
+ if (timeout) {
+ *timeout = timer->timeout;
+ }
+ if (active) {
+ *active = timer_active(timer);
+ }
+
+ return PK_OK;
+}
+
+#undef __PK_TIMER_CORE_C__
diff --git a/pk/kernel/pk_timer_init.c b/pk/kernel/pk_timer_init.c
new file mode 100644
index 00000000..6851f3d3
--- /dev/null
+++ b/pk/kernel/pk_timer_init.c
@@ -0,0 +1,122 @@
+//-----------------------------------------------------------------------------
+// *! (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"
+
+// Implementation of timer creation
+
+static int
+_pk_timer_create(PkTimer *timer,
+ PkTimerCallback callback,
+ void *arg,
+ int options)
+{
+ 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->period = 0;
+ timer->callback = callback;
+ timer->arg = arg;
+ timer->options = options;
+
+ return PK_OK;
+}
+
+
+/// Create (initialize) a preemptible 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 noncritical
+/// interrupt handlers with interrupt preemption enabled. Timer callbacks are
+/// free to enter critical sections of any priorioty if required, but must
+/// always exit with noncritical 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)
+{
+ return _pk_timer_create(timer, callback, arg,
+ PK_TIMER_CALLBACK_PREEMPTIBLE);
+}
+
+
+/// Create (initialize) a nonpreemptible 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_preemptible() 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_nonpreemptible() are always run as
+/// noncritical interrupt handlers with interrupt preemption disabled. Timer
+/// callbacks are free to later enable preemption if desired, but must always
+/// exit with noncritical interrupts disabled.
+///
+/// \note The use of pk_timer_create_nonpreemptible() should be rare, and the
+/// timer callbacks should be short and sweet to avoid long interrupt
+/// latencies for other interrupts. This API was initially introduced for use
+/// by the PK kernel itself when scheduling thread-timer callbacks to avoid
+/// potential race conditions with other interrupts that may modify thread
+/// state or the state of the time queue. Applications may also require this
+/// facility to guarantee a consistent state in the event that other
+/// interrupts may cancel the timer.
+///
+/// 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_nonpreemptible(PkTimer *timer,
+ PkTimerCallback callback,
+ void *arg)
+{
+ return _pk_timer_create(timer, callback, arg, 0);
+}
+
+
diff --git a/pk/kernel/pkkernelfiles.mk b/pk/kernel/pkkernelfiles.mk
new file mode 100644
index 00000000..958e4ddf
--- /dev/null
+++ b/pk/kernel/pkkernelfiles.mk
@@ -0,0 +1,32 @@
+# @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-TIMER-C-SOURCES += pk_timer_core.c pk_timer_init.c
+
+PK-THREAD-C-SOURCES += pk_thread_init.c pk_thread_core.c \
+ pk_semaphore_init.c pk_semaphore_core.c
+
+PK_OBJECTS += $(PK-C-SOURCES:.c=.o)
+
diff --git a/pk/ppe/Makefile b/pk/ppe/Makefile
new file mode 100644
index 00000000..833dbfdf
--- /dev/null
+++ b/pk/ppe/Makefile
@@ -0,0 +1,50 @@
+# This Makefile compiles all of the PK code required for the 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 pkppefiles.mk
+
+ifeq "$(PK_TIMER_SUPPORT)" "1"
+PPE_OBJECTS += ${PPE-TIMER-C-SOURCES:.c=.o} ${PPE-TIMER-S-SOURCES:.S=.o}
+endif
+
+ifeq "$(PK_THREAD_SUPPORT)" "1"
+PPE_OBJECTS += ${PPE-THREAD-C-SOURCES:.c=.o} ${PPE-THREAD-S-SOURCES:.S=.o}
+endif
+
+ifeq "$(PPE_ASYNC_SUPPORT)" "1"
+PPE_OBJECTS += ${PPE-ASYNC-C-SOURCES:.c=.o} ${PPE-ASYNC-S-SOURCES:.S=.o}
+endif
+
+OBJS := $(addprefix $(OBJDIR)/, $(PPE_OBJECTS))
+
+libpk.a: kernel ppe42 trace ppe
+ $(AR) crs $(OBJDIR)/libpk.a $(OBJDIR)/*.o
+
+.PHONY: clean ppe kernel ppe42 trace
+ppe: $(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/pk/ppe/pk_port.h b/pk/ppe/pk_port.h
new file mode 100644
index 00000000..f9010581
--- /dev/null
+++ b/pk/ppe/pk_port.h
@@ -0,0 +1,16 @@
+#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 CME environment header for PK.
+
+#define HWMACRO_PPE
+
+#include "ppe42.h"
+
+#endif /* __PK_PORT_H__ */
diff --git a/pk/ppe/pkppefiles.mk b/pk/ppe/pkppefiles.mk
new file mode 100644
index 00000000..79b7baae
--- /dev/null
+++ b/pk/ppe/pkppefiles.mk
@@ -0,0 +1,33 @@
+# @file pkppefiles.mk
+#
+# @brief mk for including ppe object files
+#
+# @page ChangeLogs Change Logs
+# @section pkppefiles.mk
+# @verbatim
+#
+#
+# Change Log ******************************************************************
+# Flag Defect/Feature User Date Description
+# ------ -------------- ---------- ------------ -----------
+#
+# @endverbatim
+#
+##########################################################################
+# Object Files
+##########################################################################
+
+PPE-C-SOURCES = ppe_init.c
+PPE-S-SOURCES =
+
+PPE-TIMER-C-SOURCES =
+PPE-TIMER-S-SOURCES =
+
+PPE-THREAD-C-SOURCES =
+PPE-THREAD-S-SOURCES =
+
+PPE-ASYNC-C-SOURCES =
+PPE-ASYNC-S-SOURCES =
+
+PPE_OBJECTS += $(PPE-C-SOURCES:.c=.o) $(PPE-S-SOURCES:.S=.o)
+
diff --git a/pk/ppe/ppe.h b/pk/ppe/ppe.h
new file mode 100644
index 00000000..05df6a61
--- /dev/null
+++ b/pk/ppe/ppe.h
@@ -0,0 +1,22 @@
+#ifndef __PPE_H__
+#define __PPE_H__
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pgp.h
+/// \brief The PPE environment for PK.
+
+// This is a 'circular' reference in PK, but included here to simplify PGAS
+// programming.
+
+#ifndef HWMACRO_PPE
+#define HWMACRO_PPE
+#include "ppe42.h"
+#endif
+
+#include "ppe_common.h"
+
+#endif /* __PPE_H__ */
diff --git a/pk/ppe/ppe_common.h b/pk/ppe/ppe_common.h
new file mode 100644
index 00000000..bc8519ea
--- /dev/null
+++ b/pk/ppe/ppe_common.h
@@ -0,0 +1,60 @@
+#ifndef __PPE_COMMON_H__
+#define __PPE_COMMON_H__
+
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ppe_common.h
+/// \brief Common header for PPE
+///
+/// This header is maintained as part of the PK port for PPE, but needs to be
+/// physically present in the PMX area to allow dropping PMX code as a whole
+/// to other teams.
+
+// -*- WARNING: This file is maintained as part of PK. Do not edit in -*-
+// -*- the PMX area as your edits will be lost. -*-
+
+#ifndef __ASSEMBLER__
+#include <stdint.h>
+#endif
+
+/// This constant is used to define the size of the table of interrupt handler
+/// structures as well as a limit for error checking.
+/// NOTE: This can be specific to each PPE type (SBE, PPE, GPE)
+#define EXTERNAL_IRQS 64
+
+#ifdef __ASSEMBLER__
+/// This macro contains PPE specific code for determining what IRQ caused the
+/// external exception handler to be invoked by the PPE
+
+/// Load noncritical status 0 and the handler array base address. Check
+/// for interrupts pending in status register 0 while the IRQ is
+/// computed. The IRQ is expected to be stored in r3.
+ .macro hwmacro_get_ext_irq
+
+#_lwzi %r4, %r4, OCB_ONISR0
+ cntlzw %r3, %r4
+ cmpwible %r3, 31, external_irq_found #branch if irq is lt or eq to 31
+
+ ## No IRQ pending in interrupt set 0. Try set 1.
+
+#_lwzi %r4, %r4, OCB_ONISR1
+ cntlzw %r3, %r4
+ addi %r3, %r3, 32
+
+ .endm
+
+/// Redirect the .hwmacro_irq_cfg_bitmaps macro to call our ppe specific implementation
+/// This is called from the ppe42_exceptions.S file.
+/// NOTE: The standalone version of PPE doesn't support external interrupts so this
+/// does nothing.
+ .macro .hwmacro_irq_cfg_bitmaps
+ .endm
+
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PPE_COMMON_H__ */
diff --git a/pk/ppe/ppe_init.c b/pk/ppe/ppe_init.c
new file mode 100644
index 00000000..09feea67
--- /dev/null
+++ b/pk/ppe/ppe_init.c
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file ppe_init.c
+/// \brief PK initialization for 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"
+
+/// PPE environment initial setup.
+///
+/// This is setup common to all PPE HW Macro applications. This setup takes place
+/// during boot, before main() is called.
+
+void
+__hwmacro_setup(void)
+{
+ //async_initialize();
+}
diff --git a/pk/ppe42/Makefile b/pk/ppe42/Makefile
new file mode 100644
index 00000000..5bb2b566
--- /dev/null
+++ b/pk/ppe42/Makefile
@@ -0,0 +1,26 @@
+# 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/pk/ppe42/div64.S b/pk/ppe42/div64.S
new file mode 100644
index 00000000..69de53b3
--- /dev/null
+++ b/pk/ppe42/div64.S
@@ -0,0 +1,261 @@
+//-----------------------------------------------------------------------------
+// *! (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
+
+ .set r0, 0
+ .set r1, 1
+ .set r3, 3
+ .set r4, 4
+ .set r5, 5
+ .set r6, 6
+ .set r7, 7
+ .set r8, 8
+ .set r9, 9
+ .set r10, 10
+ .set r30, 30
+ .set r31, 31
+
+ .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/pk/ppe42/pk_port_types.h b/pk/ppe42/pk_port_types.h
new file mode 100644
index 00000000..1536b77b
--- /dev/null
+++ b/pk/ppe42/pk_port_types.h
@@ -0,0 +1,41 @@
+#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/pk/ppe42/pkppe42files.mk b/pk/ppe42/pkppe42files.mk
new file mode 100644
index 00000000..3399ca7e
--- /dev/null
+++ b/pk/ppe42/pkppe42files.mk
@@ -0,0 +1,45 @@
+# @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-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/pk/ppe42/ppe42.h b/pk/ppe42/ppe42.h
new file mode 100644
index 00000000..affd7f51
--- /dev/null
+++ b/pk/ppe42/ppe42.h
@@ -0,0 +1,795 @@
+#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_SBE)
+#include "sbe.h"
+#elif defined(HWMACRO_CME)
+#include "cme.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;})
+
+/// 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)
+
+/// 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;})
+
+#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__
+
+/// 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
+
+#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 critical, non-critical and machine check
+/// exceptions. Debug interrupts are not enabled by default. This definition
+/// can be overriden by the application. If MMU protection is enabled then
+/// the IR/DR bits are also modeably set.
+
+#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__
+
+/// 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
+
+#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
+
+// PPE42 doesn't support the system call instruction so we use this illegal
+// instruction to force a program exception which then emulates the system call
+// instruction.
+#define PPE42_SC_INST 0x44000002
+
+#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;
+}
+
+/// The PK kernel thread context switch - PPE42 uses the 'sc' illegal instruction
+/// to force a program exception to occur.
+#define __pk_switch() asm volatile (".long 0x44000002")
+
+
+/// 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 USPRG0. This
+/// 32-bit value is treated as 5 distinct fields as indicated in the structure
+/// definition. For certain tests it's also helpful to look at the two
+/// interrupt counters as a single 0/non-0 field.
+typedef union {
+
+ uint32_t value;
+
+ struct {
+
+ /// The critical interrupt nesting level. If this field is non-zero,
+ /// then interrupt priority and preemption rules guarantee that a
+ /// critical interrupt handler is running, and the \c irq field will
+ /// contain the PkIrqId of the currently active critical interrupt.
+ unsigned reserved : 8;
+
+ /// The non-critical interrupt nesting level. If this field is
+ /// non-zero and the \c critical_interrupts field is 0, then interrupt
+ /// priority and preemption rules guarantee that a noncritical
+ /// interrupt handler is running, and the \c irq field will contain
+ /// the PkIrqId of the currently active noncritical interrupt.
+ unsigned noncritical_interrupts : 8;
+
+ /// The PkIrqId of the currently running (or last run) handler. If
+ /// either of the interrupt nesting levels are non-0, then this is the
+ /// PkIrqId of the IRQ that is currently executing.
+ unsigned irq : 8;
+
+ /// A flag indicating that PK is in thread mode after a call of
+ /// pk_start_threads().
+ unsigned thread_mode : 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 : 7;
+
+ } fields;
+
+ struct {
+
+ unsigned also_ignore : 8;
+
+ /// Used as a 0/non-0 flag for interrupt context.
+ unsigned interrupt_context : 8;
+
+ /// Ignore
+ unsigned ignore : 16;
+
+ } merged_fields;
+
+} __PkKernelContext;
+
+// 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.merged_fields.interrupt_context;})
+
+
+/// PK is executing an interrupt handler of any priority.
+
+#define __pk_kernel_context_any_interrupt() \
+ ({ \
+ __PkKernelContext __ctx; \
+ __ctx.value = mfspr(SPRN_SPRG0); \
+ __ctx.merged_fields.interrupt_context;})
+
+
+/// PK is executing a non-critical interrupt handler.
+
+#define __pk_kernel_context_noncritical_interrupt() \
+ ({ \
+ __PkKernelContext __ctx; \
+ __ctx.value = mfspr(SPRN_SPRG0); \
+ __ctx.fields.noncritical_interrupts && \
+ !__ctx.fields.critical_interrupts;})
+
+
+/// Return the noncritical interrupt nesting level
+
+#define __pk_noncritical_level() \
+ ({ \
+ __PkKernelContext __ctx; \
+ __ctx.value = mfspr(SPRN_SPRG0); \
+ __ctx.fields.noncritical_interrupts; })
+
+
+// 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_noncritical_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 0x80
+
+
+#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.
+
+//#if defined(SIMICS_ENVIRONMENT) && (SIMICS_ENVIRONMENT != 0)
+/*#define CODE_BREAKPOINT \
+ do { \
+ if (_code_breakpoint_enable) { \
+ SIMICS_MAGIC_BREAKPOINT; \
+ } \
+ } while (0)
+#else
+#define CODE_BREAKPOINT \
+ do { \
+ if (_code_breakpoint_enable) { \
+ PkMachineContext __ctx; \
+ pk_critical_section_enter(&__ctx); \
+ _code_breakpoint_prologue(); \
+ _code_breakpoint_epilogue(); \
+ pk_critical_section_exit(&__ctx); \
+ } \
+ } while (0)
+#endif
+*/
+
+//void
+//_code_breakpoint_prologue(void);
+
+//void
+//_code_breakpoint_epilogue(void);
+
+//extern uint32_t _code_breakpoint_enable;
+
+#endif // __ASSEMBLER__
+
+
+#endif /* __PPE42_H__ */
diff --git a/pk/ppe42/ppe42_asm.h b/pk/ppe42/ppe42_asm.h
new file mode 100644
index 00000000..4a2e3aec
--- /dev/null
+++ b/pk/ppe42/ppe42_asm.h
@@ -0,0 +1,420 @@
+#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__
+
+### ****************************************************************************
+### TODO: shouldnt these be supported by the assembler??
+### ****************************************************************************
+# .macro lis treg, imm
+# addis \treg, 0, imm
+# .endm
+
+# .macro li treg, imm
+# addi \treg, 0, imm
+# .endm
+
+# .macro beq target
+# bc 12, 2, \target
+# .endm
+
+### ****************************************************************************
+### _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
+
+#endif /* __ASSEMBLER__ */
+
+/// \endcond
+
+// Local Variables:
+// mode:asm
+// End:
+
+#endif /* __PPE42_ASM_H__ */
diff --git a/pk/ppe42/ppe42_boot.S b/pk/ppe42/ppe42_boot.S
new file mode 100644
index 00000000..7e8ecee6
--- /dev/null
+++ b/pk/ppe42/ppe42_boot.S
@@ -0,0 +1,169 @@
+//-----------------------------------------------------------------------------
+// *! (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 critical or non-critical
+ ## 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/pk/ppe42/ppe42_cache.h b/pk/ppe42/ppe42_cache.h
new file mode 100644
index 00000000..01031b41
--- /dev/null
+++ b/pk/ppe42/ppe42_cache.h
@@ -0,0 +1,102 @@
+#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/pk/ppe42/ppe42_context.h b/pk/ppe42/ppe42_context.h
new file mode 100644
index 00000000..f13ec8a7
--- /dev/null
+++ b/pk/ppe42/ppe42_context.h
@@ -0,0 +1,455 @@
+#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__
+
+ .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.
+
+// A fair amount of complexity is involved in handling the non-critical and
+// critical interrupt levels, and the emphasis on performance of fast-mode
+// interrupt handlers. Several different approaches and philosophies could
+// have been implemented - this is only one. In this implementation
+// critical/non-critical interrupt levels are treated more or less the same,
+// and the interrupt priority is just that - a kind of preemption priority.
+// Critical interrupt handling does have a little less overhead because it
+// does not have a thread scheduling step at the end.
+
+// A full context save takes place in 3 or 4 steps. Thread switches always do
+// steps 1, 2 and 3.
+// 1. The fast context that is always saved in response to every interrupt;
+// 1a. The optional save/update of the kernel context for interrupts.
+// 2. The (volatile - fast) context that is saved if an interrupt handler
+// switches from fast-mode to full-mode.
+// 3. The non-volatile context that is saved when a thread is switched out.
+
+// USPRG0 holds the __PkKernelContext structure (defined in ppe42.h) that
+// represents the current kernel context. The layout is as follows:
+//
+// Bits Meaning
+// ==============
+// 0:7 The critical interrupt count
+// 8:15 The non-critical interrupt count
+// 16:23 The IRQ currently being processed
+// 24 The 'thread_mode' flag
+// 25:31 The thread priority of the running thread
+//
+// When PK is initialized USPRG0 is initialized to 0. When thread-mode is
+// entered (by pk_start_threads()) bit 24 is set to 1. In order to support
+// PgP/OCC firmware, 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.
+//
+// Note that it would require a serious error for the interrupt counts to ever
+// equal or exceed 2**8 as this would imply runaway reentrancy and stack
+// overflow. In fact it is most likely an error if an interrupt handler is
+// ever re-entered while active.
+
+// Registers SRR2 and SRR3 are always saved in IRQ context because
+// __pk_irq_fast2full must save the (volatile - fast) context to provide
+// working registers before it can look at USPRG0 to determine critical
+// vs. non-critical context. However, when restoring a non-critical interrupt
+// or thread these registers need not be restored. SRR2 and SRR3 are never
+// saved or restored for thread context switches, because threads always
+// operate at noncritical level.
+
+// When MMU protection is enabled, relocation/protection is re-established
+// immediately upon entry to the interrupt handler, before any memory
+// operations (load/store) take place. This requires using SPRG0 and SPGR4
+// for temporary storage for noncritical/critical handlers respectively in
+// accordance with the PK conventions for SPRGn usage by fast-mode
+// interrupts.
+
+ ## ------------------------------------------------------------
+ ## 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
+
+ ## ------------------------------------------------------------
+ ## Flags for context push/pop
+ ## ------------------------------------------------------------
+
+ .set PK_THREAD_CONTEXT, 0
+ .set PK_IRQ_CONTEXT, 1
+
+ ## ------------------------------------------------------------
+ ## The PK fast context layout for Embedded PPE42
+ ## ------------------------------------------------------------
+
+ .set PK_FAST_CTX_GPR1, 0x00 # Dedicated; Stack pointer
+ .set PK_FAST_CTX_HANDLER_LR, 0x04 # Slot for handler to store LR
+ .set PK_FAST_CTX_GPR3, 0x08 # Volatile; Parameter; Return Value
+ .set PK_FAST_CTX_GPR4, 0x0c # Volatile; Parameter
+ .set PK_FAST_CTX_GPR5, 0x10 # Volatile; Parameter
+ .set PK_FAST_CTX_GPR6, 0x14 # Volatile; Parameter
+ .set PK_FAST_CTX_CR, 0x18 # Condition register
+ .set PK_FAST_CTX_LR, 0x1c # Link register SPRN 0x008
+ .set PK_FAST_CTX_KERNEL_CTX, 0x20 # Saved __PkKernelContext for IRQ
+
+ .set PK_FAST_CTX_SIZE, 0x28 # Must be 8-byte aligned
+
+ ## ------------------------------------------------------------
+ ## The PK (volatile - fast) context layout for Embedded PPE42
+ ## ------------------------------------------------------------
+
+ .set PK_VOL_FAST_CTX_GPR1, 0x00 # Dedicated; Stack pointer
+ .set PK_VOL_FAST_CTX_HANDLER_LR, 0x04 # Slot for handler to store LR
+ .set PK_VOL_FAST_CTX_GPR7, 0x08 # Volatile; Parameter
+ .set PK_VOL_FAST_CTX_GPR8, 0x0c # Volatile; Parameter
+ .set PK_VOL_FAST_CTX_GPR9, 0x10 # Volatile; Parameter
+ .set PK_VOL_FAST_CTX_GPR10, 0x14 # Volatile; Parameter
+ .set PK_VOL_FAST_CTX_XER, 0x18 # Fixed-point exception register SPRN 0x001
+ .set PK_VOL_FAST_CTX_CTR, 0x1c # Count register SPRN 0x009
+ .set PK_VOL_FAST_CTX_SRR0, 0x20 # Save/restore register 0 SPRN 0x01a
+ .set PK_VOL_FAST_CTX_SRR1, 0x24 # Save/restore register 1 SPRN 0x01b
+ .set PK_VOL_FAST_CTX_GPR0, 0x28 # Volatile; Language specific
+
+ .set PK_VOL_FAST_CTX_SIZE, 0x30 # Must be 8-byte aligned
+
+ ## ------------------------------------------------------------
+ ## The PK non-volatile context layout for Embedded PowerPC
+ ## ------------------------------------------------------------
+
+ ## The 'preferred form' for stmw is for the LSB of R31 to fall into the
+ ## end of a 16-byte aligned block.
+
+ .set PK_NON_VOL_CTX_GPR1, 0x00 # Dedicated; Stack Pointer
+ .set PK_NON_VOL_CTX_HANDLER_LR, 0x04 # Slot for handler to store LR
+ .set PK_NON_VOL_CTX_GPR28, 0x08 # Non-volatile
+ .set PK_NON_VOL_CTX_GPR29, 0x0c # Non-volatile
+ .set PK_NON_VOL_CTX_GPR30, 0x10 # Non-volatile
+ .set PK_NON_VOL_CTX_GPR31, 0x14 # Non-volatile
+
+ .set PK_NON_VOL_CTX_SIZE, 0x18 # Must be 8-byte aligned
+
+ ## ------------------------------------------------------------
+ ## Save/restore the fast context
+ ##
+ ## 11 Instructions, 8 Loads/Stores : If MMU is disabled
+ ## 17 Instructions, 8 Loads/Stores : If MMU is enabled
+ ## ------------------------------------------------------------
+ ##
+ ## Without MMU support, an EIEIO is always executed at the entry point
+ ## to gauarantee that all memory operations (especially MMIO
+ ## operations) have completed prior to execution of the interrupt
+ ## handler.
+ ##
+ ## If MMU support is enabled, address translation is re-established
+ ## immediately at the entry of each interrupt, prior to performing any
+ ## loads or stores. PK currently only supports using the MMU for
+ ## protection, not for address translation. Therfore it is 'legal'
+ ## to change translation modes a with an MTMSR followed by an
+ ## ISYNC. This is much simpler then the complex instruction sequence
+ ## that would be required if we had to set up RFI/RFCI sequences to
+ ## change the execution context at this point.
+ ##
+ ## Note that since we are not really doing address translation, it
+ ## would also be in keeping with the 'fast interrupt' idea to defer
+ ## reenabling translation (protection) until the fast-to-full sequence
+ ## was executed for full-mode interrupts, and run fast-mode interrupts
+ ## unprotected. However here we chose to run all interrupts with MMU
+ ## protection.
+ ##
+ ## Unfortunately the simple MTMSR;ISYNC sequence exposes a serious bug
+ ## in the PPE42 core that causes the stack-pointer store instruction
+ ## to generate a seemingly random, *real-mode* address in certain cases
+ ## when this instruction in a noncritical interrupt prologue is
+ ## interrupted by a critical interrupt. This bug is described in
+ ## HW239446. The workaround is to follow the ISYNC sith a SYNC - which
+ ## eliminates the problem for reasons still unknown. On the bright side
+ ## this SYNC might also serve the same purpose as the EIEIO in the
+ ## non-MMU case, guaranteeing that all MMIO has completed prior to the
+ ## interrupt handler. However without the initial EIEIO we still
+ ## experience failures, so this seemingly redundant instruction also
+ ## remains in place. This requirement is assumed to be related to the
+ ## HW239446 issue.
+
+ .macro _pk_fast_ctx_push
+
+ stwu %r1, -PK_FAST_CTX_SIZE(%r1)
+
+ stvd %d3, PK_FAST_CTX_GPR3(%r1)
+ stvd %d5, PK_FAST_CTX_GPR5(%r1)
+
+ mfcr %r3
+ mflr %r4
+
+ stvd %d3, PK_FAST_CTX_CR(%r1)
+
+ .endm
+
+
+ .macro _pk_fast_ctx_pop
+
+ lvd %d3, PK_FAST_CTX_CR(%r1)
+
+ mtcr0 %r3
+ mtlr %r4
+
+ lvd %d3, PK_FAST_CTX_GPR3(%r1)
+ lvd %d5, PK_FAST_CTX_GPR5(%r1)
+
+ lwz %r1, 0(%r1)
+
+ .endm
+
+ ## ------------------------------------------------------------
+ ## Save/update the kernel context in response to an interrupt. This is
+ ## not part of the fast context save because for external interupts the
+ ## IRQ is not determined until later.
+ ## ------------------------------------------------------------
+
+ ## The kernel context is saved, then updated with the currently active
+ ## IRQ in bits 16:23. The correct interrupt count is incremented and
+ ## the context is returned to SPRG0.
+
+ .macro _save_update_kernel_context irqreg, ctxreg
+
+ #PK_TRACE_NONCRITICAL_IRQ_ENTRY \irqreg, \ctxreg
+
+ mfsprg0 \ctxreg
+ stw \ctxreg, PK_FAST_CTX_KERNEL_CTX(%r1)
+ #rlwimi \ctxreg, \irqreg, 24, 9, 15 //set the irq #
+ rlwimi \ctxreg, \irqreg, 8, 16, 23 //set the irq #
+ #oris \ctxreg, \ctxreg, 0x4000 //set the 'processing interrupt' PI bit
+ addis \ctxreg, \ctxreg, 0x0001 //increment the irq count
+ mtsprg0 \ctxreg
+
+ .endm
+
+ ## ------------------------------------------------------------
+ ## Fast-mode context pop and RF(C)I. This is only used by
+ ## interrupt handlers - the thread context switch has its own
+ ## code to handle updating USPRG0 for thread mode.
+ ## ------------------------------------------------------------
+
+ .macro _pk_fast_ctx_pop_exit
+
+ .if PK_KERNEL_TRACE_ENABLE
+ bl __pk_trace_noncritical_irq_exit
+ .endif
+
+ lwz %r3, PK_FAST_CTX_KERNEL_CTX(%r1)
+ mtsprg0 %r3
+ _pk_fast_ctx_pop
+ rfi
+
+ .endm
+
+ ## ------------------------------------------------------------
+ ## Save/restore the (volatile - fast) context
+ ##
+ ## Thread - 15 Instructions, 11 Loads/Stores
+ ## IRQ - 19(15) Instructions, 13(11) Loads/Stores
+ ## ------------------------------------------------------------
+
+ .macro _pk_vol_fast_ctx_push
+
+ stwu %r1, -PK_VOL_FAST_CTX_SIZE(%r1)
+
+ stw %r0, PK_VOL_FAST_CTX_GPR0(%r1)
+ stvd %d7, PK_VOL_FAST_CTX_GPR7(%r1)
+ stvd %d9, PK_VOL_FAST_CTX_GPR9(%r1)
+
+ mfxer %r7
+ mfctr %r8
+ mfsrr0 %r9
+ mfsrr1 %r10
+
+ stvd %d7, PK_VOL_FAST_CTX_XER(%r1)
+ stvd %d9, PK_VOL_FAST_CTX_SRR0(%r1)
+
+ .endm
+
+
+ .macro _pk_vol_fast_ctx_pop
+
+ lvd %d7, PK_VOL_FAST_CTX_XER(%r1)
+ lvd %d9, PK_VOL_FAST_CTX_SRR0(%r1)
+
+ mtxer %r7
+ mtctr %r8
+ mtsrr0 %r9
+ mtsrr1 %r10
+
+ lwz %r0, PK_VOL_FAST_CTX_GPR0(%r1)
+ lvd %d7, PK_VOL_FAST_CTX_GPR7(%r1)
+ lvd %d9, PK_VOL_FAST_CTX_GPR9(%r1)
+
+ lwz %r1, 0(%r1)
+
+ .endm
+
+ ## ------------------------------------------------------------
+ ## Save/restore the non-volatile context on the stack
+ ##
+ ## 2 Instructions, 19 Loads/Stores
+ ## ------------------------------------------------------------
+
+ .macro _pk_non_vol_ctx_push
+
+ stwu %r1, -PK_NON_VOL_CTX_SIZE(%r1)
+ stvd %d28, PK_NON_VOL_CTX_GPR28(%r1)
+ stvd %d30, PK_NON_VOL_CTX_GPR30(%r1)
+
+ .endm
+
+
+ .macro _pk_non_vol_ctx_pop
+
+ lvd %d28, PK_NON_VOL_CTX_GPR28(%r1)
+ lvd %d30, PK_NON_VOL_CTX_GPR30(%r1)
+ lwz %r1, 0(%r1)
+
+ .endm
+
+#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_nv;
+ uint32_t link_nv;
+ uint32_t r28;
+ uint32_t r29;
+ uint32_t r30;
+ uint32_t r31;
+ uint32_t r1_vf;
+ uint32_t link_vf;
+ uint32_t r7;
+ uint32_t r8;
+ uint32_t r9;
+ uint32_t r10;
+ uint32_t xer;
+ uint32_t ctr;
+ uint32_t srr0;
+ uint32_t srr1;
+ uint32_t r0;
+ uint32_t pad;
+ uint32_t r1;
+ uint32_t link_fast;
+ uint32_t r3;
+ uint32_t r4;
+ uint32_t r5;
+ uint32_t r6;
+ uint32_t cr;
+ uint32_t lr;
+ uint32_t sprg0;
+
+} PkThreadContext;
+
+/// PK thread context of an interrupted thread (full-mode handler)
+///
+/// When a thread is interrupted by a full-mode interrupt handler, this is the
+/// layout of the stack area pointed to by either __pk_saved_sp_noncritical
+/// or __pk_saved_sp_critical.
+
+typedef struct {
+
+ uint32_t r1_vf;
+ uint32_t link_vf;
+ uint32_t r7;
+ uint32_t r8;
+ uint32_t r9;
+ uint32_t r10;
+ uint32_t xer;
+ uint32_t ctr;
+ uint32_t srr0;
+ uint32_t srr1;
+ uint32_t r0;
+ uint32_t pad;
+ uint32_t r1;
+ uint32_t link_fast;
+ uint32_t r3;
+ uint32_t r4;
+ uint32_t r5;
+ uint32_t r6;
+ uint32_t cr;
+ uint32_t lr;
+ uint32_t sprg0;
+
+} PkThreadContextFullIrq;
+
+
+/// PK thread context of an interrupted thread (fast-mode handler)
+///
+/// When a thread is interrupted by a fast-mode interrupt handler, this is the
+/// layout of the stack area pointed to by R1 - unless the fast-mode interrupt
+/// handler extends the stack.
+
+typedef struct {
+
+ uint32_t r1;
+ uint32_t link_fast;
+ uint32_t r3;
+ uint32_t r4;
+ uint32_t r5;
+ uint32_t r6;
+ uint32_t cr;
+ uint32_t lr;
+ uint32_t sprg0;
+
+} PkThreadContextFastIrq;
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PPE42_CONTEXT_H__ */
+
+
diff --git a/pk/ppe42/ppe42_core.c b/pk/ppe42/ppe42_core.c
new file mode 100644
index 00000000..f82b3bf6
--- /dev/null
+++ b/pk/ppe42/ppe42_core.c
@@ -0,0 +1,199 @@
+//-----------------------------------------------------------------------------
+// *! (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 0
+/// Get the emulated 64-bit timebase
+///
+
+PkTimebase
+pk_timebase_get(void)
+{
+ PkTimebase tb;
+ uint32_t dec_start;
+ uint32_t dec;
+ uint32_t time_since_last_update;
+ PkMachineContext ctx;
+
+ pk_critical_section_enter(&ctx);
+
+ tb = ppe42_64bit_timebase;
+ dec_start = ppe42_dec_start;
+ dec = mfspr(SPRN_DEC);
+
+ pk_critical_section_exit(&ctx);
+
+ time_since_last_update = dec_start - dec;
+
+ return (tb + time_since_last_update);
+}
+#endif
+
+/// Set the 64-bit timebase in an critical section
+///
+/// It is assumed that the caller knows what they are doing; e.g., is aware of
+/// what may happen when time warps as a result of this call.
+
+void
+pk_timebase_set(PkTimebase timebase)
+{
+ PkMachineContext ctx;
+
+ pk_critical_section_enter(&ctx);
+
+ ppe42_64bit_timebase = timebase;
+ ppe42_tb_data.dec_start = 0;
+ ppe42_tb_data.dec_change_tag++;
+
+ //This will cause TSR[DIS] to be set on the next timer tick.
+ mtspr(SPRN_DEC, 0);
+
+ pk_critical_section_exit(&ctx);
+}
+
+
+/// Enable interrupt preemption
+///
+/// This API can only be called from an interrupt context. Threads will
+/// always be preempted by interrupts unless they explicitly disable
+/// interrupts with the \c pk_interrupt_disable() API. It is legal to call
+/// this API redundantly.
+///
+/// Be careful when enabling interrupt handler preemption that the interrupt
+/// being handled does not/can not trigger again, as this could rapidly lead
+/// to stack overflows.
+///
+/// Return values other then PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -PK_ILLEGAL_CONTEXT The API call was not made from an interrupt
+/// context.
+
+int
+pk_interrupt_preemption_enable()
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT();
+ }
+
+ wrteei(1);
+
+ return PK_OK;
+}
+
+
+/// Disable interrupt preemption
+///
+/// This API can only be called from an interrupt context. Threads will
+/// always be preempted by interrupts unless they explicitly disable
+/// interrupts with the \c pk_interrupt_disable() API. It is legal to call
+/// this API redundantly.
+///
+/// Return values other then PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -PK_ILLEGAL_CONTEXT The API call was not made from an interrupt
+/// context.
+
+int
+pk_interrupt_preemption_disable()
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT();
+ }
+
+ wrteei(0);
+
+ return PK_OK;
+}
+
+
+#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.
+
+void
+__pk_schedule_hardware_timeout(PkTimebase timeout)
+{
+ PkTimebase now;
+ uint32_t new_dec;
+ PkMachineContext ctx;
+ uint32_t dec;
+
+ if (timeout != PK_TIMEBASE_MAX) {
+
+ pk_critical_section_enter(&ctx);
+
+ 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 loose 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++;
+
+ pk_critical_section_exit(&ctx);
+ }
+}
+
+
+
+#endif /* PK_TIMER_SUPPORT */
+
+#undef __PPE42_CORE_C__
diff --git a/pk/ppe42/ppe42_exceptions.S b/pk/ppe42/ppe42_exceptions.S
new file mode 100644
index 00000000..9b3afa0a
--- /dev/null
+++ b/pk/ppe42/ppe42_exceptions.S
@@ -0,0 +1,580 @@
+//-----------------------------------------------------------------------------
+// *! (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_fast_ctx_push
+
+ b __external_interrupt
+
+ ############################################################
+ # 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_fast_ctx_push
+
+ b program_exception_handler
+
+
+ ############################################################
+ # 0x0100 : DEC Interrupts
+ ############################################################
+
+ .org __vectors + 0x0100
+__dec_interrupt:
+ _pk_fast_ctx_push
+ li %r3, PPE42_IRQ_DEC
+ b dec_handler
+
+ ############################################################
+ # 0x0120 : FIT Interrupts
+ ############################################################
+ .org __vectors + 0x0120
+__fit_interrupt:
+
+ #b fit_handler
+ b .
+
+ ############################################################
+ # 0x0140 : Watchdog Interrupts
+ ############################################################
+ .org __vectors + 0x0140
+__watchdog_interrupt:
+
+ #b watchdog_handler
+ b .
+
+
+
+
+
+ .global __pk_irq_fast2full
+__pk_irq_fast2full:
+
+ ## Convert a fast-mode to a full-mode interrupt by saving the
+ ## (volatile - fast) context, and switching to the appropriate system
+ ## stack.
+
+ ## Entry invariants:
+ ## 1. The SP/stack must be exactly as it was when the fast-mode
+ ## handler was entered.
+ ## 2. No changes have been made to the MSR - the interrupt level must
+ ## remain disabled.
+ ## 3. The handler owns the fast context and has not modified the other
+ ## register context. This routine can only use the (volatile -
+ ## fast) register context.
+
+ ## 41 (linear) instructions plus alignmenmt
+
+ ## Start by pushing the (volatile - fast) context. Technically we also
+ ## need to save the CR as our contract with the handler is not to
+ ## disturb any of its register state.
+
+ _pk_vol_fast_ctx_push
+ mfcr %r10
+ mfsprg0 %r8
+
+ ## At entry here the (volatile - fast) context has been pushed,
+ ## R8 has SPRG0 and R10 contains the saved CR.
+
+ ## Note that it would violate a kernel/API invariant if this routine
+ ## were entered from outside an interrupt context.
+
+fast2full_noncritical:
+
+ ## switch to the kernel stack if we haven't already done so. (SPRG0[RI] = 0)
+ #bb1wi %r8, RI_BIT, 1f //branches if the RI_BIT is '1'
+
+ extrwi %r9, %r8, 8, 8
+ cmpwi %r9, 1
+ bne 1f
+
+ _stwsd %r1, __pk_saved_sp_noncritical
+ _lwzsd %r1, __pk_noncritical_stack
+
+1:
+
+ .if (PK_ERROR_CHECK_KERNEL | PK_ERROR_CHECK_API)
+ #bb1wi %r8, PI_BIT, 2f //branches if PI_BIT is '1'
+ cmpwi %r9, 0
+ bne 2f
+ _pk_panic PPE42_IRQ_FAST2FULL_INVARIANT
+2:
+ .endif
+
+ mtcr0 %r10
+ blr
+
+ .global __pk_irq_full_mode_exit
+__pk_irq_full_mode_exit:
+
+ ## Exit a full-mode handler.
+
+ ## Entry invariants:
+ ## 1. The SP/stack must be in exactly the same state it was left in at
+ ## the exit of __pk_irq_fast2full.
+ ## 2. It is assumed the the preemption rules of PK have been followed
+ ## - in particular that critical handlers have not enabled
+ ## non-critical interrupts.
+
+ ## We can freely modify the volatile context here - the handler is done
+ ## and we will restore the interrupted volatile context.
+
+ ## 22 linear instructions
+
+ ## If the critical count is non-zero, then the PK preemption rules
+ ## guarantee that we are exiting from a critical interrupt
+ ## handler. This test is safe to make even if critical interrupts are
+ ## enabled, because the variable is set exactly once in a critical
+ ## section.
+
+ mfsprg0 %r3
+
+ ## Exiting a full-mode non-critical handler is more complex than the
+ ## critical case, because the handler may have made a new
+ ## highest-priority thread runnable and we may need to go through a
+ ## delayed scheduling step.
+
+ ## Note that the idle thread is treated as a special case. The idle
+ ## thread has no permanent register context. To avoid having to
+ ## allocate a stack area for the idle thread, the idle thread
+ ## 'uses' the non-critical stack. When the idle thread is interrupted
+ ## the (redundant) context is pushed, but is then effectively lost.
+ ## Whenever we restore the idle thread we simply reenter the idle
+ ## thread entry point.
+
+ ## At entry:
+ ## 1. R3 holds the value of SPRG0 (__PkKernelContext)
+
+ ## 33 linear instructions.
+
+full_exit_noncritical:
+
+ ## Enter a critical section for the return from interrupt, in the event
+ ## that the handler enabled preemption.
+
+ _pk_critical_section_enter %r4, %r5
+
+ ## If the non-critical count is > 1 then this is a nested interrupt
+ ## and we can simply pop the context and RFI.
+
+ extrwi. %r4, %r3, 8, 8
+
+ ## If SPRG0[RI] = 1 then this is a recursive interrupt
+ ## and we can simply pop the context and RFI. Note that it would
+ ## violate a kernel/API invariant if this routine were entered from
+ ## outside an interrupt context (interrupt level == 0).
+
+ .if (PK_ERROR_CHECK_KERNEL | PK_ERROR_CHECK_API)
+ #bb1wi %r3, PI_BIT, 1f //branch if the PI bit is set
+ bne 1f
+ _pk_panic PPE42_IRQ_FULL_EXIT_INVARIANT
+1:
+ .endif
+
+ cmpwi %r4, 1
+ bne exit_noncritical_without_switch
+
+ ## Otherwise, restore the saved stack pointer and continue.
+
+ _lwzsd %r1, __pk_saved_sp_noncritical
+
+ ## 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.
+
+ andi. %r4, %r3, PPE42_THREAD_MODE
+ beq exit_noncritical_without_switch
+
+ ## Now, check for a delayed context switch. If none is pending, we can
+ ## exit (after a check for the idle thread special case).
+
+ _lwzsd %r3, __pk_delayed_switch
+ cmpwi %r3, 0
+ bne noncritical_switch
+
+ _lwzsd %r3, __pk_current_thread
+ cmpwi %r3, 0
+ beq __pk_idle_thread
+
+exit_noncritical_without_switch:
+ _pk_vol_fast_ctx_pop
+ b fast_exit_noncritical
+
+ ## The non-critical interrupt activated a delayed context switch. The
+ ## C-level code has taken care of the scheduling decisions - we simply
+ ## need to implement them here.
+
+noncritical_switch:
+
+ ## 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 thread_save_non_volatile_and_switch
+
+
+
+
+
+ ## 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 non-critical 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 non-critical interrupts disabled.
+ ##
+ ## The kernel context is initialized to indicate that the idle thread
+ ## is running - the idle thread priority is PK_THREADS, and the
+ ## 'thread-mode' bit is asserted as well.
+ ##
+ ## 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.
+
+ li %r3, (PK_THREADS | PPE42_THREAD_MODE)
+ mtsprg0 %r3
+ _lwzsd %r1, __pk_noncritical_stack
+
+__pk_idle_thread_from_bootloader:
+
+ #li %r3, PK_THREADS
+ #PK_TRACE_THREAD_SWITCH %r3, %r4
+ _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.
+
+ _save_update_kernel_context %r3, %r4
+
+ _liwa %r3, TSR_DIS
+ mttsr %r3
+
+ _pk_irq_fast2full __pk_timer_handler
+
+
+
+
+
+ ## Exit traces are moved here because the code area (0x100 bytes)
+ ## reserved for individual interrupts is overflowing when tracing is
+ ## enabled. This is kind of a hack: We know that this trace only
+ ## occurs when we're about to exit the fast context, at a place
+ ## where we can use any of the fast registers.
+#if 0
+__pk_trace_noncritical_irq_exit:
+ #PK_TRACE_NONCRITICAL_IRQ_EXIT %r3, %r4
+ blr
+#endif
+
+program_exception_handler:
+ ## first check if exception was caused by an illegal 'sc' instruction
+ mfspr %r3, SPRN_EDR
+ _liw %r4, PPE42_SC_INST
+ cmpwbeq %r3, %r4, __sc_helper
+ _pk_panic PPE42_ILLEGAL_INSTRUCTION
+
+ ## SRR0 is currently pointing to the 'sc' instruction. We need to advance it
+ ## to the next instruction so that we don't end up in an endless loop (something
+ ## that the ppc sc instruction does automatically).
+__sc_helper:
+ mfsrr0 %r3
+ addi %r3, %r3, 4
+ mtsrr0 %r3
+
+__system_call:
+
+ ## The program exception is used by PK as a handy way to start a
+ ## context switch, as the continuation address and MSR of the thread to
+ ## be swapped out are saved in SRR0 and SRR1.
+
+ ## Non-critical interrupts are disabled at entry.
+
+ ## Begin by saving the volatile context of the current thread.
+ ## NOTE: fast context has already been saved prior to branching here.
+
+ _pk_vol_fast_ctx_push
+
+thread_save_non_volatile_and_switch:
+
+ ## Finish the thread context save by pushing the non-volatile context
+ ## and saving the resulting stack pointer in the thread structure. If
+ ## the current thread is the idle thread this step is bypassed.
+
+ ## This symbol is also used as an entry point by the non-critical
+ ## interrupt handler - non-critical interrupts are disabled here.
+
+ _lwzsd %r3, __pk_current_thread
+ cmpwi %r3, 0
+ beq __pk_next_thread_resume
+
+ _pk_non_vol_ctx_push
+ stw %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3)
+
+ ## The next thread becomes the current thread, and we switch to its
+ ## stack - unless the new thread is the idle thread, in which case it
+ ## (the idle thread) is simply resumed.
+
+ .global __pk_next_thread_resume
+__pk_next_thread_resume:
+
+ _lwzsd %r3, __pk_next_thread
+ _stwsd %r3, __pk_current_thread
+
+ cmpwi %r3, 0
+ beq __pk_idle_thread
+
+ lwz %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3)
+
+ ## Restore the thread context and resume the new thread. The kernel
+ ## context in thread mode is simply the thread priority OR'ed with the
+ ## thread-mode flag. All other fields are cleared.
+
+ _pk_non_vol_ctx_pop
+ _pk_vol_fast_ctx_pop
+
+ _lbzsd %r3, __pk_next_priority
+ #PK_TRACE_THREAD_SWITCH %r3, %r4
+ ori %r3, %r3, PPE42_THREAD_MODE
+ mtsprg0 %r3
+
+ _pk_fast_ctx_pop
+ rfi
+
+fit_handler:
+
+ ## The FIT handler is user defined, and is a fast-mode handler. By
+ ## convention the kernel clears the interrupt by writing the FIS back
+ ## into the TSR.
+
+ _pk_fast_ctx_push
+
+ _lwzsd %r3, __ppe42_fit_arg
+ li %r4, PPE42_IRQ_FIT
+
+ _save_update_kernel_context %r4, %r6
+
+ _liwa %r6, TSR_FIS
+ mttsr %r6
+
+ _lwzsd %r6, __ppe42_fit_routine
+ mtlr %r6
+ blrl
+
+ b fast_exit_noncritical
+
+watchdog_handler:
+ ## Watchdog setup is described in the PK Specification.
+ ## The kernel clears TSR[WIS] prior to calling the handler.
+ ## The watchdog handler is a critical, fast-mode handler.
+
+ _pk_fast_ctx_push
+
+ li %r3, PPE42_IRQ_WATCHDOG
+
+ _save_update_kernel_context %r3, %r6
+
+ _liwa %r6, TSR_WIS
+ mttsr %r6
+
+ _lwzsd %r6, __ppe42_watchdog_routine
+ mtlr %r6
+ blrl
+
+ b .
+
+
+#if 0
+debug_handler:
+
+ ## PK does nothing upon reception of the debug interrupt other
+ ## than calling the handler (if non-0). The debug handler is a
+ ## fast-mode handler.
+
+ _pk_fast_ctx_push
+
+ li %r3, PPE42_IRQ_DEBUG
+
+ _save_update_kernel_context %r3, %r6
+
+ _lwzsd %r6, __ppe42_debug_routine
+ cmpwi %r6, 0
+ mtlr %r6
+ beq debug_exit
+ blrl
+
+debug_exit:
+ b fast_exit_critical
+#endif
+
+__external_interrupt:
+
+ ## The non-critical interrupt handler entry point is re-entrant - A
+ ## handler may allow preemption, which could cause another entry here.
+
+ ## Entry invariants:
+ ## 1. Non-critical interupts are disabled;
+ ## 2. The SP points to a thread stack or the non-critical stack.
+
+ ## Since fast-mode handlers can not use PK services or alter the
+ ## machine context, the exit of a fast mode handler is a simple RF(C)I.
+
+ ## Begin by pushing the fast context on the current stack.
+
+ ## _pk_fast_ctx_push was called prior to branching here. No need to call it here.
+
+ ## Load the base address for the external interrupt table
+
+ ## TODO: 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.
+ ## 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
+ ## R4 = irq
+
+external_irq_found:
+
+ _save_update_kernel_context %r4, %r5
+ 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
+
+ ## Pop the stack/RFI when (if) it returns here.
+
+fast_exit_noncritical:
+
+ _pk_fast_ctx_pop_exit
+
+
+
+/// \endcond
diff --git a/pk/ppe42/ppe42_gcc.c b/pk/ppe42/ppe42_gcc.c
new file mode 100644
index 00000000..97c2979c
--- /dev/null
+++ b/pk/ppe42/ppe42_gcc.c
@@ -0,0 +1,305 @@
+//-----------------------------------------------------------------------------
+// *! (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, &quotient, &remainder);
+ return quotient;
+}
+
+
+uint64_t
+__umoddi3(uint64_t u, uint64_t v)
+{
+ uint64_t quotient, remainder;
+
+ __ppe42_udiv64(u, v, &quotient, &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, &quotient, &remainder);
+ return quotient;
+}
+
+
+int64_t
+__moddi3(int64_t u, int64_t v)
+{
+ int64_t quotient, remainder;
+
+ __ppe42_sdiv64(u, v, &quotient, &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/pk/ppe42/ppe42_gcc.h b/pk/ppe42/ppe42_gcc.h
new file mode 100644
index 00000000..8c4179cf
--- /dev/null
+++ b/pk/ppe42/ppe42_gcc.h
@@ -0,0 +1,72 @@
+#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/pk/ppe42/ppe42_init.c b/pk/ppe42/ppe42_init.c
new file mode 100644
index 00000000..2e4a2ad2
--- /dev/null
+++ b/pk/ppe42/ppe42_init.c
@@ -0,0 +1,75 @@
+//-----------------------------------------------------------------------------
+// *! (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
+// critical or external interruts.
+
+// 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*/
+
+ // Set the DEC to decrement on every cycle and enable the DEC interrupt. Clear the status
+ // of all timers for good measure.
+
+ //andc_spr(SPRN_TCR, TCR_DS);
+ //or_spr(SPRN_TCR, TCR_DIE);
+
+ //Use dec_timer signal for decrementer
+ or_spr(SPRN_TCR, TCR_DIE | TCR_DS);
+ or_spr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
+
+#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/pk/ppe42/ppe42_irq.h b/pk/ppe42/ppe42_irq.h
new file mode 100644
index 00000000..3c45a863
--- /dev/null
+++ b/pk/ppe42/ppe42_irq.h
@@ -0,0 +1,326 @@
+#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;
+
+
+// Note: Why PK_IRQ_FAST2FULL (below) is implemented so strangely.
+//
+// I am adamant that I want to have a a macro in the 'C' environment to create
+// these bridge functions. However the limitations of the C preprocessor and
+// the intelligence of the GCC 'asm' facility consipre against a
+// straightforward solution. The only way that I was able to find to get
+// naked assembly code into the output stream is to use 'asm' with simple
+// strings - I couldn't make it work with any kind of argument, as 'asm' would
+// reinterpret the arguments and resulting assembler code in various ways.
+//
+// There is another alternative that I tried wherby I created a subroutine
+// call and then filled in the subroutine body with 'asm' code. However, the
+// subroutine wrapper that GCC creates only works for PowerPC fast-mode
+// handlers if GCC is invoked with optimization, which ensures that the
+// wrapper doesn't touch the stack pointer or other registers. True, we'll
+// always use optimization, but I did not want to have to make this
+// requirement for using this macro.
+
+/// This macro creates a 'bridge' handler that converts the initial fast-mode
+/// IRQ dispatch into a call of a full-mode IRQ handler. The full-mode
+/// handler is defined by the user (presumably as a \c C subroutine) and has
+/// the same prototype (type PkIrqHandler) as the fast handler.
+///
+/// \param fast_handler This will be the global function name of the fast
+/// IRQ handler created by this macro. This is the symbol
+/// that should be passed in as the \a handler argument
+/// of \c pk_irq_setup() and \c pk_irq_handler_set().
+///
+/// \param full_handler This is the name of the user-defined full-mode
+/// handler which is invoked through this bridge.
+///
+/// \e BUG \e ALERT : Beware of passing the \c full_handler to IRQ setup
+/// APIs. This won't be caught by the compiler (because the \c full_handler
+/// has the correct prototype) and will lead to nasty bugs. Always pass in
+/// the \c fast_handler symbol to IRQ setup APIS.
+///
+/// The code stream injected into the GCC assembler output in response to
+///
+/// PK_IRQ_FAST2FULL(fast_handler, full_handler)
+///
+/// is (comments added for clarification) :
+///
+/// \code
+/// .text
+/// .global fast_handler
+/// .align 5 # Hard-coded PPE42 cache-line alignment
+/// fast_handler = . # Can't macro expand LABEL: - this is equivalent
+/// bl __pk_irq_fast2full # The fast-mode to full-mode conversion sequence
+/// bl full_handler
+/// b __pk_irq_full_mode_exit
+/// \endcode
+///
+/// The macro also declares the prototype of the fast handler:
+///
+/// \code
+/// PK_IRQ_HANDLER(fast_handler);
+/// \endcode
+///
+
+#define PK_IRQ_FAST2FULL(fast_handler, full_handler) \
+ PK_IRQ_HANDLER(fast_handler); \
+ __PK_IRQ_FAST2FULL(.global fast_handler, fast_handler = ., bl full_handler)
+
+#define __PK_IRQ_FAST2FULL(global, label, call) \
+asm(".text"); \
+asm(#global); \
+asm(".align 5"); \
+asm(#label); \
+asm("bl __pk_irq_fast2full"); \
+asm(#call); \
+asm("b __pk_irq_full_mode_exit");
+
+#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"
+#endif
+
+/// \page ppe42_irq_macros_page PPE42 PK IRQ Assembler Macros
+///
+///
+/// \section fast2full_asm Fast-Mode to Full-Mode Handler Conversion
+///
+/// This macro produces the calling sequence required to convert a
+/// fast-mode interrupt handler to a full-mode interrupt handler. The
+/// full-mode handler is implemented by another subroutine. The
+/// requirements for invoking this macro are:
+///
+/// \li The stack pointer and stack must be exactly as they were when the
+/// fast-mode handler was entered.
+///
+/// \li No changes have been made to the MSR - the interrupt level must
+/// remain disabled.
+///
+/// \li The handler owns the fast context and has not modified the other
+/// register context. The conversion process will not modify any
+/// register in the fast context (other than the LR used for
+/// subroutine linkage).
+///
+/// The final condition above means that the \a full_handler will
+/// begin with the fast-mode context exactly as it was (save for LR)
+/// at conversion, including the contents of GPR3-7 (the first 5
+/// PowerPC ABI paramater passing registers) and the entire CR.
+///
+/// Forms:
+///
+/// \c _pk_irq_fast2full \a full_handler
+/// \cond
+
+#ifdef __ASSEMBLER__
+
+ .macro _pk_irq_fast2full full_handler
+ bl __pk_irq_fast2full
+ bl \full_handler
+ b __pk_irq_full_mode_exit
+ .endm
+
+#endif /* __ASSEMBLER__ */
+
+/// \endcond
+
+#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/pk/ppe42/ppe42_irq_core.c b/pk/ppe42/ppe42_irq_core.c
new file mode 100644
index 00000000..8e8f29f5
--- /dev/null
+++ b/pk/ppe42/ppe42_irq_core.c
@@ -0,0 +1,47 @@
+//-----------------------------------------------------------------------------
+// *! (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 critical or
+/// non-critical 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/pk/ppe42/ppe42_msr.h b/pk/ppe42/ppe42_msr.h
new file mode 100644
index 00000000..26e8c5e0
--- /dev/null
+++ b/pk/ppe42/ppe42_msr.h
@@ -0,0 +1,81 @@
+#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
+
+#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/pk/ppe42/ppe42_scom.c b/pk/ppe42/ppe42_scom.c
new file mode 100644
index 00000000..e9fe2e6c
--- /dev/null
+++ b/pk/ppe42/ppe42_scom.c
@@ -0,0 +1,241 @@
+//-----------------------------------------------------------------------------
+// *! (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"
+
+
+uint32_t putscom_abs(uint32_t i_address, uint64_t i_data)
+{
+
+ // CMO-Declaring variables tied to specific registers enables us to protect
+ // the SCOM data and address variables used in the new stvd and lvd 64-bit
+ // scom instructions. This protection is needed since the new instructions'
+ // operands are not yet properly considered by the compiler.
+ // Note that l_dataH is used to represent the "beginning", i.e. High-order,
+ // part of the 64-bit data in d8 (i.e., r8+r9).
+ uint32_t register l_dataH asm("r8")=0;
+ uint32_t register l_dataL asm("r9")=0;
+ uint32_t register l_addr_eff asm("r10")=0;
+ uint32_t register l_scratch asm("r31")=0;
+
+ l_addr_eff = i_address;
+ l_dataH = (uint32_t)(i_data>>32);
+ l_dataL = (uint32_t)(i_data);
+
+ // CMO-The following sequence forces usage of l_dataH/L and l_addr_eff
+ // and thus the population of them as well.
+ // Further note that unless l_dataH/L are placed right before the following
+ // sequence, more specifically, if they're placed at the top of putscom(),
+ // r8, or l_dataH, might be overwritten in the if(chiplet_id) section.
+ // Secondly, we test l_addr_eff for non-zero through the CR0 register
+ // (which was populated in the "mr." instruction.) This is to convince the
+ // compiler that we actually used l_addr_eff for something.
+ // At present the test result causes no action except to execute the stvd
+ // instruction in either case.
+ asm volatile ( \
+ "mr. %0, %1 \n" \
+ : "=r"(l_scratch) \
+ : "r"(l_dataH) );
+ asm volatile ( \
+ "mr. %0, %1 \n" \
+ : "=r"(l_scratch) \
+ : "r"(l_dataL) );
+ asm volatile ( \
+ "mr. %0, %1 \n" \
+ : "=r"(l_scratch) \
+ : "r"(l_addr_eff) );
+ asm volatile ( \
+ "beq 0x4 \n" );
+
+ // CMO-This instruction is not fully supported by the compiler (as of
+ // 20150108):
+ // - Works: It is correctly translated into the proper OP code
+ // format.
+ // - Works not: The compiler does not seem to recognize the usage
+ // of the two l_xyz variables in that it doesn't
+ // know prior to this command that the registers that
+ // contain the values of l_xyz need to be protected
+ // up to this point. Thus, we are forced to use those
+ // two l_xyz variables in some dummy instructions just
+ // before this point in order to fake protection.
+ asm volatile ( \
+ "stvd %[data], 0(%[effective_address]) \n" \
+ : [data]"=r"(l_dataH) \
+ : [effective_address]"r"(l_addr_eff) );
+
+ // CMO-TBD
+ // Check PIB response code in 0x00001007(17:19)
+ // Translate PIB rc to PK rc
+ // Does this rc get reset to zero on success?
+ // Do we need to check this rc prior to issuing the SCOM?
+
+ return 0;
+}
+
+uint32_t _putscom( uint32_t i_chiplet_id, uint32_t i_address, uint64_t i_data)
+{
+ uint32_t l_rc=0;
+ uint32_t l_cid=0;
+ uint32_t l_addr_eff=0;
+
+ if (i_chiplet_id)
+ {
+ // Accommodate two different ways of supplying the chiplet ID:
+ // 0xNN000000: Only bits in high-order two nibbles : Valid
+ // 0x000000NN: Only bits in low-order two nibbles : Valid
+ //
+ if ((i_chiplet_id & 0xFF000000) == i_chiplet_id)
+ {
+ // Valid: Chiplet ID in high-order two nibbles.
+ l_cid = i_chiplet_id;
+ }
+ else if ((i_chiplet_id & 0x000000FF) == i_chiplet_id)
+ {
+ // Valid: Chiplet ID in low-order two nibbles. Convert to high-order.
+ l_cid = i_chiplet_id << 24;
+ }
+ else
+ {
+ // Invalid: Invalid type of chiplet ID
+ PK_TRACE("putscom() : Invalid value of i_chiplet_id (=0x%08X)",i_chiplet_id);
+ return 1; //CMO-improve Return sensible rc here.
+ }
+
+ l_addr_eff = (i_address & 0x00FFFFFF) | l_cid;
+ }
+ else
+ {
+ // Chiplet ID is zero. Accept address as is.
+ // This is useful for PIB addresses and non-EX chiplets, and even for
+ // EX chiplets if the fully qualified EX chiplet addr is already known.
+ l_addr_eff = i_address;
+
+ }
+
+ l_rc = putscom_abs(l_addr_eff, i_data);
+
+
+ return l_rc;
+}
+
+
+uint32_t getscom_abs( uint32_t i_address, uint64_t *o_data)
+{
+
+ // CMO-Declaring variables tied to specific registers enables us to protect
+ // the SCOM data and address variables used in the new stvd and lvd 64-bit
+ // data instructions. This protection is needed since the new instructions
+ // are not yet properly considered by the compiler.
+ // Note that l_dataH is used to represent the "beginning", i.e. High-order,
+ // part of the 64-bit data in d8 (i.e., r8+r9).
+ uint32_t register l_dataH asm("r8")=0;
+ uint32_t register l_dataL asm("r9")=0;
+ uint32_t register l_addr_eff asm("r10")=0;
+ uint32_t register l_scratch asm("r31")=0;
+
+
+ l_addr_eff = i_address;
+
+ // CMO-The following sequence forces usage of l_addr_eff and thus the
+ // population of it as well.
+ // Secondly, we test l_addr_eff for non-zero through the CR0 register
+ // (which was populated in the "mr." instruction.) This is to convince the
+ // compiler that we actually used l_addr_eff for something.
+ // At present the test result causes no action except to execute the lvd
+ // instruction in either case.
+ asm volatile ( \
+ "mr. %0, %1 \n" \
+ : "=r"(l_scratch) \
+ : "r"(l_addr_eff) );
+ asm volatile ( \
+ "beq 0x4 \n" );
+
+ asm volatile ( \
+ "lvd %[data], 0(%[effective_address]) \n" \
+ : [data]"=r"(l_dataH) \
+ : [effective_address]"r"(l_addr_eff) );
+
+ // CMO-The following sequence moves the read data, in l_dataH/L, into the
+ // 64-bit o_data location.
+ asm volatile ( \
+ "stw %0, 0(%1) \n" \
+ : "=r"(l_dataH) \
+ : "r"(o_data) );
+ asm volatile ( \
+ "stw %0, 4(%1) \n" \
+ : "=r"(l_dataL) \
+ : "r"(o_data) );
+
+ // CMO-TBD
+ // Check PIB response code in 0x00001007(17:19)
+ // Translate PIB rc to PK rc
+
+ return 0;
+}
+
+uint32_t _getscom( uint32_t i_chiplet_id, uint32_t i_address, uint64_t *o_data)
+{
+ uint32_t l_rc=0;
+ uint32_t l_cid=0;
+ uint32_t l_addr_eff=0;
+
+ if (i_chiplet_id)
+ {
+ // Accommodate two different ways of supplying the chiplet ID:
+ // 0xNN000000: Only bits in high-order two nibbles : Valid
+ // 0x000000NN: Only bits in low-order two nibbles : Valid
+ //
+ if ((i_chiplet_id & 0xFF000000) == i_chiplet_id)
+ {
+ // Valid: Chiplet ID in high-order two nibbles.
+ l_cid = i_chiplet_id;
+ }
+ else if ((i_chiplet_id & 0x000000FF) == i_chiplet_id)
+ {
+ // Valid: Chiplet ID in low-order two nibbles. Convert to high-order.
+ l_cid = i_chiplet_id << 24;
+ }
+ else
+ {
+ // Invalid: Invalid type of chiplet ID
+ PK_TRACE("getscom() : Invalid value of i_chiplet_id (=0x%08X)",i_chiplet_id);
+ return 1; //CMO-improve Return sensible rc here.
+ }
+
+ l_addr_eff = (i_address & 0x00FFFFFF) | l_cid;
+ }
+ else
+ {
+ // Chiplet ID is zero. Accept address as is.
+ // This is useful for PIB addresses and non-EX chiplets, and even for
+ // EX chiplets if the fully qualified EX chiplet addr is already known.
+ l_addr_eff = i_address;
+ }
+
+ l_rc = getscom_abs(l_addr_eff, o_data);
+
+ // CMO-TBD
+ // Check PIB response code in 0x00001007(17:19)
+ // Translate PIB rc to PK rc
+
+ return l_rc;
+}
diff --git a/pk/ppe42/ppe42_scom.h b/pk/ppe42/ppe42_scom.h
new file mode 100644
index 00000000..0d57e5d4
--- /dev/null
+++ b/pk/ppe42/ppe42_scom.h
@@ -0,0 +1,87 @@
+//-----------------------------------------------------------------------------
+// *! (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
+
+/// @brief putscom with absolute address
+/// @param [in] i_address Fully formed SCOM address
+/// @param [in] i_data uint64_t data to be written
+///
+/// @retval On PPE42 platform, unmasked errors will take machine check interrupts
+uint32_t putscom_abs( 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( 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 uint64_t data to be written
+///
+/// @retval On PPE42 platform, unmasked errors will take machine check interrupts
+uint32_t _putscom( uint32_t i_chiplet, 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);
+
+
+/// Macro to abstract the underlying putscom function so that it might be replaced
+/// later with different implementations. Used directly by low level PPE calls
+/// but also used by the FAPI2 API implementation
+/*
+#define putscom (_m_chiplet, _m_address, _m_data) { \
+ _putscom( _m_chiplet, _m_address, _m_data); \
+};
+*/
+extern inline uint32_t putscom(uint32_t i_chiplet, uint32_t i_address, uint64_t i_data)
+{
+ return _putscom(i_chiplet, i_address, i_data);
+}
+
+/// Macro to abstract the underlying getscom function so that it might be replaced
+/// later with different implementations. Used directly by low level PPE calls
+/// but also used by the FAPI2 API implementation
+/*
+#define getscom (i_chiplet, i_address, o_data) { \
+ _getscom( i_chiplet, i_address, o_data); \
+};
+*/
+extern inline uint32_t getscom(uint32_t i_chiplet, uint32_t i_address, uint64_t *o_data)
+{
+ return _getscom(i_chiplet, i_address, o_data);
+}
+
+#endif // __PK_SCOM_H__
diff --git a/pk/ppe42/ppe42_spr.h b/pk/ppe42/ppe42_spr.h
new file mode 100644
index 00000000..b9a189e0
--- /dev/null
+++ b/pk/ppe42/ppe42_spr.h
@@ -0,0 +1,173 @@
+#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__
+
+ /// \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
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __PPE42_SPR_H__ */
diff --git a/pk/ppe42/ppe42_thread_init.S b/pk/ppe42/ppe42_thread_init.S
new file mode 100644
index 00000000..6e6c34fe
--- /dev/null
+++ b/pk/ppe42/ppe42_thread_init.S
@@ -0,0 +1,108 @@
+/// \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 a fast 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_FAST_CTX_SIZE(%r6)
+
+ li %r7, 0
+ stw %r7, PK_FAST_CTX_CR(%r6)
+
+ _liw %r7, pk_complete
+ stw %r7, PK_FAST_CTX_LR(%r6)
+
+ stw %r5, PK_FAST_CTX_GPR3(%r6)
+
+ _gpr_init PK_FAST_CTX_GPR, 4, 0x0404
+ _gpr_init PK_FAST_CTX_GPR, 5, 0x0505
+ _gpr_init PK_FAST_CTX_GPR, 6, 0x0606
+
+ ## Initialize the (volatile - fast) context on the thread stack. XER
+ ## and CTR are clear, SRR0 = thread_routine, SRR1 = default machine
+ ## context.
+
+ stwu %r6, -PK_VOL_FAST_CTX_SIZE(%r6)
+
+ li %r7, 0
+ stw %r7, PK_VOL_FAST_CTX_XER(%r6)
+ stw %r7, PK_VOL_FAST_CTX_CTR(%r6)
+
+ stw %r4, PK_VOL_FAST_CTX_SRR0(%r6)
+
+ _lwzsd %r7, __pk_thread_machine_context_default
+ stw %r7, PK_VOL_FAST_CTX_SRR1(%r6)
+
+ _gpr_init PK_VOL_FAST_CTX_GPR, 0, 0x0000
+ _gpr_init PK_VOL_FAST_CTX_GPR, 7, 0x0707
+ _gpr_init PK_VOL_FAST_CTX_GPR, 8, 0x0808
+ _gpr_init PK_VOL_FAST_CTX_GPR, 9, 0x0909
+ _gpr_init PK_VOL_FAST_CTX_GPR, 10, 0x1010
+
+ ## Initialize the non-volatile context on the thread stack.
+
+ stwu %r6, -PK_NON_VOL_CTX_SIZE(%r6)
+
+ _gpr_init PK_NON_VOL_CTX_GPR, 28, 0x2828
+ _gpr_init PK_NON_VOL_CTX_GPR, 29, 0x2929
+ _gpr_init PK_NON_VOL_CTX_GPR, 30, 0x3030
+ _gpr_init PK_NON_VOL_CTX_GPR, 31, 0x3131
+
+ ## 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/pk/ppe42/ppe42_timebase.S b/pk/ppe42/ppe42_timebase.S
new file mode 100644
index 00000000..16c59529
--- /dev/null
+++ b/pk/ppe42/ppe42_timebase.S
@@ -0,0 +1,82 @@
+/// \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
+
+//Use the DEC for our timebase until we have a real timebase register (uses 9 instructions)
+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
+ cmplw %r6, %r7
+ bne 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
+#if 0
+
+// use the local timebase register to keep more accurate time with just 6 instructions
+// in the common case and 7 otherwise.
+pk_timebase_get:
+
+ //load the timebase local 32bit register address
+ lwz r5, timebase_local_reg@sda21(0)
+
+ //load the 64bit timebase accumulator
+ lvd r3, ppe42_64bit_timebase@sda21(0)
+
+ //read the local timebase register
+ lwz r5, 0(r5)
+
+ //increment the upper 32 bits if the lower 32 bits have flipped
+ cmplwbgt r5, r4, update_lower_32
+ addi r3, 1 //update upper 32
+
+update_lower_32:
+ //replace the lower 32bits with what we read from the local timebase register
+ mr r4, r6
+
+ blr
+#endif
+/// \endcond
diff --git a/pk/trace/Makefile b/pk/trace/Makefile
new file mode 100644
index 00000000..69e95ebd
--- /dev/null
+++ b/pk/trace/Makefile
@@ -0,0 +1,26 @@
+# 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/pk/trace/pk_trace.h b/pk/trace/pk_trace.h
new file mode 100644
index 00000000..14a7278b
--- /dev/null
+++ b/pk/trace/pk_trace.h
@@ -0,0 +1,276 @@
+#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 1
+
+#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
+
+//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;
+
+ //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/pk/trace/pk_trace_big.c b/pk/trace/pk_trace_big.c
new file mode 100644
index 00000000..4ba212da
--- /dev/null
+++ b/pk/trace/pk_trace_big.c
@@ -0,0 +1,92 @@
+//-----------------------------------------------------------------------------
+// *! (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/pk/trace/pk_trace_binary.c b/pk/trace/pk_trace_binary.c
new file mode 100644
index 00000000..58b417a3
--- /dev/null
+++ b/pk/trace/pk_trace_binary.c
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------
+// *! (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/pk/trace/pk_trace_core.c b/pk/trace/pk_trace_core.c
new file mode 100644
index 00000000..3f416c75
--- /dev/null
+++ b/pk/trace/pk_trace_core.c
@@ -0,0 +1,152 @@
+//-----------------------------------------------------------------------------
+// *! (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.next = 0,
+ .deque.previous = 0,
+ .timeout = 0,
+ .period = 0,
+ .callback = pk_trace_timer_callback,
+ .arg = 0,
+ .options = PK_TIMER_CALLBACK_PREEMPTIBLE,
+};
+
+//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_adal_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
+ .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.
+void pk_trace_timer_callback(void* arg)
+{
+
+#if 0
+ PkTraceTime64_t footer;
+ PkTraceState state;
+ uint64_t* ptr64;
+ PkMachineContext ctx;
+
+#define TIMESTAMP64_EXISTS 0x80000000
+
+ //If the timestamp64 flag is not set then we need another 64 bit timestamp
+ if(!(g_pk_trace_buf.state.tbu32 & TIMESTAMP64_EXISTS))
+ {
+
+ //fill in the footer data
+ footer.word64 = pk_timebase_get();
+ footer.time_format.format = PK_TRACE_FORMAT_TIME64;
+
+ state.tbu32 = footer.upper32 | TIMESTAMP64_EXISTS;
+
+ //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 64bit timestamp to the buffer
+ *ptr64 = footer.word64;
+
+ //exit the critical section
+ pk_critical_section_exit(&ctx);
+
+ }
+
+#else
+
+ // doing it this way requires less code, but it also means that the
+ // trace can fill up with these traces over time.
+
+ PK_TRACE("PERIODIC TIMESTAMPING TRACE");
+#endif
+
+ // restart the timer
+ pk_timer_schedule(&g_pk_trace_timer,
+ PK_TRACE_TIMER_PERIOD,
+ 0);
+}
+
+#endif
diff --git a/pk/trace/pktracefiles.mk b/pk/trace/pktracefiles.mk
new file mode 100644
index 00000000..e3aede76
--- /dev/null
+++ b/pk/trace/pktracefiles.mk
@@ -0,0 +1,39 @@
+# @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)
+
+
+
OpenPOWER on IntegriCloud