diff options
93 files changed, 17744 insertions, 0 deletions
diff --git a/import/chips/p9/procedures/ppe/pk/README b/import/chips/p9/procedures/ppe/pk/README new file mode 100644 index 00000000..2227ec37 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/README @@ -0,0 +1,8 @@ +Subdirectories are organized as follows: + +kernel: Architecture-independent kernel code +ppe42: Kernel code that is common for all ppe42 instances +std: External interrupt support for standard ppe42 instances (SBE, CME, etc.) +gpe: External interrupt support for ppe42 instances residing in the OCC complex +ppe: Support for ppe42 instances that do not require or have external interrupts +trace: Kernel trace support diff --git a/import/chips/p9/procedures/ppe/pk/gpe/Makefile b/import/chips/p9/procedures/ppe/pk/gpe/Makefile new file mode 100644 index 00000000..d30e07ec --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/Makefile @@ -0,0 +1,74 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/gpe/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile compiles all of the PK code required for the 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/import/chips/p9/procedures/ppe/pk/gpe/gpe.h b/import/chips/p9/procedures/ppe/pk/gpe/gpe.h new file mode 100644 index 00000000..85ab0ea4 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe.h @@ -0,0 +1,59 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __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/import/chips/p9/procedures/ppe/pk/gpe/gpe_common.h b/import/chips/p9/procedures/ppe/pk/gpe/gpe_common.h new file mode 100644 index 00000000..9e982663 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe_common.h @@ -0,0 +1,91 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe_common.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __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__ +// *INDENT-OFF* +/// 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). +/// +/// r1, r2, r3, and r13 must not be modified. All other registers may be used. +/// + .macro hwmacro_get_ext_irq + + _lwzi %r5, %r5, GPE_GISR0(APPCFG_OCC_INSTANCE_ID) + cntlzw %r4, %r5 + cmpwible %r4, 31, call_external_irq_handler #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 %r6, %r6, GPE_GISR1(APPCFG_OCC_INSTANCE_ID) + cntlzw %r4, %r6 + 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 +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +#endif /* __GPE_COMMON_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/gpe/gpe_init.c b/import/chips/p9/procedures/ppe/pk/gpe/gpe_init.c new file mode 100644 index 00000000..3f1b5879 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe_init.c @@ -0,0 +1,146 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file 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(); + +} diff --git a/import/chips/p9/procedures/ppe/pk/gpe/gpe_irq.h b/import/chips/p9/procedures/ppe/pk/gpe/gpe_irq.h new file mode 100644 index 00000000..ad590bf7 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe_irq.h @@ -0,0 +1,276 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe_irq.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __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__ +// *INDENT-OFF* + + .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 + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +/// \endcond + +#endif /* __GPE_IRQ_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/gpe/gpe_irq_init.c b/import/chips/p9/procedures/ppe/pk/gpe/gpe_irq_init.c new file mode 100644 index 00000000..f743ab80 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe_irq_init.c @@ -0,0 +1,135 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe_irq_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file 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/import/chips/p9/procedures/ppe/pk/gpe/gpe_scom_handler.S b/import/chips/p9/procedures/ppe/pk/gpe/gpe_scom_handler.S new file mode 100644 index 00000000..7d42af37 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe_scom_handler.S @@ -0,0 +1,120 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe_scom_handler.S $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file gpe_scom_handler.S +/// \brief Interrupt handler code for SCOM requests from the ppc405 +/// + + .nolist +#include "pk.h" +#include "occhw_shared_data.h" + .list + + ## This function handles requests from the ppc405 to perform a getscom + ## or putscom operation. + ## + ## The ppc405 must supply a request in the following format: + ## + ## bytes : Content + ## ------------------- + ## 0-3 : Status + ## 4-7 : cmd/address (bit 0 is r/!w bit) + ## 8-15 : data (supplied by ppc405 on writes) + ## + ## Register Usage: + ## + ## r3: SRAM base address + ## r4: SCOM address + ## r5: MSR manipulation + ## d5: SCOM data + ## + ## NOTE: getscom requires 2 IC lines and 1 DC line + ## putscom requires 3 IC lines and 1 DC line + ## Both require the same number of instructions (16) + .align 5 + .global gpe_scom_handler +gpe_scom_handler: + + ## Load the hardcoded base address of the scom request + _liwa %r3, OSD_GPE_SCOM_ADDR + + ## Load the scom address from SRAM + lwz %r4, OCCHW_SCOM_ADDR_OFFSET(%r3) + + ## Mask all SIB errors + mfmsr %r7 + _oriwa %r5, %r7, MSR_SEM + mtmsr %r5 + + ## Check bit 0 of the scom address to determine if this + ## is a getscom or a putscom request + bb0wi %r4, 0, _do_putscom #branch if bit 0 is 0 + +_do_getscom: + ## the cmd bit was set, which means we're doing a getscom. + ## need to clear the bit first. + clrbwi %r4, %r4, 0 + + ## do the getscom + lvd %d5, 0(%r4) + + ## store the data into the request in SRAM + stvd %d5, OCCHW_SCOM_DATA_OFFSET(%r3) + +_get_scom_status: + + ## status is in the MSR, copy it to the scom request in SRAM + mfmsr %r5 + stw %r5, OCCHW_SCOM_STATUS_OFFSET(%r3) + + ## clear the IPI_SCOM interrupt: + ## first, load the bit we want to clear into a register + _liwa %r3, OCCHW_IRQ_MASK32(OCCHW_IRQ_IPI_SCOM) + + ## then, store it to the OISR0_CLR address + _stwi %r3, %r4, OCB_OISR0_CLR + + ## restore the MSR as it was before we changed it + mtmsr %r7 + + ## return + blr + +_do_putscom: + + ## load data from the request + lvd %d5, OCCHW_SCOM_DATA_OFFSET(%r3) + + ## do the putscom + stvd %d5, 0(%r4) + + b _get_scom_status + + diff --git a/import/chips/p9/procedures/ppe/pk/gpe/gpe_timebase.h b/import/chips/p9/procedures/ppe/pk/gpe/gpe_timebase.h new file mode 100644 index 00000000..73a40da8 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/gpe_timebase.h @@ -0,0 +1,64 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/gpe_timebase.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __GPE_TIMEBASE_H__ +#define __GPE_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file gpe_timebase.h +/// \brief support for using the OCB 32 bit timebase register +/// +/// The OCB timebase register is a 32 bit count-up register that is shared +/// by all GPE's in the OCC complex. + +#include "pk.h" + +#ifndef __ASSEMBLER__ + +#ifdef APPCFG_USE_EXT_TIMEBASE +static inline +uint32_t pk_timebase32_get(void) +{ + return in32(OCB_OTBR); +} + +#else +//assembly function is defined in ppe42_timebase.S +uint32_t pk_timebase32_get(void); + +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#else + +.macro _pk_timebase32_get rT, rA +_lwzi \rT, \rA, OCB_OTBR +.endm +#endif /* __ASSEMBLER__ */ + +#endif /* __GPE_TIMEBASE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/gpe/pk_port.h b/import/chips/p9/procedures/ppe/pk/gpe/pk_port.h new file mode 100644 index 00000000..03d22e9f --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/pk_port.h @@ -0,0 +1,40 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/gpe/pk_port.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_PORT_H__ +#define __PK_PORT_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_port.h +/// \brief The top-level GPE environment header for PK. + +#define HWMACRO_GPE + +#include "ppe42.h" +#include "gpe_timebase.h" +#endif /* __PK_PORT_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/gpe/pkgpefiles.mk b/import/chips/p9/procedures/ppe/pk/gpe/pkgpefiles.mk new file mode 100644 index 00000000..38a36795 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/gpe/pkgpefiles.mk @@ -0,0 +1,56 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/gpe/pkgpefiles.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file 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_scom_handler.S + +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/import/chips/p9/procedures/ppe/pk/kernel/Makefile b/import/chips/p9/procedures/ppe/pk/kernel/Makefile new file mode 100644 index 00000000..4098178e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/Makefile @@ -0,0 +1,50 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/kernel/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile is designed to be invoked with the -I argument set to +# the location of the "pk.mk" for the build + +include img_defs.mk +include pkkernelfiles.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +PK_OBJECTS += ${PK-TIMER-C-SOURCES:.c=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +PK_OBJECTS += ${PK-THREAD-C-SOURCES:.c=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(PK_OBJECTS)) + +all: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk.h b/import/chips/p9/procedures/ppe/pk/kernel/pk.h new file mode 100644 index 00000000..e6e029f3 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk.h @@ -0,0 +1,149 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_H__ +#define __PK_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk.h +/// \brief The combined header of the PK kernel. +/// +/// This header will be included in any C or assembler source file that +/// requires any of the PK API. All headers defined by PK and co-compiled +/// code should be protected such that they can be included without error into +/// assembly. + +#ifndef __ASSEMBLER__ + #include <stdint.h> + #include <stddef.h> +#endif /* __ASSEMBLER__ */ + +#ifndef __PK__ + #define __PK__ 1 +#endif + +/// The application environment specifies whether or not it will provide an +/// application configuration file, which must be named "pk_app_cfg.h". + +#ifndef USE_PK_APP_CFG_H + #define USE_PK_APP_CFG_H 0 +#endif + +#if USE_PK_APP_CFG_H + #include "pk_app_cfg.h" +#endif + +#include "pk_macros.h" +#include "pk_api.h" +#include "pk_port.h" +#include "pk_kernel.h" +//#include "pk_io.h" + +#ifndef __ASSEMBLER__ + +#define MIN(X, Y) \ + ({ \ + typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x < __y) ? __x : __y; }) + +#define MAX(X, Y) \ + ({ \ + typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x > __y) ? __x : __y; \ + }) + +/// \todo These don't require 32/64 bit versions, can always promote 32->64. + +#define FLOOR_LOG2_32(x) (32 - 1 - cntlz32(x)) +#define FLOOR_LOG2_64(x) (64 - 1 - cntlz64(x)) + +#define CEILING_LOG2(x) \ + ({ \ + uint64_t __x = (uint64_t)(x); \ + int __y; \ + __y = FLOOR_LOG2_64(__x); \ + if ((__x & (__x - 1)) != 0) { \ + __y++; \ + } \ + __y;}) + + +#define POW2_32(x) ((uint32_t)1 << (x)) +#define POW2_64(x) ((uint64_t)1 << (x)) + +/// Cast a pointer to another type +/// +/// This macro is necessary when casting a pointer to a longer integer type. +/// The pointer is first cast to the equivalent integer type 'unsigned long', +/// then cast to the final type. You can also use this to cast integers longer +/// than pointers back to pointers. + +#define CAST_POINTER(t, p) ((t)((unsigned long)(p))) + + +/// Create an alignment attribute. +#define ALIGNED_ATTRIBUTE(alignment) __attribute__ ((aligned (alignment))) + +/// Create a specific-section attribute +/// +/// Note that the section \a s here must be a string. Also note that data +/// specified to reside in specific sections must always be +/// initialized. Otherwise it confuses the linker which wants to put +/// uninitialized data into .bss sections. +/// +/// \code +/// +/// int foo SECTION_ATTRIBUTE(".noncacheable") = 0; +/// int bar[10] SECTION_ATTRIBUTE(".noncacheable") = {0}; +/// +/// \endcode +#define SECTION_ATTRIBUTE(s) __attribute__ ((section (s))) + +/// Create a 'used' attribute +/// +/// This is required for example to avoid "function unused" warnings when a +/// function is declared static but only referenced by inline assembler: +/// +/// \code +/// +/// static USED_ATTRIBUTE void +/// _checkstop(void* arg, PkIrqId irq, int priority) +/// { +/// PK_PANIC(VALIDATION_CHECKSTOP); +/// } +/// +/// PK_IRQ_FAST2FULL(_validationCheckstopHandler, _checkstop); +/// +/// \endcode +#define USED_ATTRIBUTE __attribute__ ((used)) + +#endif /* __ASSEMBLER__ */ + +#endif /* __PK_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_api.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_api.h new file mode 100644 index 00000000..da6e7b4a --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_api.h @@ -0,0 +1,1050 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_api.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_API_H__ +#define __PK_API_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_api.h +/// \brief Macros and declarations for the PK API. + +// Basic constants + +/// Although the number of threads is defined as a manifest constant, +/// numerous parts of the PK code assume this definition. The number of +/// supported threads _can not_ be changed simply by changing this constant. + +#define PK_THREADS 32 + +#define PK_IDLE_THREAD_PRIORITY PK_THREADS + +// Interrupt API + +#define PK_IRQ_POLARITY_ACTIVE_LOW 0 +#define PK_IRQ_POLARITY_ACTIVE_HIGH 1 + +#define PK_IRQ_TRIGGER_LEVEL_SENSITIVE 0 +#define PK_IRQ_TRIGGER_EDGE_SENSITIVE 1 + +// API return codes + +#define PK_OK 0 +#define PK_ILLEGAL_CONTEXT_THREAD_CONTEXT 0x00779002 +#define PK_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT 0x00779003 +#define PK_ILLEGAL_CONTEXT_THREAD 0x00779004 +#define PK_ILLEGAL_CONTEXT_TIMER 0x00779005 +#define PK_INVALID_THREAD_AT_RESUME1 0x00779007 +#define PK_INVALID_THREAD_AT_RESUME2 0x00779008 +#define PK_INVALID_THREAD_AT_SUSPEND1 0x00779009 +#define PK_INVALID_THREAD_AT_SUSPEND2 0x0077900a +#define PK_INVALID_THREAD_AT_DELETE 0x0077900b +#define PK_INVALID_THREAD_AT_INFO 0x0077900c +#define PK_INVALID_THREAD_AT_CHANGE 0x0077900d +#define PK_INVALID_THREAD_AT_SWAP1 0x0077900e +#define PK_INVALID_THREAD_AT_SWAP2 0x0077900f +#define PK_INVALID_THREAD_AT_CREATE 0x00779010 +#define PK_INVALID_SEMAPHORE_AT_POST 0x00779011 +#define PK_INVALID_SEMAPHORE_AT_PEND 0x00779012 +#define PK_INVALID_SEMAPHORE_AT_RELEASE 0x00779013 +#define PK_INVALID_SEMAPHORE_AT_INFO 0x00779014 +#define PK_INVALID_SEMAPHORE_AT_CREATE 0x00779015 +#define PK_INVALID_TIMER_AT_SCHEDULE 0x00779016 +#define PK_INVALID_TIMER_AT_CANCEL 0x00779017 +#define PK_INVALID_TIMER_AT_INFO 0x00779018 +#define PK_INVALID_TIMER_AT_CREATE 0x00779019 +#define PK_INVALID_ARGUMENT_IRQ_SETUP 0x0077901a +#define PK_INVALID_ARGUMENT_IRQ_HANDLER 0x0077901b +#define PK_INVALID_ARGUMENT_INTERRUPT 0x00779024 +#define PK_INVALID_ARGUMENT_CONTEXT_SET 0x00779025 +#define PK_INVALID_ARGUMENT_CONTEXT_GET 0x00779026 +#define PK_INVALID_ARGUMENT_FIT 0x00779027 +#define PK_INVALID_ARGUMENT_WATCHDOG 0x00779028 +#define PK_INVALID_ARGUMENT_INIT 0x00779029 +#define PK_INVALID_ARGUMENT_SEMAPHORE 0x0077902a +#define PK_INVALID_ARGUMENT_THREAD_CHANGE 0x0077902b +#define PK_INVALID_ARGUMENT_THREAD_PRIORITY 0x0077902c +#define PK_INVALID_ARGUMENT_THREAD1 0x0077902d +#define PK_INVALID_ARGUMENT_THREAD2 0x0077902e +#define PK_INVALID_ARGUMENT_THREAD3 0x0077902f +#define PK_STACK_OVERFLOW 0x00779030 +#define PK_TIMER_ACTIVE 0x00779031 +#define PK_TIMER_NOT_ACTIVE 0x00779032 +#define PK_PRIORITY_IN_USE_AT_RESUME 0x00779033 +#define PK_PRIORITY_IN_USE_AT_CHANGE 0x00779034 +#define PK_PRIORITY_IN_USE_AT_SWAP 0x00779035 +#define PK_SEMAPHORE_OVERFLOW 0x00779036 +#define PK_SEMAPHORE_PEND_NO_WAIT 0x00779037 +#define PK_SEMAPHORE_PEND_TIMED_OUT 0x00779038 +#define PK_SEMAPHORE_PEND_WOULD_BLOCK 0x00779039 +#define PK_INVALID_DEQUE_SENTINEL 0x0077903a +#define PK_INVALID_DEQUE_ELEMENT 0x0077903b +#define PK_INVALID_OBJECT 0x0077903c + +// Kernel panics + +#define PK_NO_TIMER_SUPPORT 0x0077903d +#define PK_START_THREADS_RETURNED 0x0077903e +#define PK_UNIMPLEMENTED 0x0077903f +#define PK_SCHEDULING_INVARIANT 0x00779040 +#define PK_TIMER_HANDLER_INVARIANT 0x00779041 +#define PK_THREAD_TIMEOUT_STATE 0x00779045 + +// Application-level panic offsets +// (Use these as offsets for your application code panics and keep +// track of them locally in your application code domain, including +// sharing the panic defines with other developers making codes +// for the same engine.) + +#define PK_APP_OFFSET_SBE 0x0077a000 +#define PK_APP_OFFSET_GPE0 0x0077b000 +#define PK_APP_OFFSET_GPE1 0x0077c000 +#define PK_APP_OFFSET_GPE2 0x0077d000 +#define PK_APP_OFFSET_GPE3 0x0077e000 +#define PK_APP_OFFSET_CME 0x0077f000 + +/// \defgroup pk_thread_states PK Thread States +/// +/// Threads are created in the state PK_THREAD_STATE_SUSPENDED_RUNNABLE. +/// When the thread is mapped it transitions to state PK_THREAD_STATE_MAPPED. +/// A mapped thread is runnable if it appears in the run queue; there is no +/// other flag or status to indicate a runnable thread. If a blocked thread +/// is suspended it goes into state PK_THREAD_STATE_SUSPENDED_BLOCKED. For +/// all threads the reason for blockage is detailed in the \a flags field of +/// the thread; See \ref pk_thread_flags. PK_THREAD_STATE_DELETED and +/// PK_THREAD_STATE_COMPLETED are effectively equivalent but named +/// individually for reporting purposes. +/// +/// \note This separation of the thread \a state and \a flags allows the use +/// of an PK semaphore as a thread barrier, as it supports a non-iterative +/// implementation of pk_semaphore_release_all() in which all threads blocked +/// on the semaphore are simultaneously inserted into the run queue with an +/// atomic operation, followed by each individual thread readjusting its flags +/// appropriately once the thread runs again. +/// +/// @{ + +#define PK_THREAD_STATE_SUSPENDED_RUNNABLE 1 +#define PK_THREAD_STATE_MAPPED 2 +#define PK_THREAD_STATE_SUSPENDED_BLOCKED 3 +#define PK_THREAD_STATE_COMPLETED 4 +#define PK_THREAD_STATE_DELETED 5 + +/// @} + + +/// \defgroup pk_thread_flags PK Thread Flags +/// +/// The \a flag field of the thread extends the information contained in the +/// \a state field; See \ref pk_thread_states. Blocked threads will show +/// PK_THREAD_FLAG_SEMAPHORE_PEND, PK_THREAD_FLAG_TIMER_PEND or both (if +/// blocked on a semaphore with timeout). The flag PK_THREAD_FLAG_TIMED_OUT +/// indicates that a thread timer timed out before the thread became +/// runnable. Currently only the semaphore-pend-with-timeout code uses this +/// flag. +/// +/// Note that a thread can be mapped and runnable (in the run queue) even +/// though PK_THREAD_FLAG_SEMAPHORE_PEND and/or PK_THREAD_FLAG_TIMER_PEND +/// are set. These flags are always cleared by the thread itself, not the code +/// that unblocks the thread. This allows the implementation of the +/// pk_semaphore_release_all() as explained in \ref pk_thread_states. +/// +/// @{ + +#define PK_THREAD_FLAG_SEMAPHORE_PEND 0x1 +#define PK_THREAD_FLAG_TIMER_PEND 0x2 +#define PK_THREAD_FLAG_TIMED_OUT 0x4 + +/// @} + + +// Critical Sections + +/// Enter a critical section, saving the current machine +/// context. + +#define pk_critical_section_enter(pctx) \ + pk_interrupt_disable(pctx) + +/// Exit a critical section by restoring the previous machine context. + +#define pk_critical_section_exit(pctx) \ + pk_machine_context_set(pctx) + + +/// Execute a statement atomically + +#define PK_ATOMIC(stmt) \ + do { \ + PkMachineContext __ctx; \ + pk_critical_section_enter(&__ctx); \ + stmt; \ + pk_critical_section_exit(&__ctx); \ + } while (0) + + +// Application-overrideable definitions + +/// Control whether or not the API functions check for errors. +/// +/// This definition can be overriden by the application. + +#ifndef PK_ERROR_CHECK_API + #define PK_ERROR_CHECK_API 1 +#endif + +/// Control whether API errors cause kernel panics or return negative error +/// codes. +/// +/// This selection is only valid if \c PK_ERROR_CHECK_API is defined +/// non-0. This definition can be overriden by the application. + +#ifndef PK_ERROR_PANIC + #define PK_ERROR_PANIC 1 +#endif + +/// Control whether or not the PK kernel checks key invariants. +/// +/// Violations of kernel invariants always cause kernel panics. This +/// definition can be overriden by the application. + +#ifndef PK_ERROR_CHECK_KERNEL + #define PK_ERROR_CHECK_KERNEL 1 +#endif + +/// Define the time interval type, which must be an unsigned type of a size +/// less then or equal to the size of \c PkTimebase. This definition can be +/// overridden by the application. + +#ifndef PK_TIME_INTERVAL_TYPE + #define PK_TIME_INTERVAL_TYPE uint32_t +#endif + +/// Provide support for the PkTimer APIs in addition to the default +/// initerrupt APIs. This definition can be overridden by the application. + +#ifndef PK_TIMER_SUPPORT + #define PK_TIMER_SUPPORT 1 +#endif + +/// Provide support for the all PK APIs. Thread support requires/implies +/// support for time services and semaphores. This definition can be +/// overridden by the application. + +#ifndef PK_THREAD_SUPPORT + #define PK_THREAD_SUPPORT 1 +#endif + +/// Control the level of stack checking. +/// +/// This definition can be overriden by the application. +/// +/// 0 : No stack prepatterning or checking is made for thread and kernel +/// stacks. +/// +/// 1 : Kernel interrupt stacks are prepatterned during +/// \c pk_initialize(). Thread stacks are prepatterned during +/// \c pk_thread_create(). +/// +/// 2 : (\b Default - Currently Unimplemented) In addition to prepatterning, +/// stack utilization is computed at the exit of context switches and +/// interrupt processing. The maximum utilization is stored in +/// the thread data structure. The kernel will panic if stack overflow is +/// detected. Stack utilization is not computed for the idle thread. + +#ifndef PK_STACK_CHECK + #define PK_STACK_CHECK 1 +#endif + +/// A hook for main() +/// +/// This hook macro is expanded in the body of __pk_main() prior to the call +/// of the application main(). The application can redefine this hook macro +/// in (or in headers referred to in) the application header +/// pk_app_cfg.h. The PK_MAIN_HOOK will run on the stack of main(). + +#ifndef PK_MAIN_HOOK + #define PK_MAIN_HOOK do {} while (0) +#endif + +/// A hook for pk_start_threads() +/// +/// This hook macro is expanded in the call-tree of pk_start_threads() before +/// threads are actually started. The application can redefine this hook +/// macro in (or in headers referred to in) the application header +/// pk_app_cfg.h. +/// +/// The PK_START_THREADS_HOOK runs as a pseudo-interrupt handler on the +/// kernel stack, with external interrupts disabled. + +#ifndef PK_START_THREADS_HOOK + #define PK_START_THREADS_HOOK do {} while (0) +#endif + +/// The maximum value of the \c PkTimebase type. + +#define PK_TIMEBASE_MAX ((PkTimebase)-1) + +/// A special value that specifies that the timebase will not be reset during +/// pk_init(). + +#define PK_TIMEBASE_CONTINUES PK_TIMEBASE_MAX + +/// By convention, a timeout value indicating 'no waiting' in a call of \c +/// pk_semaphore_pend(). + +#define PK_NO_WAIT 0 + +/// By convention, a timeout value indicating 'wait forever' in a call of \c +/// pk_semaphore_pend(). + +#define PK_WAIT_FOREVER ((PkInterval)-1) + +/// The PK timebase frequency in Hz +/// +/// Earlier version of PK defined the timbase frequency as a preprocessor +/// macro. Now, the timebase frequency is specified as a parameter of the +/// pk_initialize() API. The macro remains defined for backwards +/// compatibility, however all kernel uses of the timebase frequency are now +/// optimized around the timebase parameter. + +#define PK_TIMEBASE_FREQUENCY_HZ __pk_timebase_frequency_hz + +/// This is the unscaled timebase frequency in Hz. +#ifdef APPCFG_USE_EXT_TIMEBASE + #define PK_BASE_FREQ_HZ (uint32_t)25000000 +#else + #define PK_BASE_FREQ_HZ (uint32_t)400000000 +#endif /* APPCFG_USE_EXT_TIMEBASE */ +#define PK_BASE_FREQ_KHZ (PK_BASE_FREQ_HZ / 1000) +#define PK_BASE_FREQ_MHZ (PK_BASE_FREQ_HZ / 1000000) + +/// Scale a time interval to be _closer_ to what was actually requested +/// base on the actual timebase frequency. +#define PK_INTERVAL_SCALE(interval) ((interval) + ((interval) >> __pk_timebase_rshift)) + +/// Convert a time in integral seconds to a time interval - overflows are +/// ignored. The application can redefine this macro. + +#ifndef PK_SECONDS + #define PK_SECONDS(s) ((PkInterval)(PK_BASE_FREQ_HZ * (s))) +#endif + +/// Convert a time in integral milliseconds to a time interval - overflows are +/// ignored, and a frequency evenly (or closely) divisible by 1000 is +/// assumed. The application can redefine this macro. + +#ifndef PK_MILLISECONDS + #define PK_MILLISECONDS(m) ( (PkInterval)(PK_BASE_FREQ_KHZ * (m)) ) +#endif + +/// Convert a time in integral microseconds to a time interval - overflows are +/// ignored, and a frequncy evenly (or closely) divisible by 1,000,000 is +/// assumed. The application can redefine this macro. + +#ifndef PK_MICROSECONDS + #define PK_MICROSECONDS(u) ( (PkInterval)(PK_BASE_FREQ_MHZ * (u)) ) +#endif + +/// Convert a time in integral nanoseconds to a time interval - overflows are +/// ignored, and a frequeyncy evenly (or closely) divisible by 1,000,000 is +/// assumed. The application can redefine this macro. + +#ifndef PK_NANOSECONDS + #define PK_NANOSECONDS(n) ( (PkInterval)( ( ((PK_BASE_FREQ_MHZ<<10)/1000) * (n) ) >> 10) ) +#endif + + +/// Enable PK application tracing (enabled by default) +#ifndef PK_TRACE_ENABLE + #define PK_TRACE_ENABLE 1 +#endif + +/// Enable PK kernel tracing (disabled by default) +#ifndef PK_KERNEL_TRACE_ENABLE + #define PK_KERNEL_TRACE_ENABLE 0 +#endif + +//Application trace macros +#if !PK_TRACE_ENABLE + #define PK_TRACE(...) + #define PK_TRACE_BIN(str, bufp, buf_size) +#else + #define PK_TRACE(...) PKTRACE(__VA_ARGS__) + #define PK_TRACE_BIN(str, bufp, buf_size) PKTRACE_BIN(str, bufp, buf_size) +#endif + + +//Kernel trace macros +#if !PK_KERNEL_TRACE_ENABLE + #define PK_KERN_TRACE(...) + #define PK_KERN_TRACE_ASM16(...) +#else + #define PK_KERN_TRACE(...) PK_TRACE(__VA_ARGS__) + #define PK_KERN_TRACE_ASM16(...) PK_TRACE_ASM16(__VA_ARGS__) +#endif /* PK_KERNEL_TRACE_ENABLE */ + + +/// Add a string to the trace buffer with an optional register holding a 16bit value +/// WARNING: This calls a c function which may clobber any of the volatile registers +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) + #define PK_TRACE_ASM16(...) TRACE_ASM_HELPER16(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) +#else + #define PK_TRACE_ASM16(...) +#endif /* PK_TRACE_SUPPORT */ + +/// The following macros are helper macros for tracing. They should not be called +/// directly. +#define VARG_COUNT_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, N, ...) N +#define VARG_COUNT(...) VARG_COUNT_HELPER(, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* +#define TRACE_ASM_HELPER16_CALL(count, ...) TINY_TRACE_ASM ## count (__VA_ARGS__) +#define TRACE_ASM_HELPER16(count, ...) TRACE_ASM_HELPER16_CALL(count, __VA_ARGS__) + +#define TINY_TRACE_ASM0() .error "format string required" +#define TINY_TRACE_ASM1(str) \ + .tiny_trace_asm1 trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) +#define TINY_TRACE_ASM2(str, reg) \ + .tiny_trace_asm2 trace_ppe_hash(str, PK_TRACE_HASH_PREFIX), reg +#define TINY_TRACE_ASM3() .error "too many parameters" +#define TINY_TRACE_ASM4() .error "too many parameters" +#define TINY_TRACE_ASM5() .error "too many parameters" +#define TINY_TRACE_ASM6() .error "too many parameters" +#define TINY_TRACE_ASM7() .error "too many parameters" + +//TODO: add support for tracing more than 1 parameter and binary data in assembly + + .global pk_trace_tiny + + .macro .tiny_trace_asm1 hash16 + lis %r3, \hash16 + bl pk_trace_tiny + .endm + + .macro .tiny_trace_asm2 hash16, parm16 + clrlwi %r3, \parm16, 16 + oris %r3, %r3, \hash16 + bl pk_trace_tiny + .endm + +// *INDENT-ON* +#endif /*__ASSEMBLER__*/ + + + +#ifndef __ASSEMBLER__ + +#include <stddef.h> +#include <stdint.h> + +/// The timebase frequency in Hz; A parameter to pk_initialize() +extern uint32_t __pk_timebase_frequency_hz; + +extern uint8_t __pk_timebase_rshift; + +/// The timebase frequency in KHz +extern uint32_t __pk_timebase_frequency_khz; + +/// The timebase frequency in Mhz +extern uint32_t __pk_timebase_frequency_mhz; + + +typedef unsigned long int PkAddress; + +typedef uint8_t PkThreadState; + +typedef uint8_t PkThreadPriority; + +typedef uint8_t PkThreadFlags; + +typedef uint32_t PkSemaphoreCount; + +typedef uint64_t PkTimebase; + +typedef PK_TIME_INTERVAL_TYPE PkInterval; + +#include "pk_port_types.h" + +typedef struct +{ + + /// A priority queue of threads pending on the semaphore. + PkThreadQueue pending_threads; + + /// The current semaphore count. + PkSemaphoreCount count; + + /// The maximum allowable count - for error checking. + PkSemaphoreCount max_count; + +} PkSemaphore; + + +/// Compile-time initialize a PkSemaphore structure +/// +/// This low-level macro creates a structure initializatin of an PkSemaphore +/// structure. This can be used for example to create compile-time initialized +/// arrays of semaphores. +#define PK_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ + {.pending_threads = 0, \ + .count = (_initial_count), \ + .max_count = (_max_count)} + + +/// Declare and initialize a semaphore +#define PK_SEMAPHORE(sem, initial_count, max_count) \ + PkSemaphore sem = PK_SEMAPHORE_INITIALIZATION(initial_count, max_count) + + +/// Trace macros for C functions +#define HASH_ARG_COMBO(str, arg) \ + ((((uint32_t)trace_ppe_hash(str, PK_TRACE_HASH_PREFIX)) << 16) | ((uint32_t)(arg) & 0x0000ffff)) + +#define PKTRACE0(...) pk_trace_tiny() //will fail at compile time + +#define PKTRACE1(str) \ + pk_trace_tiny((trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) << 16)) + +#define PKTRACE2(str, parm0) \ + ((sizeof(parm0) <= 2)? \ + pk_trace_tiny(HASH_ARG_COMBO(str, parm0)): \ + pk_trace_big(HASH_ARG_COMBO(str, 1), ((uint64_t)parm0) << 32, 0)) + +#define PKTRACE3(str, parm0, parm1) \ + pk_trace_big(HASH_ARG_COMBO(str, 2), ((((uint64_t)parm0) << 32) | parm1), 0) + +#define PKTRACE4(str, parm0, parm1, parm2) \ + pk_trace_big(HASH_ARG_COMBO(str, 3), ((((uint64_t)parm0) << 32) | parm1),\ + ((uint64_t)parm2) << 32 ) + +#define PKTRACE5(str, parm0, parm1, parm2, parm3) \ + pk_trace_big(HASH_ARG_COMBO(str, 4), ((((uint64_t)parm0) << 32) | parm1),\ + ((((uint64_t)parm2) << 32) | parm3) ) + +#define PKTRACE6(...) pk_trace_tiny() //will fail at compile time +#define PKTRACE7(...) pk_trace_tiny() //will fail at compile time + +#define PKTRACE_HELPER2(count, ...) PKTRACE ## count (__VA_ARGS__) +#define PKTRACE_HELPER(count, ...) PKTRACE_HELPER2(count, __VA_ARGS__) + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) +#define PKTRACE(...) PKTRACE_HELPER(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) +#define PKTRACE_BIN(str, bufp, buf_size) \ + pk_trace_binary(((buf_size < 255)? HASH_ARG_COMBO(str, buf_size): HASH_ARG_COMBO(str, 255)), bufp) +#else +#define PKTRACE(...) +#define PKTRACE_BIN(str, bufp, buf_size) +#endif //PK_TRACE_SUPPORT + + + +/// A generic doubly-linked list object +/// +/// This object functions both as a sentinel mode for a deque as well as a +/// pointer container for elements in deques. The PK API assumes that +/// queueable structures will be defined with an PkDeque structure as the +/// initial 'data member' of the structure. This allows a pointer to a queue +/// element to be cast to a pointer to an PkDeque and vice-versa. + +typedef struct PkDeque +{ + + /// Pointer to the head or the next element in a deque. + /// + /// When an PkDeque is used as the sentinel node for a queue, \a next + /// points to the head of the queue, and the condition (next == \<self\>) + /// indicates an empty PkDeque. By convention the condition (\a next == + /// 0) is used to indicate that a queue element is not enqueued. + struct PkDeque* next; + + /// Pointer to the tail or previous element in a deque. + /// + /// When a DQueue is used as the sentinel node for a queue, \a previous + /// points to the tail of the queue. + struct PkDeque* previous; + +} PkDeque; + + +typedef void (*PkTimerCallback)(void*); + +#define PK_TIMER_CALLBACK(callback) void callback(void *) + +struct PkTimer; + +/// The PK timer object + +typedef struct PkTimer +{ + + /// The time queue management pointers + /// + /// This pointer container is defined as the first element of the + /// structure to allow the PkTimer to be cast to an PkDeque and + /// vice-versa. + PkDeque deque; + + /// The absolute timeout of the timer. + PkTimebase timeout; + + /// The timer callback + /// + /// For PK thread timers used to implement Sleep and semaphore pend + /// timeouts this field is initialized to __pk_thread_timeout(). + PkTimerCallback callback; + + /// Private data passed to the callback. + /// + /// For PK thread timers used to implement Sleep and semaphore pend this + /// field is initialized to a pointer to the thread. + void* arg; + +} PkTimer; + + +// Threads + +typedef void (*PkThreadRoutine)(void* arg); + +#define PK_THREAD_ROUTINE(f) void f(void *arg); + +typedef struct +{ + + /// Stack pointer saved during context switches. Assembler code expects + /// this to always be at address offset 0 from the thread pointer. + PkAddress saved_stack_pointer; + + /// This is 1 past the last valid byte address of the thread stack. + /// Assembler code expects this to always be at address offset (sizeof + /// PkAddress) from the thread pointer. + PkAddress stack_limit; + + /// This is the original base of the stack. + /// Assembler code expects this to always be at address offset 2 * (sizeof + /// PkAddress) from the thread pointer. + PkAddress stack_base; + + /// If the thread is blocked on a semaphore, then this is the semaphore the + /// thread is blocked on. + PkSemaphore* semaphore; + + /// The thread priority. + PkThreadPriority priority; + + /// The thread state; See \ref pk_thread_states + PkThreadState state; + + /// Thread flags; See \ref pk_thread_flags + PkThreadFlags flags; + + /// The timer structure handles Sleep and blocking on a semaphore with + /// timeout. + PkTimer timer; + +} PkThread; + + +typedef void (*PkBhHandler)(void*); + +#define PK_BH_HANDLER(handler) void handler(void *) + +typedef struct +{ + + /// The bottom half queue management pointers + /// + /// This pointer container is defined as the first element of the + /// structure to allow the PkBottomHalf to be cast to a PkDeque and + /// vice-versa. + PkDeque deque; + + /// The bottom half handler + PkBhHandler bh_handler; + + /// Private data passed to the handler. + void* arg; + +} PkBottomHalf; + + +// Initialization APIs + +int +pk_initialize(PkAddress kernel_stack, + size_t kernel_stack_size, + PkTimebase initial_timebase, + uint32_t timebase_frequency_hz); + + +// Timebase APIs + +PkTimebase +pk_timebase_get(void); + + +// Timer APIs + +int +pk_timer_create(PkTimer* timer, + PkTimerCallback callback, + void* arg); + + +int +pk_timer_schedule(PkTimer* timer, + PkInterval interval); + +int +pk_timer_cancel(PkTimer* timer); + +int +pk_timer_info_get(PkTimer* timer, + PkTimebase* timeout, + int* active); + +// Thread APIs + +int +pk_thread_create(PkThread* thread, + PkThreadRoutine thread_routine, + void* arg, + PkAddress stack, + size_t stack_size, + PkThreadPriority priority); + +int +pk_start_threads(void); + +int +pk_thread_resume(PkThread* thread); + +int +pk_thread_suspend(PkThread* thread); + +int +pk_thread_delete(PkThread* thread); + +int +pk_complete(void); + +int +pk_sleep(PkInterval interval); + +int +pk_thread_info_get(PkThread* thread, + PkThreadState* state, + PkThreadPriority* priority, + int* runnable); + +int +pk_thread_priority_change(PkThread* thread, + PkThreadPriority new_priority, + PkThreadPriority* old_priority); + +int +pk_thread_at_priority(PkThreadPriority priority, + PkThread** thread); + +int +pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b); + + +// Semaphore APIs + +int +pk_semaphore_create(PkSemaphore* semaphore, + PkSemaphoreCount initial_count, + PkSemaphoreCount max_count); + +int +pk_semaphore_post(PkSemaphore* semaphore); + +int +pk_semaphore_pend(PkSemaphore* semaphore, + PkInterval timeout); + +int +pk_semaphore_release_all(PkSemaphore* semaphore); + + +int +pk_semaphore_info_get(PkSemaphore* semaphore, + PkSemaphoreCount* count, + int* pending); + +void +pk_semaphore_post_handler(void* arg, + PkIrqId irq); + +// Misc. APIs + +void +pk_halt() __attribute__ ((noreturn)); + +// Deque APIs + +int +pk_deque_sentinel_create(PkDeque* deque); + +#define PK_DEQUE_SENTINEL_INIT(dq_addr) \ + {\ + .next = dq_addr, \ + .previous = dq_addr \ + } + +#define PK_DEQUE_SENTINEL_STATIC_CREATE(deque) \ + PkDeque deque = PK_DEQUE_SENTINEL_INIT(&deque) + +int +pk_deque_element_create(PkDeque* element); + +#define PK_DEQUE_ELEMENT_INIT() \ + {\ + .next = 0, \ + .previous = 0 \ + } + +#define PK_DEQUE_ELEMENT_STATIC_CREATE(deque) \ + PkDeque deque = PK_DEQUE_ELEMENT_INIT() + +/// Check for an empty PkDeque +/// +/// \param deque The sentinel node of a deque +/// +/// \retval 0 The PkDeque is not empty +/// +/// \retval 1 The PkDeque is empty + +static inline int +pk_deque_is_empty(PkDeque* deque) +{ + return (deque == deque->next); +} + + +/// Check if an PkDeque element is currently enqueued +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// \retval 0 The element is not currently enqueued +/// +/// \retval 1 The element is currently enqueued + +static inline int +pk_deque_is_queued(PkDeque* element) +{ + return (element->next != 0); +} + + +/// Append an element to the tail of a deque (FIFO order) +/// +/// \param deque The sentinel node of a deque +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// It is an error to call this API on an element that is already enqueued, +/// but the API does not check for this error. + +static inline void +pk_deque_push_back(PkDeque* deque, PkDeque* element) +{ + deque->previous->next = element; + element->previous = deque->previous; + element->next = deque; + deque->previous = element; +} + + +/// Push an element at the head of a deque (LIFO order) +/// +/// \param deque The sentinel node of a deque +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// It is an error to call this API on an element that is already enqueued, +/// but the API does not check for this error. + +static inline void +pk_deque_push_front(PkDeque* deque, PkDeque* element) +{ + deque->next->previous = element; + element->next = deque->next; + element->previous = deque; + deque->next = element; +} + +/// Pop an element from the head of a deque +/// +/// \param deque The sentinel node of a deque +/// +/// \retval 0 The PkDeque was empty prior to the call +/// +/// \retval non-0 A pointer to the previous head of the deque, which has been +/// removed from the deque and marked as no longer queued. + +// The cast of 'head' is used to remove the 'volatile' attribute. + +static inline PkDeque* +pk_deque_pop_front(PkDeque* deque) +{ + PkDeque* head; + + if (pk_deque_is_empty(deque)) + { + return 0; + } + else + { + head = (PkDeque*)(deque->next); + deque->next = head->next; + deque->next->previous = deque; + head->next = 0; + return head; + } +} + + +/// Remove a deque element from any position in the deque +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// It is an error to call this API on an element that is not currently +/// enqueued, but the API does not check for this error. + +static inline void +pk_deque_delete(PkDeque* element) +{ + element->previous->next = element->next; + element->next->previous = element->previous; + element->next = 0; +} + +// Bottom Half APIs + +extern PkDeque _pk_bh_queue; + +static inline void +pk_bh_schedule(PkBottomHalf* bottom_half) +{ + pk_deque_push_back(&_pk_bh_queue, (PkDeque*)bottom_half); +} + +#define PK_BH_INIT(_handler, _arg) \ + {\ + .deque = PK_DEQUE_ELEMENT_INIT(), \ + .bh_handler = _handler, \ + .arg = _arg \ + } + +#define PK_BH_STATIC_CREATE(bh_name, handler, arg) \ + PkBottomHalf bh_name = PK_BH_INIT(handler, arg) + + +//Trace function prototypes +void pk_trace_tiny(uint32_t i_parm); +void pk_trace_big(uint32_t i_hash_and_count, + uint64_t i_parm1, uint64_t i_parm2); +void pk_trace_binary(uint32_t i_hash_and_size, void* bufp); +void pk_trace_set_timebase(PkTimebase timebase); + + +/// Cast a pointer to another type, in a way that won't cause warnings + +#define PK_CAST_POINTER(t, p) ((t)((PkAddress)(p))) + +// Static Assert Macro for Compile time assertions. +// - This macro can be used both inside and outside of a function. +// - A value of false will cause the ASSERT to produce this error +// - This will show up on a compile fail as: +// <file>:<line> error: size of array '_static_assert' is negative +// - It would be trivial to use the macro to paste a more descriptive +// array name for each assert, but we will leave it like this for now. +#define PK_STATIC_ASSERT(cond) extern uint8_t _static_assert[(cond) ? 1 : -1] __attribute__ ((unused)) + +/// \page pk_errors PK API and Kernel Error Handling +/// +/// Error checking in the PK API consumes a significant amount of code space. +/// Approximately 20% of the object code in the PPC405 port is devoted to +/// error checking. Presumably a like amount of time overhead is also added to +/// PK API calls by this checking. +/// +/// API error checking can be disabled to save space and time in the kernel. +/// API errors can also be configured to cause kernel panics, allowing +/// applications to be coded without the overhead of error checking but still +/// providing an escape in the event of application errors or (unlikely) +/// hardware failures. The PK default is to check for API errors and kernel +/// invariants, and panic should errors occur. +/// +/// PK follows the Unix convention that a successful call of an API returns 0 +/// (PK_OK), but returns a negative code in the event of failure, or to +/// provide further information. The error codes are all defined as manifest +/// constants. +/// +/// Some negative codes returned by PK APIs are not considered errors. These +/// conditions are always checked, never cause a panic if they occur, and +/// their interpretation is always left to the application. See the detailed +/// documentation for each API for lists of error and non-error codes returned +/// by the API. +/// +/// There are three configuration options that control error handling in the +/// PK API and kernel: +/// +/// \c PK_ERROR_CHECK_API +/// +/// \arg \b 0 - No PK API error checking. APIs that potentially return error +/// codes will always return 0 (PK_OK) instead of an error code. Those +/// APIs that return negative codes that are not errors (see Table 1.5) +/// always return the negative non-error codes when appropriate. +/// +/// \arg \b 1 - (Default) All PK API errors are checked. The behavior in +/// the event of an error is defined by the configuration option +/// PK_ERROR_PANIC. +/// +/// \c PK_ERROR_CHECK_KERNEL +/// +/// \arg \b 0 - No kernel invariant error checking is done. +/// +/// \arg \b 1 - (Default) Selected kernel invariants are checked. The overhead +/// for these checks should be minimal. +/// +/// \c PK_ERROR_PANIC +/// +/// \arg \b 0 - PK API calls return negative error codes in the event of +/// errors. Note that PK kernel invariants always cause a panic if +/// violations occur. +/// +/// \arg \b 1 - (Default) In the event of errors PK APIs invoke PK_PANIC(code), +/// where code is a positive error code. Kernel invariant checks always +/// cause a panic if violations are detected. + +#endif /* __ASSEMBLER__ */ + +#endif /* __PK_API_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c new file mode 100644 index 00000000..ba80b1af --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c @@ -0,0 +1,55 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_bh_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_bh_core.c +/// \brief PK bottom half APIs +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present at runtime in any PK application. + +#include "pk.h" + +/// Statically initialize the bottom half queue +PK_DEQUE_SENTINEL_STATIC_CREATE(_pk_bh_queue); + +void _pk_process_bh(void) +{ + PkBottomHalf* bh; + + while((bh = (PkBottomHalf*)pk_deque_pop_front(&_pk_bh_queue)) != 0) + { + bh->bh_handler(bh->arg); + } + + return; +} + + +#undef __PK_THREAD_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_core.c new file mode 100644 index 00000000..17ea0cce --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_core.c @@ -0,0 +1,105 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_core.c +/// \brief Core routines for the PK kernel. +/// +/// The entry points in this file are routines that are expected to be needed +/// at runtime by all PK applications. This file also serves as a place for +/// kernel global variables to be realized. + +#define __PK_CORE_C__ + +#include "pk.h" + +#if !PK_TIMER_SUPPORT + +/// If there is no timer support, then any call of the timer interrupt handler +/// is considered a fatal error. + +void +__pk_timer_handler() +{ + PK_PANIC(PK_NO_TIMER_SUPPORT); +} + +#endif /* PK_TIMER_SUPPORT */ + + +/// Initialize an PkDeque sentinel node +/// +/// \param deque The sentinel node of the deque +/// +/// PK has no way of knowing whether the \a deque is currently in use, so +/// this API must only be called on unitialized or otherwise unused sentinel +/// nodes. +/// +/// \retval 0 success +/// +/// \retval -PK_INVALID_DEQUE_SENTINEL The \a deque pointer was null + +int +pk_deque_sentinel_create(PkDeque* deque) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(deque == 0, PK_INVALID_DEQUE_SENTINEL); + } + + deque->next = deque->previous = deque; + return 0; +} + + +/// Initialize an PkDeque element +/// +/// \param element Typically the PkDeque object of a queable structure +/// +/// PK has no way of knowing whether the \a element is currently in use, so +/// this API must only be called on unitialized or otherwise unused deque +/// elements. +/// +/// \retval 0 success +/// +/// \retval -PK_INVALID_DEQUE_ELEMENT The \a element pointer was null + +int +pk_deque_element_create(PkDeque* element) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(element == 0, PK_INVALID_DEQUE_ELEMENT); + } + + element->next = 0; + return 0; +} + +#undef __PK_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c new file mode 100644 index 00000000..90a6f18a --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c @@ -0,0 +1,76 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_debug_ptrs.c +/// \brief Defines a table of pointers to important kernel debug data. +/// +/// This table is placed in a special section named .debug_ptrs which can be +/// placed at a well-known memory location for tools to find. +/// + +#include "pk.h" +#include "pk_trace.h" +#include "pk_debug_ptrs.h" + +extern PkTimebase ppe42_64bit_timebase; + +#if PK_TRACE_SUPPORT + extern PkTraceBuffer g_pk_trace_buf; +#endif + +pk_debug_ptrs_t pk_debug_ptrs SECTION_ATTRIBUTE(".debug_ptrs") = +{ + .debug_ptrs_size = sizeof(pk_debug_ptrs), + .debug_ptrs_version = PK_DEBUG_PTRS_VERSION, + +#if PK_TRACE_SUPPORT + .debug_trace_ptr = &g_pk_trace_buf, + .debug_trace_size = sizeof(g_pk_trace_buf), +#else + .debug_trace_ptr = 0, + .debug_trace_size = 0, +#endif /* PK_TRACE_SUPPORT */ + +#if PK_THREAD_SUPPORT + .debug_thread_table_ptr = &__pk_priority_map, + .debug_thread_table_size = sizeof(__pk_priority_map), + .debug_thread_runq_ptr = (void*)& __pk_run_queue, + .debug_thread_runq_size = sizeof(__pk_run_queue), +#else + .debug_thread_table_ptr = 0, + .debug_thread_table_size = 0, + .debug_thread_runq_ptr = 0, + .debug_thread_runq_size = 0, +#endif /* PK_THREAD_SUPPORT */ + + .debug_timebase_ptr = &ppe42_64bit_timebase, + .debug_timebase_size = sizeof(ppe42_64bit_timebase), + +}; diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h new file mode 100644 index 00000000..20c0d11d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h @@ -0,0 +1,63 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_debug_ptrs.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_DEBUG_PTRS_H__ +#define __PK_DEBUG_PTRS_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_debug_ptrs.h +/// \brief Structure for a table of pointers to kernel debug data +/// + +#define PK_DEBUG_PTRS_VERSION 1 + +typedef struct +{ + // The size and version of this structure + unsigned short debug_ptrs_size; + unsigned short debug_ptrs_version; + + // Trace buffer location and size + void* debug_trace_ptr; + unsigned long debug_trace_size; + + // Thread table location and size + void* debug_thread_table_ptr; + unsigned long debug_thread_table_size; + + // Thread run queue location and size + void* debug_thread_runq_ptr; + unsigned long debug_thread_runq_size; + + // Emulated timebase location and size + void* debug_timebase_ptr; + unsigned long debug_timebase_size; + +} pk_debug_ptrs_t; + +#endif /*__PK_DEBUG_PTRS_H__*/ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_init.c new file mode 100644 index 00000000..bce4a1ba --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_init.c @@ -0,0 +1,216 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_init.c +/// \brief PK initialization +/// +/// The entry points in this file are initialization routines - they are never +/// needed after PK initialization and their code space could be reclaimed by +/// the application after initialization if required. + +#include "pk.h" +#include "pk_trace.h" + +uint32_t __pk_timebase_frequency_hz; + +/// The timebase frequency is passed into PK during initialization. It cannot +/// be set statically because there is a requirement to support a frequency +/// that can change from one IPL to the next. On the 405, scaling of time +/// intervals is accomplished by doing a 32x32 bit multiplication which is +/// supported by the ppc405 instruction set. PPE42 does not support 32x32 bit +/// multiplication directly and some applications can not afford to use a +/// function call to emulate the operation. Instead we scale the time +/// interval by shifting the value X bits to the right and adding it to itself. +/// This can scale the value by 2, 1.5, 1.25, 1.125, etc. +/// +/// This is the right shift value. +/// NOTE: shifting by 0 gives a 2x scale factor, shifting by 32 gives a 1x +/// scale factor. +uint8_t __pk_timebase_rshift = 32; + +void pk_set_timebase_rshift(uint32_t timebase_freq_hz) +{ + //Use 1.0 scale if less than halfway between 1.0 and 1.25 + if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3))) + { + __pk_timebase_rshift = 32; + } + + //use 1.25 scale if less than halfway between 1.25 and 1.5 + else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3) + (PK_BASE_FREQ_HZ >> 2))) + { + __pk_timebase_rshift = 2; + } + //use 1.5 scale if less than halfway between 1.5 and 2.0 + else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 2) + (PK_BASE_FREQ_HZ >> 1))) + { + __pk_timebase_rshift = 1; + } + //use 2.0 scale if greater than 1.5 + else + { + __pk_timebase_rshift = 0; + } +} + +/// Initialize PK. +/// +/// \param kernel_stack A stack area for interrupt and bottom-half handlers. +/// +/// \param kernel_stack_size The size (in bytes) of the stack area for +/// interrupt and bottom-half handlers. +/// +/// \param initial_timebase The initial value of the PK timebase. +/// If the argument is given as the special value \c PK_TIMEBASE_CONTINUES, then the +/// timebase is not reset. +/// +/// \param timebase_frequency_hz The frequency of the PK timebase in Hz. +/// +/// This routine \e must be called before any other PK / routines, and \e +/// should be called before any interrupts are enabled. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_INIT A stack pointer is 0 or is given +/// a 0 size. +/// +/// \retval -PK_STACK_OVERFLOW One or both stacks are not large enough to +/// support a minimum context save in the event of an interrupt. + +// Note that PK does not rely on any static initialization of dynamic +// variables. In debugging sessions using RAM-resident PK images it is +// assumed that the processor may be reset at any time, so we always need to +// reset everything at initialization. + +int +pk_initialize(PkAddress kernel_stack, + size_t kernel_stack_size, + PkTimebase initial_timebase, + uint32_t timebase_frequency_hz) +{ + int rc; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF((kernel_stack == 0) || + (kernel_stack_size == 0), + PK_INVALID_ARGUMENT_INIT); + } + + __pk_timebase_frequency_hz = timebase_frequency_hz; + + __pk_thread_machine_context_default = PK_THREAD_MACHINE_CONTEXT_DEFAULT; + + rc = __pk_stack_init(&kernel_stack, &kernel_stack_size); + + if (rc) + { + return rc; + } + + __pk_kernel_stack = kernel_stack; + __pk_kernel_stack_size = kernel_stack_size; + +#if PK_TIMER_SUPPORT + + // Initialize the time queue sentinel as a circular queue, set the next + // timeout and clear the cursor. + + pk_deque_sentinel_create((PkDeque*)&__pk_time_queue); + __pk_time_queue.cursor = 0; + __pk_time_queue.next_timeout = PK_TIMEBASE_MAX; + +#if PK_TRACE_SUPPORT + extern PkTimer g_pk_trace_timer; + extern PkTraceBuffer g_pk_trace_buf; + + //set the trace timebase HZ + g_pk_trace_buf.hz = timebase_frequency_hz; + + //set the shift adjustment to get us closer to the true + //timebase frequency (versus what was hardcoded) + pk_set_timebase_rshift(timebase_frequency_hz); + + if(initial_timebase != PK_TIMEBASE_CONTINUES) + { + //set the timebase ajdustment for trace synchronization + pk_trace_set_timebase(initial_timebase); + } + + // Schedule the timer that puts a 64bit timestamp in the trace buffer + // periodically. This allows us to use 32bit timestamps. + pk_timer_schedule(&g_pk_trace_timer, + PK_TRACE_TIMER_PERIOD); + +#endif /* PK_TRACE_SUPPORT */ + +#endif /* PK_TIMER_SUPPORT */ + +#if PK_THREAD_SUPPORT + + // Clear the priority map. The final entry [PK_THREADS] is for the idle + // thread. + + int i; + + for (i = 0; i <= PK_THREADS; i++) + { + __pk_priority_map[i] = 0; + } + + // Initialize the thread scheduler + + __pk_thread_queue_clear(&__pk_run_queue); + __pk_current_thread = 0; + __pk_next_thread = 0; + __pk_delayed_switch = 0; + +#endif /* PK_THREAD_SUPPORT */ + + return PK_OK; +} + + +/// Call the application main() +/// +/// __pk_main() is called from the bootloader. It's only purpose is to +/// provide a place for the PK_MAIN_HOOK to be called before main() is +/// called. + +void +__pk_main(int argc, char** argv) +{ + PK_MAIN_HOOK; + + int main(int argc, char** argv); + main(argc, argv); +} diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h new file mode 100644 index 00000000..1777cdd3 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h @@ -0,0 +1,271 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_kernel.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_KERNEL_H__ +#define __PK_KERNEL_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_kernel.h +/// \brief PK portable kernel (non-API) data and data structures +/// +/// \todo In theory, as long as the critical section entry/exit macros use GCC +/// memory barriers, we should be able to eliminate all of the 'volatile' +/// declarations in PK code. These have been added to the port, so +/// we should try it. + +#ifdef __PK_CORE_C__ + #define IF__PK_CORE_C__(x) x + #define UNLESS__PK_CORE_C__(x) +#else + #define IF__PK_CORE_C__(x) + #define UNLESS__PK_CORE_C__(x) x +#endif + +#if PK_MINIMIZE_KERNEL_CODE_SPACE + #define IF_PK_MINIMIZE_KERNEL_CODE_SPACE(x) x + #define UNLESS_PK_MINIMIZE_KERNEL_CODE_SPACE(x) +#else + #define IF_PK_MINIMIZE_KERNEL_CODE_SPACE(x) + #define UNLESS_PK_MINIMIZE_KERNEL_CODE_SPACE(x) x +#endif + + +#ifndef __ASSEMBLER__ + +/// This is the stack pointer saved when switching from a thread context to an +/// interrupt context. + +UNLESS__PK_CORE_C__(extern) +volatile +PkAddress __pk_saved_sp; + +/// The kernel stack; constant once defined by the call of +/// pk_initialize(). + +UNLESS__PK_CORE_C__(extern) +volatile +PkAddress __pk_kernel_stack; + +/// This is the run queue - the queue of mapped runnable tasks. +UNLESS__PK_CORE_C__(extern) +volatile +PkThreadQueue __pk_run_queue; + +/// This flag is set by \c __pk_schedule() if a new highest-priority thread +/// becomes runnable during an interrupt handler. The context switch will +/// take place at the end of interrupt processing, and the +/// interrupt handling code will clear the flag. + +UNLESS__PK_CORE_C__(extern) +volatile +int __pk_delayed_switch; + +/// The currently running thread, or NULL (0) to indicate the idle thread +/// +/// \a __pk_current_thread holds a pointer to the currently executing +/// thread. This pointer will be NULL (0) under the following conditions: +/// +/// - After pk_initialize() but prior to pk_start_threads() +/// +/// - After pk_start_threads(), when no threads are runnable. In this case +/// the NULL (0) value indicates that the PK idle thread is 'running'. +/// +/// - After pk_start_threads(), when the current (non-idle) thread has +/// completed or been deleted. +/// +/// If \a __pk_current_thread == 0 then there is no requirement to save any +/// register state on a context switch, either because the PK idle thread has +/// no permanent context, or because any thread context on the kernel stack is +/// associated with a deleted thread. +/// +/// If \a __pk_current_thread != 0 then \a __pk_current_thread is a pointer +/// to the currently executing thread. In an interrupt handler \a +/// pk_current_thread is a pointer to the thread whose context is saved on +/// the kernel stack. +UNLESS__PK_CORE_C__(extern) +volatile +PkThread* __pk_current_thread; + +/// The thread to switch to during the next context switch, or NULL (0). +/// +/// \a __pk_next_thread is computed by __pk_schedule(). \a +/// __pk_next_thread holds a pointer to the thread to switch to at the next +/// context switch. In a thread context the switch happens immediately if \a +/// __pk_next_thread == 0 or \a __pk_next_thread != \a __pk_current_thread. +/// In an interrupt context the check happens at the end of processing all +/// interrupts. +/// +/// \a __pk_next_thread may be NULL (0) under the following +/// conditions: +/// +/// - After pk_initialize() but prior to pk_start_threads(), assuming no +/// threads have been made runnable. +/// +/// - After pk_start_threads(), when no threads are runnable. In this case +/// the NULL (0) value indicates that the PK idle thread is the next thread +/// to 'run'. +/// +/// If \a __pk_next_thread == 0 then there is no requirement to restore +/// any register state on a context switch, because the PK idle thread has +/// no permanent context. +/// +/// If \a __pk_next_thread != 0 then \a __pk_next_thread is a pointer +/// to the thread whose context will be restored at the next context switch. +UNLESS__PK_CORE_C__(extern) +volatile +PkThread* __pk_next_thread; + +/// The priority of \a __pk_next_thread +/// +/// If \a __pk_next_thread == 0, the \a __pk_next_priority == PK_THREADS. +UNLESS__PK_CORE_C__(extern) +volatile +PkThreadPriority __pk_next_priority; + +/// This variable holds the default thread machine context for newly created +/// threads. The idle thread also uses this context. This variable is normally +/// constant after the call of \c pk_initialize(). + +UNLESS__PK_CORE_C__(extern) +volatile +PkMachineContext __pk_thread_machine_context_default; + + +/// The size of the kernel stack (bytes). + +UNLESS__PK_CORE_C__(extern) +volatile +size_t __pk_kernel_stack_size; + +/// This table maps priorities to threads, and contains PK_THREADS + 1 +/// entries. The final entry is for the idle thread and will always be null +/// after initizlization. + +UNLESS__PK_CORE_C__(extern) +volatile +PkThread* __pk_priority_map[PK_THREADS + 1]; + +/// The PK time queue structure +/// +/// This structure is defined for use by the kernel, however applications +/// could also use this structure to define their own time queues. + +typedef struct +{ + + /// A sentinel node for the time queue. + /// + /// The time queue is an PkDeque managed as a FIFO queue for queue + /// management purpose, although events time out in time order. + /// + /// This pointer container is defined as the first element of the + /// structure to allow the PkTimeQueue to be cast to an PkDeque. + PkDeque queue; + + /// The next timeout in absolute time. + PkTimebase next_timeout; + + /// A pointer to allow preemption of time queue processing + /// + /// If non-0, then this is the next timer in the time queue to handle, or + /// a pointer to the \a queue object indicating no more timers to handle. + /// + /// \a cursor != 0 implies that time queue handler is in the midst of + /// processing the time queue, but has enabled interrupt preemption for + /// processing a timer handler. This means that 1) if the timer pointed to + /// by \a cursor is deleted then the cursor must be assigned to the + /// next timer in the queue; and 2) if a new timer is scheduled then + /// activating the next timeout will be handled by the timer handler. + PkDeque* cursor; + +} PkTimeQueue; + +UNLESS__PK_CORE_C__(extern) +PkTimeQueue __pk_time_queue; + +/// Return a pointer to the PkThread object of the currently running thread, +/// or NULL (0) if PK is idle or has not been started. +/// +/// In this API the current thread is not volatile - it will never change +/// inside application code - thus the 'volatile' is cast away. The PK kernel +/// does not (must not) use this API. + +UNLESS__PK_CORE_C__(extern) +inline PkThread* +pk_current(void) +{ + return (PkThread*)__pk_current_thread; +} + +/// Schedule the next timeout in a machine-specific way. + +void +__pk_schedule_hardware_timeout(PkTimebase timeout); + +/// The thread timeout handler. Portable. + +PK_TIMER_CALLBACK(__pk_thread_timeout); + +/// Generic stack initialization. Portable. + +int +__pk_stack_init(PkAddress* stack, + size_t* size); + +/// Machine-specific thread context initialization. + +void +__pk_thread_context_initialize(PkThread* thread, + PkThreadRoutine thread_routine, + void* arg); + +/// Machine specific resumption of __pk_next_thread at __pk_next_priority +/// without saving the current context. +void +__pk_next_thread_resume(void); + +/// Schedule a timer in the time queue. Portable. +void +__pk_timer_schedule(PkTimer* timer); + +/// Remove a timer from the time queue. Portable. +int +__pk_timer_cancel(PkTimer* timer); + +void +__pk_schedule(void); + + +// Call the application main(). Portable. + +void +__pk_main(int argc, char** argv); + +#endif /* __ASSEMBLER__ */ + +#endif /* __PK_KERNEL_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h new file mode 100644 index 00000000..c6076f19 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h @@ -0,0 +1,134 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_macros.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_MACROS_H__ +#define __PK_MACROS_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_macros.h +/// \brief Boilerplate macros for PK + +/// This macro encapsulates error handling boilerplate for code that uses the +/// PK API-type error handling, for errors that do not occur in critical +/// sections. + +#define PK_ERROR(code) \ + do { \ + if (PK_ERROR_PANIC) { \ + PK_PANIC(code); \ + } else { \ + return -(code); \ + } \ + } while (0) + + +/// This macro encapsulates error handling boilerplate in the PK API +/// functions, for errors that do not occur in critical sections. + +#define PK_ERROR_IF(condition, code) \ + do { \ + if (condition) { \ + PK_ERROR(code); \ + } \ + } while (0) + + +/// This macro encapsulates error handling boilerplate in the PK API +/// functions, for errors that do not occur in critical sections and always +/// force a kernel panic, indicating a kernel or API bug. + +#define PK_PANIC_IF(condition, code) \ + do { \ + if (condition) { \ + PK_PANIC(code); \ + } \ + } while (0) + + +/// This macro encapsulates error handling boilerplate in the PK API +/// functions, for errors that do not occur in critical sections. +/// The error handling will only be enabled when PK_ERROR_CHECK_API +/// is enabled. + +#define PK_ERROR_IF_CHECK_API(condition, code) \ + do { \ + if (PK_ERROR_CHECK_API) { \ + PK_ERROR_IF(condition, code); \ + } \ + } while (0) + +/// This macro encapsulates error handling boilerplate in the PK API +/// functions, for errors that occur in critical sections. + +#define PK_ERROR_IF_CRITICAL(condition, code, context) \ + do { \ + if (condition) { \ + if (PK_ERROR_PANIC) { \ + PK_PANIC(code); \ + pk_critical_section_exit(context); \ + } else { \ + pk_critical_section_exit(context); \ + return -(code); \ + } \ + } \ + } while (0) + + +/// This is a general macro for errors that require cleanup before returning +/// the error code. + +#define PK_ERROR_IF_CLEANUP(condition, code, cleanup) \ + do { \ + if (condition) { \ + if (PK_ERROR_PANIC) { \ + PK_PANIC(code); \ + cleanup; \ + } else { \ + cleanup; \ + return -(code); \ + } \ + } \ + } while (0) + + + +/// Some PK APIs can only be called from thread contexts - these are APIs +/// that threads call on 'themselves'. + +#define PK_ERROR_UNLESS_THREAD_CONTEXT() \ + PK_ERROR_IF(!__pk_kernel_context_thread(), \ + PK_ILLEGAL_CONTEXT_THREAD_CONTEXT) + + +/// Some PK APIs must be called from an interrupt context only. + +#define PK_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT() \ + PK_ERROR_IF(!__pk_kernel_context_any_interrupt(), \ + PK_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT) + +#endif /* __PK_MACROS_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c new file mode 100644 index 00000000..99453e81 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c @@ -0,0 +1,365 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_semaphore_core.c +/// \brief PK semaphore APIs +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present at runtime in any PK application that enables +/// semaphores. + +#include "pk.h" + +/// Post a count to a semaphore +/// +/// \param semaphore A pointer to the semaphore +/// +/// If any thread is pending on the semaphore, the highest priority thread +/// will be made runnable and the internal count will remain 0. +/// +/// If no thread is pending on the semaphore then the internal count will be +/// incremented by 1, with overflow wrapping the internal count through 0. If +/// the \a max_count argument supplied when the semaphore was created is +/// non-zero and the new internal count is greater than the \a max_count, an +/// overflow error will be signalled. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_SEMAPHORE_AT_POST The \a semaphore is a null (0) pointer. +/// +/// \retval -PK_SEMAPHORE_OVERFLOW The \a max_count argument supplied when +/// the semaphore was created is non-zero and the new internal count is +/// greater than the \a max_count. + +int +pk_semaphore_post(PkSemaphore* semaphore) +{ + PkMachineContext ctx; + PkThreadPriority priority; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_POST); + } + + pk_critical_section_enter(&ctx); + + priority = __pk_thread_queue_min(&(semaphore->pending_threads)); + + if (priority != PK_IDLE_THREAD_PRIORITY) + { + + __pk_thread_queue_delete(&(semaphore->pending_threads), priority); + __pk_thread_queue_insert(&__pk_run_queue, priority); + + PK_KERN_TRACE("THREAD_SEMAPHORE_POST(%d)", priority); + + __pk_schedule(); + + } + else + { + + semaphore->count++; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF((semaphore->max_count > 0) && + (semaphore->count > semaphore->max_count), + PK_SEMAPHORE_OVERFLOW); + } + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// Pend on a semaphore with timeout +/// +/// \param semaphore A pointer to the semaphore +/// +/// \param timeout A relative timeout in PK timebase ticks, including the +/// special values PK_NO_WAIT and PK_WAIT_FOREVER +/// +/// This API is normally called from threads, and can only be successfully +/// called from interupt handlers under special conditions. +/// +/// If the internal count of the \a semaphore is non-zero, the internal count +/// is decremented by one and execution of the caller continues. +/// +/// If the internal count of the \a semaphore is zero and the \a timeout is +/// PK_NO_WAIT (0) then the call returns immediately with the informational +/// code -PK_SEMAPHORE_PEND_NO_WAIT. +/// +/// If the internal count of the \a semaphore is zero and the \a timeout is +/// non-zero then a thread will block until either a semaphore count is +/// acquired or the relative timeout expires. If this condition occurs in a +/// call from an interrupt context or before threads have been started then +/// the call will fail with the error \c -PK_SEMAPHORE_PEND_WOULD_BLOCK. +/// +/// Once timed out the thread is removed from the semaphore pending queue and +/// made runnable, and the pk_semaphore_pend() operation will fail, even if +/// the semaphore count becomes available before the thread runs again. The +/// pk_semaphore_pend() API returns the informational code +/// -PK_SEMAPHORE_PEND_TIMED_OUT in this case. +/// +/// By convention, a timeout interval equal to the maximum possible value of +/// the \c PkInterval type is taken to mean "wait forever". A thread blocked +/// on a semaphore in this mode will never time out. PK provides this +/// constant as \c PK_WAIT_FOREVER. +/// +/// Return values other than PK_OK (0) are not necessarily errors; see \ref +/// pk_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_SEMAPHORE_PEND_NO_WAIT timeout is set to PK_NO_WAIT +/// +/// \retval -PK_SEMAPHORE_PEND_TIMED_OUT The semaphore was not acquired +/// before the timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -PK_INVALID_SEMAPHORE_AT_PEND The \a semaphore is a null (0) +/// pointer. +/// +/// \retval -PK_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), the semaphore +/// internal count was 0 and a non-zero timeout was specified. + +// Note: Casting __pk_current_thread removes the 'volatile' attribute. + +int +pk_semaphore_pend(PkSemaphore* semaphore, + PkInterval timeout) +{ + PkMachineContext ctx; + PkThreadPriority priority; + PkThread* thread; + PkTimer* timer = 0; + PkInterval scaled_timeout = PK_INTERVAL_SCALE(timeout); + + int rc = PK_OK; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_PEND); + } + + pk_critical_section_enter(&ctx); + + if (semaphore->count != 0) + { + + semaphore->count--; + + } + else if (timeout == PK_NO_WAIT) + { + + rc = -PK_SEMAPHORE_PEND_NO_WAIT; + + } + else + { + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF_CRITICAL(!__pk_kernel_context_thread(), + PK_SEMAPHORE_PEND_WOULD_BLOCK, + &ctx); + } + + thread = (PkThread*)__pk_current_thread; + priority = thread->priority; + + __pk_thread_queue_insert(&(semaphore->pending_threads), priority); + + thread->semaphore = semaphore; + thread->flags |= PK_THREAD_FLAG_SEMAPHORE_PEND; + + PK_KERN_TRACE("THREAD_SEMAPHORE_PEND(%d)", priority); + + if (timeout != PK_WAIT_FOREVER) + { + timer = &(thread->timer); + timer->timeout = pk_timebase_get() + scaled_timeout; + __pk_timer_schedule(timer); + thread->flags |= PK_THREAD_FLAG_TIMER_PEND; + } + + __pk_thread_queue_delete(&__pk_run_queue, priority); + __pk_schedule(); + + thread->flags &= ~PK_THREAD_FLAG_SEMAPHORE_PEND; + + if (thread->flags & PK_THREAD_FLAG_TIMER_PEND) + { + if (thread->flags & PK_THREAD_FLAG_TIMED_OUT) + { + rc = -PK_SEMAPHORE_PEND_TIMED_OUT; + __pk_thread_queue_delete(&(semaphore->pending_threads), thread->priority); + } + else + { + __pk_timer_cancel(timer); + } + + thread->flags &= + ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT); + } + } + + pk_critical_section_exit(&ctx); + + return rc; +} + + +/// Release all threads blocked on a semaphore +/// +/// \param semaphore A pointer to a semaphore +/// +/// This API is provided to allow an PK semaphore to be used as a thread +/// barrier. pk_semaphore_release_all() simultaneously unblocks all threads +/// (if any) currently pending on a semaphore. A semaphore to be used as a +/// thread barrier will typically be initialized with +/// pk_semaphore_create(\a sem, 0, 0), and sxx_semaphore_post() would never be +/// called on the \a sem. +/// +/// This API never modifies the \a count field of the semaphore; If any +/// threads are blocked on a semaphore the semaphore count is 0 by definition. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_SEMAPHORE_AT_RELEASE The \a semaphore is a null (0) +/// pointer. + +int +pk_semaphore_release_all(PkSemaphore* semaphore) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_RELEASE); + } + + pk_critical_section_enter(&ctx); + + __pk_thread_queue_union(&__pk_run_queue, &(semaphore->pending_threads)); + __pk_thread_queue_clear(&(semaphore->pending_threads)); + __pk_schedule(); + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// Get information about a semaphore. +/// +/// \param semaphore A pointer to the PkSemaphore to query +/// +/// \param count The value returned through this pointer is the current count +/// of the semaphore. The caller can set this parameter to the null pointer +/// (0) if this information is not required. +/// +/// \param pending The value returned through this pointer is the current +/// number of threads pending on the semaphore. The caller can set this +/// parameter to the null pointer (0) if this information is not required. +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from a critical section. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_SEMAPHORE_AT_INFO The \a semaphore is a null (0) +/// pointer. + +int +pk_semaphore_info_get(PkSemaphore* semaphore, + PkSemaphoreCount* count, + int* pending) + +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_INFO); + } + + if (count) + { + *count = semaphore->count; + } + + if (pending) + { + *pending = __pk_thread_queue_count(&(semaphore->pending_threads)); + } + + return PK_OK; +} + + +/// An simple interrupt handler that posts to a semaphore. +/// +/// To implement basic event-driven blocking of a thread, install +/// pk_semaphore_post_handler() as the handler for an interrupt +/// and provide a pointer to the semaphore as the \a arg argument in +/// pk_irq_handler_set(). The semaphore should be initialized with +/// pk_semaphore_create(&sem, 0, 1). This handler simply disables (masks) +/// the interrupt, clears the status and calls pk_semaphore_post() on the +/// semaphore. +/// +/// Note that clearing the status in the interrupt controller as done here is +/// effectively a no-op for level-sensitive interrupts. In the level-sensitive +/// case any thread pending on the semaphore must reset the interrupt +/// condition in the device before re-enabling the interrupt. +#if 0 +void +pk_semaphore_post_handler(void* arg, PkIrqId irq, int priority) +{ + pk_irq_disable(irq); + pk_irq_status_clear(irq); + pk_semaphore_post((PkSemaphore*)arg); +} + +#endif diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c new file mode 100644 index 00000000..5b3e4487 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c @@ -0,0 +1,102 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_semaphore_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_semaphore_init.c +/// \brief PK semaphore API initialization routines +/// +/// The entry points in this file are routines that are typically used during +/// initialization, and their code space could be deallocated and recovered if +/// no longer needed by the application after initialization. + +#include "pk.h" + +/// Create (initialize) a semaphore +/// +/// \param semaphore A pointer to an PkSemaphore structure to initialize +/// +/// \param initial_count The initial count of the semaphore +/// +/// \param max_count The maximum count allowed in the semaphore, for error +/// checking +/// +/// Semaphores are created (initialized) by a call of \c +/// pk_semaphore_create(), using an application-provided instance of an \c +/// PkSemaphore structure. This structure \e is the semaphore, so the +/// application must never modify the structure if the semaphore is in use. +/// PK has no way to know if an \c PkSemaphore structure provided to +/// \c pk_semaphore_create() is safe to use as a semaphore, and will silently +/// modify whatever memory is provided. +/// +/// PK provides two simple overflow semantics based on the value of max_count +/// in the call of \c pk_semaphore_create(). +/// +/// If \a max_count = 0, then posting to the semaphore first increments the +/// internal count by 1. Overflows are ignored and will wrap the internal +/// count through 0. +/// +/// If \a max_count != 0, then posting to the semaphore first increments the +/// internal count by 1, wrapping through 0 in the event of overflow. If the +/// resulting count is greater than max_count, \c pk_semaphore_post() will +/// return the error \c -PK_SEMAPHORE_POST_OVERFLOW to the caller. +/// +/// In most applications it is probably best to use the \a max_count != 0 +/// semantics to trap programming errors, unless there is a specific +/// application where overflow is expected and ignorable. As a fine point of +/// the specification, a \a max_count of 0 is equivalent to a max_count of +/// 0xFFFFFFFF. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_SEMAPHORE_AT_CREATE The \a semaphore is a null (0) +/// pointer. +/// +/// \retval -PK_INVALID_ARGUMENT_SEMAPHORE The \a max_count is non-zero +/// and less than the \a initial_count. + +int +pk_semaphore_create(PkSemaphore* semaphore, + PkSemaphoreCount initial_count, + PkSemaphoreCount max_count) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_CREATE); + PK_ERROR_IF((max_count != 0) && (initial_count > max_count), + PK_INVALID_ARGUMENT_SEMAPHORE); + } + + __pk_thread_queue_clear(&(semaphore->pending_threads)); + semaphore->count = initial_count; + semaphore->max_count = max_count; + + return PK_OK; +} diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c new file mode 100644 index 00000000..99848d43 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c @@ -0,0 +1,118 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_stack_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_stack_init.c +/// \brief PK stack initialization +/// +/// The entry points in this file are initialization routines - they are never +/// needed after PK initialization and their code space could be reclaimed by +/// the application after initialization if required. +/// +/// This code was split out from "pk_init.c" because it may be needed in a +/// thread configuration if threads are being created dynamically. in an +/// interrupt-only configuration it is not needed after \c pk_initialize(). + +#include "pk.h" + +/// Initialize a stack area. +/// +/// \param stack A pointer to the smallest legal address of the stack. The +/// stack address is modified as the stack is aligned and initialized. +/// +/// \param size A pointer to the size of the stack (in bytes). The size is +/// modified as the stack is aligned and initialized. At exit this is the +/// final usable stack area size aligned to the size of the PK_STACK_TYPE. +/// +/// PK makes no assumptions about size or alignment of the area provided as a +/// stack, and carefully aligns and initializes the stack. Regardless of how +/// the stack grows, the \a stack parameter is considered to be the lowest +/// legal address of the stack. + +int +__pk_stack_init(PkAddress* stack, + size_t* size) +{ + PkAddress mask; + size_t excess, i, count; + PK_STACK_TYPE* p; + + if (PK_STACK_DIRECTION < 0) + { + + // Stacks grow down. The initial stack pointer is set to just above + // the last allocated stack address. This is legal for pre-decrement + // stacks, otherwise the initial address is first brought into range + // before alignment. The stack is aligned downward, then the size is + // adjusted to a multiple of the stack type. Stacks are optionally + // prepatterned. Alignment is assumed to be a power of 2. + + *stack += *size; + + if (!PK_STACK_PRE_DECREMENT) + { + *stack -= sizeof(PK_STACK_TYPE); + *size -= sizeof(PK_STACK_TYPE); + } + + mask = PK_STACK_ALIGNMENT - 1; + excess = *stack & mask; + *stack -= excess; + *size -= excess; + *size = (*size / sizeof(PK_STACK_TYPE)) * sizeof(PK_STACK_TYPE); + + if (PK_STACK_CHECK) + { + p = (PK_STACK_TYPE*)(*stack); + count = *size / sizeof(PK_STACK_TYPE); + + for (i = 0; i < count; i++) + { + if (PK_STACK_PRE_DECREMENT) + { + *(--p) = PK_STACK_PATTERN; + } + else + { + *(p--) = PK_STACK_PATTERN; + } + } + } + + __pk_stack_create_initial_frame(stack, size); + + } + else + { + + PK_PANIC(PK_UNIMPLEMENTED); + } + + return PK_OK; +} diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h new file mode 100644 index 00000000..0b5970cd --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h @@ -0,0 +1,80 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_THREAD_H__ +#define __PK_THREAD_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_thread.h +/// \brief Contains private declarations and definitions needed for threads +/// + +void +__pk_thread_map(PkThread* thread); + +void +__pk_thread_unmap(PkThread* thread); + + +// Interrupts must be disabled at entry. + +static inline int +__pk_thread_is_active(PkThread* thread) +{ + return ((thread->state != PK_THREAD_STATE_COMPLETED) && + (thread->state != PK_THREAD_STATE_DELETED)); +} + + +// Interrupts must be disabled at entry. + +static inline int +__pk_thread_is_mapped(PkThread* thread) +{ + return (thread->state == PK_THREAD_STATE_MAPPED); +} + + +// Interrupts must be disabled at entry. This is only called on mapped threads. + +static inline int +__pk_thread_is_runnable(PkThread* thread) +{ + return __pk_thread_queue_member(&__pk_run_queue, thread->priority); +} + + +// Interrupts must be disabled at entry. + +static inline PkThread* +__pk_thread_at_priority(PkThreadPriority priority) +{ + return (PkThread*)__pk_priority_map[priority]; +} + +#endif /* __PK_THREAD_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c new file mode 100644 index 00000000..87bc2d8d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c @@ -0,0 +1,645 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_thread_core.c +/// \brief PK thread APIs +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present at runtime in any PK application that enables threads. + +#include "pk.h" +#include "pk_thread.h" + +#define __PK_THREAD_CORE_C__ + + +// This routine is only used locally. Interrupts must be disabled +// at entry. The caller must also have checked that the priority is free. +// This routine is only called on threads known to be in a suspended state, +// either PK_THREAD_STATE_SUSPENDED_RUNNABLE or +// PK_THREAD_STATE_SUSPENDED_BLOCKED. Mapping a runnable thread adds it to +// the run queue. Mapping a thread pending on a semaphore either takes the +// count and becomes runnable or adds the thread to the pending queue for the +// semaphore. Mapping a sleeping thread requires no further action +// here. Scheduling after the map must be handled by the caller. + +void +__pk_thread_map(PkThread* thread) +{ + PkThreadPriority priority; + + priority = thread->priority; + __pk_priority_map[priority] = thread; + + if (thread->state == PK_THREAD_STATE_SUSPENDED_RUNNABLE) + { + + __pk_thread_queue_insert(&__pk_run_queue, priority); + + } + else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) + { + + if (thread->semaphore->count) + { + + thread->semaphore->count--; + __pk_thread_queue_insert(&__pk_run_queue, priority); + + } + else + { + + __pk_thread_queue_insert(&(thread->semaphore->pending_threads), + priority); + } + } + + thread->state = PK_THREAD_STATE_MAPPED; + + if (PK_KERNEL_TRACE_ENABLE) + { + if (__pk_thread_is_runnable(thread)) + { + PK_KERN_TRACE("THREAD_MAPPED_RUNNABLE(%d)", priority); + } + else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) + { + PK_KERN_TRACE("THREAD_MAPPED_SEMAPHORE_PEND(%d)", priority); + } + else + { + PK_KERN_TRACE("THREAD_MAPPED_SLEEPING(%d)", priority); + } + } +} + + +// This routine is only used locally. Interrupts must be disabled +// at entry. This routine is only ever called on threads in the +// PK_THREAD_STATE_MAPPED. Unmapping a thread removes it from the priority +// map, the run queue and any semaphore pend, but does not cancel any +// timers. Scheduling must be handled by the code calling +// __pk_thread_unmap(). + +void +__pk_thread_unmap(PkThread* thread) +{ + PkThreadPriority priority; + + priority = thread->priority; + __pk_priority_map[priority] = 0; + + if (__pk_thread_is_runnable(thread)) + { + + thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; + __pk_thread_queue_delete(&__pk_run_queue, priority); + + } + else + { + + thread->state = PK_THREAD_STATE_SUSPENDED_BLOCKED; + + if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) + { + __pk_thread_queue_delete(&(thread->semaphore->pending_threads), + priority); + } + } +} + + +// Schedule and run the highest-priority mapped runnable thread. +// +// The priority of the next thread to run is first computed. This may be +// PK_THREADS, indicating that the only thread to run is the idle thread. +// This will always cause (or defer) a 'context switch' to the idle thread. +// Otherwise, if the new thread is not equal to the current thread this will +// also cause (or defer) a context switch. Note that scheduling is defined in +// terms of priorities but actually implemented in terms of PkThread pointers. +// +// If we are not yet in thread mode we're done - threads will be started by +// pk_start_threads() later. If we're in thread context a context switch +// happens immediately. In an interrupt context the switch is deferred to the +// end of interrupt processing. + +void +__pk_schedule(void) +{ + __pk_next_priority = __pk_thread_queue_min(&__pk_run_queue); + __pk_next_thread = __pk_priority_map[__pk_next_priority]; + + if ((__pk_next_thread == 0) || + (__pk_next_thread != __pk_current_thread)) + { + + if (__pk_kernel_mode_thread()) + { + if (__pk_kernel_context_thread()) + { + if (__pk_current_thread != 0) + { + __pk_switch(); + } + else + { + __pk_next_thread_resume(); + } + } + else + { + __pk_delayed_switch = 1; + } + } + } +} + + +// This routine is only used locally. +// +// Completion and deletion are pretty much the same thing. Completion is +// simply self-deletion of the current thread (which is mapped by +// definition.) The complete/delete APIs have slightly different error +// conditions but are otherwise the same. +// +// Deleting a mapped thread first unmaps (suspends) the thread, which takes +// care of removing the thread from any semaphores it may be pending on. Then +// any outstanding timer is also cancelled. +// +// If the current thread is being deleted we install the idle thread as +// __pk_current_thread, so scheduling is forced and no context is saved on +// the context switch. +// +// Note that we do not create trace events for unmapped threads since the trace +// tag only encodes the priority, which may be in use by a mapped thread. + +void +__pk_thread_delete(PkThread* thread, PkThreadState final_state) +{ + PkMachineContext ctx; + int mapped; + + pk_critical_section_enter(&ctx); + + mapped = __pk_thread_is_mapped(thread); + + if (mapped) + { + __pk_thread_unmap(thread); + } + + __pk_timer_cancel(&(thread->timer)); + thread->state = final_state; + + if (mapped) + { + + if (PK_KERNEL_TRACE_ENABLE) + { + if (final_state == PK_THREAD_STATE_DELETED) + { + PK_KERN_TRACE("THREAD_DELETED(%d)", thread->priority); + } + else + { + PK_KERN_TRACE("THREAD_COMPLETED(%d)", thread->priority); + } + } + + if (thread == __pk_current_thread) + { + __pk_current_thread = 0; + } + + __pk_schedule(); + } + + pk_critical_section_exit(&ctx); +} + + +// Generic thread timeout +// +// This routine is called as a timer callback either because a sleeping thread +// has timed out or a thread pending on a semaphore has timed out. If the +// thread is not already runnable then the the timeout flag is set, and if the +// thread is mapped it is scheduled. +// +// This implementation allows that a thread blocked on a timer may have been +// made runnable by some other mechanism, such as acquiring a semaphore. In +// order to provide an iteration-free implementation of +// pk_semaphore_release_all(), cancelling any semaphore timeouts is deferred +// until the thread runs again. +// +// Note that we do not create trace events for unmapped threads since the trace +// tag only encodes the priority, which may be in use by a mapped thread. + +void +__pk_thread_timeout(void* arg) +{ + PkMachineContext ctx; + PkThread* thread = (PkThread*)arg; + + pk_critical_section_enter(&ctx); + + switch (thread->state) + { + + case PK_THREAD_STATE_MAPPED: + if (!__pk_thread_is_runnable(thread)) + { + thread->flags |= PK_THREAD_FLAG_TIMED_OUT; + __pk_thread_queue_insert(&__pk_run_queue, thread->priority); + __pk_schedule(); + } + + break; + + case PK_THREAD_STATE_SUSPENDED_RUNNABLE: + break; + + case PK_THREAD_STATE_SUSPENDED_BLOCKED: + thread->flags |= PK_THREAD_FLAG_TIMED_OUT; + thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; + break; + + default: + PK_PANIC(PK_THREAD_TIMEOUT_STATE); + } + + pk_critical_section_exit(&ctx); +} + + +// This routine serves as a container for the PK_START_THREADS_HOOK and +// actually starts threads. The helper routine __pk_call_pk_start_threads() +// arranges this routine to be called with interrupts disabled while running +// on the kernel stack. +// +// The reason for this roundabout is that we want to be able to run a hook +// routine (transparent to the application) that can hand over every last byte +// of free memory to "malloc()" - including the stack of main(). Since we +// always need to run on some stack, we chose to run the hook on the kernel +// stack. However to do this safely we need to make sure +// that no interrupts will happen during this time. When __pk_thread_resume() +// is finally called all stack-based context is lost but it doesn't matter at +// that point - it's a one-way street into thread execution. +// +// This is considered part of pk_start_threads() and so is also considered a +// 'core' routine. + +void +__pk_start_threads(void) +{ + PK_START_THREADS_HOOK; + + __pk_next_thread_resume(); + + PK_PANIC(PK_START_THREADS_RETURNED); +} + + +/// Start PK threads +/// +/// This routine starts the PK thread scheduler infrastructure. This routine +/// must be called after a call of \c pk_initialize(). This routine never +/// returns. Interrupt (+ timer) only configurations of PK need not call this +/// routine. +/// +/// Note: This tiny routine is considered a 'core' routine so that the +/// initialziation code can safely recover all 'init' code space before +/// starting threads. +/// +/// This routine typically does not return - any return value indicates an +/// error; see \ref pk_errors +/// +/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was called twice. + +int +pk_start_threads(void) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(__pk_kernel_mode_thread(), PK_ILLEGAL_CONTEXT_THREAD); + } + + __pk_call_pk_start_threads(); + + return 0; +} + + +/// Resume a suspended thread +/// +/// \param thread The thread to resume +/// +/// PK only allows one thread at a time to run at a given priority, and +/// implements the notion of a thread \e claiming a priority. A suspended +/// thread claims a priority when it is mapped by a call of +/// pk_thread_resume(). This API will succeed only if no other active thread +/// is currently mapped at the priority assigned to the thread. PK provides +/// the pk_thread_at_priority() API which allows an application-level +/// scheduler to correctly manage multiple threads running at the same +/// priority. +/// +/// If the thread was sleeping while suspended it remains asleep. However if +/// the sleep timer timed out while the thread was suspended it will be +/// resumed runnable. +/// +/// If the thread was blocked on a semaphore when it was suspended, then when +/// the thread is resumed it will attempt to reacquire the semaphore. +/// However, if the thread was blocked on a semaphore with timeout while +/// suspended and the timeout interval has passed, the thread will be resumed +/// runnable and see that the semaphore pend timed out. +/// +/// It is not an error to call pk_thread_resume() on a mapped +/// thread. However it is an error to call pk_thread_resume() on a completed +/// or deleted thread. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion, including calls on a \a thread that is +/// already mapped. +/// +/// \retval -PK_INVALID_THREAD_AT_RESUME1 The \a thread is a null (0) pointer. +/// +/// \retval -PK_INVALID_THREAD_AT_RESUME2 The \a thread is not active, +/// i.e. has completed or been deleted. +/// +/// \retval -PK_PRIORITY_IN_USE_AT_RESUME Another thread is already mapped at +/// the priority of the \a thread. + +int +pk_thread_resume(PkThread* thread) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_RESUME1); + } + + pk_critical_section_enter(&ctx); + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF_CRITICAL(!__pk_thread_is_active(thread), + PK_INVALID_THREAD_AT_RESUME2, + &ctx); + } + + if (!__pk_thread_is_mapped(thread)) + { + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF_CRITICAL(__pk_priority_map[thread->priority] != 0, + PK_PRIORITY_IN_USE_AT_RESUME, + &ctx); + } + + __pk_thread_map(thread); + __pk_schedule(); + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// Suspend a thread +/// +/// Any active thread can be suspended. A suspended thread 1) remains active +/// but will not be scheduled; 2) relinquishes its priority assignment, +/// allowing another thread to be resumed at the suspended thread's priority; +/// and 3) disassociates from any semaphore mutual exclusion it may have been +/// participating in. +/// +/// If a sleeping thread is suspended, the sleep timer remains active but a +/// timeout of the timer simply marks the thread as runnable, but does not +/// resume the thread. +/// +/// If a thread blocked on a semaphore is suspended, the thread no longer +/// participates in the semaphore mutual exclusion. If the thread is later +/// resumed it will attempt to acquire the semaphore again the next time it +/// runs (unless it was blocked with a timeout and the timeout has expired). +/// +/// If a thread blocked on a semaphore with timeout is suspended, the +/// semaphore timeout timer continues to run. If the timer times out while the +/// thread is suspended the thread is simply marked runnable. If the thread is +/// later resumed, the suspended call of \c pk_semaphore_pend() will return the +/// timeout code -PK_SEMAPHORE_PEND_TIMED_OUT. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion, including calls on a \a thread that is +/// already suspended. +/// +/// \retval -PK_INVALID_THREAD_AT_SUSPEND1 The \a thread is a null (0) pointer +/// +/// \retval -PK_INVALID_THREAD_AT_SUSPEND2 The \a thread is not active, +/// i.e. has completed or been deleted. + +int +pk_thread_suspend(PkThread* thread) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF((thread == 0), PK_INVALID_THREAD_AT_SUSPEND1); + } + + pk_critical_section_enter(&ctx); + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF_CRITICAL(!__pk_thread_is_active(thread), + PK_INVALID_THREAD_AT_SUSPEND2, + &ctx); + } + + if (__pk_thread_is_mapped(thread)) + { + + PK_KERN_TRACE("THREAD_SUSPENDED(%d)", thread->priority); + __pk_thread_unmap(thread); + __pk_schedule(); + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// Delete a thread +/// +/// Any active thread can be deleted. If a thread is deleted it is removed +/// from the run queue, deleted from the timer queue (if sleeping or blocked +/// on a semaphore with timeout), and deleted from the semaphore mutual +/// exclusion if blocked on a semaphore. The thread control block is then +/// marked as deleted. +/// +/// Once a thread has completed or been deleted the thread structure and +/// thread stack areas can be used for other purposes. +/// +/// \param thread The thread to delete +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors. If a +/// thread deletes itself this API does not return at all. +/// +/// \retval 0 Successful completion, including calls on a \a thread that has +/// completed or had already been deleted. +/// +/// +/// \retval -PK_INVALID_THREAD_AT_DELETE The \a thread is a null (0) pointer. + +int +pk_thread_delete(PkThread* thread) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_DELETE); + } + + __pk_thread_delete(thread, PK_THREAD_STATE_DELETED); + + return PK_OK; +} + + +/// Complete a thread +/// +/// If a thread ever returns from the subroutine defining the thread entry +/// point, the thread is removed from all PK kernel data structures and +/// marked completed. The thread routine can also use the API pk_complete() +/// to make this more explicit if desired. PK makes no distinction between +/// completed and deleted threads, but provides these indications for +/// the benefit of the application. +/// +/// Note that this API is only available from the current thread to mark its +/// own completion. +/// +/// Once a thread has completed or been deleted the thread structure and +/// thread stack areas can be used for other purposes. +/// +/// Any return value indicates an error; see \ref pk_errors. In the event of +/// a successful completion this API does not return to the caller, which is +/// always the thread context being completed. +/// +/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread +/// context. + +// Note: Casting __pk_current_thread removes the 'volatile' attribute. + +int +pk_complete(void) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_UNLESS_THREAD_CONTEXT(); + } + + __pk_thread_delete((PkThread*)__pk_current_thread, + PK_THREAD_STATE_COMPLETED); + + return PK_OK; +} + +/// Sleep a thread for an interval relative to the current time. +/// +/// \param interval A time interval relative to the current timebase. +/// +/// Threads can use this API to sleep for a time relative to the current +/// timebase. The absolute timeout is \c pk_timebase_get() + \a interval. +/// +/// Sleeping threads are not scheduled, although they maintain their +/// priorities. This differs from thread suspension, where the suspended +/// thread relinquishes its priority. When the sleep timer times out the +/// thread becomes runnable again, and will run as soon as it becomes the +/// highest-priority mapped runnable thread. +/// +/// Sleeping threads may also be later suspended. In this case the Sleep timer +/// continues to run, and if it times out before the thread is resumed the +/// thread will be immediately runnable when it is resumed. +/// +/// See the PK specification for a full discussion of how PK handles +/// scheduling events at absolute times "in the past". Briefly stated, if the +/// \a interval is 0 or is so small that the absolute time becomes a "past" +/// time before the Sleep is actually scheduled, the thread will Sleep for the +/// briefest possible period supported by the hardware. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion. +/// +/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread +/// context. + +int +pk_sleep(PkInterval interval) +{ + PkTimebase time; + PkMachineContext ctx; + PkThread* current; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_UNLESS_THREAD_CONTEXT(); + } + + time = pk_timebase_get() + PK_INTERVAL_SCALE(interval); + + pk_critical_section_enter(&ctx); + + current = (PkThread*)__pk_current_thread; + + current->timer.timeout = time; + __pk_timer_schedule(&(current->timer)); + + current->flags |= PK_THREAD_FLAG_TIMER_PEND; + + PK_KERN_TRACE("THREAD_SLEEP(%d)", current->priority); + + __pk_thread_queue_delete(&__pk_run_queue, current->priority); + __pk_schedule(); + + current->flags &= ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT); + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + +#undef __PK_THREAD_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c new file mode 100644 index 00000000..17f9804e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c @@ -0,0 +1,154 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_thread_init.c +/// \brief PK thread API initialization routines +/// +/// The entry points in this file are routines that are typically used during +/// initialization, and their code space could be deallocated and recovered if +/// no longer needed by the application after initialization. + +#include "pk.h" + +/// Create (initialize) a thread +/// +/// \param thread A pointer to an PkThread structure to initialize +/// +/// \param thread_routine The subroutine that implements the thread +/// +/// \param arg Private data to be passed as the argument to the thread +/// routine when it begins execution +/// +/// \param stack The stack space of the thread +/// +/// \param stack_size The size of the stack in bytes +/// +/// \param priority The initial priority of the thread +/// +/// The \a thread argument must be a pointer to an uninitialized or completed +/// or deleted thread. This \c PkThread structure \em is the thread, so this +/// memory area must not be modified by the application until the thread +/// completes or is deleted. PK can not tell if an PkThread structure is +/// currently in use as a thread control block.pk_thread_create() will +/// silently overwrite an PkThread structure that is currently in use. +/// +/// The stack area must be large enough to hold the dynamic stack requirements +/// of the entry point routine, and all subroutines and functions that might +/// be invoked on any path from the entry point. The stack must also always +/// be able to hold the thread context in the event the thread is preempted, +/// plus other critical context. PK aligns stack areas in machine-specific +/// ways, so that the actual stack area may reduced in size slightly if it is +/// not already aligned. +/// +/// Threads are created runnable but unmapped. A newly created thread will +/// not be eligible to run until a call of pk_thread_resume() targets the +/// thread. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_THREAD_AT_CREATE The \a thread is a null (0) pointer. +/// +/// \retval -PK_INVALID_ARGUMENT_THREAD1 the \a thread_routine is null (0) +/// +/// \retval -PK_INVALID_ARGUMENT_THREAD2 the \a priority is invalid, +/// +/// \retval -PK_INVALID_ARGUMENT_THREAD3 the stack area wraps around +/// the end of memory. +/// +/// \retval -PK_STACK_OVERFLOW The stack area at thread creation is smaller +/// than the minimum safe size. + +int +pk_thread_create(PkThread* thread, + PkThreadRoutine thread_routine, + void* arg, + PkAddress stack, + size_t stack_size, + PkThreadPriority priority) +{ + int rc; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CREATE); + PK_ERROR_IF((thread_routine == 0) || + (priority >= PK_THREADS), + PK_INVALID_ARGUMENT_THREAD1); + } + + rc = __pk_stack_init(&stack, &stack_size); + + if (rc) + { + return rc; + } + + thread->saved_stack_pointer = stack; + thread->stack_base = stack; + + if (PK_STACK_DIRECTION < 0) + { + + thread->stack_limit = stack - stack_size; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread->stack_limit > thread->stack_base, + PK_INVALID_ARGUMENT_THREAD2); + } + + } + else + { + + thread->stack_limit = stack + stack_size; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread->stack_limit < thread->stack_base, + PK_INVALID_ARGUMENT_THREAD3); + } + } + + thread->semaphore = 0; + thread->priority = priority; + thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE; + thread->flags = 0; + + pk_timer_create(&(thread->timer), + __pk_thread_timeout, + (void*)thread); + + __pk_thread_context_initialize(thread, thread_routine, arg); + + return rc; +} diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c new file mode 100644 index 00000000..a3b241e7 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c @@ -0,0 +1,341 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_thread_util.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_thread_util.c +/// \brief PK thread utility APIs +/// +/// The entry points in this file are considered extra routines that will +/// only be included in a PK application that enables threads and uses at +/// least one of these interfaces. + +#include "pk.h" +#include "pk_thread.h" + +/// Get information about a thread. +/// +/// \param thread A pointer to the PkThread to query +/// +/// \param state The value returned through this pointer is the current state +/// of the thread; See \ref pk_thread_states. The caller can set this +/// parameter to the null pointer (0) if this information is not required. +/// +/// \param priority The value returned through this pointer is the current +/// priority of the thread. The caller can set this parameter to the null +/// pointer (0) if this information is not required. +/// +/// \param runnable The value returned through this pointer is 1 if the thread +/// is in state PK_THREAD_STATE_MAPPED and is currently in the run queue +/// (i.e., neither blocked on a semaphore nor sleeping), otherwise 0. The +/// caller can set this parameter to the null pointer (0) if this information +/// is not required. +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from a critical section. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_THREAD_AT_INFO The \a thread is a null (0) pointer. + +int +pk_thread_info_get(PkThread* thread, + PkThreadState* state, + PkThreadPriority* priority, + int* runnable) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_INFO); + } + + if (state) + { + *state = thread->state; + } + + if (priority) + { + *priority = thread->priority; + } + + if (runnable) + { + *runnable = ((thread->state == PK_THREAD_STATE_MAPPED) && + __pk_thread_queue_member(&__pk_run_queue, + thread->priority)); + } + + return PK_OK; +} + + +/// Change the priority of a thread. +/// +/// \param thread The thread whose priority will be changed +/// +/// \param new_priority The new priority of the thread +/// +/// \param old_priority The value returned through this pointer is the +/// old priority of the thread prior to the change. The caller can set +/// this parameter to the null pointer (0) if this information is not +/// required. +/// +/// Thread priorities can be changed by the \c pk_thread_priority_change() +/// API. This call will fail if the thread pointer is invalid or if the thread +/// is mapped and the new priority is currently in use. The call will succeed +/// even if the \a thread is suspended, completed or deleted. The +/// application-level scheduling algorithm is completely responsible for the +/// correctness of the application in the event of suspended, completed or +/// deleted threads. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion, including the redundant case of +/// attempting to change the priority of the thread to its current priority. +/// +/// \retval -PK_INVALID_THREAD_AT_CHANGE The \a thread is null (0) or +/// otherwise invalid. +/// +/// \retval -PK_INVALID_ARGUMENT_THREAD_CHANGE The \a new_priority is invalid. +/// +/// \retval -PK_PRIORITY_IN_USE_AT_CHANGE The \a thread is mapped and the \a +/// new_priority is currently in use by another thread. + +int +pk_thread_priority_change(PkThread* thread, + PkThreadPriority new_priority, + PkThreadPriority* old_priority) +{ + PkMachineContext ctx; + PkThreadPriority priority; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CHANGE); + PK_ERROR_IF(new_priority > PK_THREADS, + PK_INVALID_ARGUMENT_THREAD_CHANGE); + } + + pk_critical_section_enter(&ctx); + + priority = thread->priority; + + if (priority != new_priority) + { + + if (!__pk_thread_is_mapped(thread)) + { + + thread->priority = new_priority; + + } + else + { + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF_CRITICAL(__pk_priority_map[new_priority] != 0, + PK_PRIORITY_IN_USE_AT_CHANGE, + &ctx); + } + + __pk_thread_unmap(thread); + thread->priority = new_priority; + __pk_thread_map(thread); + __pk_schedule(); + } + } + + if (old_priority) + { + *old_priority = priority; + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// Return a pointer to the thread (if any) mapped at a given priority. +/// +/// \param priority The thread priority of interest +/// +/// \param thread The value returned through this pointer is a pointer to the +/// thread currently mapped at the given priority level. If no thread is +/// mapped, or if the \a priority is the priority of the idle thread, the +/// pointer returned will be null (0). +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from a critical section. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion. +/// +/// \retval -PK_INVALID_ARGUMENT_THREAD_PRIORITY The \a priority is invalid +/// or the \a thread parameter is null (0). + +int +pk_thread_at_priority(PkThreadPriority priority, + PkThread** thread) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF((priority > PK_THREADS) || (thread == 0), + PK_INVALID_ARGUMENT_THREAD_PRIORITY); + } + + *thread = __pk_thread_at_priority(priority); + + return PK_OK; +} + + +/// Swap thread priorities +/// +/// \param thread_a A pointer to an initialized PkThread +/// +/// \param thread_b A pointer to an initialized PkThread +/// +/// This API swaps the priorities of \a thread_a and \a thread_b. The API is +/// provided to support general and efficient application-directed scheduling +/// algorithms. The requirements on the \a thread_a and \a thread_b arguments +/// are that they are valid pointers to initialized PkThread structures, that +/// the current thread priorities of both threads are legal, and that if a +/// thread is currently mapped, that the new thread priority is not otherwise +/// in use. +/// +/// The API does not require either thread to be mapped, or even to be active. +/// It is legal for one or both of the swap partners to be suspended, deleted +/// or completed threads. The application is completely responsible for the +/// correctness of scheduling algorithms that might operate on inactive or +/// suspended threads. +/// +/// The API does not change the mapped status of a thread. A thread will be +/// mapped after the call of pk_thread_priority_swap() if and only if it was +/// mapped prior to the call. If the new priority of a mapped thread is +/// currently in use (by a thread other than the swap partner), then the +/// PK_PRIORITY_IN_USE_AT_SWAP error is signalled and the swap does not take +/// place. This could only happen if the swap partner is not currently mapped. +/// +/// It is legal for a thread to swap its own priority with another thread. The +/// degenerate case that \a thread_a and \a thread_b are equal is also legal - +/// but has no effect. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion, including the redundant cases that do not +/// actually change priorities, or the cases that assign new priorities to +/// suspended, completed or deleted threads. +/// +/// \retval -PK_INVALID_THREAD_AT_SWAP1 One or both of \a thread_a and +/// \a thread_b is null (0) or otherwise invalid, +/// +/// \retval -PK_INVALID_THREAD_AT_SWAP2 the priorities of One or both of +/// \a thread_a and \a thread_b are invalid. +/// +/// \retval -PK_INVALID_ARGUMENT One or both of the priorities +/// of \a thread_a and \a thread_b is invalid. +/// +/// \retval -PK_PRIORITY_IN_USE_AT_SWAP Returned if a thread is mapped and the +/// new thread priority is currently in use by another thread (other than the +/// swap partner). + +int +pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b) +{ + PkMachineContext ctx; + PkThreadPriority priority_a, priority_b; + int mapped_a, mapped_b; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF((thread_a == 0) || (thread_b == 0), + PK_INVALID_THREAD_AT_SWAP1); + } + + pk_critical_section_enter(&ctx); + + if (thread_a != thread_b) + { + + mapped_a = __pk_thread_is_mapped(thread_a); + mapped_b = __pk_thread_is_mapped(thread_b); + priority_a = thread_a->priority; + priority_b = thread_b->priority; + + if (PK_ERROR_CHECK_API) + { + int priority_in_use; + PK_ERROR_IF_CRITICAL((priority_a > PK_THREADS) || + (priority_b > PK_THREADS), + PK_INVALID_THREAD_AT_SWAP2, + &ctx); + priority_in_use = + (mapped_a && !mapped_b && + (__pk_thread_at_priority(priority_b) != 0)) || + (!mapped_a && mapped_b && + (__pk_thread_at_priority(priority_a) != 0)); + PK_ERROR_IF_CRITICAL(priority_in_use, + PK_PRIORITY_IN_USE_AT_SWAP, &ctx); + } + + if (mapped_a) + { + __pk_thread_unmap(thread_a); + } + + if (mapped_b) + { + __pk_thread_unmap(thread_b); + } + + thread_a->priority = priority_b; + thread_b->priority = priority_a; + + if (mapped_a) + { + __pk_thread_map(thread_a); + } + + if (mapped_b) + { + __pk_thread_map(thread_b); + } + + __pk_schedule(); + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c new file mode 100644 index 00000000..29063c80 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c @@ -0,0 +1,450 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_timer_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_timer_core.c +/// \brief PK portable kernel timer handler +/// +/// This file contains core routines that would be needed by any application +/// that requires PK timer support at runtime. +/// +/// PK implements a 'tickless' kernel - all events are scheduled at absolute +/// times of the PK timebase. This approach gives the application full +/// control over granularity of event scheduling. Scheduling in absolute time +/// opens up the possibility of scheduling events "in the past". PK +/// uniformly handles this case by scheduling "past" events to occur 1 +/// timebase tick in the future, so that timer callbacks are always run in the +/// expected interrupt context. +/// +/// PK implements the time queue as a simple unordered list of events, plus a +/// dedicated variable that holds the earliest timeout of any event in the +/// list. This is thought to be an appropriate data structure for the +/// following reasons: +/// +/// - PK applications will be small and will not schedule a large number of +/// events. Therefore the cost of scanning the list each time an event times +/// out is balanced against the cost of maintaining the list as a sorted data +/// structure each time an event is added or removed from the event queue. +/// +/// - PK applications may schedule and cancel many, many more events (safety +/// timeouts) than are ever allowed to expire. Events can be added and deleted +/// from the simple DEQUE very quickly since there is no sorting +/// overhead. +/// +/// Events are added to the queue simply by placing them at the end of the +/// queue. If the new event times out earlier than the previous earliest +/// event, the hardware timeout is rescheduled for the new event time. Events +/// are deleted from the queue (cancelled) simply by deleting them. Deletion +/// does not affect the hardware timeout, even if the deleted event would have +/// been the next to time out. It is not an error for the timer handler to +/// take a timer interrupt and find no events pending. Pending events can +/// also be rescheduled in place. +/// +/// When a timeout occurs the event list is scanned from the beginning, and +/// any event that has timed out has its callback processed. +/// Since event and callback processing take +/// time, the list is potentially scanned multiple times until there are no +/// more timed-out events in the list. +/// +/// Note that callbacks are not necessarily processed in time-order. In this +/// sense the PK time queue is like a traditional tick-based time queue in +/// that events are effectively lumped into groups of events that time out +/// together. In a tick-based kernel the 'lump' is the tick interval; here +/// the 'lump' is a variable interval that corresponds to the time it takes to +/// process the entire event list. +/// +/// Timer callbacks are typically run with interrupt preemption enabled. +/// Special callbacks may run without preemption. This is the only part of +/// the PK kernel where data structures of indeterminate size are processed. +/// During processing of the event list by the timer interrupt handler, the +/// consideration of each event always includes a window of preemptability. + +#define __PK_TIMER_CORE_C__ + +#include "pk.h" + +// Declare the timer bottom half handler +static PK_BH_HANDLER(__pk_timer_bh_handler); + +// Define the timer bottom half handler that the interrupt handler will +// schedule +PK_BH_STATIC_CREATE(pk_timer_bh, __pk_timer_bh_handler, 0); + + +// This routine is only used in this file, and will always be called in a +// critical section. + +static inline int +timer_active(PkTimer* timer) +{ + return pk_deque_is_queued((PkDeque*)timer); +} + + +// This is the kernel version of pk_timer_cancel(). +// +// This routine is used here and by thread and semaphore routines. +// External interrupts must be disabled at entry. +// +// If the timer is active, then there is a special case if we are going to +// delete the 'cursor' - that is the timer that __pk_timer_handler() is going +// to handle next. In this case we need to move the cursor to the next timer +// in the queue. +// +// Note that cancelling a timer does not cause a re-evaluation of the next +// timeout. This will happen naturally when the current timeout expires. + +int +__pk_timer_cancel(PkTimer* timer) +{ + int rc; + PkDeque* timer_deque = (PkDeque*)timer; + PkTimeQueue* tq = &__pk_time_queue; + + if (!timer_active(timer)) + { + + rc = -PK_TIMER_NOT_ACTIVE; + + } + else + { + + if (timer_deque == tq->cursor) + { + tq->cursor = tq->cursor->next; + } + + pk_deque_delete(timer_deque); + rc = 0; + } + + return rc; +} + + +// This is the kernel version of pk_timer_schedule(). +// +// This routine is used here and by thread and semaphore routines. +// interrupts must be disabled at entry. +// +// Unless the timer is already active it is enqueued in the doubly-linked +// timer list by inserting the timer at the end of the queue. Then the +// hardware timeout is scheduled if necessary. If the time queue 'cursor' != 0 +// we are in the midst of processing the time queue, and the end of time queue +// processing will schedule the next hardware timemout. + +void +__pk_timer_schedule(PkTimer* timer) +{ + PkTimeQueue* tq = &__pk_time_queue; + + if (!timer_active(timer)) + { + pk_deque_push_back((PkDeque*)tq, (PkDeque*)timer); + } + + if (timer->timeout < tq->next_timeout) + { + tq->next_timeout = timer->timeout; + + if (tq->cursor == 0) + { + __pk_schedule_hardware_timeout(tq->next_timeout); + } + } +} + + +// The tickless timer mechanism has timed out. Note that due to timer +// deletions and other factors, there may not actually be a timer in the queue +// that has timed out - but it doesn't matter (other than for efficiency). +// +// This routine must not be entered reentrantly. +// +// First, time out any timers that have expired. Timers in the queue are +// unordered, so we have to check every one. Since passing through the +// loop takes time, we may have to make multiple passes until we know +// that there are no timers in the queue that have already timed +// out. Note that it would also work to only go through the loop once and +// let the hardware scheduler take care of looping, but that would imply +// more overhead than the current implementation. +// +// On each pass through the loop tq->next_timeout computes the minimum timeout +// of events remaining in the queue. This is the only part of the kernel that +// searches a list of indefinite length. Kernel interrupt latency is mitigated +// by running this function as a bottom half. As such, interrupts are only +// disabled when explicitly requested. +// +// Because interrupt preemption is enabled during processing, and preempting +// handlers may invoke time queue operations, we need to establish a pointer +// to the next entry to be examined (tq->cursor) before enabling interupts. +// It's possible that this pointer will be changed by other interrupt handlers +// that cancel the timer pointed to by tq->cursor. +// +// The main loop iterates on the PkDeque form of the time queue, casting each +// element back up to the PkTimer as it is processed. + +static void +__pk_timer_bh_handler(void* arg) +{ + PkMachineContext ctx; + PkTimeQueue* tq; + PkTimebase now; + PkTimer* timer; + PkDeque* timer_deque; + PkTimerCallback callback; + + tq = &__pk_time_queue; + + // Check if we entered the function while it was running in another context. + if (PK_ERROR_CHECK_KERNEL) + { + if (tq->cursor != 0) + { + PK_PANIC(PK_TIMER_HANDLER_INVARIANT); + } + } + + pk_critical_section_enter(&ctx); + + while ((now = pk_timebase_get()) >= tq->next_timeout) + { + tq->next_timeout = PK_TIMEBASE_MAX; + timer_deque = ((PkDeque*)tq)->next; + + // Iterate through the entire timer list, calling the callback of + // timed-out elements and finding the timer that will timeout next, + // which is stored in tq->next_timeout. + while (timer_deque != (PkDeque*)tq) + { + + timer = (PkTimer*)timer_deque; + + // Setting this to a non-zero value indicates we are in the middle + // of processing the time queue. + tq->cursor = timer_deque->next; + + if (timer->timeout <= now) + { + + // The timer timed out. It is removed from the queue. + // + // The callback may be made with interrupt preemption enabled + // or disabled. However to mitigate kernel interrupt latency + // we go ahead and open up to interrupts after the callback if + // the callback itself was not preemptible. + + pk_deque_delete(timer_deque); + + pk_critical_section_exit(&ctx); + + callback = timer->callback; + + if (callback) + { + callback(timer->arg); + } + + } + else + { + + // This timer has not timed out. Its timeout will simply + // participate in the computation of the next timeout. + tq->next_timeout = MIN(timer->timeout, tq->next_timeout); + pk_critical_section_exit(&ctx); + } + + timer_deque = tq->cursor; + pk_critical_section_enter(&ctx); + } + + // Time has passed since we checked the time. Loop back + // to check the time again and see if enough time has passed + // that the next timer has timed out too. + } + + pk_critical_section_exit(&ctx); + + // This marks that we are no longer processing the time queue + tq->cursor = 0; + + // Finally, reschedule the next timeout + __pk_schedule_hardware_timeout(tq->next_timeout); +} + + +void +__pk_timer_handler(void) +{ + //schedule the timer bottom half handler which + //is preemptible. + pk_bh_schedule(&pk_timer_bh); +} + + +/// Schedule a timer for an interval relative to the current time. +/// +/// \param timer The PkTimer to schedule. +/// +/// \param interval The timer will be scheduled to time out at the current +/// time (pk_timebase_get()) plus this \a interval. +/// +/// Once created with pk_timer_create() a timer can be \e scheduled, which +/// queues the timer in the kernel time queue. It is not an error to call \c +/// pk_timer_schedule() on a timer that is already scheduled in the time +/// queue - the timer is simply rescheduled with the new characteristics. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_TIMER_AT_SCHEDULE A a null (0) pointer was provided as +/// the \a timer argument. +/// + +int +pk_timer_schedule(PkTimer* timer, + PkInterval interval) +{ + PkMachineContext ctx; + PkTimebase timeout = pk_timebase_get() + PK_INTERVAL_SCALE(interval); + + pk_critical_section_enter(&ctx); + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_SCHEDULE); + } + + timer->timeout = timeout; + __pk_timer_schedule(timer); + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// Cancel (dequeue) a timer. +/// +/// \param timer The PkTimer to cancel. +/// +/// Timers can be canceled at any time. It is never an error to call +/// pk_timer_cancel() on an PkTimer object after it is created. Memory used +/// by an PkTimer can be safely reused for another purpose after a successful +/// call ofpk_timer_cancel(). +/// +/// Return values other than PK_OK (0) are not necessarily errors; see \ref +/// pk_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_TIMER_NOT_ACTIVE The \a timer is not currently scheduled, +/// i.e. it was never scheduled or has timed out. This code is returned for +/// information only and is not considered an error. +/// +/// The following return codes are error codes: +/// +/// \retval -PK_INVALID_TIMER_AT_CANCEL The \a timer is a null (0) pointer. +/// + +int +pk_timer_cancel(PkTimer* timer) +{ + PkMachineContext ctx; + int rc = PK_OK; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_CANCEL); + } + + pk_critical_section_enter(&ctx); + + rc = __pk_timer_cancel(timer); + + pk_critical_section_exit(&ctx); + + return rc; +} + + +/// Get information about a timer. +/// +/// \param timer The PkTimer to query +/// +/// \param timeout The API returns the absolute timeout of the timer through +/// this pointer. If the timer is active, this is the current timeout. If +/// the timer has timed out then this is the previous absolute timeout. If +/// the timer was never scheduled this will be 0. The caller can set this +/// parameter to the null pointer (0) if this information is not required. +/// +/// \param active If the value returned through this pointer is 1 then the +/// timer is active (currently scheduled), otherwise the value will be 0 +/// indicating an inactive timer. The caller can set this parameter to the +/// null pointer (0) if this information is not required. +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from a critical section. +/// +/// Return values other than PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_TIMER_AT_INFO The \a timer is a null (0) pointer. + +int +pk_timer_info_get(PkTimer* timer, + PkTimebase* timeout, + int* active) + +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_INFO); + } + + if (timeout) + { + *timeout = timer->timeout; + } + + if (active) + { + *active = timer_active(timer); + } + + return PK_OK; +} + +#undef __PK_TIMER_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c new file mode 100644 index 00000000..bfb34218 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c @@ -0,0 +1,85 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/kernel/pk_timer_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_timer_init.c +/// \brief PK timer initialization +/// +/// The entry points in this file might only be used during initialization of +/// the application. In this case the code space for these routines could be +/// recovered and reused after initialization. + +#include "pk.h" + + +/// Create (initialize) a timer. +/// +/// \param timer The PkTimer to initialize. +/// +/// \param callback The timer callback +/// +/// \param arg Private data provided to the callback. +/// +/// Once created with pk_timer_create() a timer can be scheduled with +/// pk_timer_schedule() or pk_timer_schedule_absolute(), which queues the +/// timer in the kernel time queue. Timers can be cancelled by a call of +/// pk_timer_cancel(). +/// +/// Timers created with pk_timer_create() are always run as +/// bottom-half handlers with interrupt preemption enabled. Timer callbacks are +/// free to enter critical sections if required, but must +/// always exit with interrupts enabled. +/// +/// Caution: PK has no way to know if an PkTimer structure provided to +/// pk_timer_create() is safe to use as a timer, and will silently modify +/// whatever memory is provided. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_TIMER_AT_CREATE The \a timer is a null (0) pointer. + +int +pk_timer_create(PkTimer* timer, + PkTimerCallback callback, + void* arg) +{ + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF((timer == 0), PK_INVALID_TIMER_AT_CREATE); + } + + pk_deque_element_create((PkDeque*)timer); + timer->timeout = 0; + timer->callback = callback; + timer->arg = arg; + + return PK_OK; +} diff --git a/import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk b/import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk new file mode 100644 index 00000000..424163df --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk @@ -0,0 +1,55 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/kernel/pkkernelfiles.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file pkkernelfiles.mk +# +# @brief mk for including architecture independent pk object files +# +# @page ChangeLogs Change Logs +# @section pkkernelfiles.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Include +########################################################################## + + +########################################################################## +# Object Files +########################################################################## +PK-C-SOURCES = pk_core.c pk_init.c pk_stack_init.c pk_bh_core.c pk_debug_ptrs.c + +PK-TIMER-C-SOURCES += pk_timer_core.c pk_timer_init.c + +PK-THREAD-C-SOURCES += pk_thread_init.c pk_thread_core.c pk_thread_util.c \ + pk_semaphore_init.c pk_semaphore_core.c + +PK_OBJECTS += $(PK-C-SOURCES:.c=.o) diff --git a/import/chips/p9/procedures/ppe/pk/ppe/Makefile b/import/chips/p9/procedures/ppe/pk/ppe/Makefile new file mode 100644 index 00000000..aef761ba --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/Makefile @@ -0,0 +1,74 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile compiles all of the PK code required for the 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/import/chips/p9/procedures/ppe/pk/ppe/pk_port.h b/import/chips/p9/procedures/ppe/pk/ppe/pk_port.h new file mode 100644 index 00000000..83d7ad32 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/pk_port.h @@ -0,0 +1,41 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe/pk_port.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_PORT_H__ +#define __PK_PORT_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_port.h +/// \brief The top-level CME environment header for PK. + +#define HWMACRO_PPE + +#include "ppe42.h" +#include "ppe_timebase.h" + +#endif /* __PK_PORT_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe/pkppefiles.mk b/import/chips/p9/procedures/ppe/pk/ppe/pkppefiles.mk new file mode 100644 index 00000000..fe27a99b --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/pkppefiles.mk @@ -0,0 +1,56 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe/pkppefiles.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file 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/import/chips/p9/procedures/ppe/pk/ppe/ppe.h b/import/chips/p9/procedures/ppe/pk/ppe/ppe.h new file mode 100644 index 00000000..a73f10f6 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/ppe.h @@ -0,0 +1,46 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe/ppe.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __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/import/chips/p9/procedures/ppe/pk/ppe/ppe_common.h b/import/chips/p9/procedures/ppe/pk/ppe/ppe_common.h new file mode 100644 index 00000000..9a67522d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/ppe_common.h @@ -0,0 +1,73 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe/ppe_common.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __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. + /// Since standalone models of the PPE do not support external interrupts + /// we just set the code to 64 (phantom interrupt) + .macro hwmacro_get_ext_irq + + li % r4, 64 + + .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/import/chips/p9/procedures/ppe/pk/ppe/ppe_init.c b/import/chips/p9/procedures/ppe/pk/ppe/ppe_init.c new file mode 100644 index 00000000..44294daa --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/ppe_init.c @@ -0,0 +1,49 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe/ppe_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file 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/import/chips/p9/procedures/ppe/pk/ppe/ppe_timebase.h b/import/chips/p9/procedures/ppe/pk/ppe/ppe_timebase.h new file mode 100644 index 00000000..ad8f4e64 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe/ppe_timebase.h @@ -0,0 +1,52 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe/ppe_timebase.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE_TIMEBASE_H__ +#define __PPE_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe_timebase.h +/// \brief support for using the OCB 32 bit timebase register +/// + +#include "pk.h" + +//The timebase register will never be supported in the base ppe model +#ifdef APPCFG_USE_EXT_TIMEBASE + #error "External timebase is not supported on the PPE standalone model" +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#ifndef __ASSEMBLER__ + + //assembly function is defined in ppe42_timebase.S + uint32_t pk_timebase32_get(void); + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE_TIMEBASE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/Makefile b/import/chips/p9/procedures/ppe/pk/ppe42/Makefile new file mode 100644 index 00000000..34a6d2e1 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/Makefile @@ -0,0 +1,50 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe42/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile is designed to be invoked with the -I argument set to +# the location of the "pk.mk" for the build + +include img_defs.mk +include pkppe42files.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +PPE42_OBJECTS += ${PPE42-TIMER-C-SOURCES:.c=.o} ${PPE42-TIMER-S-SOURCES:.S=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +PPE42_OBJECTS += ${PPE42-THREAD-C-SOURCES:.c=.o} ${PPE42-THREAD-S-SOURCES:.S=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(PPE42_OBJECTS)) + +all: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/div64.S b/import/chips/p9/procedures/ppe/pk/ppe42/div64.S new file mode 100644 index 00000000..15858e19 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/div64.S @@ -0,0 +1,272 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/div64.S $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file div64.S +/// \brief Unsigned 64/64 bit division +/// +/// This is IBM code, originally part of OS Open. The code has been slightly +/// modified from its original form, both to be compatible with PK and to +/// change the function prototype slightly. +/// +/// The code was provided by Matt Tyrlik in Raleigh. + +/* @#START#@ +** +** PSCN (Power Service and Control Network) +** Cage Controller OS Open Code +** +** (C) Copyright International Business Machines Corporation 2002 +** All Rights Reserved +** Licensed Material - Program Property of I B M +** Refer to copyright instructions: Form G120-2083 +** +** Module: +** div64.s +** +** Description: +** Divide 64 bit unsigned values on 32 bit CPU +** div64(uint64_t dividen, uint64_t divisor, +** uint64_t *quotient, uint64_t *remainder) +** +** Original source from: +** "The PowerPC Compiler Writer's Guide", pp62-65 by +** Steve Hoxey, Faraydon Karim, Bill Hay, Hank Warray, +** published by Warthman Associates, 240 Hamilton Avenue, +** Palo Alto, CA 94301, USA, 1996 for IBM. +** ISBN 0-9649654-0-2. +** +** This version checks for divisor equal to zero. +** +** Environment: +** OS Open (XCOFF) +** +** Linkage: +** AIX 4.3.3 +** +** @author +** Thomas Richter +** +** History: +** Date Author Description +** ----------------------------------------------------------------------------- +** 23-Sep-02 Richter Created +** +** @#END#@*/ + + .nolist +#include "pk.h" + .list + + .global_function __ppe42_udiv64 + + /* + ** Code comment notation: + ** msw = most-significant (high-order) word, i.e. bits 0..31 + ** lsw = least-significant (low-order) word, i.e. bits 32..63 + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** + ** R3:R4 = Input parameter, dividend. + ** R5:R6 = Input parameter, divisor. + ** R7 = Output parameter, pointer to quotient. + ** R8 = Output parameter, pointer to remainder. + ** + ** Pointer arguments point to a uint64_t. + ** + ** Division is achieved using a shift/rotate/substract algorithsm + ** described above. + ** The registers are used as follows: + ** R3:R4 = dividend (upper 32bits:lower 32bits) + ** R5:R6 = divisor (upper 32bits:lower 32bits) + ** + ** R7:R8 = temporary 64 bit register (upper 32bits:lower 32bits) + ** count the number of leading 0s in the dividend + ** + ** Here is the description from the book. The dividend is placed + ** in the low order part of a 4 (32bit) register sequence named + ** tmp-high:tmp-low:dividend-high:dividend:low or tmp:dvd for short. + ** + ** Each iteration includes the following steps: + ** 1. Shift tmp:dvd by one bit to the left. + ** 2. Subtract the divisor from tmp. This is a 64 bit operation. + ** 3. If result is greater than or equal, place result in tmp and + ** set the low order bit of dividend + ** 4. If result is negative, do not modify tmp and + ** clear the low order bit of dividend + ** 5. If the number of iterations is less than the width of the + ** dividend, goto step 1. + ** + ** Now the algorithm can be improved by reducing the number of + ** iterations to be executed. + ** 1. Calculate the leading zeroes of the dividend. + ** 2. Calculate the leading zeroes of the divisor. + ** 3. Calculate the significant ones of the dividend. + ** 4. Calculate the significant ones of the divisor. + ** + ** Initial tmp := dvd >> (dvd.SD - dvs.SD) + ** Initial dvd := dvd << (dvd.LZ + dvs.SD) + ** Loops: dvd.SD - dvs.SD. + ** + ** Warning: Special care must be taken if dvd.LZ == dvs.LZ. The code + ** below does so by reducing the number of dvs.SD by one. This leads + ** to the loop being executed 1 more time than really necessary, + ** but avoids to check for the case when dvd.LZ == dvs.LZ. + ** This case (dvd.LZ == dvs.LZ) only checks for the number of leading + ** zeroes, but does not check if dividend is really greater than the + ** divisor. + ** Consider 16/17, both have an LZ value of 59. The code sets dvs.LZ + ** 60. This resutls in dvs.SD to 4, thus one iteration after which + ** tmp is the remainder 16. + */ + +__ppe42_udiv64: // PK + + /* push R30 & R31 onto the stack */ + stwu r1, -16(r1) + stvd r30, 8(r1) + + /* Save result pointers on volatile spare registers */ + ori r31, r8, 0 /* Save remainder address */ + ori r30, r7, 0 /* Save quotient address */ + + /* count the number of leading 0s in the dividend */ + cmpwi cr0, r3, 0 /* dvd.msw == 0? */ + cntlzw r0, r3 /* R0 = dvd.msw.LZ */ + cntlzw r9, r4 /* R9 = dvd.lsw.LZ */ + bne cr0, lab1 /* if(dvd.msw == 0) dvd.LZ = dvd.msw.LZ */ + addi r0, r9, 32 /* dvd.LZ = dvd.lsw.LZ + 32 */ +lab1: + /* count the number of leading 0s in the divisor */ + cmpwi cr0, r5, 0 /* dvd.msw == 0? */ + cntlzw r9, r5 /* R9 = dvs.msw.LZ */ + cntlzw r10, r6 /* R10 = dvs.lsw.LZ */ + bne cr0, lab2 /* if(dvs.msw == 0) dvs.LZ = dvs.msw.LZ */ + cmpwi cr0, r6, 0 /* dvd.lsw == 0? */ + beq cr0, lab10 /* dvs.msw == 0 */ + addi r9, r10, 32 /* dvs.LZ = dvs.lsw.LZ + 32 */ + +lab2: + /* Determine shift amounts to minimize the number of iterations */ + cmpw cr0, r0, r9 /* Compare dvd.LZ to dvs.LZ */ + subfic r10, r0, 64 /* R10 = dvd.SD */ + bgt cr0, lab9 /* if(dvs > dvd) quotient = 0 */ + addi r9, r9, 1 /* See comment above. ++dvs.LZ (or --dvs.SD) */ + subfic r9, r9, 64 /* R9 = dvs.SD */ + add r0, r0, r9 /* (dvd.LZ + dvs.SD) = left shift of dvd for */ + /* initial dvd */ + subf r9, r9, r10 /* (dvd.SD - dvs.SD) = right shift of dvd for */ + /* initial tmp */ + mtctr r9 /* Number of iterations = dvd.SD - dvs.SD */ + + /* R7:R8 = R3:R4 >> R9 */ + cmpwi cr0, r9, 32 /* compare R9 to 32 */ + addi r7, r9, -32 + blt cr0, lab3 /* if(R9 < 32) jump to lab3 */ + srw r8, r3, r7 /* tmp.lsw = dvd.msw >> (R9 - 32) */ + addi r7, r0, 0 /* tmp.msw = 0 */ + b lab4 + +lab3: + srw r8, r4, r9 /* R8 = dvd.lsw >> R9 */ + subfic r7, r9, 32 + slw r7,r3,r7 /* R7 = dvd.msw << 32 - R9 */ + or r8, r8,r7 /* tmp.lsw = R8 | R7 */ + srw r7,r3,r9 /* tmp.msw = dvd.msw >> R9 */ +lab4: + /* R3:R4 = R3:R4 << R0 */ + cmpwi cr0, r0, 32 /* Compare R0 to 32 */ + addic r9, r0, -32 + blt cr0, lab5 /* if(R0 < 32) jump to lab5 */ + slw r3, r4, r9 /* dvd.msw = dvd.lsw << R9 */ + addi r4, r0, 0 /* dvd.lsw = 0 */ + b lab6 + +lab5: + slw r3, r3, r0 /* r3 = dvd.msw << r0 */ + subfic r9, r0, 32 + srw r9, r4, r9 /* r9 = dvd.lsw >> 32 - r0 */ + or r3, r3, r9 /* dvd.msw = r3 | r9 */ + slw r4, r4, r0 /* dvd.lsw = dvd.lsw << r0 */ +lab6: + /* Restoring division shift and subtract loop */ + addi r10, r0, -1 /* r10 = -1 */ + addic r7, r7, 0 /* Clear carry bit before loop starts */ +lab7: + /* + ** tmp:dvd is considered one large register + ** each portion is shifted left 1 bit by adding it to itself + ** adde sums the carry from the previous and creates a new carry + */ + adde r4, r4, r4 /* Shift dvd.lsw left 1 bit */ + adde r3, r3, r3 /* Shift dvd.msw to left 1 bit */ + adde r8, r8, r8 /* Shift tmp.lsw to left 1 bit */ + adde r7, r7, r7 /* Shift tmp.msw to left 1 bit */ + subfc r0, r6, r8 /* tmp.lsw - dvs.lsw */ + subfe. r9, r5, r7 /* tmp.msw - dvs.msw */ + blt cr0, lab8 /* if(result < 0) clear carry bit */ + or r8, r0, r0 /* Move lsw */ + or r7, r9, r9 /* Move msw */ + addic r0, r10, 1 /* Set carry bit */ + +lab8: + bdnz lab7 + + /* Write quotient and remainder */ + adde r4, r4, r4 /* quo.lsw (lsb = CA) */ + adde r3, r3, r3 /* quo.msw (lsb from lsw) */ + stw r4, 4(r30) + stw r3, 0(r30) + stw r8, 4(r31) /* rem.lsw */ + stw r7, 0(r31) /* rem.msw */ + b lab11 + +lab9: + /* Qoutient is 0, divisor > dividend */ + addi r0, r0, 0 + stw r3, 0(r31) /* Store remainder */ + stw r4, 4(r31) + stw r0, 0(r30) /* Set quotient to zero */ + stw r0, 4(r30) + b lab11 + +lab10: + /* Divisor is 0 */ + addi r0, r0, -1 + stw r0, 0(r31) /* Set remainder to zero */ + stw r0, 4(r31) + stw r0, 0(r30) /* Set quotient to zero */ + stw r0, 4(r30) + +lab11: + //pop r30 & r31 from stack + lvd r30, 8(r1) + lwz r1, 0(r1) + blr + .epilogue __ppe42_udiv64 diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h b/import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h new file mode 100644 index 00000000..fdc6c0bf --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h @@ -0,0 +1,65 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/pk_port_types.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_PORT_TYPES_H__ +#define __PK_PORT_TYPES_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_port_types.h +/// \brief Type definitions required by the PK port. +/// +/// \todo GCC provides a portable version of cntlzw called __builtin_clz(). +/// We should make the PK priority queues portable by using this facility. +/// +/// \todo I think that if more of the port-dependent types were moved here, we +/// could break the circular dependencies in some of the header inclusion and +/// simplify the way the PK/port/chip headers are included. + +/// An PkIrqId is an integer in the range of valid interrupts defined by the +/// interrupt controller. + +typedef uint8_t PkIrqId; + +/// PK requires the port to define the type PkThreadQueue, which is a +/// priority queue (where 0 is the highest priority). This queue must be able +/// to handle PK_THREADS + 1 priorities (the last for the idle thread). The +/// port must also define methods for clearing, insertion, deletion and min +/// (with assumed legal priorities). The min operation returns PK_THREADS if +/// the queue is empty. (Or a queue could be initialized with the PK_THREADS +/// entry always present - PK code never tries to delete the idle thread from +/// a thread queue). +/// +/// These queues are used both for the run queue and the pending queue +/// associated with every semaphore. +/// +/// On PPE42 with 32 threads (implied), this is a job for a uint32_t and +/// cntlzw(). + +typedef uint32_t PkThreadQueue; + +#endif /* __PK_PORT_TYPES_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk b/import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk new file mode 100644 index 00000000..17003f1c --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk @@ -0,0 +1,67 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe42/pkppe42files.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file pkppe42files.mk +# +# @brief mk for including ppe42 object files +# +# @page ChangeLogs Change Logs +# @section pkppe42files.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Include Files +########################################################################## + + + +########################################################################## +# Object Files +########################################################################## +PPE42-C-SOURCES = ppe42_core.c \ + ppe42_init.c \ + ppe42_irq_core.c\ + ppe42_gcc.c\ + ppe42_scom.c + +PPE42-S-SOURCES = ppe42_boot.S \ + ppe42_exceptions.S\ + div64.S\ + ppe42_timebase.S + +PPE42-TIMER-C-SOURCES = +PPE42-TIMER-S-SOURCES = + +PPE42-THREAD-C-SOURCES += +PPE42-THREAD-S-SOURCES += ppe42_thread_init.S + + +PPE42_OBJECTS += $(PPE42-C-SOURCES:.c=.o) $(PPE42-S-SOURCES:.S=.o) diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h new file mode 100644 index 00000000..91c2107a --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h @@ -0,0 +1,841 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_H__ +#define __PPE42_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42.h +/// \brief PPE42 port header for PK + +// Macros to define where declared code is actually compiled + +#ifdef __PPE42_C__ + #define IF__PPE42_CORE_C__(x) x + #define UNLESS__PPE42_CORE_C__(x) +#else + #define IF__PPE42_CORE_C__(x) + #define UNLESS__PPE42_CORE_C__(x) x +#endif + +#ifdef __PPE42_IRQ_CORE_C__ + #define IF__PPE42_IRQ_CORE_C__(x) x + #define UNLESS__PPE42_IRQ_CORE_C__(x) +#else + #define IF__PPE42_IRQ_CORE_C__(x) + #define UNLESS__PPE42_IRQ_CORE_C__(x) x +#endif + +#ifdef HWMACRO_GPE + #include "gpe.h" +#elif defined(HWMACRO_STD) + #include "std.h" +#elif defined(HWMACRO_PPE) + #include "ppe.h" +#else + #error "Macro Type not specified. Are you building from the correct directory?" +#endif + + +#include "ppe42_asm.h" +#include "ppe42_gcc.h" +#include "ppe42_spr.h" +#include "ppe42_msr.h" + + +///start + +/// The synchronization macros defined here all create a compiler +/// memory barrier that will cause GCC to flush/invalidate all memory data +/// held in registers before the macro. This is consistent with other systems, +/// e.g., the PowerPC Linux kernel, and is the safest way to define these +/// macros. + + +// Condition register fields + +#define CR_LT(n) (0x80000000u >> (4 * (n))) +#define CR_GT(n) (0x40000000u >> (4 * (n))) +#define CR_EQ(n) (0x20000000u >> (4 * (n))) +#define CR_SO(n) (0x10000000u >> (4 * (n))) + + +#ifndef __ASSEMBLER__ + +#include "stdint.h" + +/// ssize_t is defined explictly rather than bringing in all of <unistd.h> +#ifndef __ssize_t_defined + #define __ssize_t_defined + typedef int ssize_t; +#endif + +/// A memory barrier +#define barrier() asm volatile ("" : : : "memory") + +/// Ensure In-order Execution of Input/Output +#define eieio() asm volatile ("sync" : : : "memory") + +/// Memory barrier +#define sync() asm volatile ("sync" : : : "memory") + +/// Instruction barrier +#define isync() asm volatile ("sync" : : : "memory") + +/// CouNT Leading Zeros Word +#define cntlzw(x) \ + ({uint32_t __x = (x); \ + uint32_t __lzw; \ + asm volatile ("cntlzw %0, %1" : "=r" (__lzw) : "r" (__x)); \ + __lzw;}) + +/// CouNT Leading Zeros : uint32_t +static inline int +cntlz32(uint32_t x) +{ + return cntlzw(x); +} + +/// CouNT Leading Zeros : uint64_t +static inline int +cntlz64(uint64_t x) +{ + if (x > 0xffffffff) + { + return cntlz32(x >> 32); + } + else + { + return 32 + cntlz32(x); + } +} + + +/// 32-bit population count +static inline int +popcount32(uint32_t x) +{ + return __builtin_popcount(x); +} + + +/// 64-bit population count +static inline int +popcount64(uint64_t x) +{ + return __builtin_popcountll(x); +} + + +// NB: Normally we wouldn't like to force coercion inside a macro because it +// can mask programming errors, but for the MMIO macros the addresses are +// typically manifest constants or 32-bit unsigned integer expressions so we +// embed the coercion to avoid warnings. + +/// 8-bit MMIO Write +#define out8(addr, data) \ + do {*(volatile uint8_t *)(addr) = (data);} while(0) + +/// 8-bit MMIO Read +#define in8(addr) \ + ({uint8_t __data = *(volatile uint8_t *)(addr); __data;}) + +/// 16-bit MMIO Write +#define out16(addr, data) \ + do {*(volatile uint16_t *)(addr) = (data);} while(0) + +/// 16-bit MMIO Read +#define in16(addr) \ + ({uint16_t __data = *(volatile uint16_t *)(addr); __data;}) + +/// 32-bit MMIO Write +#define out32(addr, data) \ + do {*(volatile uint32_t *)(addr) = (data);} while(0) + +/// 32-bit MMIO Read +#define in32(addr) \ + ({uint32_t __data = *(volatile uint32_t *)(addr); __data;}) + +#ifdef HWMACRO_GPE + +/// 64-bit MMIO Write +#define out64(addr, data) \ + do { \ + uint64_t __data = (data); \ + volatile uint32_t *__addr_hi = (uint32_t *)(addr); \ + volatile uint32_t *__addr_lo = __addr_hi + 1; \ + *__addr_hi = (__data >> 32); \ + *__addr_lo = (__data & 0xffffffff); \ + } while(0) + +#else /* standard PPE's require a 64 bit write */ + +/// 64-bit MMIO Write +#define out64(addr, data) \ + {\ + uint64_t __d = (data); \ + uint32_t* __a = (uint32_t*)(addr); \ + asm volatile \ + (\ + "stvd %1, %0 \n" \ + : "=o"(*__a) \ + : "r"(__d) \ + ); \ + } + +#endif /* HWMACRO_GPE */ + +#ifdef HWMACRO_GPE +/// 64-bit MMIO Read +#define in64(addr) \ + ({ \ + uint64_t __data; \ + volatile uint32_t *__addr_hi = (uint32_t *)(addr); \ + volatile uint32_t *__addr_lo = __addr_hi + 1; \ + __data = *__addr_hi; \ + __data = (__data << 32) | *__addr_lo; \ + __data;}) + +#else /* Standard PPE's require a 64 bit read */ + +#define in64(addr) \ + ({\ + uint64_t __d; \ + uint32_t* __a = (uint32_t*)(addr); \ + asm volatile \ + (\ + "lvd %0, %1 \n" \ + :"=r"(__d) \ + :"o"(*__a) \ + ); \ + __d; \ + }) + +#endif /* HWMACRO_GPE */ + +#endif /* __ASSEMBLER__ */ + +#include "ppe42_irq.h" + +#ifndef __ASSEMBLER__ + +/// Store revision information as a (global) string constant +#define REVISION_STRING(symbol, rev) const char* symbol = rev; + +#else // __ASSEMBLER__ +// *INDENT-OFF* + +/// Store revision information as a global string constant + .macro .revision_string, symbol:req, rev:req + .pushsection .rodata + .balign 4 + .global \symbol +\symbol\(): + .asciz "\rev" + .balign 4 + .popsection + .endm + +// *INDENT-ON* +#endif // __ASSEMBLER__ + + + +#include "ppe42_context.h" + +// PPE42 stack characteristics for PK. The pre-pattern pattern is selected +// to be easily recognizable yet be an illegal instruction. + +#define PK_STACK_DIRECTION -1 +#define PK_STACK_PRE_DECREMENT 1 +#define PK_STACK_ALIGNMENT 8 +#define PK_STACK_TYPE unsigned int +#define PK_STACK_PATTERN 0x03abcdef + +// Kernel data structure offsets for assembler code + +#define PK_THREAD_OFFSET_SAVED_STACK_POINTER 0 +#define PK_THREAD_OFFSET_STACK_LIMIT 4 +#define PK_THREAD_OFFSET_STACK_BASE 8 + +// PK boot loader panic codes + +#define PPE42_BOOT_VECTORS_NOT_ALIGNED 0x00405001 + +// Interrupt handler panic codes + +#define PPE42_DEFAULT_IRQ_HANDLER 0x00405010 +#define PPE42_DEFAULT_SPECIAL_HANDLER 0x00405011 +#define PPE42_PHANTOM_INTERRUPT 0x00405012 +#define PPE42_PROGRAM_HALT 0x00405013 + + +// Exception handling invariant panic codes + +#define PPE42_IRQ_FULL_EXIT_INVARIANT 0x00405020 +#define PPE42_IRQ_FAST2FULL_INVARIANT 0x00405021 + + +// API error panic codes + + +// Application-overrideable definitions + +/// The default thread machine context has MSR[CE], MSR[EE] and MSR[ME] set, +/// and all other MSR bits cleared. +/// +/// The default definition allows external and machine check exceptions. This +/// definition can be overriden by the application. + +#ifndef PK_THREAD_MACHINE_CONTEXT_DEFAULT +#define PK_THREAD_MACHINE_CONTEXT_DEFAULT \ + (MSR_UIE | MSR_EE | MSR_ME) + +#endif + + +#ifndef __ASSEMBLER__ + +/// The PK kernel default panic sequence for C code +/// +/// By default a kernel panic from C code forces external debug mode then +/// generates a \c trap instruction followed by the error code. The \a code +/// argument must be a compile-time integer immediate. This definition can be +/// overriden by the application. +/// +/// The OCC may be running in internal debug mode for various reasons, and +/// TRAP-ing in internal debug mode would lead to an infinite loop in the +/// default Program Interrupt handler - which itself would be a TRAP (since +/// that's the default implementation of PK_PANIC(). Therefore by default +/// the panic is implemented as a special code sequence that forces the core +/// into external debug mode before issuing a TRAP which will halt the core. +/// To preserve the state we use the special global variables +/// __pk_panic_save_dbcr0 and __pk_panic_save_r3 defined in ppe42_core.c. +/// The original value of DBCR0 is destroyed, but can be recovered from the +/// global. In the end %r3 is reloaded from temporary storage and will be +/// unchanged at the halt. +/// +/// Note that there is a small chance that an interrupt will fire and +/// interrupt this code before the halt - in general there is no way around +/// this. +/// +/// The Simics environment does not model Debug events correctly. It executes +/// the TRAP as an illegal instruction and branches to the Program Interrupt +/// handler, destroying the contents of SRR0 and SRR1. Therefore we always +/// insert a special Simics magic breakpoint (which is an effective NOP) +/// before the hardware trap. The special-form magic instruction is +/// recognized by our Simics support scripts which decode the kernel state and +/// try to help the user interpret what happened based on the TRAP code. + +#ifndef PK_PANIC + +/*#define PK_PANIC(code) \ + do { \ + barrier(); \ + asm volatile ("stw %r3, __pk_panic_save_r3@sda21(0)"); \ + asm volatile ("mfdbcr0 %r3"); \ + asm volatile ("stw %r3, __pk_panic_save_dbcr0@sda21(0)"); \ + asm volatile ("lwz %r3, __pk_panic_dbcr0@sda21(0)"); \ + asm volatile ("mtdbcr0 %r3"); \ + asm volatile ("isync"); \ + asm volatile ("lwz %r3, __pk_panic_save_r3@sda21(0)"); \ + asm volatile ("rlwimi 1,1,0,0,0"); \ + asm volatile ("trap"); \ + asm volatile (".long %0" : : "i" (code)); \ + } while (0) +*/ +#define PK_PANIC(code) \ + do { \ + barrier(); \ + asm volatile ("b ."); \ + } while (0) + +// These variables are used by the PK_PANIC() definition above to save and +// restore state. __pk_panic_dbcr0 is the value loaded into DBCR0 to force +// traps to halt the OCC and freeze the timers. + +//#ifdef __PPE42_CORE_C__ +//uint32_t __pk_panic_save_r3; +//uint32_t __pk_panic_save_dbcr0; +//uint32_t __pk_panic_dbcr0 = DBCR0_EDM | DBCR0_TDE | DBCR0_FT; +//#endif + +#endif // PK_PANIC + +/// This is the Simics 'magic breakpoint' instruction. +/// +/// Note that this form does not include a memory barrier, as doing so might +/// change the semantics of the program. There is an alternative form +/// SIMICS_MAGIC_BREAKPOINT_BARRIER that does include a barrier. + +//#define SIMICS_MAGIC_BREAKPOINT asm volatile ("rlwimi 0,0,0,0,0") + +/// This is the Simics 'magic breakpoint' instruction including a memory +/// barrier. +/// +/// Note that the memory barrier guarantees that all variables held in +/// registers are flushed to memory before the breakpoint, however this might +/// change the semantics of the program. There is an alternative form of +/// SIMICS_MAGIC_BREAKPOINT that does not include a barrier. If the idea is +/// to use the breakpoint for tracing code execution in Simics, the barrier +/// form may be preferred so that variable values will be visible in memory. + +/*#define SIMICS_MAGIC_BREAKPOINT_BARRIER \ + asm volatile ("rlwimi 0,0,0,0,0" : : : "memory") +*/ + +#else // __ASSEMBLER__ +// *INDENT-OFF* + +/// This is the Simics 'magic breakpoint' instruction. An assembler macro +/// form is also provided for use within macros. + +//#define SIMICS_MAGIC_BREAKPOINT rlwimi 0,0,0,0,0 + +// .macro _simics_magic_breakpoint +// rlwimi 0,0,0,0,0 +// .endm + +/// The PK kernel panic default panic sequence for assembler code +/// +/// By default a kernel panic from assembler forces external debug mode then +/// generates a \c trap instruction followed by the error code. The \a code +/// argument must be a compile-time integer immediate. This definition can be +/// overriden by the application. +/// +/// See the comments for the non-ASSEMBLER version for further details. Note +/// that the code space reserved for exception handlers is only 8 +/// instructions, so in the assembler context we don't save DBCR0 as doing so +/// would require 10. + +#ifndef PK_PANIC + +#define PK_PANIC(code) _pk_panic code + + .macro _pk_panic, code + b . + .endm + +#endif // PK_PANIC + +// *INDENT-ON* +#endif // __ASSEMBLER__ + + +// Application-overridible definitions for the PK boot loader + +/// In order to enable the default kernel panic (a trap) to halt the machine, +/// the Debug Control Register 0 (DBCR0) is initialized in externel debug +/// mode, with the Trap Debug Event enabled so that the trap will not cause a +/// program exception, and the FT bit set so that the timers will freeze. +/// This definition can be overridden by the application. +/// +/// NB: It is expected that a reliable production system will redefine all of +/// the 'panic' macros and the default DBCR0 setup. + +#ifndef PPE42_DBCR_INITIAL +#define PPE42_DBCR_INITIAL DBCR_TRAP +#endif + +/// This is the value of the MSR used during initialization. Once PK threads +/// are started (with \c pk_start_threads()), all machine contexts derive +/// from the default thread context \c +/// PK_THREAD_MACHINE_CONTEXT_DEFAULT. This definition can be overriden by +/// the application. +/// +/// The default is to enable machine checks only. + +#ifndef PPE42_MSR_INITIAL +#define PPE42_MSR_INITIAL MSR_ME +#endif + +/// The \a argc argument passed to \c main(). This definition can be overriden +/// by the application. + +#ifndef PPE42_ARGC_INITIAL +#define PPE42_ARGC_INITIAL 0 +#endif + +/// The \a argv argument passed to \c main(). This definition can be overriden +/// by the application. + +#ifndef PPE42_ARGV_INITIAL +#define PPE42_ARGV_INITIAL 0 +#endif + +/// Optionally trap the reset for the debugger, which means that the PPE42 +/// will simply spin at the symbol \c __reset_trap after a chip reset. Set R0 +/// to a non-zero value in the debugger to continue execution. This definition +/// can be overriden by the application. + +#ifndef PPE42_RESET_TRAP +#define PPE42_RESET_TRAP 0 +#endif + +#ifndef __ASSEMBLER__ + +/// The PPE42 PK machine context is simply the MSR, a 32-bit integer. + +typedef uint32_t PkMachineContext; + +/// Disable interrupts and return the current +/// context. +/// +/// \param context A pointer to an PkMachineContext, this is the context that +/// existed before interrupts were disabled. Typically this +/// context is restored at the end of a critical section. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_INTERRUPT An illegal priority was specified. + +UNLESS__PPE42_CORE_C__(extern) +inline int +pk_interrupt_disable(PkMachineContext* context) +{ +*context = mfmsr(); + +wrteei(0); + +return PK_OK; +} + +/// Set the machine context. +/// +/// \param context A pointer to an PkMachineContext +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_CONTEXT_SET A null pointer was provided as +/// the \a context argument or an illegal machine context was specified. + +UNLESS__PPE42_CORE_C__(extern) +inline int +pk_machine_context_set(PkMachineContext* context) +{ +if (PK_ERROR_CHECK_API) +{ +PK_ERROR_IF(context == 0, PK_INVALID_ARGUMENT_CONTEXT_SET); +} + +mtmsr(*context); + +return PK_OK; +} + +/// Get the machine context. +/// +/// \param context A pointer to an PkMachineContext. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_CONTEXT_GET A null pointer was provided as +/// the \a context argument. + +UNLESS__PPE42_CORE_C__(extern) +inline int +pk_machine_context_get(PkMachineContext* context) +{ +if (PK_ERROR_CHECK_API) +{ +PK_ERROR_IF(context == 0, PK_INVALID_ARGUMENT_CONTEXT_GET); +} + +*context = mfmsr(); + +return PK_OK; +} + +extern void __ctx_switch(); +/// The PK context switch for the PPE kernel +// There is no protected mode in PPE42 so just call kernel code +#define __pk_switch() __ctx_switch() + + +/// In the PowerPC EABI all initial stack frames require 8 bytes - the 4 bytes +/// at the SP are zeroed to indicate the end of the stack, and the 4 bytes +/// behind the SP are for the initial subroutine's LR. + +static inline void +__pk_stack_create_initial_frame(PkAddress* stack, size_t* size) \ +{ +*stack -= 8; +* size -= 8; +* ((PK_STACK_TYPE*)(*stack)) = 0; +} + +/// The PK Kernel Context for PPE42 +/// +/// The PK portable kernel does not define how the kernel keeps track of +/// whether PK is running, interrupt levels, and other debug +/// information. Instead it defines an API that the port must provide to the +/// portable kernel. +/// +/// In the PPE42 port, the kernel context is maintained in SPRG0. This +/// 32-bit value is treated as 6 distinct fields as indicated in the structure +/// definition. +typedef union +{ + +uint32_t value; + +struct +{ + +/// A flag indicating that PK is in thread mode after a call of +/// pk_start_threads(). +unsigned thread_mode : 1; + +/// If this field is non-zero then PK is processing an interrupt +/// and the \c irq field will contain the PkIrqId of the interrupt +/// that kicked off interrupt processing. +unsigned processing_interrupt : 1; + +/// The priority of the currently running thread. In an interrupt +/// context, this is the priority of the thread that was interrupted. +unsigned thread_priority : 6; + +/// This bit tracks whether the current context can be discarded or +/// if the context must be saved. If the processor takes an interrupt +/// and this bit is set, then the current context will be discarded. +/// This bit is set at the end of handling an interrupt and prior +/// to entering the wait enabled state. +unsigned discard_ctx : 1; + +/// The PkIrqId of the currently running (or last run) handler. If +/// \c processing_interrupt is set, then this is the +/// PkIrqId of the IRQ that is currently executing. +unsigned irq : 7; + +/// Each PPE application will define (or not) the interpretation of +/// this field. Since SPRG0 is saved and restored during during thread +/// context switches, this field can be used to record the progress of +/// individual threads. The kernel and/or application will provide +/// APIs or macros to read and write this field. +unsigned app_specific : 16; + +} fields; + +} __PkKernelContext; + +// These APIs are provided for applications to get and set the app_specific +// field of the kernel context which is held in sprg0. + +static inline uint16_t ppe42_app_ctx_get(void) +{ +__PkKernelContext __ctx; +__ctx.value = mfspr(SPRN_SPRG0); +return __ctx.fields.app_specific; +} + +static inline void ppe42_app_ctx_set(uint16_t app_ctx) +{ +PkMachineContext mctx; +__PkKernelContext __ctx; +mctx = mfmsr(); +wrteei(0); +__ctx.value = mfspr(SPRN_SPRG0); +__ctx.fields.app_specific = app_ctx; +mtspr(SPRN_SPRG0, __ctx.value); +mtmsr(mctx); +} + +// These APIs are provided to the PK portable kernel by the port. + +/// PK threads have been started by a call of pk_start_threads(). + +#define __pk_kernel_mode_thread() \ + ({ \ + __PkKernelContext __ctx; \ + __ctx.value = mfspr(SPRN_SPRG0); \ + __ctx.fields.thread_mode;}) + + +/// PK is executing in a thread context (not an interrupt handler). + +#define __pk_kernel_context_thread() \ + ({ \ + __PkKernelContext __ctx; \ + __ctx.value = mfspr(SPRN_SPRG0); \ + __ctx.fields.thread_mode && !__ctx.fields.processing_interrupt;}) + + +/// PK is executing an interrupt handler of any priority. + +#define __pk_kernel_context_any_interrupt() \ + ({ \ + __PkKernelContext __ctx; \ + __ctx.value = mfspr(SPRN_SPRG0); \ + __ctx.fields.processing_interrupt;}) + + +// PK requires the port to define the type PkThreadQueue, which is a +// priority queue (where 0 is the highest priority). This queue must be able +// to handle PK_THREADS + 1 priorities (the last for the idle thread) The +// port must also define methods for clearing, insertion, deletion and min +// (with assumed legal priorities). The min operation returns PK_THREADS if +// the queue is empty (or a queue could be initialized with that entry always +// present - PK code never tries to delete the idle thread from a thread +// queue). +// +// These queues are used both for the run queue and the pending queue +// associated with every semaphore. +// +// On PPE42 with 32 threads (implied), this is a job for a uint32_t and +// cntlzw(). + +static inline void +__pk_thread_queue_clear(volatile PkThreadQueue* queue) +{ +*queue = 0; +} + +static inline void +__pk_thread_queue_insert(volatile PkThreadQueue* queue, PkThreadPriority priority) +{ +*queue |= (0x80000000u >> priority); +} + +static inline void +__pk_thread_queue_delete(volatile PkThreadQueue* queue, PkThreadPriority priority) +{ +*queue &= ~(0x80000000u >> priority); +} + +static inline PkThreadPriority +__pk_thread_queue_min(volatile PkThreadQueue* queue) +{ +return cntlzw(*queue); +} + +static inline int +__pk_thread_queue_member(volatile PkThreadQueue* queue, PkThreadPriority priority) +{ +return ((*queue >> (31 - priority)) & 1); +} + +static inline void +__pk_thread_queue_union(volatile PkThreadQueue* queue0, +volatile PkThreadQueue* queue1) +{ +*queue0 |= *queue1; +} + +static inline int +__pk_thread_queue_count(volatile PkThreadQueue* queue) +{ +return __builtin_popcount(*queue); +} + + +/// This macro is used to call __pk_start_threads() using the kernel stack, +/// in a critical section. + +#define __pk_call_pk_start_threads() \ + do { \ + PkMachineContext ctx; \ + pk_critical_section_enter(&ctx); \ + asm volatile ("mr 1, %0; mtlr %1; blrl" : : \ + "r" (__pk_kernel_stack), \ + "r" (__pk_start_threads)); \ + PK_PANIC(PK_START_THREADS_RETURNED); \ + } while (0) + + +#endif /* __ASSEMBLER__ */ + +/// The __PkKernelContext 'thread_mode' bit as a flag + +#define PPE42_THREAD_MODE 0x8000 +#define PPE42_PROC_IRQ 0x4000 +#define PPE42_DISCARD_CTX 0x0080 + +#define PPE42_THREAD_MODE_BIT 0 +#define PPE42_PROC_IRQ_BIT 1 +#define PPE42_DISCARD_CTX_BIT 8 + +#ifndef __ASSEMBLER__ + +/// Code breakpoints for PPE42 +/// +/// This macro inserts a special PPE42-only breakpoint into the object code +/// at the place the macro invocation appears. This facility is designed for +/// VBU/VPO procedure debugging. This type of breakpoint may not be required +/// on real hardware as we will then have the full power of RISCWatch, gdb, +/// etc. Once inserted into the code, code breakpoints can be enabled or +/// disabled by manipulating the global variable _code_breakpoint_enable, +/// which defaults to 1. +/// +/// The code breakpoint is implemented as a setup routine and a teardown +/// routine, executed in an critical section. The actual break +/// will occur at the address of the call of the teardown routine, in the +/// context of the calling code. The setup routine saves the state of DBCR0/1 +/// and IAC4, then programs the DBCR for an external debug mode, IAC4 +/// breakpoint. The IAC4 breakpoint is set for the address of the call of the +/// teardown routine. The teardown routine simply restores the state of the +/// debug registers that existed before the code breakpoint. +/// +/// Once hit, restarting from the break requires clearing IAC4 and restarting +/// instructions: +/// +/// \code +/// +/// putspr pu.occ iac4 0 +/// cipinstruct pu.occ start +/// +/// \endcode +/// +/// The above restart processes is also encapsulated as the p8_tclEcmd +/// procedure 'unbreakOcc'. +/// +/// In code built for the Simics environment (i.e., with the preprocessor +/// macro SIMICS_ENVIRONMENT=1) this macro simply expands into +/// SIMICS_MAGIC_BREAKPOINT, and simulation can be continued from the break as +/// normal. This Simics magic breakpoint is also under the control of +/// _code_breakpoint_enable. In code not built with SIMICS_ENVIROMENT=1, note +/// that the CODE_BREAKPOINT is ignored by the Simics PPE42 model as it does +/// not model debug events. + +//void +//_code_breakpoint_prologue(void); + +//void +//_code_breakpoint_epilogue(void); + +//extern uint32_t _code_breakpoint_enable; + +#endif // __ASSEMBLER__ + + +#endif /* __PPE42_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h new file mode 100644 index 00000000..bed547b7 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h @@ -0,0 +1,634 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_asm.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_ASM_H__ +#define __PPE42_ASM_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_asm.h +/// \brief Generic assembler macros for 32-bit PPE42 + +// Doxygen is confused by assembler; the best I know how to make it +// work is to put all of the documentation at the beginning like below +// and effectively comment out the code using Doxygen cond/endcond. + +/// \page ppe42_asm Generic assembler macros for 32-bit PPE42 +/// +/// +/// \section _lxzi _l<w,h,b>zi - Load register and Zero from Immediate address +/// +/// These macros encapsulate the 2-instruction sequence required to +/// load from a 32-bit immediate address. +/// +/// \arg \c dreg A register to receive the load data. +/// \arg \c areg A register to hold the immediate address. This can \e +/// not be register 0. Note that if \a areg != \a dreg +/// then \a areg will contain the address at the end of +/// the macro sequence. +/// \arg \c addr A 32-bit immediate address, which may be either an +/// absolute or relocatable expression. +/// +/// Forms: +/// +/// \b _lbzi \a dreg, \a areg, \a addr - Load Byte and Zero from Immediate address \n +/// \b _lhzi \a dreg, \a areg, \a addr - Load Halfword and Zero from Immediate address \n +/// \b _lwzi \a dreg, \a areg, \a addr - Load Word and Zero from Immediate address \n +/// +/// +/// \section _stxi _st<w,h,b>i - STore register to Immediate address +/// +/// These macros encapsulate the 2-instruction sequence required to +/// store to a 32-bit immediate address. +/// +/// \arg \c dreg The register to store. +/// \arg \c areg A register to hold the immediate address. This can \e +/// not be register 0, and can not be the same as \a dreg. +/// Note that \a areg will contain the address at the end of +/// the macro sequence. +/// \arg \c addr A 32-bit immediate address, which may be either an +/// absolute or relocatable expression. +/// +/// Forms: +/// +/// \b _stbi \a dreg, \a areg, \a addr - STore Byte to Immediate address \n +/// \b _sthi \a dreg, \a areg, \a addr - STore Halfword to Immediate address \n +/// \b _stwi \a dreg, \a areg, \a addr - STore Word to Immediate address \n +/// +/// +/// \section _lstzsd _<l,st><w,h,b><z>sd - Load/STore register from/to Small Data area +/// +/// These macros encapulate the small data area relocations for access +/// to storage in the small data sections .sbss, .sdata, .sbss2 and +/// .sdata2. Use of these macros implies small data area support in +/// the compile environment (for variables shared between compiled and +/// assembled code) and initialization code that sets up the small data +/// area registers R13 (and optionally R2). +/// +/// The relocations generated by this macro will work for both SVR4 ABI +/// and EABI environments. In particular, for EABI environments +/// the link editor will insert offsets to either R13 or R2 depending +/// on the section of the symbol. +/// +/// \arg \c dreg The register to load or store. +/// \arg \c addr A 32-bit immediate address, assumed to be a +/// relocatable address in one of the small data sections. +/// +/// Forms: +/// +/// \b _lbzsd \a dreg, \a addr - Load Byte and Zero from Small Data area \n +/// \b _lhzsd \a dreg, \a addr - Load Halfword and Zero from Small Data area \n +/// \b _lwzsd \a dreg, \a addr - Load Word and Zero from Small Data area \n +/// \b _stbsd \a dreg, \a addr - STore Byte to Small Data area \n +/// \b _sthsd \a dreg, \a addr - STore Halfword to Small Data area \n +/// \b _stwsd \a dreg, \a addr - STore Word to Small Data area \n +/// +/// +/// \section _liw _liw<a> - Load Immediate Word (Absolute) +/// +/// These macros encapsulate the two instructions required to load a +/// 32-bit immediate value into a register. If the immediate is an +/// absolute expression, then the \c 'a' form may be able to optimize +/// to a single instruction depending on whether only the high- or +/// low-order bits of the immediate are non-zero. +/// +/// Forms: +/// +/// \b _liw \a rd, \a imm - Load register \a rd with the 32-bit immediate \a imm \n +/// \b _liwa \a rd, \a imm - Load register \a rd with the 32-bit absolute immediate \a imm \n +/// +/// +/// \section _oriwa _oriwa - OR Immediate Word Absolute +/// +/// This macro encapsulates the logical OR of a 32-bit immediate with a +/// register. The immediate value must be an absolute expression. +/// +/// The PowerPC has instructions for OR-ing 16-bit immediates into the +/// upper (\c oris) and lower (\c ori) portions of a register. This +/// macro optimizes the generated code based on which bits (if any) of +/// the absolte immediate are non-zero. +/// +/// This special macro is only provided for the OR function. For other +/// logical operations and recording forms it is necessary in general +/// to first load the 32-bit immediate into a register (e.g., with \c +/// _liwa) then perform the logical operation. +/// +/// \arg \c rd The destination register; at the end will contain \c rs +/// OR \a imm +/// \arg \c rs The source register. +/// \arg \c imm 32-bit absolute expression. +/// +/// Forms: +/// +/// \b _oriwa \a rd, \a rs, \a imm - \a rd gets \a rs OR \a imm \n +/// +/// +/// \section _incr64_fast - 64-bit increment for fast interrupt handlers +/// +/// This macros implements 64-bit counter update in fast interrupt handlers +/// which are forbidden from using the carry-bit in the XER (without +/// saving/restoring it.) +/// +/// \arg \c rs Scratch register +/// \arg \c ra Register containing the counter address at entry +/// +/// \a rs and \a ra must be unique. At the end of the macro the count +/// is updated to memory and \a ra is unmodified. +/// +/// +/// \section _setclear_bits Set/Clear/Copy Bits from Immediate Positions +/// +/// There are situations where it is easier/faster to clear individual bits +/// and bit fields, set bits or copy fields, based on immediate bit numbers +/// and locations, rather than loading masks, since setting up a mask +/// requires 2 instruction in general, whereas these macros generate a single +/// instruction. +/// +/// \arg \c rd - The destination register +/// \arg \c rs - The source register +/// \arg \c n - An immediate size of a bit field, in the range 0 to 32 +/// \arg \c b - An immediate big-endian bit number in the range 0 to 31 +/// +/// Forms: +/// +/// \b _clrfield \a rd, \a rs, \a n, \a b - Clear an \a n bit field from \a rs +/// to \a rd starting from bit \a b \n +/// \b _clrbit \a rd, \a rs, \a b - Clear bit \a b \n +/// \b _setbit \a rd, \a rs, \a b - Set bit \a b \n +/// \b _copyfield \a rd, \a rs, \a n, \a b - Copy an n-bit field from \a rs to +/// \a rd starting from bit \a b \n +/// +/// +/// \section pseudo_ops Assembler Pseudo-Ops Macros +/// +/// Several macros define new 'pseudo-ops'. +/// +/// \subsection cache_align .cache_align +/// +/// The \c .cache_align pseudo-op is used to force alignment on a +/// cache-line boundary. It requires a preprocessor symbol definition for +/// \c LOG_CACHE_LINE_SIZE +/// +/// Forms: +/// +/// \b .cache_align \n +/// +/// +/// \subsection global_function Local and Global Functions +/// +/// The \c .function and \c .global_function pseudo-ops define function +/// symbols in the \c .text section. +/// +/// Forms: +/// +/// \b .function \a symbol - Define a local function \a symbol \n +/// \b .global_function \a symbol - Define a global function \a symbol \n +/// +/// +/// \subsection epilogue .epilogue +/// +/// The \c .epilogue pseudo-op adds size and type information for +/// functions defined in assembler. +/// +/// \arg \c symbol - Assembler epilogue for the function \a symbol. +/// +/// Forms: +/// +/// \b .epilogue \a symbol \n +/// +/// +/// \cond + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + + +### **************************************************************************** +### _l<b,h,w>zi +### _st<b,h,w>i +### **************************************************************************** + + .macro _lbzi dreg, areg, addr + lis \areg, \addr@ha + .ifc \areg, \dreg + lbz \dreg, \addr@l(\areg) + .else + lbzu \dreg, \addr@l(\areg) + .endif + .endm + + .macro _lhzi dreg, areg, addr + lis \areg, \addr@ha + .ifc \areg, \dreg + lhz \dreg, \addr@l(\areg) + .else + lhzu \dreg, \addr@l(\areg) + .endif + .endm + + .macro _lwzi dreg, areg, addr + lis \areg, \addr@ha + .ifc \areg, \dreg + lwz \dreg, \addr@l(\areg) + .else + lwzu \dreg, \addr@l(\areg) + .endif + .endm + + .macro _stbi dreg, areg, addr + .ifc \areg, \dreg + .err + .endif + lis \areg, \addr@ha + stbu \dreg, \addr@l(\areg) + .endm + + .macro _sthi dreg, areg, addr + .ifc \areg, \dreg + .err + .endif + lis \areg, \addr@ha + sthu \dreg, \addr@l(\areg) + .endm + + .macro _stwi dreg, areg, addr + .ifc \areg, \dreg + .err + .endif + lis \areg, \addr@ha + stwu \dreg, \addr@l(\areg) + .endm + + +### **************************************************************************** +### _l<b,h,w>zsd +### _st<b,h,w>sd +### **************************************************************************** + + .macro _lbzsd dreg, addr + lbz \dreg, \addr@sda21(0) + .endm + + .macro _lhzsd dreg, addr + lhz \dreg, \addr@sda21(0) + .endm + + .macro _lwzsd dreg, addr + lwz \dreg, \addr@sda21(0) + .endm + + .macro _stbsd dreg, addr + stb \dreg, \addr@sda21(0) + .endm + + .macro _sthsd dreg, addr + sth \dreg, \addr@sda21(0) + .endm + + .macro _stwsd dreg, addr + stw \dreg, \addr@sda21(0) + .endm + + +### **************************************************************************** +### _liw<a> +### _oriwa +### **************************************************************************** + + .macro _liw rd, imm + lis \rd, \imm@h + ori \rd, \rd, \imm@l + .endm + + .macro _liwa rd, imm + .if (\imm & 0xffff0000) + lis \rd, \imm@h + .if (\imm & 0xffff) + ori \rd, \rd, \imm@l + .endif + .else + li \rd, \imm@l + .endif + .endm + + .macro _oriwa rd, rs, imm + .if (\imm & 0xffff0000) + oris \rd, \rs, \imm@h + .if (\imm & 0xffff) + ori \rd, \rd, \imm@l + .endif + .else + ori \rd, \rs, \imm@l + .endif + .endm + +### **************************************************************************** +### _incr64_fast +### **************************************************************************** + + .macro _incr64_fast, rs:req, ra:req + + lwz \rs, 4(\ra) + addi \rs, \rs, 1 + cmpwi \rs, 0 + stw \rs, 4(\ra) + bne 233643278f + + lwz \rs, 0(\ra) + addi \rs, \rs, 1 + stw \rs, 0(\ra) +233643278: + + .endm + +### **************************************************************************** +### _clrfield +### _clrbit +### _setbit +### _copyfield +### **************************************************************************** + + .macro _clrfield, rd, rs, n, b + rlwinm \rd, \rs, 0, (\b + \n) & 0x1f, (\b - 1) & 0x1f + .endm + + .macro _clrbit, rd, rs, b + _clrfield \rd, \rs, 1, \b + .endm + + .macro _setbit, rd, rs, b + .ifle \b - 15 + oris \rd, \rs, 1 << (15 - \b) + .else + ori \rd, \rs, 1 << (31 - \b) + .endif + .endm + + .macro _copyfield, rd, rs, n, b + rlwimi \rd, \rs, 0, \b , (\b + \n - 1) + .endm + +### **************************************************************************** +### .cache_align +### .<global_>function +### .epilogue +### **************************************************************************** + + .set _log_cache_line_size, LOG_CACHE_LINE_SIZE + + .macro .cache_align + .align _log_cache_line_size + .endm + + .macro .function symbol + .text + .align 2 + .endm + + .macro .global_function symbol + .text + .align 2 + .global \symbol + .endm + + .macro .epilogue symbol + .type \symbol, @function + .size \symbol, . - \symbol + .endm + +### *************************************************************************** +### 64-bit macros +### *************************************************************************** + +### *************************************************************************** +### Using symbols for register names makes the code more readable and allows +### us to do register arithmetic within macros. +### *************************************************************************** + +.equiv r0, 0 +.equiv r1, 1 +.equiv sp, 1 +.equiv r3, 3 +.equiv r4, 4 +.equiv r5, 5 +.equiv r6, 6 +.equiv r7, 7 +.equiv r8, 8 +.equiv r9, 9 +.equiv r10, 10 + +.equiv r28, 28 +.equiv r29, 29 +.equiv r30, 30 +.equiv r31, 31 + +.equiv d3, 3 +.equiv d4, 4 +.equiv d5, 5 +.equiv d6, 6 +.equiv d7, 7 +.equiv d8, 8 +.equiv d9, 9 +.equiv d10, 10 +.equiv d28, 28 +.equiv d29, 29 +.equiv d30, 30 +.equiv d31, 31 + +### *************************************************************************** +### Load virtual doubleword generic. Load a virtual doubleword from a relocatable +### address expression. If the optional RA is specified, the address remains in +### RA. +### *************************************************************************** +.macro _lvdg DT:req addr:req RA=-1 + .if \RA == -1 + lis \DT, (\addr)@ha + lvd \DT, (\addr)@l(\DT) + .else + lis \RA, (\addr)@ha + lvdu \DT, (\addr)@l(\RA) + .endif +.endm + +### *************************************************************************** +### Load virtual doubleword from a relocatable small data area address +### *************************************************************************** +.macro _lvdsd DT:req addr:req + lvd \DT, (\addr)@sda21(0) +.endm + +### *************************************************************************** +### Store virtual doubleword generic. Store a virtual doubleword based on a +### relocatable address expression. The address remains in RA. +### *************************************************************************** +.macro _stvdg DS:req addr:req RA:req + lis \RA, (\addr)@ha + stvdu \DS, (\addr)@l(\RA) +.endm + +### *************************************************************************** +### Store virtual doubleword to a relocatable small data address expression +### *************************************************************************** +.macro _stvdsd DS:req addr:req + stvd \DS, (\addr)@sda21(0) +.endm + +### *************************************************************************** +### Load virtual doubleword absolute. Set DT to an absolute 64-bit constant +### *************************************************************************** +.macro _lvda DT, cvalue + lwa (\DT + 1)%32, (\cvalue) & 0x00000000ffffffff + lwa \DT, (\cvalue) >> 32 +.endm + +### *************************************************************************** +### +### 64-bit arithmetic macros +### +### *************************************************************************** + +.macro check_overlap2 DA, DB + .if ((\DA - \DB) % 32) == 1 || ((\DA - \DB) % 32) == -1 + .error "virtual doubleword registers must be identical or non-overlapping" + .endif +.endm + +.macro check_overlap3 DA, DB, DC + check_overlap2 \DA, \DB + check_overlap2 \DA, \DC + check_overlap2 \DB, \DC +.endm + +### *************************************************************************** +### Add virtual doubleword carrying +### *************************************************************************** +.macro _addvdc DT, DA, DB + check_overlap3 \DT, \DA, \DB + addc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + adde \DT, \DA, \DB +.endm + +### *************************************************************************** +### Add virtual doubleword to signed 16-bit immediate carrying +### *************************************************************************** +.macro _addvdic DT, DA, SI + .if \DA == 31 + .error "d31 for addend register is not supported" + .endif + check_overlap2 \DT, \DA + addi (\DT+1)%32, \DA+1, SI + addze \DT, \DA +.endm + +### *************************************************************************** +### Add virtual doubleword to unsigned word carrying +### *************************************************************************** +.macro _addvdwuc DT, DA, RB + check_overlap2 \DT, \DA + addc (\DT+1)%32, (\DA+1)%32, \RB + addze \DT, \DA +.endm + +### *************************************************************************** +### Subtract virtual doubleword carrying +### *************************************************************************** +.macro _subvdc DT, DA, DB + check_overlap3 \DT, \DA, \DB + subfc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + subfe \DT, \DA, \DB +.endm + +### *************************************************************************** +### +### 64-bit logic macros +### +### *************************************************************************** + +### *************************************************************************** +### AND virtual doubleword +### *************************************************************************** +.macro _andvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + and (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + and \DT, \DA, \DB +.endm + +### *************************************************************************** +### ANDC virtual doubleword +### *************************************************************************** +.macro _andcvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + andc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + andc \DT, \DA, \DB +.endm + +### *************************************************************************** +### EQV virtual doubleword +### *************************************************************************** +.macro _eqvvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + eqv (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + eqv \DT, \DA, \DB +.endm + +### *************************************************************************** +### OR virtual doubleword +### *************************************************************************** +.macro _orvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + or (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + or \DT, \DA, \DB +.endm + +### *************************************************************************** +### ORC virtual doubleword +### *************************************************************************** +.macro _orcvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + orc (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + orc \DT, \DA, \DB +.endm + +### *************************************************************************** +### XOR virtual doubleword +### *************************************************************************** +.macro _xorvd DT, DA, DB + check_overlap3 \DT, \DA, \DB + xor (\DT+1)%32, (\DA+1)%32, (\DB+1)%32 + xor \DT, \DA, \DB +.endm + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +/// \endcond + +// Local Variables: +// mode:asm +// End: + +#endif /* __PPE42_ASM_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S new file mode 100644 index 00000000..dcfa6fa6 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S @@ -0,0 +1,192 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_boot.S $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_boot.S +/// \brief PK bootloader for PPE42 + + .nolist +#include "pk.h" + .list + +### PK Bootloader for PPE42 +### +### This is the basic restart initialization of the processor. +### Parts of this code were derived from examples in the IBM OSopen +### OpenBIOS for the 405GP written by James Burke. +### +### This code does not really do very much, just makes sure that there will +### be a reasonable state in the machine when control is turned over to +### the PK application. Any core setup that requires SPR access will be done +### here. All other setup is expected to take place in system-specific +### routines. +### +### From the PowerPC 405-S Embedded Core User's manual: +### +### "In general, the contents of SPRs are undefined after a core, chip or +### system reset. Some SPRs retain the content they had before the reset +### occurred." +### +### Registers fully reset: +### DBCR1 - Data compares disabled +### DCWR - Data cache write-through disabled +### ESR - No exception syndromes +### MSR - No exceptions/interrupts are allowed +### +### Registers partially reset: +### CCR0 = 0x00700000 - Sets ICU and DCU PLB Priority +### DBCR0 [EDM] = 0 - External debug mode disabled +### [RST] = 0 - No reset action +### DBSR [MRR] = x - x indicates most recent reset action +### SGR = 0xffffffff - Storage is guarded +### TCR [WRC] = 0 - Watchdog timer reset disabled +### TSR [WRS] = x - x is a copy of TCR[WRC] Watchdog reset status +### [PIS] = x - undefined + + .global_function __pk_boot + .global __reset_trap + +__pk_boot: + + ## Trap the reset for the debugger. Set R0 to a non-zero value in the + ## debugger to continue. + + .if PPE42_RESET_TRAP + li %r0, 0 +__reset_trap: + cmpwi %r0, 0 + beq __reset_trap + .endif + + ## Set up PowerPC EABI constant registers. These registers are never + ## again touched by the PK kernel or the application (if they are + ## behaving). + + _liw %r2, _SDA2_BASE_ + _liw %r13, _SDA_BASE_ + + ## Clear the timer control register. This masks all timer interrupts. + + li %r3, 0 + mttcr %r3 + + ## The stack pointer is initialized for use by the remainder of the + ## initialization, including the application main(). The linker script + ## defines the initial stack area. + ## + ## Stacks are always 8-byte aligned. A '0' is stored at the + ## stack pointer to indicate the end of the stack chain. Stack frames + ## always consist of at least 8 bytes - the backchain pointer and the + ## slot above the backchain pointer for the callee's LR. + + _liw %r1, _PK_INITIAL_STACK + _clrfield %r1, %r1, 3, 29 # 8-byte align + li %r3, 0 + stwu %r3, -8(%r1) + + ## SPRG0 (__PkKernelContext) is initialized to 0 + ## indicating that the PK kernel is not in thread mode, and no + ## interrupts are active. + + li %r3, 0 + mtsprg0 %r3 + + ## Set up the initial value of Debug Control Register 0. Note that + ## DBCR1 is specified to be cleared at reset. VBU simulation requested + ## an option that this register not be modified so that they could + ## completely control debug behavior from reset of the PPE42. + +#ifndef NO_INIT_DBCR0 + _liwa %r3, PPE42_DBCR_INITIAL + mtdbcr %r3 +#endif + + ## The exception vector prefix is set - it must be 512 byte aligned. + ## NOTE: for PPE42, the IVPR is read only, but can be changed through scoms + + #_liw %r3, __vectors + #andi. %r4, %r3, 0x01ff + #beq 1f + #_pk_panic PPE42_BOOT_VECTORS_NOT_ALIGNED +#1: + #mtivpr %r3 + #sync + + ## The MSR to be used during the rest of intialization is + ## established. This MSR should NOT enable + ## interrupts, but could enable machine check exceptions. + + _liwa %r3, PPE42_MSR_INITIAL + mtmsr %r3 + sync + +#ifdef PK_BOOT_FROM_ROM + + ## NB: I don't think the old linker scripts were necessarily the most + ## optimal. We need to revisit this if we actually do ROM boots in PK + ## Version 2. Not sure the comments are correct. + + ## Data is copied from the initial ROM image to the RAM. The + ## address symbols are defined in the linker command file. The linker + ## will have zeroed this area in the ROM image. + + liw %r3, __pk_ram_lma - 4 # src + liw %r4, __pk_ram_vma - 4 # dest + liw %r5, __pk_ram_size + liw %r6, 2 + srw %r5, %r5, %r6 # Number of word transfers + mtctr %r5 + +copy_loop: + lwzu %r5, 4(%r3) + stwu %r5, 4(%r4) + bdnz copy_loop + +#endif /* PK_BOOT_FROM_ROM */ + + + ## Call the system setup code. + + bl __ppe42_system_setup + + ## Call the application. If for some reason we return from + ## the call of the application we call an alternate entry point of the + ## idle thread. + ## + ## An initial argc/argv can be passed into main(). argc is expected to + ## be a 32-bit immediate integer, and argv is expected to be a 32-bit + ## absolute or relocatable expression. + + _liwa %r3, PPE42_ARGC_INITIAL + _liw %r4, PPE42_ARGV_INITIAL + bl __pk_main + + b __pk_idle_thread_from_bootloader + + .epilogue __pk_boot diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h new file mode 100644 index 00000000..13293639 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h @@ -0,0 +1,126 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_cache.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_CACHE_H__ +#define __PPE42_CACHE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_cache.h +/// \brief PowerPC-lite (PPE) cache management header for PK +/// +/// The data cache flush/invalidate macros defined here create a compiler +/// memory barrier that will cause GCC to flush/invalidate all memory data +/// held in registers before the macro. + +#ifndef __ASSEMBLER__ + +/// Determine cache-alignment of a pointer or byte-count +#define cache_aligned(x) \ + ((((unsigned long)(x)) & (POW2_32(LOG_CACHE_LINE_SIZE) - 1)) == 0) + +/// Cache-align a pointer or byte count. If the 'direction' is <= 0 then we +/// round down, else round up. +#define cache_align(x, direction) \ + ({ \ + unsigned long __x = (unsigned long)(x); \ + unsigned long __r; \ + if ((direction) <= 0) { \ + __r = __x & ~(((unsigned long)CACHE_LINE_SIZE) - 1); \ + } else { \ + if (__x % CACHE_LINE_SIZE) { \ + __r = __x + (CACHE_LINE_SIZE - (__x % CACHE_LINE_SIZE)); \ + } \ + } \ + (void *)__r; \ + }) + +/// Data Cache Block Flush +#define dcbf(p) asm volatile ("dcbf 0, %0" : : "r" (p) : "memory") + +/// Data Cache Block Touch +#define dcbt(p) asm volatile ("dcbt 0, %0" : : "r" (p) : "memory") + +/// Data Cache Block Invalidate (Privileged) +#define dcbi(p) asm volatile ("dcbi 0, %0" : : "r" (p) : "memory") + +void +dcache_invalidate_all(void); + +void +dcache_flush_all(void); + +void +dcache_invalidate(void* p, size_t bytes); + +void +dcache_flush(void* p, size_t bytes); + +/// Invalidate a line in the D-cache +/// +/// \param p An address withing the cache line to be invalidated. +/// +/// The dcache_invalidate_line() API is used to invalidate a single cache line +/// containing the address \a p. Note that invalidation is a destructive +/// operation that may cause the loss of information. It is the caller's +/// responsibility to insure that no useful data is inadverdently invalidated. +/// D-cache invalidation is more-or-less a no-op for data either not in the +/// cache or marked as non-cacheable. +/// +/// This API always issues a sync() after the invalidation. + +static inline void +dcache_invalidate_line(void* p) +{ + dcbi(p); + sync(); +} + +/// Flush and invalidate a line from the D-cache +/// +/// \param p An address within the cache line to be flushed. +/// +/// The dcache_flush_line() API can be used as a shortcut to flush and +/// invalidate a single cache line. Note that flushing is not a destructive +/// operation in the sense that no information is lost, however the caller +/// must make sure that the entirity of the data to be flushed is contained in +/// the line that includes the address \a p. D-cache flush is more-or-less a +/// no-op for data either not in the cache or marked as non-cacheable. +/// +/// This API always issues a sync() after the flush. + +static inline void +dcache_flush_line(void* p) +{ + dcbf(p); + sync(); +} + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_CAHE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h new file mode 100644 index 00000000..9b356cef --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h @@ -0,0 +1,226 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_context.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_CONTEXT_H__ +#define __PPE42_CONTEXT_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_context.h +/// \brief PPE42 Machine and Thread context for PK + +/// \page ppe42_machine_context PPE42 Assembler Macros for PK Machine +/// Context (Critical Sections) +/// +/// \section _pk_enter_critical \b _pk_critical_section_enter/exit +/// +/// These macro encapsulates the instruction sequences required to enter and +/// exit critical sections, along with the machine context save for later +/// exiting the critical section. +/// +/// \arg \c ctxreg A register that will hold (holds) the machine context (MSR) +/// prior to entering the critical section (to be restored) for \c +/// _pk_critical_section_enter (\c _pk_critical_section_exit). +/// +/// \arg \c scrreg A scratch register required for the computation of +/// \c _pk_critical_section_enter. +/// +/// Forms: +/// +/// \b _pk_critical_section_enter \a priority, \a ctxreg, \a scrreg - Enter a +/// critical section \n +/// \b _pk_critical_section_exit \a ctxreg - Exit a critical section + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + + .set _msr_ee_bit, MSR_EE_BIT + + .macro _pk_critical_section_enter ctxreg, scrreg + mfmsr \ctxreg + wrteei 0 + .endm + + .macro _pk_critical_section_exit ctxreg + mtmsr \ctxreg + .endm + +// **************************************************************************** +// PK context save/restore macros for 32-bit Embedded PowerPC +// **************************************************************************** + +// All stack frames are 8-byte aligned in conformance with the EABI. PK +// never saves or restores GPR2 or GPR13. GPR13 is constant in (E)ABI +// applications - the base of the read-write small data area. GPR2 is +// system-reserved in ABI applications, and is the base for read-only small data +// in EABI applications. + +// USPRG0 holds the __PkKernelContext structure (defined in ppe42.h) that +// represents the current kernel context. The layout is as follows: +// +// Bits Meaning +// ============== +// 0 The 'thread_mode' flag +// 1 The 'processing_interrupt" flag +// 2:7 The thread priority of the running thread +// 8 The 'discard_ctx' flag +// 9:15 The IRQ currently being processed +// 16:31 The application specific data +// +// When PK is initialized USPRG0 is initialized to 0. When thread-mode is +// entered (by pk_start_threads()) bit 0 is set to 1. If desired, +// once initialized (with pk_initialize()) PK can simply +// handle interrupts, reverting back to the non-thread-mode idle loop when +// there's nothing to do. +// + + ## ------------------------------------------------------------ + ## Unused registers for embedded PPE42` + ## ------------------------------------------------------------ + + ## Registers GPR2 and GPR13 are never saved or restored. In ABI and + ## EABI applications these registers are constant. + + .set UNUSED_GPR2, 0x2 # Dedicated; EABI read-only small data area + .set UNUSED_GPR13, 0xd # Dedicated; (E)ABI read-write small data area + + ## ------------------------------------------------------------ + ## The PK context layout for Embedded PPE42 + ## ------------------------------------------------------------ + + .set PK_CTX_GPR1, 0x00 # Dedicated; Stack pointer + .set PK_CTX_LINKAGE, 0x04 # Slot for handler to store LR + .set PK_CTX_GPR3, 0x08 # Volatile; Parameter; Return Value + .set PK_CTX_GPR4, 0x0c # Volatile; Parameter + .set PK_CTX_GPR5, 0x10 # Volatile; Parameter + .set PK_CTX_GPR6, 0x14 # Volatile; Parameter + .set PK_CTX_CR, 0x18 # Condition register + .set PK_CTX_LR, 0x1c # Link register + + .set PK_CTX_GPR7, 0x20 # Volatile; Parameter + .set PK_CTX_GPR8, 0x24 # Volatile; Parameter + .set PK_CTX_GPR9, 0x28 # Volatile; Parameter + .set PK_CTX_GPR10, 0x2c # Volatile; Parameter + .set PK_CTX_GPR28, 0x30 # Non-volatile + .set PK_CTX_GPR29, 0x34 # Non-volatile + .set PK_CTX_GPR30, 0x38 # Non-volatile + .set PK_CTX_GPR31, 0x3c # Non-volatile + + .set PK_CTX_XER, 0x40 # Fixed-point exception register + .set PK_CTX_CTR, 0x44 # Count register + .set PK_CTX_SRR0, 0x48 # Save/restore register 0 + .set PK_CTX_SRR1, 0x4c # Save/restore register 1 + .set PK_CTX_GPR0, 0x50 # Volatile; Language specific + .set PK_CTX_KERNEL_CTX, 0x54 # Saved __PkKernelContext for IRQ + + .set PK_CTX_SIZE, 0x58 # Must be 8-byte aligned + + ## ------------------------------------------------------------ + ## Push the interrupted context if necessary + ## + ## This macro saves off some context in preparation for calling + ## the pk_ctx_check_discard routine. This is an attempt to use + ## the 32 byte cache more efficiently. + ## + ## 8 Instructions + ## ------------------------------------------------------------ + ## + + .macro _pk_ctx_push_as_needed branch_addr:req + + stwu %r1, -PK_CTX_SIZE(%r1) + stvd %d3, PK_CTX_GPR3(%r1) + mfcr %r3 + mflr %r4 + stvd %d3, PK_CTX_CR(%r1) + _liw %r3, \branch_addr + b ctx_check_discard + .endm + + + ## ------------------------------------------------------------ + ## update the kernel context in response to an interrupt. + ## ------------------------------------------------------------ + + ## The kernel context is updated with the currently active + ## IRQ in bits 9:15. + + .macro _update_kernel_context irqreg, ctxreg + rlwimi \ctxreg, \irqreg, 16, 9, 15 //set the irq # + oris \ctxreg, \ctxreg, 0x4000 //set the 'processing_interrupt' flag + mtsprg0 \ctxreg + +#if PK_KERNEL_TRACE_ENABLE + mr %r31, \irqreg + srwi \ctxreg, \ctxreg, 16 + PK_KERN_TRACE_ASM16("INTERRUPT_CONTEXT(0x%04x)", \ctxreg) + mr \irqreg, %r31 +#endif + + .endm +// *INDENT-ON* + +#else /* __ASSEMBLER__ */ + +/// PK thread context layout as a C structure. +/// +/// This is the structure of the stack area pointed to by +/// thread->saved_stack_pointer when a thread is fully context-switched out. + +typedef struct +{ + uint32_t r1; + uint32_t linkage; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t cr; + uint32_t lr; + + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r28; + uint32_t r29; + uint32_t r30; + uint32_t r31; + + uint32_t xer; + uint32_t ctr; + uint32_t srr0; + uint32_t srr1; + uint32_t r0; + uint32_t sprg0; + +} PkThreadContext; + + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_CONTEXT_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c new file mode 100644 index 00000000..1ccb4404 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c @@ -0,0 +1,157 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_core.c +/// \brief The final bits of PK runtime code required to complete the PPE42 +/// port. +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present during runtime in any PK application. + +#define __PPE42_CORE_C__ + +#include "pk.h" + +typedef union +{ + uint64_t value; + struct + { + uint32_t dec_start; + uint32_t dec_change_tag; + }; +} ppe42_timebase_data_t; + +ppe42_timebase_data_t ppe42_tb_data = {0}; +PkTimebase ppe42_64bit_timebase = 0; + + +#if PK_TIMER_SUPPORT + +// The tickless kernel timer mechanism for PPE42 +// +// This routine must be called from a critical section. +// +// Tickless timeouts are provided by programming the PIT timer based on when +// the next timeout will occur. If the timeout is for the end of time there's +// nothing to do - PK does not use auto-reload mode so no more PIT interrupts +// will be arriving. Otherwise, if the timeout is longer than the 32-bit PIT +// timer can handle, we simply schedule the timeout for 2**32 - 1 and +// __pk_timer_handler() will keep rescheduling it until it finally occurs. +// If the \a timeout is in the past, we schedule the PIT interrupt for 1 tick +// in the future in accordance with the PK specification. + +#ifndef APPCFG_USE_EXT_TIMEBASE +void +__pk_schedule_hardware_timeout(PkTimebase timeout) +{ + PkTimebase now; + uint32_t new_dec; + uint32_t dec; + + if (timeout != PK_TIMEBASE_MAX) + { + + now = pk_timebase_get(); + + if (timeout <= now) + { + new_dec = 1; + } + else if ((timeout - now) > 0xffff0000) + { + new_dec = 0xffff0000; + } + else + { + new_dec = timeout - now; + } + + //read and write the DEC back-to-back so that we lose as little time + //as possible + dec = mfspr(SPRN_DEC); + mtspr(SPRN_DEC, new_dec); + + //update our 64bit accumulator with how much time has advanced since + //we last changed it. + ppe42_64bit_timebase += ppe42_tb_data.dec_start - dec; + + //update our start time so we know how much time has advanced since + //this update of the accumulator + ppe42_tb_data.dec_start = new_dec; + ppe42_tb_data.dec_change_tag++; + } +} + +#else + +void +__pk_schedule_hardware_timeout(PkTimebase timeout) +{ + PkTimebase now; + PkTimebase diff; + uint32_t new_dec; + + if (timeout != PK_TIMEBASE_MAX) + { + + now = pk_timebase_get(); + + //update our 64bit accumulator with the current snapshot + ppe42_64bit_timebase = now; + + if (timeout <= now) + { + new_dec = 1; + } + else + { + diff = (timeout - now); + + if (diff > 0xfffffffful) + { + new_dec = 0xffffffff; + } + else + { + new_dec = diff; + } + } + + mtspr(SPRN_DEC, new_dec); + + } +} + +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#endif /* PK_TIMER_SUPPORT */ + +#undef __PPE42_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S new file mode 100644 index 00000000..fdb32a1b --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S @@ -0,0 +1,525 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_exceptions.S $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_exceptions.S +/// \brief PPE42 exception vector area. +/// +/// \cond + + .nolist +#include "pk.h" + .list + +## declare and initializes global variables that hold external irq config data +## Each PPE macro type (GPE, CME, and SBE) will have it's own implementation of this macro +## defined in (gpe, cme, sbe)_common.h + .hwmacro_irq_cfg_bitmaps + +### **************************************************************************** +### .vectors - This section contains all ppe42 exception vectors +### +### **************************************************************************** + + .section .vectors, "ax", @progbits + + .global __vectors + +__vectors: + + ############################################################ + # 0x0000 : Machine Check + ############################################################ + + ### Unmaskable interrupts (including program interrupts) are promoted + ### to machine check interrupts if MSR[UIE] = 0 and MSR[ME] = 1. + ### If the machine check was caused by a program interrupt it + ### will be forwarded to the program exception handler. +__machine_check: + + PPE42_MACHINE_CHECK_HANDLER + + ############################################################ + # 0x0040 : System Reset + ############################################################ + .global __system_reset + .org __vectors + 0x0040 +__system_reset: + b __pk_boot + + ############################################################ + # 0x0060 : Data Storage Interrupt + ############################################################ + + .org __vectors + 0x0060 +__data_storage: + + PPE42_DATA_STORAGE_HANDLER + + ############################################################ + # 0x0080 : Instruction Storage Interrupt + ############################################################ + + .org __vectors + 0x0080 +__instruction_storage: + + PPE42_INSTRUCTION_STORAGE_HANDLER + + + ############################################################ + # 0x00A0 : External Interrupt + ############################################################ + + .org __vectors + 0x00A0 +__external_interrupt_vector: + _pk_ctx_push_as_needed __get_ext_irq + + ############################################################ + # 0x00C0 : Alignment Exception + ############################################################ + + .org __vectors + 0x00C0 +__alignment_exception: + + PPE42_ALIGNMENT_HANDLER + + + ############################################################ + # 0x00E0 : Program Interrupt + ############################################################ + + .org __vectors + 0x00E0 + + ### Program exceptions are utilized for emulating the system call + ### instruction (0x44000002) which is used for doing context + ### switches between threads. They can also be used by the code + ### to signal an exception in an error scenario. +__program_exception: + _pk_ctx_push_as_needed program_exception_handler + + + ############################################################ + # 0x0100 : DEC Interrupts + ############################################################ + + .org __vectors + 0x0100 +__dec_interrupt: + _pk_ctx_push_as_needed dec_handler + + ############################################################ + # 0x0120 : FIT Interrupts + ############################################################ + .org __vectors + 0x0120 +__fit_interrupt: + + _pk_ctx_push_as_needed fit_handler + + ############################################################ + # 0x0140 : Watchdog Interrupts + ############################################################ + .org __vectors + 0x0140 +__watchdog_interrupt: + + _pk_ctx_push_as_needed watchdog_handler + + +### **************************************************************************** +### The rest of the code in this file doesn't have to be placed anywhere +### special, so just place it in the .text section. +### **************************************************************************** + + .section .text, "ax", @progbits + + + ## The idle thread has no permanent register context. The idle thread + ## entry point is re-entered whenever the idle thread is scheduled. + + .global __pk_idle_thread + .global __pk_idle_thread_from_bootloader + +__pk_idle_thread: + + ## The idle thread 'uses' the kernel stack. Any register context + ## pushed here is redundant and is wiped out/ignored every time the + ## idle thread is re-scheduled. + + ## The idle thread simply establishes a default machine context and + ## enters the wait-enable state. The idle thread is always entered + ## with interrupts disabled. + ## + ## The kernel context is initialized to indicate that the idle thread + ## is running - the idle thread priority is PK_THREADS, the + ## 'thread-mode' bit is asserted and so is the 'discard-ctx" bit. + ## In addition, the previous kernel context is stored in the lower + ## 16 bits. + ## + ## This loop can also be called from the PK bootloader if main() + ## returns - in which case we don't muck with the SPRG0 or the stack + ## pointer. + mfsprg0 %r3 + srwi %r3, %r3, 16 + oris %r3, %r3, (PK_THREADS << 8) | PPE42_THREAD_MODE | PPE42_DISCARD_CTX + mtsprg0 %r3 + _lwzsd %r1, __pk_kernel_stack + +__pk_idle_thread_from_bootloader: + + PK_KERN_TRACE_ASM16("ENTER_IDLE_STATE") + + _lwzsd %r3, __pk_thread_machine_context_default + _oriwa %r3, %r3, MSR_WE + mtmsr %r3 + b . + + ## pk_halt() is implemented on the ppe42 by writing a value of 0x3 to + ## the RST field of the DBCR. + .global pk_halt +pk_halt: + lis %r31, 0x3000 + mtdbcr %r31 + .long 0 + + +dec_handler: + + ## The portable timer handler of PK is a full-mode handler with the prototype: + ## void (*pk_timer_handler)(void). + ## + ## To support the portable specification, the kernel clears the + ## interrupt by writing the DIS back into the TSR before calling the + ## handler. The timer handler does not take any arguments. + + li %r4, PPE42_IRQ_DEC + _update_kernel_context %r4, %r3 + + _liwa %r3, TSR_DIS + mttsr %r3 + + bl __pk_timer_handler + b check_for_ext_interrupt + +program_exception_handler: + _pk_panic PPE42_ILLEGAL_INSTRUCTION + + .global __pk_next_thread_resume +__pk_next_thread_resume: + + _lwzsd %r3, __pk_next_thread + _stwsd %r3, __pk_current_thread + + ## Enter the wait enabled state if the thread pointer is null + bwz %r3, __pk_idle_thread + + ## switch to the new thread stack + lwz %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3) + + ## load sprg0 from the stack and update the thread priority + ## in case it changed. +restore_and_update_sprg0: + _lbzsd %r31, __pk_next_priority + + PK_KERN_TRACE_ASM16("RESUME_THREAD(%d)", %r31) + + lwz %r3, PK_CTX_KERNEL_CTX(%r1) + rlwimi %r3, %r31, 24, 2, 7 + mtsprg0 %r3 + + b ctx_pop + +fit_handler: + + ## The FIT handler is user defined. By + ## convention the kernel clears the interrupt by writing the FIS back + ## into the TSR. + + li %r4, PPE42_IRQ_FIT + + _update_kernel_context %r4, %r3 + + _lwzsd %r3, __ppe42_fit_arg + + _liwa %r6, TSR_FIS + mttsr %r6 + + _lwzsd %r6, __ppe42_fit_routine + mtlr %r6 + blrl + + b check_for_ext_interrupt + +watchdog_handler: + ## Watchdog setup is described in the PK Specification. + ## The kernel clears TSR[WIS] prior to calling the handler. + + li %r4, PPE42_IRQ_WATCHDOG + + _update_kernel_context %r4, %r3 + + _liwa %r6, TSR_WIS + mttsr %r6 + + _lwzsd %r6, __ppe42_watchdog_routine + mtlr %r6 + blrl + + b check_for_ext_interrupt + + + ## Check if we can disard the interrupted context. + ## This routine expects r3, r4, lr, and cr to already be pushed. + ## It also expects r3 to hold the address of the function to jump + ## to after the interrupted context has been pushed (if necessary). + + .align 5 +ctx_check_discard: + + ## Prepare to jump to the branch function that was passed in + mtlr %r3 + + ## Check if the DISCARD_CTX bit is set in the kernel context + mfsprg0 %r3 + bb0wi %r3, PPE42_DISCARD_CTX_BIT, ctx_continue_push + +ctx_discard: + ## DISCARD_CTX bit was set. Discard stack and branch to interrupt + ## handler code + addi %r1, %r1, PK_CTX_SIZE + blr + + ## DISCARD_CTX bit was not set. Continue saving full context. + ## (r3, r4, lr, and cr have already been saved for us) and + ## r3 contains the interrupted kernel context + + .global __ctx_switch +__ctx_switch: + stwu %r1, -PK_CTX_SIZE(%r1) + stvd %d3, PK_CTX_GPR3(%r1) + mfcr %r3 + mflr %r4 + stvd %d3, PK_CTX_CR(%r1) + _liw %r3 __pk_next_thread_resume + mtlr %r3 + ## emulate what interrupt would do + mtsrr0 %r4 + mfmsr %r3 + mtsrr1 %r3 + + ## ctx_continue_push expects r3 to be value of sprg0 + mfsprg0 %r3 + +ctx_continue_push: + + stvd %d5, PK_CTX_GPR5(%r1) + stvd %d7, PK_CTX_GPR7(%r1) + stvd %d9, PK_CTX_GPR9(%r1) + stvd %d28, PK_CTX_GPR28(%r1) + stvd %d30, PK_CTX_GPR30(%r1) + mfxer %r5 + mfctr %r6 + stvd %d5, PK_CTX_XER(%r1) + mfsrr0 %r7 + mfsrr1 %r8 + stvd %d7, PK_CTX_SRR0(%r1) + stw %r0, PK_CTX_GPR0(%r1) + stw %r3, PK_CTX_KERNEL_CTX(%r1) + + ## If the 'processing interrupt' bit is set then we were already + ## using the kernel stack and don't need to modify or save the current + ## stack pointer. + bb1wi %r3, PPE42_PROC_IRQ_BIT, ctx_push_completed + + ## load the pointer to the current thread control block + _lwzsd %r4, __pk_current_thread + + ## don't save the stack pointer in the thread control block + ## if the current thread was the idle thread (null pointer) + bwz %r4, switch_to_kernel_stack + + ## we interrupted a bonafide thread, so save off the stack + ## pointer + stw %r1, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r4) + +switch_to_kernel_stack: + _stwsd %r1, __pk_saved_sp + _lwzsd %r1, __pk_kernel_stack + +ctx_push_completed: + blr + +__get_ext_irq: + + ## Entry invariants: + ## 1. external interupts are disabled; + ## 2. previous context has ben saved off + ## 3. r3 contains the kernel context + ## 4. r1 points to the kernel stack + + ## This is HW Macro specific code that is responsible for finding the + ## IRQ # and storing it in r4 (phantom IRQ's are assigned a value of EXTERNAL_IRQS). + + hwmacro_get_ext_irq + + ## An active or phantom IRQ was found. + ## R3 has the context of the interrupted thread or bottom half + ## R4 has the IRQ number. + ## The IRQ is converted into a pointer to an 8-byte handler + ## structure, and the handler is dispatched. The call is made with the + ## parameters: + + ## R3 = private data ptr + ## R4 = irq + +call_external_irq_handler: + + _update_kernel_context %r4, %r3 + slwi %r3, %r4, 3 //multiply the irq# by 8 + _liw %r6, __ppe42_irq_handlers + lwzx %r5, %r6, %r3 + addi %r3, %r3, 4 + lwzx %r3, %r6, %r3 + mtlr %r5 + blrl + + ## Once the interrupt handler returns, check if any interrupts are + ## waiting and handle them now. + +check_for_ext_interrupt: + + ## Set the CTX_DISCARD bit in the kernel context so that if there is + ## an interrupt it will not bother saving the full context. + mfsprg0 %r31 + oris %r31, %r31, PPE42_DISCARD_CTX + mtsprg0 %r31 + + ###### Enable/Disable External Interrupts ##### + wrteei 1 + wrteei 0 + + ## If we made it this far, there must not be any interrupts pending. + ## If bottom half processing was interrupted we need to restore it +check_interrupted_bh: + + ## If the thread ID is 33 then the bottom half handler was interrupted + ## and needs to be restored. + extrwi %r4, %r31, 6, 2 + cmpwi %r4, 33 + beq ctx_pop_with_sprg0 + +check_for_bh: + ## if the bottom half queue is pointing to itself then the queue is + ## empty and there are no bottom halves that need processing. + _lwzsd %r4, _pk_bh_queue + lwz %r5, 0(%r4) + cmplwbeq %r4, %r5, restore_interrupted_sp + +process_bottom_halves: + ## Clear the CTX_DISCARD bit so that interrupted bottom half context + ## will be saved in case an interrupt occurs after this point. Also + ## set the thread ID to 33 so that we know to restore the bottom half + ## context that was interrupted. + rlwinm %r3, %r31, 0, 9, 1 //clear thread id + discard bit + oris %r3, %r3, 0x2100 //set thread id to 33 + mtsprg0 %r3 //set bottom half context + + ## branch to a C function that processes bottom halves + wrteei 1 + bl _pk_process_bh + wrteei 0 + + ## restore the previous kernel context (with discard bit set) + mtsprg0 %r31 + +restore_interrupted_sp: + ## restore the interrupted thread stack pointer + _lwzsd %r1, __pk_saved_sp + + ## If we are not in thread mode (i.e., we took an interrupt in an + ## interupt-only configuration of PK or after pk_initialize() but + ## before pk_start_threads) simply pop the context and RFI - in this + ## case we'll most likely be returning to main() or the non-thread-mode + ## idle thread. + +check_thread_mode: + bb0wi %r31, PPE42_THREAD_MODE_BIT, ctx_pop_with_sprg0 + + ## Check if external interrupt activated a delayed context switch. The + ## C-level code has taken care of the scheduling decisions - we simply + ## need to implement them here. +check_for_ctx_switch: + + _lwzsd %r3, __pk_delayed_switch + bwz %r3, check_for_idle_thread + + ## Clear the delayed switch flag and go to the context switch code to + ## finish the switch. + + li %r3, 0 + _stwsd %r3, __pk_delayed_switch + + b __pk_next_thread_resume + + ## check if we should switch to the wait enabled state (idle) +check_for_idle_thread: + _lwzsd %r3, __pk_current_thread + bwz %r3, __pk_idle_thread + +ctx_pop_with_sprg0: + ## we must ensure that interrupts are disabled while restoring context + ## + ## restore sprg0 from the saved context + lwz %r0, PK_CTX_KERNEL_CTX(%r1) + mtsprg0 %r0 + +#if PK_KERNEL_TRACE_ENABLE + srwi %r0, %r0, 16 + PK_KERN_TRACE_ASM16("RESUME_CONTEXT(0x%04x)", %r0) +#endif + +ctx_pop: + lwz %r0, PK_CTX_GPR0(%r1) + lvd %d7, PK_CTX_SRR0(%r1) + mtsrr1 %r8 + mtsrr0 %r7 + lvd %d5, PK_CTX_XER(%r1) + mtctr %r6 + mtxer %r5 + lvd %d30, PK_CTX_GPR30(%r1) + lvd %d28, PK_CTX_GPR28(%r1) + lvd %d9, PK_CTX_GPR9(%r1) + lvd %d7, PK_CTX_GPR7(%r1) + lvd %d5, PK_CTX_GPR5(%r1) + lvd %d3, PK_CTX_CR(%r1) + mtlr %r4 + mtcr0 %r3 + lvd %d3, PK_CTX_GPR3(%r1) + addi %r1, %r1, PK_CTX_SIZE + + rfi + +/// \endcond diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c new file mode 100644 index 00000000..3687bebc --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c @@ -0,0 +1,361 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_gcc.h +/// \brief 32-bit PowerPC functions expected by GCC +/// +/// GCC expects certain built-in functions to be defined in the environment. +/// Since PK applications are statically linked, we must define these +/// functions ourselves to avoid a static link with the GCC libraries, which +/// would legaly require us to distribute (at least) the binary forms of PK +/// applications. +/// +/// We obviously had to look at some GCC library code to understand the +/// specifications of these routines. However, all of the code here is new - +/// no structure definitions or lines of executable code were copied from the +/// GCC sources. + +#include "pk.h" +#include "ppe42_gcc.h" + +/// A 64-bit logical right shift. +/// +/// Note that shifts with negative shift counts or shifts with shift counts +/// longer than 63 bits are undefined. + +uint64_t +__lshrdi3(uint64_t x, int i) +{ + Uint64 input, result; + + if (i == 0) + { + return x; + } + + input.value = x; + + if (i >= 32) + { + result.word[0] = 0; + result.word[1] = input.word[0] >> (i - 32); + } + else + { + result.word[0] = input.word[0] >> i; + result.word[1] = (input.word[1] >> i) | (input.word[0] << (32 - i)); + } + + return result.value; +} + + +/// A 64 bit arithmetic left shift. +/// +/// Note that shifts with negative shift counts or shifts with shift counts +/// longer than 63 bits are undefined. + +uint64_t +__ashldi3(uint64_t x, int i) +{ + Uint64 input, result; + + if (i == 0) + { + return x; + } + + input.value = x; + + if (i >= 32) + { + result.word[1] = 0; + result.word[0] = input.word[1] << (i - 32); + } + else + { + result.word[1] = input.word[1] << i; + result.word[0] = (input.word[0] << i) | (input.word[1] >> (32 - i)); + } + + return result.value ; + +} + + +/// A 64 bit arithmetic right shift. +/// +/// Note that shifts with negative shift counts or shifts with shift counts +/// longer than 63 bits are undefined. + +uint64_t +__ashrdi3(uint64_t x, int i) +{ + Int64 input, result; + + if (i == 0) + { + return x; + } + + input.value = x; + + if (i >= 32) + { + result.word[0] = input.word[0] >> 31; + result.word[1] = input.word[0] >> (i - 32); + } + else + { + result.word[0] = input.word[0] >> i; + result.word[1] = + (((uint32_t)input.word[1]) >> i) | + (input.word[0] << (32 - i)); + } + + return result.value ; + +} + + +/// 32-bit Population count + +// This is a well-known divide-and-conquer algorithm, e.g. look on Wikipedia +// under "Hamming Weight". The idea is to compute sums of adjacent bit +// segments in parallel, in place. + +int +__popcountsi2(uint32_t x) +{ + uint32_t m1 = 0x55555555; + uint32_t m2 = 0x33333333; + uint32_t m4 = 0x0f0f0f0f; + x -= (x >> 1) & m1; /* Sum pairs of bits */ + x = (x & m2) + ((x >> 2) & m2);/* Sum 4-bit segments */ + x = (x + (x >> 4)) & m4; /* Sum 8-bit segments */ + x += x >> 8; /* Sum 16-bit segments */ + return (x + (x >> 16)) & 0x3f; /* Final sum */ +} + + +/// 64-bit Population count + +int +__popcountdi2(uint64_t x) +{ + return __popcountsi2(x >> 32) + __popcountsi2(x & 0xffffffff); +} + + +// 64-bit divides +// +// For the unsigned case, note that divide by 0 returns quotient = remainder = +// 0. +// +// For the signed case, in general we perform the division on the absolute +// values and fix the signs of the quotient and remainder at the end. +// +// For the signed case, the convention in other libraries seems to be to +// ignore the case of the most-negative integer. Although it seems "wrong" to +// return the wrong answer when the right answer can be easily computed, in +// the interest of code size we follow the convention here and ignore the most +// negative integer. +// +// The assembler routine __ppe42_udiv64() assembles to ??? bytes. The full C +// routine __ppc_sdiv64 compiles to ??? bytes with the most-negative checks, +// but only ??? bytes as configured here. + +// For the signed cases, we need to handle the special case that the dividend +// or divisor is the most negative integer. +// +// If the dividend is the most negative integer, then dividing this integer by +// -1 would overflow as a positive quotient, so we set quotient and remainder +// to 0 in this case. For divide by 1, the quotient is the most negative +// integer. Otherwise we adjust the dividend by the absolute value of the +// divisor, then fix up the quotient later by adding or subtracting 1. +// +// If the divisor is the most negative integer, then the quotient is always 0 +// unless the dividend is also the most negative integer, in which case the +// quotient is 1 and the remainder is 0. +// + +uint64_t +__udivdi3(uint64_t u, uint64_t v) +{ + uint64_t quotient, remainder; + + __ppe42_udiv64(u, v, "ient, &remainder); + return quotient; +} + + +uint64_t +__umoddi3(uint64_t u, uint64_t v) +{ + uint64_t quotient, remainder; + + __ppe42_udiv64(u, v, "ient, &remainder); + return remainder; +} + + +#if 0 + #define INT64_T_MIN ((int64_t)(0x8000000000000000ull)) +#endif + +void +__ppe42_sdiv64(int64_t u, int64_t v, + int64_t* quotient, int64_t* remainder) +{ + int q_negate, r_negate; + uint64_t uu, uv; +#if 0 + int fixup = 0; +#endif + + q_negate = (u < 0) ^ (v < 0); + r_negate = (u < 0); + uu = (u < 0 ? -u : u); + uv = (v < 0 ? -v : v); + +#if 0 + + if (u == INT64_T_MIN) + { + if (v == -1) + { + *quotient = 0; + *remainder = 0; + return; + } + else if (v == 1) + { + *quotient = INT64_T_MIN; + *remainder = 0; + return; + } + else if (v == INT64_T_MIN) + { + *quotient = 1; + *remainder = 0; + return; + } + else + { + fixup = 1; + u += (v < 0 ? -v : v); + } + } + else if (v == INT64_T_MIN) + { + *quotient = 0; + *remainder = u; + return; + } + +#endif + + __ppe42_udiv64(uu, uv, (uint64_t*)quotient, (uint64_t*)remainder); + +#if 0 + + if (fixup) + { + *quotient += 1; + } + +#endif + + if (q_negate) + { + *quotient = -(*quotient); + } + + if (r_negate) + { + *remainder = -(*remainder); + } +} + + +int64_t +__divdi3(int64_t u, int64_t v) +{ + int64_t quotient, remainder; + + __ppe42_sdiv64(u, v, "ient, &remainder); + return quotient; +} + + +int64_t +__moddi3(int64_t u, int64_t v) +{ + int64_t quotient, remainder; + + __ppe42_sdiv64(u, v, "ient, &remainder); + return remainder; +} + + +/// 64-bit unsigned compare as a function, returning 0 (<), 1 (==) or 2 (>). + +int +__ucmpdi2(uint64_t i_a, uint64_t i_b) +{ + Uint64 a, b; + int rv; + + a.value = i_a; + b.value = i_b; + + if (a.word[0] < b.word[0]) + { + rv = 0; + } + else if (a.word[0] > b.word[0]) + { + rv = 2; + } + else if (a.word[1] < b.word[1]) + { + rv = 0; + } + else if (a.word[1] > b.word[1]) + { + rv = 2; + } + else + { + rv = 1; + } + + return rv; +} diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h new file mode 100644 index 00000000..afaf7b4d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h @@ -0,0 +1,98 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_gcc.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_GCC_H__ +#define __PPE42_GCC_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_gcc.h +/// \brief 32-bit functions expected by GCC + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +/// A 64-bit unsigned integer type + +typedef union +{ + uint64_t value; + uint32_t word[2]; +} Uint64; + +/// A 64-bit signed integer type + +typedef union +{ + int64_t value; + int32_t word[2]; +} Int64; + +uint64_t +__lshrdi3(uint64_t x, int i); + +uint64_t +__ashldi3(uint64_t x, int i); + +uint64_t +__ashrdi3(uint64_t x, int i); + +int +__popcountsi2(uint32_t x); + +int +__popcountdi2(uint64_t x); + +/// Unsigned 64/64 bit divide, returning quotient and remainder via pointers. + +void +__ppe42_udiv64(uint64_t u, uint64_t v, uint64_t* q, uint64_t* r); + +/// Signed 64/64 bit divide, returning quotient and remainder via pointers. + +void +__ppe42_sdiv64(int64_t u, int64_t v, int64_t* q, int64_t* r); + +uint64_t +__udivdi3(uint64_t u, uint64_t v); + +int64_t +__divdi3(int64_t u, int64_t v); + +int64_t +__moddi3(int64_t u, int64_t v); + +uint64_t +__umoddi3(uint64_t u, uint64_t v); + +int +__ucmpdi2(uint64_t a, uint64_t b); + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_GCC_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c new file mode 100644 index 00000000..12050eec --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c @@ -0,0 +1,100 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_init.c +/// \brief PPE42 initialization routines +/// +/// The entry points in this file are routines that are typically used during +/// initialization, and their code space could be deallocated and recovered if +/// no longer needed by the application after initialization. + +#include "pk.h" +#include "pk_trace.h" + +// Note that __ppe42_system_setup() is called from the PK bootloader early +// in the initialization, at a point before the aplication has enabled +// interrupts. + +// This function is expected to be defined by the macro specific code (GPE, CME, SBE) +void __hwmacro_setup(void); + + +void +__ppe42_system_setup() +{ + //Only do this if the application hasn't provided a static table definition +#ifndef STATIC_IRQ_TABLE + PkIrqId irq; + + // Initialize the interrupt vectors. + for (irq = 0; irq < EXTERNAL_IRQS; irq++) + { + __ppe42_irq_handlers[irq].handler = __ppe42_default_irq_handler; + } + + //NOTE: EXTERNAL_IRQS is the phantom interrupt assigned irq + __ppe42_irq_handlers[irq].handler = __ppe42_phantom_irq_handler; + + // Initialize special interrupt handlers + + __ppe42_fit_routine = __ppe42_default_irq_handler; + __ppe42_fit_arg = 0; + + __ppe42_watchdog_routine = __ppe42_default_irq_handler; + __ppe42_watchdog_arg = 0; + + /* + __ppe42_debug_routine = __ppe42_default_irq_handler; + __ppe42_debug_arg = 0; + */ +#endif /*STATIC_IRQ_TABLE*/ + + //Clear all status bits in the TSR + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + +#ifdef APPCFG_USE_EXT_TIMEBASE + //Enable the DEC interrupt and configure it to use the external dec_timer signal + mtspr(SPRN_TCR, TCR_DIE | TCR_DS); +#else + //Enable the DEC interrupt and configure it to use the internal clock signal + mtspr(SPRN_TCR, TCR_DIE); +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#if PK_TIMER_SUPPORT +#if PK_TRACE_SUPPORT + extern PkTraceBuffer g_pk_trace_buf; + //set the ppe instance id + g_pk_trace_buf.instance_id = (uint16_t)(mfspr(SPRN_PIR) & PIR_PPE_INSTANCE_MASK); +#endif /* PK_TRACE_SUPPORT */ +#endif /* PK_TIMER_SUPPORT */ + + //call macro-specific setup + __hwmacro_setup(); +} diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h new file mode 100644 index 00000000..8361782d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h @@ -0,0 +1,243 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_IRQ_H__ +#define __PPE42_IRQ_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_irq.h +/// \brief PPE42 interrupt handling for PK +/// +/// Interrupt handling protocols and interrupt controller programming are +/// inherently non-portable, however PK defines APIs that may be useful among +/// different machines. +/// + + +// Define pseudo-IRQ numbers for PPE42 built-in interrupts. These numbers +// will appear in bits 16:23 of SPRG0 (__PkKernelContext) when the handlers +// are active + +#define PPE42_EXC_MACHINE_CHECK 0x50 +#define PPE42_EXC_DATA_STORAGE 0x53 +#define PPE42_EXC_INSTRUCTION_STORAGE 0x54 +#define PPE42_EXC_ALIGNMENT 0x56 +#define PPE42_EXC_PROGRAM 0x57 +#define PPE42_IRQ_DEC 0x58 +#define PPE42_IRQ_FIT 0x59 +#define PPE42_IRQ_WATCHDOG 0x5A + + +// Unhandled exceptions default to a kernel panic, but the application can +// override these definition. Note that the exception area only allocates 32 +// bytes (8 instructions) to an unhandled exception, so any redefinition +// would most likely be a branch to an application-defined handler. + +#ifndef PPE42_MACHINE_CHECK_HANDLER + #define PPE42_MACHINE_CHECK_HANDLER PK_PANIC(0x0200) +#endif + +#ifndef PPE42_DATA_STORAGE_HANDLER + #define PPE42_DATA_STORAGE_HANDLER PK_PANIC(0x0300) +#endif + +#ifndef PPE42_INSTRUCTION_STORAGE_HANDLER + #define PPE42_INSTRUCTION_STORAGE_HANDLER PK_PANIC(0x0400) +#endif + +#ifndef PPE42_ALIGNMENT_HANDLER + #define PPE42_ALIGNMENT_HANDLER PK_PANIC(0x0600) +#endif + + +//////////////////////////////////////////////////////////////////////////// +// PK API +//////////////////////////////////////////////////////////////////////////// + +#ifndef __ASSEMBLER__ + +/// An IRQ handler takes 2 arguments: +/// \arg \c arg - Private handler data installed by \c ssx_irq_setup() or +/// \c ssx_irq_handler_set(). +/// \arg \c irq - The IRQ id; to enable a generic handler to manipulate +/// its own interrupt status . + +typedef void (*PkIrqHandler)(void* arg, PkIrqId irq); + +/// Declare a subroutine as an IRQ handler + +#define PK_IRQ_HANDLER(f) void f(void* arg, PkIrqId irq) + +int pk_irq_setup(PkIrqId irq, + int polarity, + int trigger); + +int pk_irq_handler_set(PkIrqId irq, + PkIrqHandler handler, + void* arg); + +void pk_irq_enable(PkIrqId irq); +void pk_irq_disable(PkIrqId irq); +void pk_irq_statusclear(PkIrqId irq); + +PK_IRQ_HANDLER(__ppe42_default_irq_handler); +PK_IRQ_HANDLER(__ppe42_phantom_irq_handler); + + +int +ppe42_fit_setup(int tcr_fp, PkIrqHandler handler, void* arg); + + +/// The address of the optional FIT interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +PkIrqHandler __ppe42_fit_routine; + + +/// The private data of the optional FIT interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +void* __ppe42_fit_arg; + + +int +ppe42_watchdog_setup(int tcr_wp, int tcr_wrc, + PkIrqHandler handler, void* arg); + + +/// The address of the optional Watchdog interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +PkIrqHandler __ppe42_watchdog_routine; + + +/// The private data of the optional Watchdog interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +void* __ppe42_watchdog_arg; + + +int +ppe42_debug_setup(PkIrqHandler handler, void* arg); + + +/// The address of the optional Debug interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +PkIrqHandler __ppe42_debug_routine; + + +/// The private data of the optional Watchdog interrupt handler + +UNLESS__PPE42_IRQ_CORE_C__(extern) +volatile +void* __ppe42_debug_arg; + +#endif /* __ASSEMBLER__ */ + +// It's hard to be portable and get all of the definitions and headers in the +// correct order. We need to bring in the system IRQ header here. + +#ifdef HWMACRO_GPE + #include "gpe_irq.h" +#else + #ifdef HWMACRO_STD + #include "std_irq.h" + #endif +#endif + +/// \page ppe42_irq_macros_page PPE42 PK IRQ Assembler Macros +/// +/// + +#ifndef __ASSEMBLER__ + + +/// This structure holds the interrupt handler routine addresses and private +/// data. Assembler code assumes the given structure layout, so any changes +/// to this structure will need to be reflected down into the interrupt +/// dispatch assembler code. + +typedef struct +{ + PkIrqHandler handler; + void* arg; +} Ppe42IrqHandler; + + +#ifdef STATIC_IRQ_TABLE + +#define IRQ_HANDLER(func, arg) \ + {func, arg}, + +#define IRQ_HANDLER_DEFAULT \ + {__ppe42_default_irq_handler, 0}, + +#define EXTERNAL_IRQ_TABLE_END \ + {__ppe42_phantom_irq_handler, 0}\ + }; + +#define EXTERNAL_IRQ_TABLE_START \ + Ppe42IrqHandler __ppe42_irq_handlers[EXTERNAL_IRQS + 1] = \ + { + +#else + +#define EXTERNAL_IRQ_TABLE_START + +#define IRQ_HANDLER(func, arg) + +#define IRQ_HANDLER_DEFAULT + +#define EXTERNAL_IRQ_TABLE_END + +#endif /*STATIC_IRQ_TABLE*/ + +/// Interrupt handlers for real (implemented interrupts) plus one for the phantom interrupt handler +extern Ppe42IrqHandler __ppe42_irq_handlers[EXTERNAL_IRQS + 1]; + + +/// The 'phantom interrupt' handler +/// +/// A 'phantom' interrupt occurs when the interrupt handling code in the +/// kernel is entered, but no interrupt is found pending in the controller. +/// This is considered a serious bug, as it indictates a short window +/// condition where a level-sensitive interrupt has been asserted and then +/// quickly deasserted before it can be handled. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +Ppe42IrqHandler __ppe42_phantom_irq; + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_IRQ_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c new file mode 100644 index 00000000..e42c2db8 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c @@ -0,0 +1,71 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_irq_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_irq_core.c +/// \brief Core IRQ routines required of any PPE42 configuration of PK +/// +/// This file is mostly only a placeholder - where 'extern inline' API +/// functions and 'extern' variables are realized. A couple of default +/// handlers are also installed here. The entry points in this file are +/// considered 'core' routines that will always be present at runtime in any +/// PK application. + +#define __PPE42_IRQ_CORE_C__ + +#include "pk.h" + +#ifndef STATIC_IRQ_TABLE + Ppe42IrqHandler __ppe42_irq_handlers[EXTERNAL_IRQS + 1]; +#endif + +/// This function is installed by default for interrupts not explicitly set up +/// by the application. These interrupts should never fire. + +void +__ppe42_default_irq_handler(void* arg, PkIrqId irq) +{ + PK_PANIC(PPE42_DEFAULT_IRQ_HANDLER); +} + + +/// This function is installed by default to handle the case that the +/// interrupt dispatch code is entered in response to an external +/// interrupt, but no interrupt is found pending in the interrupt +/// controller. This should never happen, as it would indicate that a +/// 'glitch' occurred on the external interrupt input +/// to the PPE42 core. + +void __ppe42_phantom_irq_handler(void* arg, PkIrqId irq) +{ + PK_PANIC(PPE42_PHANTOM_INTERRUPT); +} + + +#undef __PPE42_IRQ_CORE_C__ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h new file mode 100644 index 00000000..f3f48a4d --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h @@ -0,0 +1,112 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_msr.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_MSR_H__ +#define __PPE42_MSR_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_msr.h +/// \brief Everything related to the PPE42 Machine State Register +/// +/// All of the macros defined here that \e modify the MSR create a compiler +/// memory barrier that will cause GCC to flush/invalidate all memory data +/// held in registers before the macro. This is consistent with other systems, +/// e.g., the PowerPC Linux kernel, and is the safest way to define these +/// macros as it guarantess for example that kernel data structure updates +/// have completed before exiting a critical section. + +#define MSR_SEM 0x7f000000 /* SIB Error Mask */ +#define MSR_IS0 0x00800000 /* Instance-Specific Field 0 */ +#define MSR_SIBRC 0x00700000 /* Last SIB return code */ +#define MSR_LP 0x00080000 /* Low Priority */ +#define MSR_WE 0x00040000 /* Wait State Enable */ +#define MSR_IS1 0x00020000 /* Instance-Specific Field 1 */ +#define MSR_UIE 0x00010000 /* Unmaskable Interrupt Enable */ +#define MSR_EE 0x00008000 /* External Interrupt Enable */ +#define MSR_ME 0x00001000 /* Machine Check Exception Enable */ +#define MSR_IPE 0x00000100 /* Imprecise Mode Enable */ +#define MSR_SIBRCA 0x000000ff /* SIB Return Code Accumulator */ + +//#define MSR_CE_BIT 14 +#define MSR_EE_BIT 16 +//#define MSR_IR_BIT 26 +//#define MSR_DR_BIT 27 + + +#define MSR_SEM_START_BIT 1 +#define MSR_SEM_LEN 7 +#define MSR_SIBRC_START_BIT 9 +#define MSR_SIBRC_LEN 3 + + +#ifndef __ASSEMBLER__ + +/// Move From MSR + +#define mfmsr() \ + ({uint32_t __msr; \ + asm volatile ("mfmsr %0" : "=r" (__msr)); \ + __msr;}) + + +/// Move to MSR + +#define mtmsr(value) \ + asm volatile ("mtmsr %0" : : "r" (value) : "memory") + + +/// Read-Modify-Write the MSR with OR (Set MSR bits). This operation is only +/// guaranteed atomic in a critical section. + +#define or_msr(x) \ + mtmsr(mfmsr() | (x)) + + +/// Read-Modify-Write the MSR with AND complement (Clear MSR bits). This +/// operation is only guaranteed atomic in a critical section. + +#define andc_msr(x) \ + mtmsr(mfmsr() & ~(x)) + + +/// Write MSR[EE] with an immediate value (0/1) +/// +/// Note that the immediate value \a i must be a compile-time constant. + +#define wrteei(i) \ + asm volatile ("wrteei %0" : : "i" (i) : "memory") + + +/// Write MSR[EE] from the EE bit of another MSR + +#define wrtee(other_msr) \ + asm volatile ("wrtee %0" : : "r" (other_msr) : "memory") + +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_MSR_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c new file mode 100755 index 00000000..a0a1a0d6 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c @@ -0,0 +1,103 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_scom.c +/// \brief Lowest level PK SCOM definitions. +/// +/// Currently these SCOM functions are only optimized for functionality, not +/// speed. Speed optimization will be done when we have full compiler support +/// for the low-level stvd and lvd SCOM OPs. +/// +/// A FAPI-lite SCOM can call these PK SCOM functions. +/// +/// Comment: +/// - No need to poll for SCOM completion, nor return error code of SCOM fails. +/// A SCOM fail will cause the GPE to hang if configured to do so. But do we +/// necessarily have to do this? Wouldn't a gentle recovery from a SCOM fail +/// be preferred? + +#include "pk.h" +#include "ppe42_scom.h" +#include "ppe42_msr.h" + + +uint32_t putscom_abs(const uint32_t i_address, uint64_t i_data) +{ + + // Perform the Store Virtual Double instruction + PPE_STVD(i_address, i_data); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); + +} + +uint32_t _putscom( uint32_t i_chiplet_id, uint32_t i_address, uint64_t i_data) +{ + + // Perform the Store Virtual Double Index instruction + PPE_STVDX(i_chiplet_id, i_address, i_data); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); + +} + +uint32_t getscom_abs( const uint32_t i_address, uint64_t* o_data) +{ + uint64_t temp; + // Perform the Load Virtual Double instruction + PPE_LVD(i_address, temp); + PPE_STVD(o_data, temp); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); +} + + +uint32_t _getscom( const uint32_t i_chiplet_id, const uint32_t i_address, uint64_t* o_data) +{ + uint64_t temp; + // Perform the Load Virtual Double Index instruction + PPE_LVDX(i_chiplet_id, i_address, temp); + PPE_STVD(o_data, temp); + + // Get the MSR[SIBRC] as the return code + uint32_t rc = mfmsr(); + rc = ((rc & MSR_SIBRC) >> (32 - (MSR_SIBRC_START_BIT + MSR_SIBRC_LEN))); + return (rc); + +} diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h new file mode 100755 index 00000000..ffef2495 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h @@ -0,0 +1,158 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_scom.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_scom.h +/// \brief Include file for PK SCOMs +/// + +#ifndef __PK_SCOM_H__ +#define __PK_SCOM_H__ + +/// SCOM operations return non-zero error codes that may or may not indicate +/// an actual error, depending on which SCOM is begin accessed. This error +/// code will appear in the MSR[SIBRC] field, bits[9:11] right after the +/// SCOM OP returns. The error code value increases with the severity of the +/// error. +#define PCB_ERROR_NONE 0 +#define PCB_ERROR_RESOURCE_OCCUPIED 1 +#define PCB_ERROR_CHIPLET_OFFLINE 2 +#define PCB_ERROR_PARTIAL_GOOD 3 +#define PCB_ERROR_ADDRESS_ERROR 4 +#define PCB_ERROR_CLOCK_ERROR 5 +#define PCB_ERROR_PACKET_ERROR 6 +#define PCB_ERROR_TIMEOUT 7 + +#ifdef __cplusplus +extern "C" { +#endif + + +/// PPE Load Virtual Double operation +#define PPE_LVD(_m_address, _m_data) \ + asm volatile \ + ( \ + "lvd %[data], 0(%[address]) \n" \ + : [data]"=r"(_m_data) \ + : [address]"b"(_m_address) \ + ); + + +// PPE Store Virtual Double operation +#define PPE_STVD(_m_address, _m_data) \ + asm volatile \ + ( \ + "stvd %[data], 0(%[address]) \n" \ + : [data]"=&r"(_m_data) \ + : "[data]"(_m_data), \ + [address]"b"(_m_address) \ + : "memory" \ + ); + +/// PPE Load Virtual Double Indexed operation +#define PPE_LVDX(_m_base, _m_offset, _m_data) \ + asm volatile \ + ( \ + "lvdx %[data], %[base], %[offset] \n" \ + : [data]"=r"(_m_data) \ + : [base]"b"(_m_base), \ + [offset]"r"(_m_offset) \ + ); + + +// PPE Store Virtual Double Indexed operation +#define PPE_STVDX(_m_base, _m_offset, _m_data) \ + asm volatile \ + ( \ + "stvdx %[data], %[base], %[offset] \n" \ + : [data]"=&r"(_m_data) \ + : "[data]"(_m_data), \ + [base]"b"(_m_base), \ + [offset]"r"(_m_offset) \ + : "memory" \ + ); + +#define PPE_MFMSR(_m_data) \ + asm volatile \ + ( \ + "mfmsr %[data] \n" \ + : [data]"=&r"(*_m_data) \ + : "[data]"(*_m_data) \ + ); + +/// @brief putscom with absolute address +/// @param [in] i_address Fully formed SCOM address +/// @param [in] i_data Pointer to uint64_t data to be written. A pointer is used +/// to optimize the underlying hardware execution +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts +uint32_t putscom_abs(const uint32_t i_address, uint64_t i_data); + +/// @brief getscom with absolute address +/// @param [in] i_address Fully formed SCOM address +/// @param [in] *o_data Pointer to uint64_t data read +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts + +uint32_t getscom_abs( const uint32_t i_address, uint64_t* o_data); + +/// @brief Implementation of PPE putscom functionality +/// @param [in] i_chiplet Chiplet ID (@todo Should only be right justified) +/// @param [in] i_address Base SCOM address +/// @param [in] i_data Pointer to uint64_t data to be written. A pointer is used +/// to optimize the underlying hardware execution +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts +uint32_t _putscom( const uint32_t i_chiplet, const uint32_t i_address, uint64_t i_data); + + +/// @brief Implementation of PPE getscom functionality +/// @param [in] i_chiplet Chiplet ID (@todo Should only be right justified) +/// @param [in] i_address Base SCOM address +/// @param [in] i_data Pointer to uint64_t data read +/// +/// @retval On PPE42 platform, unmasked errors will take machine check interrupts +uint32_t _getscom( uint32_t i_chiplet, uint32_t i_address, uint64_t* o_data); + +extern inline uint32_t putscom(const uint32_t i_chiplet, const uint32_t i_address, uint64_t i_data) +{ + return _putscom(i_chiplet, i_address, i_data); +} + + +extern inline uint32_t getscom(const uint32_t i_chiplet, const uint32_t i_address, uint64_t* o_data) +{ + return _getscom(i_chiplet, i_address, o_data); +} + +#ifdef __cplusplus +} // extern C +#endif + +#endif // __PK_SCOM_H__ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h new file mode 100644 index 00000000..2c1e903c --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h @@ -0,0 +1,201 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_spr.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PPE42_SPR_H__ +#define __PPE42_SPR_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ppe42_spr.h +/// \brief Everything related to PPE42-specific SPRs + +/// \defgroup ppe42_sprs PPE42 SPRs +/// +/// These are the documented SPRs of the PPE42. Most of these SPRs are +/// available in RISCWatch and eCmd using the defined names (minus SPRN_). In +/// some cases RISCWatch/eCMD use different names, which appear in square +/// brackets in the brief comments for each register. RISCWatch/eCMD also +/// allow CR, MSR and IAR (Instruction Address Register) to be accessed as +/// SPRs. +/// +/// @{ + +#define SPRN_XER 0x001 /// Fixed-point exception register +#define SPRN_LR 0x008 /// Link register +#define SPRN_CTR 0x009 /// Count register +#define SPRN_DEC 0x016 /// Decrementer +#define SPRN_SRR0 0x01a /// Save/restore register 0 +#define SPRN_SRR1 0x01b /// Save/restore register 1 +#define SPRN_EDR 0x03d /// Error Data Register +#define SPRN_ISR 0x03e /// Interrupt Status Register +#define SPRN_IVPR 0x03f /// Interrupt Vector Prefix Register +#define SPRN_SPRG0 0x110 /// SPR general register 0 +#define SPRN_PIR 0x11e /// Processor Identification Register +#define SPRN_PVR 0x11f /// Processor version register +#define SPRN_DBCR 0x134 /// Debug Control Register +#define SPRN_DACR 0x13c /// Debug Address Compare Register +#define SPRN_TSR 0x150 /// Timer Status Register +#define SPRN_TCR 0x154 /// Timer Control Register + +/* DBCR - Debug Control Register */ + +#define DBCR_RST 0x30000000 /* Reset: 01=Soft Reset, 10=Hard Reset, 11=Halt */ +#define DBCR_TRAP 0x01000000 /* Trap Instruction Enable */ +#define DBCR_IACE 0x00800000 /* Instruction Address Compare Enable */ +#define DBCR_DACE 0x000c0000 /* Data Address Compare Enable: 01=store, 10=load, 11=both */ + +/* TCR - Timer Control Register */ + +#define TCR_WP_MASK 0xc0000000 /* Watchdog timer select bits */ +#define TCR_WP_0 0x00000000 /* WDT uses timer 0 */ +#define TCR_WP_1 0x40000000 /* WDT uses timer 1 */ +#define TCR_WP_2 0x80000000 /* WDT uses timer 2 */ +#define TCR_WP_3 0xc0000000 /* WDT uses timer 3 */ +#define TCR_WRC_MASK 0x30000000 /* Watchdog Reset Control mask */ +#define TCR_WRC_NONE 0x00000000 /* WDT results in no action */ +#define TCR_WRC_SOFT 0x10000000 /* WDT results in Soft reset */ +#define TCR_WRC_HARD 0x20000000 /* WDT results in Hard reset */ +#define TCR_WRC_HALT 0x30000000 /* WDT results in Halt */ +#define TCR_WIE 0x08000000 /* Watchdog Interrupt Enable */ +#define TCR_DIE 0x04000000 /* Decrementer Interrupt Enable */ +#define TCR_FP_MASK 0x03000000 /* FIT Timer Select bits*/ +#define TCR_FP_0 0x00000000 /* FIT uses timer 0 */ +#define TCR_FP_1 0x01000000 /* FIT uses timer 1 */ +#define TCR_FP_2 0x02000000 /* FIT uses timer 2 */ +#define TCR_FP_3 0x03000000 /* FIT uses timer 3 */ +#define TCR_FIE 0x00800000 /* FIT Interrupt Enable */ +#define TCR_DS 0x00400000 /* Decrementer timer select: 0=every cycle, 1=use dec_timer input signal */ + +#ifndef __ASSEMBLER__ + +typedef union +{ + uint32_t value; + struct + { + unsigned int wp : 2; + unsigned int wrc : 2; + unsigned int wie : 1; + unsigned int die : 1; + unsigned int fp : 2; + unsigned int fie : 1; + unsigned int ds : 1; + unsigned int reserved : 22; + } fields; +} Ppe42TCR; + +#endif /* __ASSEMBLER__ */ + +/* TSR - Timer Status Register */ + +#define TSR_ENW 0x80000000 /* Enable Next Watchdog */ +#define TSR_WIS 0x40000000 /* Watchdog Interrupt Status */ +#define TSR_WRS_MASK 0x30000000 /* Watchdog Reset Status */ +#define TSR_WRS_NONE 0x00000000 /* No watchdog reset has occurred */ +#define TSR_WRS_SOFT 0x10000000 /* Soft reset was forced by the watchdog */ +#define TSR_WRS_HARD 0x20000000 /* Hard reset was forced by the watchdog */ +#define TSR_WRS_HALT 0x30000000 /* Halt was forced by the watchdog */ +#define TSR_DIS 0x08000000 /* Decrementer Interrupt Status */ +#define TSR_FIS 0x04000000 /* FIT Interrupt Status */ + +/* PIR - Processor Identification Register */ +#define PIR_PPE_TYPE_MASK 0x000000E0 +#define PIR_PPE_TYPE_GPE 0x00000020 +#define PIR_PPE_TYPE_CME 0x00000040 +#define PIR_PPE_INSTANCE_MASK 0x0000001F + +#ifndef __ASSEMBLER__ + +/// Move From SPR +/// +/// Note that \a sprn must be a compile-time constant. + +#define mfspr(sprn) \ + ({uint32_t __value; \ + asm volatile ("mfspr %0, %1" : "=r" (__value) : "i" (sprn)); \ + __value;}) + + +/// Move to SPR +/// +/// Note that \a sprn must be a compile-time constant. + +#define mtspr(sprn, value) \ + ({uint32_t __value = (value); \ + asm volatile ("mtspr %0, %1" : : "i" (sprn), "r" (__value)); \ + }) + + +/// Read-Modify-Write an SPR with OR (Set SPR bits) +/// +/// Note that \a sprn must be a compile-time constant. This operation is only +/// guaranteed atomic in a critical section. + +#define or_spr(sprn, x) \ + mtspr(sprn, mfspr(sprn) | (x)) + + +/// Read-Modify-Write an SPR with AND complement (Clear SPR bits) +/// +/// Note that \a sprn must be a compile-time constant. This operation is only +/// guaranteed atomic in a critical section. + +#define andc_spr(sprn, x) \ + mtspr(sprn, mfspr(sprn) & ~(x)) + +#endif /* __ASSEMBLER__ */ + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* + + /// \cond + + // Use this macro to define new mt<spr> and mf<spr> instructions that + // may not exist in the assembler. + + .macro _sprinstrs, name, num + .macro mt\name, reg + mtspr \num, \reg + .endm + .macro mf\name, reg + mfspr \reg, \num + .endm + .endm + + _sprinstrs dbcr, SPRN_DBCR + _sprinstrs tcr, SPRN_TCR + _sprinstrs tsr, SPRN_TSR + _sprinstrs sprg0, SPRN_SPRG0 + _sprinstrs ivpr, SPRN_IVPR + _sprinstrs dec, SPRN_DEC + + /// \endcond + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +#endif /* __PPE42_SPR_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S new file mode 100644 index 00000000..f1b51bb5 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S @@ -0,0 +1,134 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_thread_init.S $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +/// \file ppe42_thread_init.S +/// \brief PPE42-specific thread initialization +/// +/// The entry points in this file are routines that are typically used during +/// initialization, and their code space could be deallocated and recovered if +/// no longer needed by the application after initialization. + + .nolist +#include "pk.h" + .list + +/// \fn void __pk_thread_context_initialize(PkThread *thread, PkThreadRoutine thread_routine, void *private) +/// \brief Create the initial thread context on the stack +/// +/// The non-reserved GPRs are prepatterned with 0x0000\<rn\>\<rn\> where \<rn\> is +/// the register number (as decimal). The initial context is set up with the +/// thread running in the default machine context, and when the thread is +/// switched in it will begin executing at the entry point of the thread +/// routine with the \c private parameter in R3. The LR is initialized such +/// that when the thread returns, it will return to the entry point of \c +/// pk_complete(). +#ifdef DOXYGEN_ONLY +void +__pk_thread_context_initialize(PkThread *thread, + PkThreadRoutine thread_routine, + void *private); +#endif +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \cond + + .global_function __pk_thread_context_initialize + +__pk_thread_context_initialize: + + ## R3 = thread (param) + ## R4 = thread_routine (param) + ## R5 = private (param) + ## R6 = thread stack pointer (computed) + ## R7 = scratch + + .macro _gpr_init, prefix, reg, val + li %r7, \val + stw %r7, \prefix\reg(%r6) + .endm + + ## Initialize volatile context on the thread stack. The CR is cleared, + ## the LR = pk_complete(), R3 has the private parameter. + + lwz %r6, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3) + + stwu %r6, -PK_CTX_SIZE(%r6) + + li %r7, 0 + stw %r7, PK_CTX_CR(%r6) + + _liw %r7, pk_complete + stw %r7, PK_CTX_LR(%r6) + + stw %r5, PK_CTX_GPR3(%r6) + + _gpr_init PK_CTX_GPR, 4, 0x0404 + _gpr_init PK_CTX_GPR, 5, 0x0505 + _gpr_init PK_CTX_GPR, 6, 0x0606 + + ## XER and CTR are clear, SRR0 = thread_routine, SRR1 = default machine + ## context. + + li %r7, 0 + stw %r7, PK_CTX_XER(%r6) + stw %r7, PK_CTX_CTR(%r6) + + stw %r4, PK_CTX_SRR0(%r6) + + _lwzsd %r7, __pk_thread_machine_context_default + stw %r7, PK_CTX_SRR1(%r6) + + _gpr_init PK_CTX_GPR, 0, 0x0000 + _gpr_init PK_CTX_GPR, 7, 0x0707 + _gpr_init PK_CTX_GPR, 8, 0x0808 + _gpr_init PK_CTX_GPR, 9, 0x0909 + _gpr_init PK_CTX_GPR, 10, 0x1010 + + ## Initialize the non-volatile context on the thread stack. + + _gpr_init PK_CTX_GPR, 28, 0x2828 + _gpr_init PK_CTX_GPR, 29, 0x2929 + _gpr_init PK_CTX_GPR, 30, 0x3030 + _gpr_init PK_CTX_GPR, 31, 0x3131 + + ## Initialize the kernel context on the thread stack. + ## Note: Thread priority is set later each time the thread is + ## resumed. + + lis %r7, PPE42_THREAD_MODE + stw %r7, PK_CTX_KERNEL_CTX(%r6) + + ## Initialization is done - the stack pointer is stored back in the + ## thread. + + stw %r6, PK_THREAD_OFFSET_SAVED_STACK_POINTER(%r3) + blr + + .epilogue __pk_thread_context_initialize + +/// \endcond diff --git a/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S new file mode 100644 index 00000000..490a1ab6 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S @@ -0,0 +1,140 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/ppe42/ppe42_timebase.S $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/// \file ppe42_timebase.S +/// \brief PPE42-specific 64 bit timebase emulation +/// + .nolist +#include "pk.h" + .list + +/// \fn PkTimebase pk_timebase_get(void) +/// \brief Returns a 64 bit timebase +/// +#ifdef DOXYGEN_ONLY +PkTimebase +pk_timebase_get(void); +#endif +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \cond + .global ppe42_64bit_timebase + .global ppe42_tb_data + .global_function pk_timebase_get + +#ifndef APPCFG_USE_EXT_TIMEBASE + + /// Note that it is ok to use this function in a fast interrupt + /// context + .align 5 + .global_function pk_timebase32_get +pk_timebase32_get: + + //load the decrementer start time and change tag + lvd %r4, ppe42_tb_data@sda21(0) + + //load the lower 32 bits of the 64bit timebase accumulator + lwz %r3, ppe42_64bit_timebase+4@sda21(0) + + //load the current decrementer value + mfdec %r0 + + //load the change tag again (should already be in the cache) + lwz %r6, ppe42_tb_data+4@sda21(0) + + //loop until the change tag is the same (typically should be same) + cmplwbne %r5, %r6, pk_timebase32_get + + //calculate how much time has passed since the decrementer was started and store in r6 + subf %r5, %r0, %r4 + + //add the 32bit difference to our 32bit timebase accumulator + add %r3, %r5, %r3 + + blr + + +/// Use the DEC for our timebase until we have a real timebase register (uses +/// 9 instructions). +/// Note: It is not ok to use this function in a fast interrupt context due to +/// its use of r7 + .align 5 +pk_timebase_get: + + //load the decrementer start time and change tag + lvd %r5, ppe42_tb_data@sda21(0) + + //load 64bit timebase accumulator + lvd %r3, ppe42_64bit_timebase@sda21(0) + + + //load the current decrementer value + mfdec %r0 + + //load the change tag again (should already be in the cache) + lwz %r7, ppe42_tb_data+4@sda21(0) + + //loop until the change tag is the same + cmplwbne %r6, %r7, pk_timebase_get + + //calculate how much time has passed since the decrementer was started and store in r6 + subf %r6, %r0, %r5 + + //add the 32bit difference to the 64bit timebase accumulator + addc %r4, %r6, %r4 + addze %r3, %r3 + + blr + +//enable this once we have a local timebase register in the model +#else + +// use the local timebase register to keep more accurate time with just 6 instructions +// in the common case and 7 otherwise. + .align 5 +pk_timebase_get: + + //load the 64bit timebase accumulator + lvd r3, ppe42_64bit_timebase@sda21(0) + + //read the local timebase register (2 instructions) + _pk_timebase32_get r5, r5 + + //increment the upper 32 bits if the lower 32 bits have flipped + cmplwbge r5, r4, update_lower_32 + + //increment the upper 32 bits + addi r3, r3, 1 + +update_lower_32: + //replace the lower 32bits with what we read from the local timebase register + mr r4, r5 + + blr +#endif /* APPCFG_USE_EXT_TIMEBASE */ +/// \endcond diff --git a/import/chips/p9/procedures/ppe/pk/std/Makefile b/import/chips/p9/procedures/ppe/pk/std/Makefile new file mode 100644 index 00000000..8dbfb349 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/Makefile @@ -0,0 +1,74 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/std/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile compiles all of the PK code required for the STD (standard PPE) port +# of PK. See the "pk.mk" file in this directory. + +#all generated files from this makefile will end up in obj/$(IMAGE_NAME)/pk +export SUB_OBJDIR = /pk + +include img_defs.mk +include pkstdfiles.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +STD_OBJECTS += ${STD-TIMER-C-SOURCES:.c=.o} ${STD-TIMER-S-SOURCES:.S=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +STD_OBJECTS += ${STD-THREAD-C-SOURCES:.c=.o} ${STD-THREAD-S-SOURCES:.S=.o} +endif + +ifeq "$(STD_ASYNC_SUPPORT)" "1" +STD_OBJECTS += ${STD-ASYNC-C-SOURCES:.c=.o} ${STD-ASYNC-S-SOURCES:.S=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(STD_OBJECTS)) + +libpk.a: kernel ppe42 trace std + $(AR) crs $(OBJDIR)/libpk.a $(OBJDIR)/*.o + +.PHONY: clean std kernel ppe42 trace +std: $(OBJS) + +trace: + $(MAKE) -I $(IMAGE_SRCDIR) -C ../trace + +kernel: + $(MAKE) -I $(IMAGE_SRCDIR) -C ../kernel + +ppe42: + $(MAKE) -I $(IMAGE_SRCDIR) -C ../ppe42 + + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +clean: + rm -fr $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif diff --git a/import/chips/p9/procedures/ppe/pk/std/pk_port.h b/import/chips/p9/procedures/ppe/pk/std/pk_port.h new file mode 100644 index 00000000..45325bc6 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/pk_port.h @@ -0,0 +1,41 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/pk_port.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_PORT_H__ +#define __PK_PORT_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_port.h +/// \brief The top-level standard PPE environment header for PK. + +#define HWMACRO_STD + +#include "ppe42.h" +#include "std_timebase.h" + +#endif /* __PK_PORT_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk b/import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk new file mode 100644 index 00000000..777669a8 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk @@ -0,0 +1,56 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/std/pkstdfiles.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file pkstdfiles.mk +# +# @brief mk for including std object files +# +# @page ChangeLogs Change Logs +# @section pkstdfiles.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Object Files +########################################################################## + +STD-C-SOURCES = std_init.c std_irq_init.c +STD-S-SOURCES = + +STD-TIMER-C-SOURCES = +STD-TIMER-S-SOURCES = + +STD-THREAD-C-SOURCES = +STD-THREAD-S-SOURCES = + +STD-ASYNC-C-SOURCES = +STD-ASYNC-S-SOURCES = + +STD_OBJECTS += $(STD-C-SOURCES:.c=.o) $(STD-S-SOURCES:.S=.o) diff --git a/import/chips/p9/procedures/ppe/pk/std/std.h b/import/chips/p9/procedures/ppe/pk/std/std.h new file mode 100644 index 00000000..965ea5be --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std.h @@ -0,0 +1,44 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STD_H__ +#define __STD_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pgp.h +/// \brief The STD environment for PK. + +#ifndef HWMACRO_STD + #define HWMACRO_STD + #include "ppe42.h" +#endif + +#include "std_register_addresses.h" +#include "std_common.h" + +#endif /* __STD_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_common.h b/import/chips/p9/procedures/ppe/pk/std/std_common.h new file mode 100644 index 00000000..db43ad3c --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_common.h @@ -0,0 +1,80 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_common.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STD_COMMON_H__ +#define __STD_COMMON_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file cme_common.h +/// \brief Common header for standard PPE's +/// + +#ifndef __ASSEMBLER__ + #include <stdint.h> +#endif + +//#include "cmehw_interrupts.h" +#include "pk.h" +#include "std_irq_config.h" + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* +/// This macro contains standard PPE code for determining what IRQ caused the +/// external exception handler to be invoked by the PPE + +/// Check for interrupts pending in the interrupt status register while the IRQ +/// is computed. The IRQ is expected to be stored in r4. If no IRQ is +/// pending then load the phantom irq # (EXTERNAL_IRQS). +/// +/// r1, r2, r3, and r13 must not be modified. All other registers may be used. +/// + .macro hwmacro_get_ext_irq + + _lvdg d5, STD_LCL_EISTR #load the 64bit interrupt status into d5 + cntlzw r4, r5 + cmpwible r4, 31, call_external_irq_handler #branch if irq is lt or eq to 31 + + ## No IRQ pending in r5. Try r6. + ## Note: irq # will be 64 (phantom irq) if no bits were set in either register + + cntlzw r4, r6 + addi r4, r4, 32 + + .endm + +/// Redirect the .hwmacro_irq_cfg_bitmaps macro to call our standard PPE implementation +/// This is called from the ppe42_exceptions.S file. + .macro .hwmacro_irq_cfg_bitmaps + .std_irq_cfg_bitmaps + .endm + +// *INDENT-ON* +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_COMMON_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_init.c b/import/chips/p9/procedures/ppe/pk/std/std_init.c new file mode 100644 index 00000000..400c624b --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_init.c @@ -0,0 +1,74 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_init.c +/// \brief PK initialization for a standard PPE. +/// +/// The entry points in this routine are used during initialization. This +/// code space can be deallocated and reassigned after application +/// initialization if required. + +#include "pk.h" + +/// Standard PPE environment initial setup. +/// +/// This is setup common to all standard PPE Macro applications. This setup takes place +/// during boot, before main() is called. + +void +__hwmacro_setup(void) +{ + //mask all interrupts + out64(STD_LCL_EIMR_OR, 0xffffffffffffffffull); + + //Set all interrupts to active low, level sensitive by default + out64(STD_LCL_EIPR_CLR, 0xffffffffffffffffull); + out64(STD_LCL_EITR_CLR, 0xffffffffffffffffull); + + //set up the configured type + out64(STD_LCL_EITR_OR, g_ext_irqs_type); + + //set up the configured polarity + out64(STD_LCL_EIPR_OR, g_ext_irqs_polarity); + + //clear the status of all active-high interrupts (has no affect on + //level sensitive interrupts) + out64(STD_LCL_EISR_CLR, g_ext_irqs_polarity); + + //clear the status of all active-low interrupts (has no affect on + //level sensitive interrupts) + out64(STD_LCL_EISR_OR, ~g_ext_irqs_polarity); + + //unmask the interrupts that are to be enabled by default + out64(STD_LCL_EIMR_CLR, g_ext_irqs_enable); + + //wait for the last operation to complete + sync(); +} diff --git a/import/chips/p9/procedures/ppe/pk/std/std_irq.h b/import/chips/p9/procedures/ppe/pk/std/std_irq.h new file mode 100644 index 00000000..cde5ce6e --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_irq.h @@ -0,0 +1,113 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_irq.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STD_IRQ_H__ +#define __STD_IRQ_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_irq.h +/// \brief Standard PPE Externnal Interrupt handling for PK +/// +/// The standard PPE interrupt controller supports a maximum of 64 interrupts with +/// simple OR combining of the interrupt signals. +/// +/// The standard PPE interrupt controller allows interrupt status to be set directly by +/// software. It contains a 'mask' register, unlike most 405 interrupt +/// controllers that have an 'enable' register. The standard PPE mask and status +/// registers also have atomic CLR/OR function so that it is never necessary +/// to enter a critical section to enable/disable/clear interrupts and +/// interrupt status. + +#include "std_common.h" +#include "std_register_addresses.h" +#include "ppe42.h" + +#ifndef __ASSEMBLER__ + +/// Enable an interrupt by clearing the mask bit. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_enable(PkIrqId irq) +{ + out64(STD_LCL_EIMR_CLR, STD_IRQ_MASK64(irq)); +} + + +/// Disable an interrupt by setting the mask bit. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_disable(PkIrqId irq) +{ + out64(STD_LCL_EIMR_OR, STD_IRQ_MASK64(irq)); +} + + +/// Clear interrupt status with an CLR mask. Only meaningful for +/// edge-triggered interrupts. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_status_clear(PkIrqId irq) +{ + out64(STD_LCL_EISR_CLR, STD_IRQ_MASK64(irq)); +} + + +/// Get IRQ status as a 0 or non-0 integer + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline int +pk_irq_status_get(PkIrqId irq) +{ + return (in64(STD_LCL_EISR) & STD_IRQ_MASK64(irq)) != 0; +} + + +/// Set or clear interrupt status explicitly. + +UNLESS__PPE42_IRQ_CORE_C__(extern) +inline void +pk_irq_status_set(PkIrqId irq, int value) +{ + if (value) + { + out64(STD_LCL_EISR_OR, STD_IRQ_MASK64(irq)); + } + else + { + out64(STD_LCL_EISR_CLR, STD_IRQ_MASK64(irq)); + } +} + + +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_IRQ_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_irq_config.h b/import/chips/p9/procedures/ppe/pk/std/std_irq_config.h new file mode 100644 index 00000000..3e624acd --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_irq_config.h @@ -0,0 +1,169 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_irq_config.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STD_IRQ_CONFIG_H__ +#define __STD_IRQ_CONFIG_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_irq_config.h +/// \brief Contains data and macros pertaining to external interrupt +/// configuration for a standard PPE. +/// + +#include "pk_app_cfg.h" + +/// This constant is used to define the size of the table of interrupt handler +/// structures as well as a limit for error checking. +#define EXTERNAL_IRQS 64 + +// Standard interrupt type values (level or edge) +#define STD_IRQ_TYPE_LEVEL 0 +#define STD_IRQ_TYPE_EDGE 1 + +// Standard interrupt polarity values (high or low, rising falling) +#define STD_IRQ_POLARITY_LO 0 +#define STD_IRQ_POLARITY_FALLING 0 +#define STD_IRQ_POLARITY_HI 1 +#define STD_IRQ_POLARITY_RISING 1 + +// Standard interrupt mask values (masked or enabled) +#define STD_IRQ_MASKED 0 +#define STD_IRQ_ENABLED 1 + +// Fail to compile if the application does not define this +#ifndef APPCFG_EXT_IRQS_CONFIG + #error "APPCFG_EXT_IRQS_CONFIG must be defined in pk_app_cfg.h" +#endif + +// Fail to compile if the application does not define this +#ifndef APPCFG_IRQ_INVALID_MASK + #error "APPCFG_IRQ_INVALID_MASK must be defined in pk_app_cfg.h" +#endif + +#ifndef __ASSEMBLER__ + +/// This expression recognizes only those IRQ numbers that have named +/// (non-reserved) interrupts in the standard PPE interrupt controller. +#define STD_IRQ_VALID(irq) \ + ({unsigned __irq = (unsigned)(irq); \ + ((__irq < EXTERNAL_IRQS) && \ + ((STD_IRQ_MASK64(__irq) & \ + APPCFG_IRQ_INVALID_MASK) == 0));}) + +/// This is a 64-bit mask, with big-endian bit 'irq' set. +#define STD_IRQ_MASK64(irq) (0x8000000000000000ull >> (irq)) + +#else + +//Untyped assembler version of STD_IRQ_MASK64 +#define STD_IRQ_MASK64(irq) (0x8000000000000000 >> (irq)) + +#endif /* __ASSEMBLER__ */ + +#ifndef __ASSEMBLER__ + /// These globals are statically initialized elsewhere + extern uint64_t g_ext_irqs_type; + extern uint64_t g_ext_irqs_valid; + extern uint64_t g_ext_irqs_polarity; + extern uint64_t g_ext_irqs_enable; +#endif + +#ifdef __ASSEMBLER__ +// *INDENT-OFF* +/// These macros aid in the initialization of the external interrupt globals. I would +/// prefer to use CPP macros, but they don't support recursive macros which I use to +/// convert the variable number of interrupts that a processor can control into static +/// bitmaps used by __hwmacro_setup() at runtime. + + + //helper macro for setting up the irq configuration bitmaps for a standard PPE + .macro .std_irq_config irq_num=-1 irq_type=-1 irq_polarity=-1 irq_mask=-1 parms:vararg + .if (( \irq_num == -1 ) && ( \irq_type == -1 ) && ( \irq_polarity == -1 ) && ( \irq_mask == -1 )) +#.if ( .ext_irqs_defd != .ext_irqs_valid ) +#.error "###### .std_irq_config: Missing configuration for one or more interrupts ######" +#.endif + + .section .sdata + .align 3 + .global g_ext_irqs_type + .global g_ext_irqs_polarity + .global g_ext_irqs_enable + g_ext_irqs_polarity: + .quad .ext_irqs_polarity + g_ext_irqs_type: + .quad .ext_irqs_type + g_ext_irqs_enable: + .quad .ext_irqs_enable + .else + .if (( \irq_num < 0 ) || ( \irq_num > (EXTERNAL_IRQS - 1))) + .error "###### .std_irq_config: invalid irq number \irq_num ######" + .elseif ((.ext_irqs_valid & (1 << ( EXTERNAL_IRQS - 1 - \irq_num ))) == 0 ) + .error "###### .std_irq_config: Attempt to configure invalid irq number \irq_num ######" + .elseif (.ext_irqs_defd & (1 << ( EXTERNAL_IRQS - 1 - \irq_num ))) + .error "###### .std_irq_config: duplicate definition for irq \irq_num ######" + .else + .ext_irqs_defd = .ext_irqs_defd | (1 << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .if (( \irq_type < 0 ) || ( \irq_type > 1 )) + .error "###### .std_irq_config: invalid/unspecified irq type \irq_type for irq \irq_num ######" + .else + .ext_irqs_type = .ext_irqs_type | ( \irq_type << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .if (( \irq_polarity < 0 ) || ( \irq_polarity > 1 )) + .error "###### .std_irq_config: invalid/unspecified irq polarity ( \irq_polarity ) for irq \irq_num ######" + .else + .ext_irqs_polarity = .ext_irqs_polarity | ( \irq_polarity << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .if (( \irq_mask < 0 ) || ( \irq_mask > 1 )) + .error "###### .std_irq_config: invalid/unspecified irq mask ( \irq_mask ) for irq \irq_num ######" + .else + .ext_irqs_enable = .ext_irqs_enable | ( \irq_mask << ( EXTERNAL_IRQS - 1 - \irq_num )) + .endif + + .std_irq_config \parms + .endif + .endm + + //Top level macro for generating interrupt configuration globals for a standard PPE + .macro .std_irq_cfg_bitmaps + .ext_irqs_valid = ~(APPCFG_IRQ_INVALID_MASK) + .ext_irqs_type = 0 + .ext_irqs_polarity = 0 + .ext_irqs_enable = 0 + .irq_mask = 0 + .ext_irqs_defd = 0 + .std_irq_config APPCFG_EXT_IRQS_CONFIG + .endm + +// *INDENT-ON* +#endif /*__ASSEMBLER__*/ + +#endif /*__STD_IRQ_CONFIG_H__*/ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_irq_init.c b/import/chips/p9/procedures/ppe/pk/std/std_irq_init.c new file mode 100644 index 00000000..7ec46793 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_irq_init.c @@ -0,0 +1,134 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_irq_init.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_irq_init.c +/// \brief Standard PPE IRQ initialization code for PK +/// +/// The entry points in this file are initialization routines that could be +/// eliminated/deallocated by the application to free up storage if they are +/// no longer needed after initialization. + +#include "pk.h" + +/// Define the polarity and trigger condition for an interrupt. +/// +/// It is up to the application to take care of any side effects that may +/// occur from programming or reprogramming the interrupt controller. For +/// example, changing edge/level sensitivity or active level may set or clear +/// interrupt status in the controller. +/// +/// Note that PK allows this API to be called from any context, and changes +/// to the interrupt controller are made from a critical section. +/// +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_IRQ_SETUP One or more arguments are invalid, +/// including an invalid \a irq, or invalid \a polarity or \a trigger parameters. + +int +pk_irq_setup(PkIrqId irq, + int polarity, + int trigger) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(!STD_IRQ_VALID(irq) || + !((polarity == PK_IRQ_POLARITY_ACTIVE_HIGH) || + (polarity == PK_IRQ_POLARITY_ACTIVE_LOW)) || + !((trigger == PK_IRQ_TRIGGER_LEVEL_SENSITIVE) || + (trigger == PK_IRQ_TRIGGER_EDGE_SENSITIVE)), + PK_INVALID_ARGUMENT_IRQ_SETUP); + } + + pk_critical_section_enter(&ctx); + + if (polarity == PK_IRQ_POLARITY_ACTIVE_HIGH) + { + out64(STD_LCL_EIPR_OR, STD_IRQ_MASK64(irq)); + } + else + { + out64(STD_LCL_EIPR_CLR, STD_IRQ_MASK64(irq)); + } + + if (trigger == PK_IRQ_TRIGGER_EDGE_SENSITIVE) + { + out64(STD_LCL_EITR_OR, STD_IRQ_MASK64(irq)); + } + else + { + out64(STD_LCL_EITR_CLR, STD_IRQ_MASK64(irq)); + } + + pk_critical_section_exit(&ctx); + + return PK_OK; +} + + +/// (Re)define the IRQ handler and priority for an interrupt. +/// Return values other then PK_OK (0) are errors; see \ref pk_errors +/// +/// Note that PK allows this API to be called from any context, and changes +/// to the interrupt controller are made from a critical section. +/// +/// \retval 0 Successful completion +/// +/// \retval -PK_INVALID_ARGUMENT_IRQ_HANDLER One or more arguments are +/// invalid, including an invalid \a irq, a null (0) \a handler, +/// or invalid \a priority. + +int +pk_irq_handler_set(PkIrqId irq, + PkIrqHandler handler, + void* arg) +{ + PkMachineContext ctx; + + if (PK_ERROR_CHECK_API) + { + PK_ERROR_IF(!STD_IRQ_VALID(irq) || + (handler == 0), + PK_INVALID_ARGUMENT_IRQ_HANDLER); + } + + pk_critical_section_enter(&ctx); + + __ppe42_irq_handlers[irq].handler = handler; + __ppe42_irq_handlers[irq].arg = arg; + + pk_critical_section_exit(&ctx); + + return PK_OK; +} diff --git a/import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h b/import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h new file mode 100644 index 00000000..123e6f87 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h @@ -0,0 +1,72 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_register_addresses.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STD_REGISTER_ADDRESSES_H__ +#define __STD_REGISTER_ADDRESSES_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_register_addresses.h +/// \brief Symbolic addresses for a standard PPE + +#include "pk.h" + +// Define the base address for the external interrupt controller registers +// This can be overridden in the pk_app_cfg.h file +#ifndef STD_EIC_BASE + #define STD_EIC_BASE 0xC0000000 +#endif + +// Define the base address for the PPE mode registers +// This can be overridden in the pk_app_cfg.h file +#ifndef STD_PMR_BASE + #define STD_PMR_BASE 0xC0000100 +#endif + +// Note: This list only contains registers that are needed by PK. If +// an application requires other registers, it should define them +// elsewhere (i.e., cme_register_addresses.h) +#define STD_LCL_EISR (STD_EIC_BASE + 0x0000) +#define STD_LCL_EISR_OR (STD_EIC_BASE + 0x0010) +#define STD_LCL_EISR_CLR (STD_EIC_BASE + 0x0018) +#define STD_LCL_EIMR (STD_EIC_BASE + 0x0020) +#define STD_LCL_EIMR_OR (STD_EIC_BASE + 0x0030) +#define STD_LCL_EIMR_CLR (STD_EIC_BASE + 0x0038) +#define STD_LCL_EIPR (STD_EIC_BASE + 0x0040) +#define STD_LCL_EIPR_OR (STD_EIC_BASE + 0x0050) +#define STD_LCL_EIPR_CLR (STD_EIC_BASE + 0x0058) +#define STD_LCL_EITR (STD_EIC_BASE + 0x0060) +#define STD_LCL_EITR_OR (STD_EIC_BASE + 0x0070) +#define STD_LCL_EITR_CLR (STD_EIC_BASE + 0x0078) +#define STD_LCL_EISTR (STD_EIC_BASE + 0x0080) +#define STD_LCL_EINR (STD_EIC_BASE + 0x00a0) + +#define STD_LCL_TSEL (STD_PMR_BASE + 0x0000) +#define STD_LCL_TBR (STD_PMR_BASE + 0x0040) + +#endif // __STD_REGISTER_ADDRESSES_H__ diff --git a/import/chips/p9/procedures/ppe/pk/std/std_timebase.h b/import/chips/p9/procedures/ppe/pk/std/std_timebase.h new file mode 100644 index 00000000..8ea84c21 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/std/std_timebase.h @@ -0,0 +1,66 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/std/std_timebase.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STD_TIMEBASE_H__ +#define __STD_TIMEBASE_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2015 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file std_timebase.h +/// \brief support for using the standard PPE 32 bit timebase register +/// +/// Each standard PPE has it's own timebase register that runs at a constant +/// frequency. + +#include "pk.h" + +#ifndef __ASSEMBLER__ + +#ifndef APPCFG_USE_EXT_TIMEBASE +static inline +uint32_t pk_timebase32_get(void) +{ + return (uint32_t)((in64(STD_LCL_TBR)) >> 32); +} + +#else +//assembly function is defined in ppe42_timebase.S +uint32_t pk_timebase32_get(void); + +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +#else + +.macro _pk_timebase32_get rT, rA +lis \rA, STD_LCL_TBR@ha +lvd \rT, STD_LCL_TBR@l(\rA) +.endm + +#endif /* __ASSEMBLER__ */ + +#endif /* __STD_TIMEBASE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/trace/Makefile b/import/chips/p9/procedures/ppe/pk/trace/Makefile new file mode 100644 index 00000000..851f7831 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/Makefile @@ -0,0 +1,50 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/trace/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile is designed to be invoked with the -I argument set to +# the location of the "pk.mk" for the build + +include img_defs.mk +include pktracefiles.mk + +ifeq "$(PK_TIMER_SUPPORT)" "1" +PKTRACE_OBJECTS += ${PKTRACE-TIMER-C-SOURCES:.c=.o} ${PKTRACE-TIMER-S-SOURCES:.S=.o} +endif + +ifeq "$(PK_THREAD_SUPPORT)" "1" +PKTRACE_OBJECTS += ${PKTRACE-THREAD-C-SOURCES:.c=.o} ${PKTRACE-THREAD-S-SOURCES:.S=.o} +endif + +OBJS := $(addprefix $(OBJDIR)/, $(PKTRACE_OBJECTS)) + +all: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace.h b/import/chips/p9/procedures/ppe/pk/trace/pk_trace.h new file mode 100644 index 00000000..3b243421 --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace.h @@ -0,0 +1,305 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PK_TRACE_H__ +#define __PK_TRACE_H__ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_trace.h +/// \brief Macros and declarations for the PK Firmware Tracing Facility. +/// + +#include <stdint.h> + +#define PK_TRACE_VERSION 2 + +#ifndef PK_TRACE_SZ + #define PK_TRACE_SZ 256 +#endif + +//Fail compilation if size is not a power of 2 +#if ((PK_TRACE_SZ - 1) & PK_TRACE_SZ) + #error "PK_TRACE_SZ is not a power of two!!!" +#endif + +//Fail compilation if size is smaller than 64 bytes +#if (PK_TRACE_SZ < 64) + #error "PK_TRACE_SZ must be at least 64 bytes!!!" +#endif + +//Mask for calculating offsets into the trace circular buffer +#define PK_TRACE_CB_MASK (PK_TRACE_SZ - 1) + +#define STRINGIFY_HELPER(x) #x +#define STRINGIFY(x) STRINGIFY_HELPER(x) + +#define PPE_IMG_STRING STRINGIFY(IMAGE_NAME) + +#ifdef PK_TRACE_HASH_PREFIX + #if (PK_TRACE_HASH_PREFIX > 0xffff) + #error PK_TRACE_HASH_PREFIX must be defined as a 16 bit constant value + #endif +#endif //PK_TRACE_HASH_PREFIX + +//This provides a 128ns tick (assuming a 32ns clock period) +//and 4 different format values +#define PK_TRACE_TS_BITS 30 + +#define PK_TRACE_FORMAT_BITS (32 - PK_TRACE_TS_BITS) + +#define PK_TRACE_TS_MASK (0xfffffffful << PK_TRACE_FORMAT_BITS) +#define PK_TRACE_FORMAT_MASK (~PK_TRACE_TS_MASK) + +#define PK_GET_TRACE_FORMAT(w32) (PK_TRACE_FORMAT_MASK & w32) +#define PK_GET_TRACE_TIME(w32) (PK_TRACE_TS_MASK & w32) + +//Set the trace timer period to be the maximum +//32 bit time minus 2 seconds (assuming a 32ns tick) +//This allows for up to 1 second of interrupt latency + +//1 second for PK_TRACE_MTBT while only requiring a trace +//every 135 seconds in order to maintain the 64bit timebase. +#define PK_TRACE_TIMER_PERIOD (0xfffffffful - 62500000) + +//The Maximum Time Between Traces. In order to reduce the time that interrupts +//are disabled for tracing, reading of the time stamp is not done atomically +//with alocating an entry in the circular buffer. This means that the +//timestamps might not appear in order in the trace buffer. This is a +//problem because our calculation of the 64 bit timebase uses the unsigned +//difference of the current 32bit timestamp and the previous one and if they +//are out of order it will result in a very large difference. To solve this +//problem, any time that the parser code sees a very large difference (larger +//than PK_TRACE_MTBT) it will treat it as a negative number. +#define PK_TRACE_MTBT (0xfffffffful - 31250000) + +#define PK_TRACE_MAX_PARMS 4 + +//This is the maximum number of bytes allowed to be traced in a binary trace +//entry. +//The trace version needs to change if this changes. +#define PK_TRACE_MAX_BINARY 256 + +//clip the largest binary trace according to the trace buffer size. +//(The trace version does not need to change if this changes as long +// as it remains less than PK_TRACE_MAX_BINARY) +#if (PK_TRACE_SZ <= 256) + #define PK_TRACE_CLIPPED_BINARY_SZ PK_TRACE_SZ / 2 +#else + #define PK_TRACE_CLIPPED_BINARY_SZ PK_TRACE_MAX_BINARY +#endif + +//Trace formats that are supported +typedef enum +{ + PK_TRACE_FORMAT_EMPTY, + PK_TRACE_FORMAT_TINY, + PK_TRACE_FORMAT_BIG, + PK_TRACE_FORMAT_BINARY, +} PkTraceFormat; //pk_trace_format_t; + +//This combines the timestamp and the format bits into a +//single 32 bit word. +typedef union +{ + struct + { + uint32_t timestamp : + PK_TRACE_TS_BITS; + uint32_t format : + PK_TRACE_FORMAT_BITS; + }; + uint32_t word32; +} PkTraceTime; //pk_trace_time_t; + +//PK trace uses a 16 bit string format hash value +typedef uint16_t PkTraceHash; //pk_trace_hash_t; + +//The constant 16 bit hash value is combined with a +//16 bit parameter value when doing a tiny trace +typedef union +{ + struct + { + PkTraceHash string_id; + uint16_t parm; + }; + uint32_t word32; +} PkTraceTinyParms; //pk_trace_tiny_parms_t; + +//A tiny trace fits within a single 8 byte word. This includes +//the timestamp, format bits, hash id, and a 16 bit parameter. +typedef union +{ + struct + { + PkTraceTinyParms parms; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceTiny; //pk_trace_tiny_t; + +//Larger traces that require a 32 bit parameter or more than one +//parameter use the big trace format. The number of parms and +//the 'complete' flag are combined with the hash id. 'complete' +//is set to 0 initially and set to one only after all of the trace +//data has been written. +typedef union +{ + struct + { + PkTraceHash string_id; + uint8_t complete; + uint8_t num_parms; + }; + uint32_t word32; +} PkTraceBigParms; //pk_trace_big_parms_t; + +typedef union +{ + struct + { + PkTraceBigParms parms; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceBig; //pk_trace_big_t; + +//Binary traces are handled in a similar fashion to big traces, except +//that instead of having a number of parameters, we have number of bytes. +typedef union +{ + struct + { + PkTraceHash string_id; + uint8_t complete; + uint8_t num_bytes; + }; + uint32_t word32; +} PkTraceBinaryParms; //pk_trace_binary_parms_t; + +typedef union +{ + struct + { + PkTraceBinaryParms parms; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceBinary; //pk_trace_binary_t; + +//This is a generic structure that can be used to retrieve data +//for tiny, big, and binary formatted entries. +typedef union +{ + struct + { + PkTraceHash string_id; + union + { + uint16_t parm16; + struct + { + uint8_t complete; + uint8_t bytes_or_parms_count; + }; + }; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceGeneric; //pk_trace_generic_t; + +//This is a format that might be used in the future for tracing +//a 64 bit timestamp so that we don't fill up the buffer with periodic +//timer traces. It is not currently used. +#if 0 +typedef union +{ + struct + { + uint32_t upper32; + PkTraceTime time_format; + }; + uint64_t word64; +} PkTraceTime64; //pk_trace_time64_t; +#endif + +//It would probably be more accurate to call this a footer since it +//actually resides at the highest address of each trace entry. These eight +//bytes contain information that allow us to walk the trace buffer from the +//most recent entry to the oldest entry. +typedef union +{ + PkTraceGeneric generic; + PkTraceBinary binary; + PkTraceBig big; + PkTraceTiny small; +} PkTraceEntryFooter; //pk_trace_entry_header_t; + + +//This is the data that is updated (in the buffer header) every time we add +//a new entry to the buffer. +typedef union +{ + struct + { + uint32_t tbu32; + uint32_t offset; + }; + uint64_t word64; +} PkTraceState; //pk_trace_state_t; + +#define PK_TRACE_IMG_STR_SZ 16 + +//Header data for the trace buffer that is used for parsing the data. +//Note: pk_trace_state_t contains a uint64_t which is required to be +//placed on an 8-byte boundary according to the EABI Spec. This also +//causes cb to start on an 8-byte boundary. +typedef struct +{ + //these values are needed by the parser + uint16_t version; + uint16_t rsvd; + char image_str[PK_TRACE_IMG_STR_SZ]; + uint16_t instance_id; + uint16_t partial_trace_hash; + uint16_t hash_prefix; + uint16_t size; + uint32_t max_time_change; + uint32_t hz; + uint32_t pad; + uint64_t time_adj64; + + //updated with each new trace entry + PkTraceState state; + + //circular trace buffer + uint8_t cb[PK_TRACE_SZ]; +} PkTraceBuffer; //pk_trace_buffer_t; + +extern PkTraceBuffer g_pk_trace_buf; + +#endif /* __PK_TRACE_H__ */ diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c new file mode 100644 index 00000000..52ed1bbd --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c @@ -0,0 +1,115 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace_big.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_trace_big.c +/// \brief PK Trace function that supports up to four 32-bit parameters +/// +/// The pk_trace_big function is only called (via some macro magic) if the +/// caller passes in a single parameter (not including the format string) +/// that is larger than 16 bits to the PK_TRACE(...) macro. +/// + +#include "pk.h" +#include "pk_trace.h" + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) +void pk_trace_big(uint32_t i_hash_and_count, + uint64_t i_parm1, uint64_t i_parm2) +{ + PkTraceBig footer; + PkTraceBig* footer_ptr; + PkTraceState state; + uint64_t* ptr64; + uint64_t tb64; + PkMachineContext ctx; + uint32_t parm_size; + uint32_t cur_offset; + uint32_t footer_offset; + + //fill in the footer data + tb64 = pk_timebase_get(); + footer.parms.word32 = i_hash_and_count; //this has the parm count and hash + state.tbu32 = tb64 >> 32; + footer.time_format.word32 = tb64 & 0x00000000ffffffffull; + footer.time_format.format = PK_TRACE_FORMAT_BIG; + + //round up to 8 byte boundary + if(footer.parms.num_parms <= 2) + { + parm_size = 8; + } + else + { + parm_size = 16; + } + + //*****The following operations must be done atomically***** + pk_critical_section_enter(&ctx); + + //load in the offset in the cb for the entry we are adding + cur_offset = g_pk_trace_buf.state.offset; + + //Find the offset for the footer (at the end of the entry) + footer_offset = cur_offset + parm_size; + + //calculate the address of the footer + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[footer_offset & PK_TRACE_CB_MASK]; + + //calculate the offset for the next entry in the cb + state.offset = footer_offset + sizeof(PkTraceBig); + + //update the cb state (tbu and offset) + g_pk_trace_buf.state.word64 = state.word64; + + //write the data to the circular buffer including the + //timesamp, string hash, and 16bit parameter + *ptr64 = footer.word64; + + //*******************exit the critical section*************** + pk_critical_section_exit(&ctx); + + + //write parm values to the circular buffer + footer_ptr = (PkTraceBig*)ptr64; + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[cur_offset & PK_TRACE_CB_MASK]; + *ptr64 = i_parm1; + + if(parm_size > 8) + { + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[(cur_offset + 8) & PK_TRACE_CB_MASK]; + *ptr64 = i_parm2; + } + + //Mark the trace entry update as being completed + footer_ptr->parms.complete = 1; + +} + +#endif diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c new file mode 100644 index 00000000..61218a5b --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c @@ -0,0 +1,113 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace_binary.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_trace_binary.c +/// \brief PK Trace function for dumping memory contents +/// +/// The pk_trace_binary function is called by the PK_TRACE_BINARY() macro. +/// + + +#include "pk.h" +#include "pk_trace.h" + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) +void pk_trace_binary(uint32_t i_hash_and_size, void* bufp) +{ + PkTraceBinary footer; + PkTraceBinary* footer_ptr; + PkTraceState state; + uint64_t* ptr64; + uint64_t tb64; + PkMachineContext ctx; + uint32_t data_size; + uint32_t cb_offset; + uint32_t footer_offset; + uint8_t* dest; + uint8_t* src; + uint32_t index; + + //fill in the footer data + tb64 = pk_timebase_get(); + footer.parms.word32 = i_hash_and_size; //this has the size and hash + state.tbu32 = tb64 >> 32; + footer.time_format.word32 = tb64 & 0x00000000ffffffffull; + footer.time_format.format = PK_TRACE_FORMAT_BINARY; + + //round up to 8 byte boundary + data_size = (footer.parms.num_bytes + 7) & ~0x00000007ul; + + //limit data size + if(data_size > PK_TRACE_CLIPPED_BINARY_SZ) + { + data_size = PK_TRACE_CLIPPED_BINARY_SZ; + } + + //*****The following operations must be done atomically***** + pk_critical_section_enter(&ctx); + + //load in the offset in the cb for the entry we are adding + cb_offset = g_pk_trace_buf.state.offset; + + //Find the offset for the footer (at the end of the entry) + footer_offset = cb_offset + data_size; + + //calculate the address of the footer + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[footer_offset & PK_TRACE_CB_MASK]; + + //calculate the offset for the next entry in the cb + state.offset = footer_offset + sizeof(PkTraceBinary); + + //update the cb state (tbu and offset) + g_pk_trace_buf.state.word64 = state.word64; + + //write the footer data to the circular buffer including the + //timesamp, string hash and data size + *ptr64 = footer.word64; + + //*******************exit the critical section*************** + pk_critical_section_exit(&ctx); + + //write data to the circular buffer + for(src = bufp, index = 0; + index < data_size; + index++) + { + dest = &g_pk_trace_buf.cb[(cb_offset + index) & PK_TRACE_CB_MASK]; + *dest = *(src++); + } + + //Mark the trace entry update as being completed + footer_ptr = (PkTraceBinary*)ptr64; + footer_ptr->parms.complete = 1; + +} + +#endif diff --git a/import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c new file mode 100644 index 00000000..7ccc066a --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c @@ -0,0 +1,144 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/pk/trace/pk_trace_core.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file pk_trace_core.c +/// \brief PK Trace core data and code. +/// +/// This file includes the minimal code/data required to do minimal tracing. +/// This includes the periodic timer initialization and the pk_trace_tiny +/// function. The pk_trace_tiny function is called by the PK_TRACE() macro +/// when there is one or less parameters (not including the format string) +/// and the parameter size is 16 bits or smaller. +/// + +#include "pk.h" +#include "pk_trace.h" + +void pk_trace_timer_callback(void* arg); + +#if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) + +//Static initialization of the trace timer +PkTimer g_pk_trace_timer = +{ + .deque = PK_DEQUE_ELEMENT_INIT(), + .timeout = 0, + .callback = pk_trace_timer_callback, + .arg = 0, +}; + +//Static initialization of the pk trace buffer +PkTraceBuffer g_pk_trace_buf = +{ + .version = PK_TRACE_VERSION, + .image_str = PPE_IMG_STRING, + .hash_prefix = PK_TRACE_HASH_PREFIX, + .partial_trace_hash = trace_ppe_hash("PARTIAL TRACE ENTRY. HASH_ID = %d", PK_TRACE_HASH_PREFIX), + .size = PK_TRACE_SZ, + .max_time_change = PK_TRACE_MTBT, + .hz = 500000000, //default value. Actual value is set in pk_init.c + .time_adj64 = 0, + .state.word64 = 0, + .cb = {0} +}; + +//Needed for buffer extraction in simics for now +PkTraceBuffer* g_pk_trace_buf_ptr = &g_pk_trace_buf; + +// Creates an 8 byte entry in the trace buffer that includes a timestamp, +// a format string hash value and a 16 bit parameter. +// +// i_parm has the hash value combined with the 16 bit parameter +void pk_trace_tiny(uint32_t i_parm) +{ + PkTraceTiny footer; + PkTraceState state; + uint64_t* ptr64; + uint64_t tb64; + PkMachineContext ctx; + + //fill in the footer data + footer.parms.word32 = i_parm; + tb64 = pk_timebase_get(); + state.tbu32 = tb64 >> 32; + footer.time_format.word32 = tb64 & 0x00000000ffffffffull; + + footer.time_format.format = PK_TRACE_FORMAT_TINY; + + //The following operations must be done atomically + pk_critical_section_enter(&ctx); + + //load the current byte count and calculate the address for this + //entry in the cb + ptr64 = (uint64_t*)&g_pk_trace_buf.cb[g_pk_trace_buf.state.offset & PK_TRACE_CB_MASK]; + + //calculate the offset for the next entry in the cb + state.offset = g_pk_trace_buf.state.offset + sizeof(PkTraceTiny); + + //update the cb state (tbu and offset) + g_pk_trace_buf.state.word64 = state.word64; + + //write the data to the circular buffer including the + //timesamp, string hash, and 16bit parameter + *ptr64 = footer.word64; + + //exit the critical section + pk_critical_section_exit(&ctx); +} + + +// This function is called periodically in order to ensure that the max ticks +// between trace entries is no more than what will fit inside a 32bit value. +#ifndef PK_TRACE_TIMER_OUTPUT + #define PK_TRACE_TIMER_OUTPUT 1 +#endif +void pk_trace_timer_callback(void* arg) +{ +#if PK_TRACE_TIMER_OUTPUT + // guarantee at least one trace before the lower 32bit timebase flips + PK_TRACE("PERIODIC TIMESTAMPING TRACE"); +#endif + // restart the timer + pk_timer_schedule(&g_pk_trace_timer, + PK_TRACE_TIMER_PERIOD); +} + +// Use this function to synchronize the timebase between multiple PPEs. +// PPE A can send PPE B it's current timebase and then PPE B can set that +// as the current timebase for tracing purposes. It can also be used +// to set the current time to 0. This function changes the timebase for +// all entries that are currently in the trace buffer. Setting the current +// timebase to 0 will cause previous traces to have very large timestamps. +void pk_trace_set_timebase(PkTimebase timebase) +{ + g_pk_trace_buf.time_adj64 = timebase - pk_timebase_get(); +} + +#endif diff --git a/import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk b/import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk new file mode 100644 index 00000000..977531de --- /dev/null +++ b/import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk @@ -0,0 +1,60 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/pk/trace/pktracefiles.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file pkppe42files.mk +# +# @brief mk for including ppe42 object files +# +# @page ChangeLogs Change Logs +# @section pkppe42files.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# Include Files +########################################################################## + + + +########################################################################## +# Object Files +########################################################################## +PKTRACE-C-SOURCES = pk_trace_core.c pk_trace_big.c pk_trace_binary.c + +PKTRACE-S-SOURCES = + +PKTRACE-TIMER-C-SOURCES = +PKTRACE-TIMER-S-SOURCES = + +PKTRACE-THREAD-C-SOURCES += +PKTRACE-THREAD-S-SOURCES += + + +PKTRACE_OBJECTS += $(PKTRACE-C-SOURCES:.c=.o) $(PKTRACE-S-SOURCES:.S=.o) diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/Makefile b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/Makefile new file mode 100644 index 00000000..0ac02601 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/Makefile @@ -0,0 +1,46 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +export SUB_OBJDIR = /p2p + +include img_defs.mk +include p2pfiles.mk + +OBJS := $(addprefix $(OBJDIR)/, $(P2P_OBJECTS)) + +libp2p.a: $(OBJS) + $(AR) crs $(OBJDIR)/libp2p.a $(OBJDIR)/*.o + +.PHONY: clean p2p +p2p: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +include $(OBJS:.o=.d) +endif diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2p-test-gen.py b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2p-test-gen.py new file mode 100755 index 00000000..1e9721d5 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2p-test-gen.py @@ -0,0 +1,168 @@ +#!/usr/bin/python2.6 +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2p-test-gen.py $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +# \file p2p-test-gen.py +# \brief this program generates random constructed test cases +# in the form of input file consumed by ppc-ppe-pcp.py +# \usage create a file named 'test.s' and make sure it has at +# least one blank line before executing this program. + +import fileinput +import random + +DotLabel = ['', 'Label:', '.Label'] + +Comments = ['', '// Comments', '/* Comments */'] + +TabSpace = ['', '\t', ' ', '\t ', ' \t', ' \t '] + +RegLabel = ['', '%r'] + +Register = [0,1,2,3,4,5,6,7,8,9,10,13,28,29,30,31] + +TestEnable = [0,1,2,3] + +TestBook = {'eieio' : 0, + 'isync' : 0, + 'icbi' : 0, + 'icbt' : 0, + 'stbux' : 3, + 'sthux' : 3, + 'stwux' : 3, + 'lbzux' : 3, + 'lhzux' : 3, + 'lwzux' : 3, + 'lha' : 2, + 'lhau' : 2, + 'lhax' : 3, + 'lhaux' : 3, + 'mulhhw' : 3, + 'mulhhwu' : 3, + 'mulhw' : 3, + 'mulhwu' : 3, + 'mullw' : 3, + 'mulli' : 1, + 'divw' : 3, + 'divwu' : 3, + 'lmw' : 2, + 'stmw' : 2, + 'lwz' : 4, + 'stw' : 4, + 'cmplw' : 5, + 'cmpw' : 5, + 'cmpwi' : 5} + +BranchList = ['bc', 'bcl', 'blt', 'bltl', 'ble', 'blel', 'bgt', 'bgtl', 'bge', + 'bgel', 'beq', 'beql', 'bne', 'bnel'] + +def p2p_test(): + for line in fileinput.input('test.s', inplace=1): + print '// start generating test cases:', + for opcode,format in TestBook.iteritems(): + opcode += ' ' + if random.randint(1, 10) > 5: + print random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] +\ + random.sample(TabSpace,1)[0] + else: + print random.sample(TabSpace,1)[0] + random.sample(DotLabel,1)[0] +\ + random.sample(TabSpace,1)[0] + if format == 0 in TestEnable: + print random.sample(TabSpace,1)[0] + opcode +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + if format == 3 in TestEnable: + regs = random.sample(Register, 3) + reg_field = random.sample(RegLabel,1)[0] + str(regs[0]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + random.sample(RegLabel,1)[0] + str(regs[1]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + random.sample(RegLabel,1)[0] + str(regs[2]) + print random.sample(TabSpace,1)[0] + opcode +\ + random.sample(TabSpace,1)[0] + reg_field +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + if format == 1 in TestEnable: + regs = random.sample(Register, 2) + reg_field = random.sample(RegLabel,1)[0] + str(regs[0]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + random.sample(RegLabel,1)[0] + str(regs[1]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + str(random.randint(-128, 128)) + print random.sample(TabSpace,1)[0] + opcode +\ + random.sample(TabSpace,1)[0] + reg_field +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + if format == 2 in TestEnable: + regs = random.sample(Register, 2) + reg_field = random.sample(RegLabel,1)[0] + str(regs[0]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + str(random.randint(-128, 128)) +\ + '(' + random.sample(RegLabel,1)[0] + str(regs[1]) + ')' + print random.sample(TabSpace,1)[0] + opcode +\ + random.sample(TabSpace,1)[0] + reg_field +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + if format == 4 in TestEnable: + for i in [1,2]: + regs = random.sample(Register, 2) + reg_field = random.sample(RegLabel,1)[0] + str(regs[0]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + str(random.randint(-128, 128)) +\ + '(' + random.sample(RegLabel,1)[0] + str(regs[1]) + ')' + print random.sample(TabSpace,1)[0] + opcode +\ + random.sample(TabSpace,1)[0] + reg_field +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + if format == 5 in TestEnable: + if 'i' in opcode: + regs = random.sample(Register, 1) + reg_field = random.sample(RegLabel,1)[0] + str(regs[0]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + random.sample(RegLabel,1)[0] +\ + str(random.randint(-128, 128)) + else: + regs = random.sample(Register, 2) + reg_field = random.sample(RegLabel,1)[0] + str(regs[0]) + ',' +\ + random.sample(TabSpace,1)[0] +\ + random.sample(RegLabel,1)[0] + str(regs[1]) + print random.sample(TabSpace,1)[0] + opcode +\ + random.sample(TabSpace,1)[0] + reg_field +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + branch = random.sample(BranchList, 1)[0] + ' ' + if 'bc' in branch: + reg_field = random.sample(TabSpace,1)[0] +\ + str(random.randint(0, 15)) + ',' +\ + random.sample(TabSpace,1)[0] +\ + str(random.randint(0, 7)) + ',' +\ + random.sample(TabSpace,1)[0] +\ + str(random.randint(-128, 128)) +\ + random.sample(TabSpace,1)[0] + else: + reg_field = random.sample(TabSpace,1)[0] +\ + str(random.randint(-128, 128)) +\ + random.sample(TabSpace,1)[0] + print random.sample(TabSpace,1)[0] + branch +\ + random.sample(TabSpace,1)[0] + reg_field +\ + random.sample(TabSpace,1)[0] + random.sample(Comments,1)[0] + fileinput.close() + +if __name__ == '__main__': + p2p_test() diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2pfiles.mk b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2pfiles.mk new file mode 100644 index 00000000..7adaa87f --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2pfiles.mk @@ -0,0 +1,36 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/p2pfiles.mk $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file p2pfiles.mk +# +# @brief mk for including P2P support library object files +# + +########################################################################## +# Object Files +########################################################################## +P2P-S-SOURCES = ppe42_mulhw.S ppe42_mulhwu.S ppe42_mullw.S \ + ppe42_divw.S ppe42_divwu.S + +P2P_OBJECTS = $(P2P-S-SOURCES:.S=.o) diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppc-ppe-pcp.py b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppc-ppe-pcp.py new file mode 100755 index 00000000..35d8da20 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppc-ppe-pcp.py @@ -0,0 +1,1006 @@ +#!/usr/bin/python2.6 +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppc-ppe-pcp.py $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +# \file ppc-ppe-pcp.py +# \brief PPC405 Assembly to PPE42 Assembly Post-Compiler Processor (P2P) +# +# --------------------------------------------------------------- +# Revision History +# --------------------------------------------------------------- +# 10-07-2014: project completed +# daviddu added optimization profile support +# +# 10-06-2014: added fused compare and branch supprot +# daviddu added support for combining two ld/st into one double word +# added support to insert branch upon .p2align directive +# +# 09-27-2014: added subroutine support for mul* and div* +# daviddu added virtual double word replacing multiple word support +# +# 09-13-2014: initial version +# daviddu only instruction inline replacement is supported +# --------------------------------------------------------------- + +P2P_VERSION = "10-07-2014" # version number as last modified date +P2P_PPC_EXT = '.s' # PPC Assembly filename extension +P2P_PPE_EXT = '.es' # PPE Assembly filename extension +P2P_PPE_PRE = '__ppe42_' # PPE Assembly subroutine prefix + +import sys +import os +import re +import fnmatch +import fileinput + +# --------------------------------------------------------------- +# PPC405 Assembly to PPE42 Assembly Post-Compiler Processor (P2P) +# --------------------------------------------------------------- +# +# Description: +# +# This post-compiler processor will take PPC405 assembly file(s) produced +# by powerpc-linux-gcc or hand coded and replace some of the instructions +# supported by PPC405 ISA but not PPE42 ISA with a set of instructions +# supported by PPE42 ISA. Outcome of this program is PPE42 assembly file(s). +# +# Assumptions: +# +# - Input/Output File Name Extension: +# +# PPC405 assembly file generated by powerpc-linux-gcc has filename extension +# defined by "P2P_PPC_EXT" global variable, while PPE42 assembly file +# consumed by PPE Assembler has filename extension defined by "P2P_PPE_EXT" +# global variable. Both should be consistant with Makefile rules. +# +# - Registers: +# +# Instructions in input file should only use registers supported by PPE, +# that is R0-R10,R13,R28-R31 for GPRs and PPE only SPRs(for example, PPE +# only has CR0 instead of CR0-7). +# +# GCC flag -ffixed can be used to enforce compiler to not use certain +# registers if compiler generates input files to this script. Note certian +# optimization level, such as -Os, of GGC will still use certain registers +# regardless if -ffixed flag is used. Furthermore, compiler should not +# generate multiple word instructions(lmw/stmw) that covers the registers +# forbidden to use by -ffixed flag. +# +# Example of using -ffixed flag in this case: +# -ffixed-r11 -ffixed-r12 -ffixed-r14 -ffixed-r15 \ +# -ffixed-r16 -ffixed-r17 -ffixed-r18 -ffixed-r19 \ +# -ffixed-r20 -ffixed-r21 -ffixed-r22 -ffixed-r23 \ +# -ffixed-r24 -ffixed-r25 -ffixed-r26 -ffixed-r27 \ +# -ffixed-cr1 -ffixed-cr2 -ffixed-cr3 -ffixed-cr4 \ +# -ffixed-cr5 -ffixed-cr6 -ffixed-cr7 +# +# - Instructions: +# +# Instructions in input file should only use PowerPC 405 instructions +# covered by "PowerPC 405-S Embedded Processor Core" manual; however, +# there is an assumption on certain catalog of instructions will never be +# generated by power-linux-gcc compiler(or disabled by compiler switch). +# +# Also, compiler should generate extended mnemonics instead of its base +# instruction when extended mnemonics fits. +# +# Via -falign-labels=n and -O2 or -O3, the compiler inserts .p2align +# directive to help instruction alignment for best cache performance. +# +# - Assembly Syntax: +# +# There should be only white spaces before instruction mnemonics, in +# another word, all inline comments should be put behind the instrution. +# +# "Label:" and an instruction should not be on the same line, hand coded +# assembly should be consistant to this same compiler output format. +# +# Depandences: +# +# In order to utilize assembly subroutines implemented for supporting +# missing instructions of multiplication and division in PPE42 ISA, a given +# library(with assembly files and header) must be compiled and linked with +# any source code that use this program to generate PPE binary. +# +# Usage: +# +# ./<ThisScript> -f <a filename with path> --- process single file +# ./<ThisScript> -d <a directory path> --- process multiple files +# ./<ThisScript> -h --- detailed usage on other flags +# ./<ThisScript> -v --- version of the program +# ./<ThisScript> -d <a directory path> -s --- perform result profiling +# +# Functions: +# +# p2p_main - main function, parse options and arguments +# p2p_onefile - processing single PPC Assembly File +# p2p_combine - processing two PPC instructions in input file +# p2p_replace - processing single PPC instruction in input file +# +# Data Structures: +# +# ReplaceRules = { ppc_op : [rule, ppe_op] } +# CombineRules = { ppc_op : [rule, ppe_op] } +# FuseBranches = [ list of branches qualified for fusing with compares ] +# +#------------------------------------------------------------------------------ +# ReplaceRules: [ 'r', 'u', 'a', 'h', 's', 'o', 'd', 'm' ] +#-------|-------------------------|-------------------------------------------- +# Rule | Example (PPC to PPE) | Description +#-------|-------------------------|-------------------------------------------- +# 'r' | [ppc] ppc_op RT, RA, RB | simply 'replace' ppc opcode with ppe opcode +# 0 0 | [ppe] ppe_op RT, RA, RB | while the operands, if any, stay the same +#-------|-------------------------|-------------------------------------------- +# 'ru' | [ppc] ppc_op RT, RA, RB | on top of 'r' rule, emulate 'update' by +# | [ppe] ppe_op RT, RA, RB | appending "add" instruction after the +# +1 +1 | add RA, RA, RB | original instruction to update RA +#-------|-------------------------|-------------------------------------------- +# 'ra' | [ppc] ppc_op RT, D(RA) | on top of 'r' rule, emulate 'algebraic' by +# | [ppe] ppe_op RT, D(RA) | appending "extsh" instruction after the +# +1 +1 | extsh RT, RT | original instruction to sign-extend RT +#-------|-------------------------|-------------------------------------------- +# 'rau' | [ppc] ppc_op RT, RA, RB | combining rule 'r', 'a', and 'u' above. +# | [ppe] ppe_op RT, RA, RB | Note: there is no requirement on whether +# | add RA, RA, RB | rule 'a' or rule 'u' should be applied +# +2 +2 | extsh RT, RT | first, the outcome should be the same. +#-------|-------------------------|-------------------------------------------- +# 'h' | [ppc] ppc_op RT, RA, RB | rule of 'halfword' multiplication +# | [ppe] srwi RA, 16 | emulate multiply "high halfword" with +# | srwi RB, 16 | multiply "low halfword" by shifting +# +2 +2 | ppe_op RT, RA, RB | the operands first +#-------|-------------------------|-------------------------------------------- +# 's' | [ppc] ppc_op RT, RA, RB | emulate word multiply and divide +# | [ppe] stwu R1, -24(R1)| instructions with calling 'subroutines' +# | stvd D3, 8(R1) | implemented in ppe42_mul.S and ppe42_div.S +# | mflr R3 | +# | stw R3, 16(R1) | Calling Conventions:(SS = Stack Size) +# | mr R3, RA | +# | mr R4, RB | Caller is responsible for +# | bl target | 1) create stack frame +# | mr RT, R3 | 2) save off R3 and R4 to stack +# | lwz R3, 16(R1) | 3) save off link register to stack +# | mtlr R3 | 4) put operands into R3, R4 before branch +# | lvd D3, 8(R1) | 5) put result in R3 to RT after branch +# | lwz R1, 0(R1) | 6) restore link register from stack +# | | 7) restore R3 and R4 from stack +# | [sub] stwu R1, -SS(R1) | 8) remove the stack frame +# | <save volatile> | +# | (subroutine body) | Callee is responsible for +# | <restore volatile>| 1) create and remove stack frame +# | lwz R1, 0(R1) | 2) save and restore volatile registers +# +X +Y | blr | 3) subroutine will not touch LR again +#-------|-------------------------|-------------------------------------------- +# 'o' | [ppc] ppc_op[o] RT ... | rule of 'o' form for overflow +# | [ppe] ppe_op RT ... | Note: "mullwo", "divwo" and "divwuo" each +# | <inst specific> | has unique setting for XER[OV,SO] if OE = 1 +#-------|-------------------------|-------------------------------------------- +# 'd' | [ppc] ppc_op[.] RT ... | rule of '.' or 'dot' form for recording +# | [ppe] ppe_op RT ... | using "cmpwli" to emulate the [.] form +# | cmpwli RT, 0 | to the instruction result and CR0 fields +#-------|-------------------------|-------------------------------------------- +# 'm' | [ppc] ppc_op RT, D(RA) | emulate PowerPC load/store multiple word +# | [ppe] ppe_op DT, D(RA) | instructions with PPE specific +# | (doubleword ld/st)| 'virtual doubleword' instructions if target +# | or | address is 8-byte aligned; otherwise, using +# | (singleword ld/st)| single word instructions instead or mix both +# | or | Note only RA == R1/R2/R13 will always meet +# -1 -1 | (single & double) | alignment requirement of virtual doubleword +#-------|-------------------------|-------------------------------------------- +# +ReplaceRules = {#ppc_op : [ rule | ppe_op ] + #---------------------------- + #synchronization instructions + 'eieio' : [ 'r', 'sync' ], + 'isync' : [ 'r', 'nop' ], + 'icbi' : [ 'r', 'nop' ], + 'icbt' : [ 'r', 'nop' ], + 'mtcr' : [ 'r', 'mtcr0'], + #load/store with [u/x/a] form + 'stbux' : [ 'ru', 'stbx' ], + 'sthux' : [ 'ru', 'sthx' ], + 'stwux' : [ 'ru', 'stwx' ], + 'lbzux' : [ 'ru', 'lbzx' ], + 'lhzux' : [ 'ru', 'lhzx' ], + 'lwzux' : [ 'ru', 'lwzx' ], + 'lha' : [ 'ra', 'lhz' ], + 'lhau' : [ 'ra', 'lhzu' ], + 'lhax' : [ 'ra', 'lhzx' ], + 'lhaux' : [ 'rau', 'lhzx' ], + #multiply/divide with [./o] form + 'mulhhw' : [ 'h', 'mullhw' ], + 'mulhhw.' : [ 'h', 'mullhw.' ], + 'mulhhwu' : [ 'h', 'mullhwu' ], + 'mulhhwu.': [ 'h', 'mullhwu.' ], + 'mulhw' : [ 's', 'mulhw' ], + 'mulhw.' : [ 'sd', 'mulhw' ], + 'mulhwu' : [ 's', 'mulhwu' ], + 'mulhwu.' : [ 'sd', 'mulhwu' ], + 'mullw' : [ 's', 'mullw' ], + 'mullw.' : [ 'sd', 'mullw' ], + 'mullwo' : [ 'so', 'mullw' ], + 'mullwo.' : [ 'sod', 'mullw' ], + 'mulli' : [ 's', 'mullw' ], + 'divw' : [ 's', 'divw' ], + 'divw.' : [ 'sd', 'divw' ], + 'divwo' : [ 'so', 'divw' ], + 'divwo.' : [ 'sod', 'divw' ], + 'divwu' : [ 's', 'divwu' ], + 'divwu.' : [ 'sd', 'divwu' ], + 'divwuo' : [ 'so', 'divwu' ], + 'divwuo.' : [ 'sod', 'divwu' ], + #load/store multiple word(Rx-R31) + 'lmw' : [ 'm', 'lvd,lwz' ], + 'stmw' : [ 'm', 'stvd,stw' ]} + + +#------------------------------------------------------------------------------ +# CombineRules: [ 'f', 'v', 'l' ] +#-------|-------------------------|-------------------------------------------- +# 'f' | [ppc] ppc_op(cmp*) | rule for 'fusing' adjacent pair of compare +# | ppc_op(b*) | and branch(PPE specific). Note: only +# -1 0 | [ppe] ppe_op(cmp*b*) | extended mnemonics of compares are handled +#-------|-------------------------|-------------------------------------------- +# 'v' | [ppc] ppc_op(lwz/stw) | rule for combining double word aligned +# | ppc_op(lwz/stw) | load/store pairs into signle 'virtual' +# -1 -1 | [ppe] ppe_op(lvd/stvd) | double word instructions(PPE specific) +#-------|-------------------------|-------------------------------------------- +# 'l' | [ppc] .p2align | compiler will insert ".p2align" directive to +# | Label: | help instructions align from label to label. +# | [ppe] b Label | then assembler will insert "nop" on .p2align +# | .p2align | directive. a "branch" to skip the nops will +# 0 -1 | Label: | improve the performance while still aligned +#-------|-------------------------|-------------------------------------------- +# +CombineRules = {#ppc_op : [ rule | ppe_cp ] + #-------------------------- + #8byte aligned loads/stores + 'lwz' : [ 'v', 'lvd' ], + 'stw' : [ 'v', 'stvd' ], + #compares fusable to branch + 'cmplw' : [ 'f', 'cmplw' ], + 'cmpw' : [ 'f', 'cmpw' ], + 'cmpwi' : [ 'f', 'cmpwi' ], + #'.p2align' before 'label:' + '.p2align' : [ 'l', 'b' ]} + + +#------------------------------------------------------------------------------ +# FuseBranches: [ Branches can be fused into cmp*b* ] +#------------------------------------------------------------------------------ +# +FuseBranches = ['bc', 'bcl', + 'blt', 'bltl', 'ble', 'blel', + 'bgt', 'bgtl', 'bge', 'bgel', + 'beq', 'beql', 'bne', 'bnel'] + + +# ----------------------------------------------------------------------------- +# p2p_replace: +# process each line(filtered) in the assembly file to replace PPC instruction +# to supported PPE instruction(s) +# +# Arguments: +# string: line - assembly file line to be replaced +# ppc_op - detected PPC opcode that needs to be replaced +# Return: +# boolean: True - Return without Error +# False - Error Detected +# Variables: +# string: inst, rule, ppe_op, newline, temp_op +# double_inst, single_inst, virtual_reg, base_offset, address_reg +# Subroutine: +# NONE +# ----------------------------------------------------------------------------- +def p2p_replace(line, ppc_op): + + # parse PPC instruction as in I or D form with opcode and upto 3 operands: + # possible forms: opcode + # opcode RT, RA, RB + # opcode RT, RA, IM + # opcode RT, D(RA) + # inst.group(0) : <whole instruction> + # inst.group(1) : " " + # inst.group(2) : Opcode(.) + # inst.group(3) : " " + # inst.group(4) : GPR + # inst.group(5) : " , " + # inst.group(6) : GPR or Immediate(D) + # inst.group(7) : " , " or " ( " + # inst.group(8) : GPR or Immediate(IM) + # inst.group(9) : " ) " + inst = re.search(r"([\s]*)([a-zA-Z\.]+)([\s]*)([%r0-9]*)([\s,]*)([%r0-9\-]*)([\s,\(]*)([%r0-9\-]*)([\s\)]*)", line) + + # detect an error + if inst is None or ppc_op != inst.group(2): + return False + + # look up rule to process the instruction + rule, ppe_op = ReplaceRules[ppc_op] + + # if enabled, put a mark in the output file + if P2P_COMMENT: print "#P2P(%s):" % rule + line, + + # start cases of replacing PPC instruction with PPE instruction(s) + #---r------------------------------------------------------------------------ + if 'r' in rule: + + # replace opcode under rule 'r' and rewrite the instruction + newline = line.replace(ppc_op, ppe_op) + print newline, + + # do not continue if there is 'a' or 'u' rule to process on this line + if 'u' not in rule and 'a' not in rule: + return True + + #---u------------------------------------------------------------------------ + if 'u' in rule: + + # construct and write "add RA, RA, RB" under rule 'u' + newline = inst.group(1) + 'add' + inst.group(3) + inst.group(6) +\ + inst.group(5) + inst.group(6) + inst.group(7) + inst.group(8) + print newline + + # do not continue if there is 'a' rule to process on this line + if 'a' not in rule: + return True + + #---a------------------------------------------------------------------------ + if 'a' in rule: + + # construct and write "extsh RT, RT" under rule 'a' + newline = inst.group(1) + 'extsh' + inst.group(3) + inst.group(4) +\ + inst.group(5) + inst.group(4) + print newline + return True + + #---h------------------------------------------------------------------------ + if 'h' in rule: + + # construct and write "srwi RA, 16" under rule 'h' + newline = inst.group(1) + 'srwi' + inst.group(3) + inst.group(6) +\ + inst.group(5) + "16" + print newline + + # construct and write "srwi RB, 16" under rule 'h' + newline = inst.group(1) + 'srwi' + inst.group(3) + inst.group(8) +\ + inst.group(5) + "16" + print newline + + # replace opcode in original instruction and write under rule 'h' + newline = line.replace(ppc_op, ppe_op) + print newline + return True + + #---s------------------------------------------------------------------------ + if 's' in rule: + + # construct branch target label + ppe_op = P2P_PPE_PRE + ppe_op + + # construct and write "stwu R1, -24(R1)" to create the stack frame + newline = inst.group(1) + 'stwu' + inst.group(3) + '1' +\ + inst.group(5) + '-24(1)' + print newline + + # construct and write "stvd D3, 8(R1)" to save off R3 and R4 + newline = inst.group(1) + 'stvd' + inst.group(3) + '3' +\ + inst.group(5) + '8(1)' + print newline + + # construct and write "mflr R3" to fetch the current link address + newline = inst.group(1) + 'mflr' + inst.group(3) + '3' + print newline + + # construct and write "stw R3, 16(R1)" to save off current LR to stack + newline = inst.group(1) + 'stw' + inst.group(3) + '3' +\ + inst.group(5) + '16(1)' + print newline + + # construct and write "mr R3, RA" to copy the operand RA to R3 + newline = inst.group(1) + 'mr' + inst.group(3) + '3' +\ + inst.group(5) + inst.group(6) + print newline + + # if 'mulli' is detected, using 'li' instead of 'mr' for second operand + if ppc_op == 'mulli': + temp_op = 'li' + else: + temp_op = 'mr' + + # construct and write "mr R4, RB" to copy the operand RB to R4 + # or in 'mulli' case, "li R4, IM" to copy the operand IM to R4 + newline = inst.group(1) + temp_op + inst.group(3) + '4' +\ + inst.group(5) + inst.group(8) + print newline + + # using branch and link(bl) to branch to subroutine + # later subroutine can branch back using branch link register(blr) + # Assumption: the subroutine will be responsible for saving + # and restoring all the volatilo registers used in the subroutine + newline = inst.group(1) + 'bl' + inst.group(3) + ppe_op + print newline + + # construct and write "mr RT, R3" to copy the result in R3 to RT + newline = inst.group(1) + 'mr' + inst.group(3) + inst.group(4) +\ + inst.group(5) + '3' + print newline + + # construct and write "lwz R3, 16(R1)" to fetch the LR value from stack + newline = inst.group(1) + 'lwz' + inst.group(3) + '3' +\ + inst.group(5) + '16(1)' + print newline + + # construct and write "mtlr R3" to restore the link register + newline = inst.group(1) + 'mtlr' + inst.group(3) + '3' + print newline + + # construct and write "lvd D3, 8(R1)" to restore R3 and R4 + newline = inst.group(1) + 'lvd' + inst.group(3) + '3' +\ + inst.group(5) + '8(1)' + print newline + + # construct and write "lwz R1, 0(R1)" to destroy the stack frame + newline = inst.group(1) + 'lwz' + inst.group(3) + '1' +\ + inst.group(5) + '0(1)' + print newline + return True + + #---m------------------------------------------------------------------------ + if 'm' in rule: + + # parse instruction information + # note register can be in either "N" form or "%rN" form + double_inst,single_inst = ppe_op.split(',') + virtual_reg = int(re.search(r'\d+', inst.group(4)).group()) + base_offset = int(inst.group(6)) + address_reg = int(re.search(r'\d+', inst.group(8)).group()) + + # consider illegal if multiple word instruction covers non-exist registers + if virtual_reg < 28: + return False + + # loop until and include GPR31 + while virtual_reg < 32: + # page 270 of 405 manual, only do this for load instructions + if virtual_reg == address_reg != 31 and 'l' in single_inst: + base_offset += 4 + virtual_reg += 1 + continue + + # if other GPRs being address_reg there is no guarantee for alignment + if address_reg not in [1,2,13]: + # construct and write "lwz/stw RT, D(RA)" for every registers + newline = inst.group(1) + single_inst + inst.group(3) +\ + str(virtual_reg) + inst.group(5) + str(base_offset) +\ + inst.group(7) + inst.group(8) + inst.group(9) + print newline + base_offset += 4 + virtual_reg += 1 + else: + # if base_offset is also aligned with base address in the address_reg + # & there are at least two more registers to perform doubleword ld/st + if not (base_offset % 8) and (virtual_reg + 1) < 32: + # construct and write "lvd/stvd DR, D(RA)" under rule 'v' + newline = inst.group(1) + double_inst + inst.group(3) +\ + str(virtual_reg) + inst.group(5) + str(base_offset) +\ + inst.group(7) + inst.group(8) + inst.group(9) + print newline + base_offset += 8 + virtual_reg += 2 + # either only one register left or base_offset isnt aligned + else: + # construct and write "lwz/stwz SR, D(RA)" under rule 'v' + newline = inst.group(1) + single_inst + inst.group(3) +\ + str(virtual_reg) + inst.group(5) + str(base_offset) +\ + inst.group(7) + inst.group(8) + inst.group(9) + print newline + base_offset += 4 + virtual_reg += 1 + # end of this if-else + # end of while loop + return True + # end of last if + + +# ----------------------------------------------------------------------------- +# p2p_combine: +# process each two lines(filtered) in the assembly file to combine two PPC +# instructions to one PPE specific instruction for better performance +# +# Arguments: +# string: first_line - 1st assembly file line to be combined +# second_line - 2nd assembly file line to be combined +# first_op - 1st detected PPC opcode that needs to be combined +# second_op - 2nd detected PPC opcode that needs to be combined +# Return: +# boolean: done - True: return without error +# - False: return with error detected +# match - True: eventually matched and combined +# - False: fail to qualify to be combined +# Variables: +# string: first_inst, second_inst, rule, ppe_op, newline +# bo, px_bix, compare_operands, target +# Subroutine: +# NONE +# ----------------------------------------------------------------------------- +def p2p_combine(first_line, second_line, first_op, second_op): + + global P2P_SPACE; global P2P_CYCLE + global P2P_COMPARE_BRANCH; global P2P_VIRTUAL_DOUBLE + + # parse PPC instruction as in I or B or D form with opcode and upto 3 operands + # possible form : [1st] opcode [CR,] RA, RB + # [1st] opcode [CR,] RA, IM + # [1st] opcode RT, D(RA) + # [2nd] opcode [CR,] Target + # [2nd] opcode BO, BI, Target + # [2nd] opcode RT, D(RA) + # inst.group(0) : <whole instruction> + # inst.group(1) : " " + # inst.group(2) : Opcode(+/-/.) + # inst.group(3) : " " + # inst.group(4) : GPR or CR or BO or Target + # inst.group(5) : " , " + # inst.group(6) : GPR or IM or D(label+offset@sda21) or BI or Target + # inst.group(7) : " , " or " ( " + # inst.group(8) : GPR or IM or Target + # inst.group(9) : " ) " + first_inst = re.search(r"([\s]*)([a-zA-Z\+\-\.]+)([\s]*)([%a-zA-Z0-9_\.]*)([\s,]*)([%a-zA-Z0-9_@\.\-\+]*)([\s,\(]*)([%a-zA-Z0-9_\.\-]*)([\)]*)", first_line) + second_inst = re.search(r"([\s]*)([a-zA-Z\+\-\.]+)([\s]*)([%a-zA-Z0-9_\.]*)([\s,]*)([%a-zA-Z0-9_@\.\-\+]*)([\s,\(]*)([%a-zA-Z0-9_\.\-]*)([\)]*)", second_line) + + # detect an error + if first_inst is None or second_inst is None or \ + first_op != first_inst.group(2) or second_op not in second_inst.group(2): + return False,False + + # look up rule to process the instruction + rule, ppe_op = CombineRules[first_op] + + # start cases of combining two PPC instructions into PPE instruction + #---f------------------------------------------------------------------------ + if 'f' in rule: + + if not P2P_COMPARE_BRANCH: + return True,False + + # fusing compare and branch + ppe_op = ppe_op + second_op + + # for cmpwib* case, there is a difference between + # cmpwi SI operand as signed 16-bit integer and then got sign extended and + # cmpwib* UIX operand as unsigned 5-bit integer and then got zero extended + # thus, will not fuse the two if the integer operand is not in range(0,31) + # if cr field is omitted: + if ',' in first_inst.group(7): + # cr field must be cr0 or 0, error out if it is something else: + if '0' not in first_inst.group(4): + return False, True + if 'i' in first_op and (int(first_inst.group(8)) < 0 or \ + int(first_inst.group(8)) > 31): + return True,False + else: + compare_operands = first_inst.group(6) + first_inst.group(7) + \ + first_inst.group(8) + ', ' + else: + if 'i' in first_op and (int(first_inst.group(6)) < 0 or \ + int(first_inst.group(6)) > 31): + return True,False + else: + compare_operands = first_inst.group(4) + first_inst.group(5) + \ + first_inst.group(6) + ', ' + + # if 'bc' then extract BO,BI fields and convert to PX,BIX fields of 'cmp*b*' + # Note CTR decreament and branch always cases are not handled, and + # python bin() outputs "0bXXXXX" form so bo[2] is actually BO bit 0 + # else there is no need for PX,BIX fields for extended mnemonics + if 'bc' in second_op: + bo = bin(int(second_inst.group(4))) + + # do not handle CRT decreament or branch always cases + if bo[4] == 0 or bo[2] == 1: + return True,False + + # PX = BO bit 1, BIX = BI = [0,1,2,3] due to only CR0 is used + px_bix = bo[3] + second_inst.group(5) + \ + second_inst.group(6) + second_inst.group(7) + target = second_inst.group(8) + else: + px_bix = "" + # if cr field is omitted: + if ',' in second_inst.group(5): + # cr field must be cr0 or 0, error out if it is something else: + if '0' not in second_inst.group(4): + return False, True + target = second_inst.group(6) + else: + target = second_inst.group(4) + + # profile: space--, cycle is the same because 1+2==3 + P2P_SPACE -= 1 + + # if enabled, put a mark in the output file + if P2P_COMMENT: + print "#P2P(%s):" % rule + first_line, + print "#P2P(%s):" % rule + second_line, + + # construct and write "cmp*b* [PX,BIX,] RA,RB/IM,target" under 'f' rule + newline = first_inst.group(1) + ppe_op + first_inst.group(3) +\ + px_bix + compare_operands + target + print newline + return True,True + + + #---v------------------------------------------------------------------------ + if 'v' in rule: + + if not P2P_VIRTUAL_DOUBLE: + return True,False + + global P2P_VDW_SDA + + # Combinable Conditions: + # 1) base address registers must be the same and one of R1/R2/R13 + # 2) address offsets have to be 8-bytes continuous and aligned + # 3) target or source registers must qualify to be double word register + # Note: label+offset@sda21 format is coverted to target r13 after link + # assume data go in and out r13 or SDA space is always 8-byte aligned + # here we only check the continous of address offset and register pair + if first_inst.group(8) == second_inst.group(8) in ['1','2','13'] or \ + ("@sda21" in first_inst.group(6) and \ + "@sda21" in second_inst.group(6) and \ + P2P_VDW_SDA): + + if ((first_inst.group(6).replace("@sda21","") + "+4" == \ + second_inst.group(6).replace("@sda21","") and P2P_VDW_SDA) or \ + ((first_inst.group(6).isdigit() and \ + not int(first_inst.group(6)) % 8) and \ + int(first_inst.group(6)) + 4 == int(second_inst.group(6)))) and \ + (int(first_inst.group(4)) + 1 == int(second_inst.group(4)) or \ + (int(first_inst.group(4)) == 31 and \ + int(second_inst.group(4)) == 0)): + newline = first_line.replace(first_op, ppe_op) + elif ((second_inst.group(6).replace("@sda21","") + "+4" == \ + first_inst.group(6).replace("@sda21","") and P2P_VDW_SDA) or \ + ((second_inst.group(6).isdigit() and \ + not int(second_inst.group(6)) % 8) and \ + int(second_inst.group(6)) + 4 == int(first_inst.group(6)))) and \ + (int(second_inst.group(4)) + 1 == int(first_inst.group(4)) or \ + (int(second_inst.group(4)) == 31 and \ + int(first_inst.group(4)) == 0)): + newline = second_line.replace(second_op, ppe_op) + else: + return True,False + + # profile: space--, cycle--(same delay but 1 less from issue) + P2P_SPACE -= 1; P2P_CYCLE -= 1 + + # if enabled, put a mark in the output file + if P2P_COMMENT: + print "#P2P(%s):" % rule + first_line, + print "#P2P(%s):" % rule + second_line, + + print newline, + return True,True + else: + return True,False + + +# ----------------------------------------------------------------------------- +# p2p_onefile: +# process single PPC assembly file to convert it into PPE assembly file +# also filter out non-instruction lines before calling the subroutine +# +# Arguments: +# string: ppcFileName +# Return: +# boolean: done - True if file processing completed without error +# - False if file processing failed due to an error +# Variables: +# boolean: match, done +# string: ppeFileName, line, ppc_op, pre_op, pre_line, section, label +# integer: line_num, first_label_ln, second_label_ln, misalign +# Subroutine: +# p2p_combine +# p2p_replace +# ----------------------------------------------------------------------------- +def p2p_onefile(ppcFileName): + + global P2P_SPACE; P2P_SPACE = 0 # profile count + global P2P_CYCLE; P2P_CYCLE = 0 # profile count + + if P2P_VERBOSE : print "Translate PPC assembly: " + ppcFileName + + # new PPE assembly file is renamed as <filename>.s + ppeFileName = ppcFileName.replace(P2P_PPC_EXT, P2P_PPE_EXT) + os.rename(ppcFileName, ppeFileName) + + # initialize storage variables for previous line that needs to be remembered + pre_line = "" + pre_op = "" + + # use inline file editing, back up original PPC assembly file as <filename>.S + for line in fileinput.input(ppeFileName, inplace=1, backup='.405'): + + # in case of "mtmsr 0; isync" + line = line.replace('isync','nop') + + # skip blank line + if not line.strip(): + if pre_line: + print pre_line, + pre_line = "" + print line, + continue + + # skip comments line + if re.search("^[\s]*(//|#)", line): + if pre_line: + print pre_line, + pre_line = "" + print line, + continue + + # skip .section code except .p2align and label: + section = re.search("^[\s]*(\.[0-9a-zA-Z_]+)", line) + if section is not None and ':' not in line and \ + section.group(1) != '.p2align': + if pre_line: + print pre_line, + pre_line = "" + print line, + continue + + # apply specical 'l' rule in CombineRules for '.p2align' before a 'label:' + label = re.search("^[\s]*[\.0-9a-zA-Z_]+[:]+", line) + if label is not None: + if pre_line and pre_op == '.p2align': + second_label_ln = fileinput.lineno() + misalign = 8 - (second_label_ln - first_label_ln - 2) % 8 + if misalign in [3,4,5,6,7]: + # profile: same space, but save cycles, branch penalty is 2 + P2P_CYCLE -= misalign - 2 + if P2P_COMMENT: print "#P2P(l):" + print '\tb ' + label.group(0).split(':')[0] + print pre_line, + pre_line = "" + first_label_ln = fileinput.lineno() + if pre_line: + print pre_line, + pre_line = "" + print line, + continue + + # extract opcode field from line + ppc_op = line.split()[0] + done,match = False,False + + # detect the 2nd possible combinable instruction + if pre_line and P2P_COMBINE: + # ignore +/- signs for branch prediction + if '+' in ppc_op or '-' in ppc_op: + ppc_op = ppc_op[:-1] + if 'cmp' in pre_op and ppc_op in FuseBranches or \ + 'cmp' not in pre_op and ppc_op == pre_op: + done,match = p2p_combine(pre_line, line, pre_op, ppc_op) + if not match: + print pre_line, + else: + print pre_line, + done,match = True,False + pre_line = "" + + # detect the 1st possible combinable instruction + if not pre_line and not match and P2P_COMBINE: + if ppc_op in CombineRules.keys(): + pre_op = ppc_op + pre_line = line + done,match = True,True + else: + done,match = True,False + + # defect possible replacable instruction + if not match: + if ppc_op in ReplaceRules.keys() and P2P_REPLACE: + done = p2p_replace(line, ppc_op) + else: + print line, + done = True + + # if instruction process is not done due to error + if not done: + line_num = fileinput.lineno() + break + + # close the output file and restore the original input file + fileinput.close() + os.rename(ppeFileName+'.405', ppcFileName) + + # in case last line of the file qualified to be a pre_line and was not printed + if pre_line: + f = open(ppeFileName, 'a') + f.write(pre_line) + f.close() + + # print error debug message + if not done: + print "Error: target instruction detected at line [%d]:" % line_num + print " " + line + print " but fail to recognize instruction format." + # terminate Makefile or execution if an error is detected + sys.exit(1) + + if P2P_COMMENT and P2P_PROFILE: + f = open(ppeFileName, 'a') + f.write("#P2P: space(%d) cycle(%d)" % (P2P_SPACE,P2P_CYCLE)) + f.close() + + if P2P_VERBOSE: + print "Generated PPE assembly: " + ppeFileName + if P2P_PROFILE: + print "Optimization Profiling: " + str(P2P_SPACE*4) + " bytes, " +\ + str(P2P_CYCLE) + " cycles." + + +# ----------------------------------------------------------------------------- +# p2p_profile +# profiling how much performance and code size are saved by optimization +# +# Arguments: +# string: ppcFileName +# Return: +# list: [space, cycle] +# Variables: +# string: line, profile +# Subroutine: +# None +# ----------------------------------------------------------------------------- +def p2p_profile(ppcFileName): + f = open(ppcFileName.replace(P2P_PPC_EXT, P2P_PPE_EXT), 'r') + for line in f: + pass + f.close() + profile = re.search(r"^\#P2P: space\(([0-9\-]+)\) cycle\(([0-9\-]+)\)", line) + if profile is not None: + return [int(profile.group(1)), int(profile.group(2))] + else: + return [0,0] + +# ----------------------------------------------------------------------------- +# p2p_main: +# main of this script +# print usage info +# parse options and arguments +# process one file or a directory of files +# ----------------------------------------------------------------------------- +def p2p_main(): + + # command-line option parsing + from optparse import OptionParser + usage = "usage: %prog [options]" + version= "%prog v." + P2P_VERSION + parser = OptionParser(usage=usage, version=version) + parser.add_option("-d", "--directory", metavar="PATH", dest="ppcPath", + help="process all files in a directory given by PATH") + parser.add_option("-f", "--filename", metavar="FILE", dest="ppcFile", + help="process single file(with path in the filename)") + parser.add_option("-p", "--parallel", + action="store_true", dest="parallel", default=False, + help="processing all files in parallel processes") + parser.add_option("-s", "--statistics", + action="store_true", dest="profile", default=False, + help="optimization profiling, require comment in outputs") + parser.add_option("-c", "--combine-only", + action="store_false", dest="replace", default=True, + help="enable only combine function by disabling replace") + parser.add_option("-r", "--replace-only", + action="store_false", dest="combine", default=True, + help="enable only replace function by disabling combine") + parser.add_option("-b", "--compare branch disable", + action="store_false", dest="compare_branch", default=True, + help="only disabling fused compare branch function") + parser.add_option("-v", "--virtual double disable", + action="store_false", dest="virtual_double", default=True, + help="only disabling fused virtual double function") + parser.add_option("-e", "--eabi", + action="store_true", dest="vdw_sda", default=False, + help="enable virtual double word fusion targeting sda") + parser.add_option("-n", "--no-comment", + action="store_false", dest="comment", default=True, + help="don't leave comment mark in output file") + parser.add_option("-q", "--quiet", + action="store_false", dest="verbose", default=True, + help="don't print status messages to stdout") + (options, args) = parser.parse_args() + # global program output verbose switch + global P2P_VERBOSE; P2P_VERBOSE = options.verbose + # leave a comment mark in output files + global P2P_COMMENT; P2P_COMMENT = options.comment + # space/performance profiling function + global P2P_PROFILE; P2P_PROFILE = options.profile + # enable instruction replace functions + global P2P_REPLACE; P2P_REPLACE = options.replace + # enable instruction combine functions + global P2P_COMBINE; P2P_COMBINE = options.combine + # enable virtual double word fusion targeting sda + global P2P_VDW_SDA; P2P_VDW_SDA = options.vdw_sda + # enable only fused compare and branch function + global P2P_COMPARE_BRANCH; P2P_COMPARE_BRANCH = options.compare_branch + # enable only combined virtual double function + global P2P_VIRTUAL_DOUBLE; P2P_VIRTUAL_DOUBLE = options.virtual_double + + if P2P_VERBOSE : + print "PPC405 Assembly to PPE42 Assembly Post-Compiler Proceesor (P2P)" + print "Version: " + P2P_VERSION + + # single file processing + if options.ppcFile: + + if P2P_VERBOSE : + print "Processing signle file: " + options.ppcFile + + p2p_onefile(options.ppcFile) + + # multiple files processing + if options.ppcPath: + + if P2P_VERBOSE : + print "Accessing all files at: " + options.ppcPath + print "*Parallel Process Mode: " + ("Off", "On")[options.parallel] + + if options.profile: + bytes = 0; cycles = 0 + + fileList = [] + for root, subdirs, files in os.walk(options.ppcPath): + for file in fnmatch.filter(files, '*'+P2P_PPC_EXT): + if options.parallel : + fileList.append(os.path.join(root, file)) + else: + if options.profile: + space,cycle = p2p_profile(os.path.join(root, file)) + bytes += space*4 + cycles += cycle + else: + p2p_onefile(os.path.join(root, file)) + + if options.profile: + print "Optimization Profiling: " + str(bytes) + " bytes, " +\ + str(cycles) + " cycles." + + # parallel processing mode + if options.parallel: + from multiprocessing import Pool + p = Pool() + p.map(p2p_onefile, fileList) + p.close() + p.join() + + if P2P_VERBOSE : print "Done" + + +# ----------------------------------------------------------------------------- +# python main +if __name__ == '__main__': + p2p_main() diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divw.S b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divw.S new file mode 100644 index 00000000..dac81cf4 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divw.S @@ -0,0 +1,231 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divw.S $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +/// \file ppe42_divw.S +/// \brief PPC405 word division instructions implemented by PPE ISA +/// +/// This file includes implementation for the following PPC405 instructions +/// divw RT, RA, RB +/// +/// Note: PPE ISA specific "fused compare and branch" instructions are used +/// +/// Revision History: +/// 09-22-2014: Initial Version by daviddu +/// + + .file "ppe42_divw.S" + .section ".text" + + /* + ** 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 + ** msh = most-significant (high-order) halfword, i.e. bits 0..15 + ** lsh = least-significant (low-order) halfword, i.e. bits 16..63 + ** + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** OW = Register is overwritten, previous value is lost, + ** correct if previous value is no longer needed. + ** FU = Register is not overwritten, but its value is no longer needed, + ** in another word, the register is "free for use". + ** + ** PPE GPR Registers are: R0-R10, R13, R28-R31 + ** Volatile Registers are: R0, R3-R10 + ** Non-volatile registers are R28-R31 + */ + + /* + ** Caling Convention + ** + ** R2 and R13 are never saved or restored. In ABI or EABI application + ** these registers are constant. The other touched volatile registers + ** will be saved and restored by the subroutines. Note the caller + ** wont be saving those registers because these subroutines will be + ** instrumented into caller's body without compiler knowledge. + ** + ** Note R3 is not saved and restored because it will be changed for + ** return value anyways, the p2p script will make sure to restore it. + ** Also CR is hanlded because of compare and branch, but XER/CTR/LR + ** are not hanlded because they are untouched by the instructions used. + ** + ** Stack layout: + ** + ** 0x00 -- R1, Dedicated for Stack Pointer + ** 0x04 -- slot reserved for LR + ** 0x08 -- R4, Volatile, Private + ** 0x0c -- R5, Volatile, Private + ** 0x10 -- R6, Volatile, Private + ** 0x14 -- R7, Volatile, Private + ** 0x18 -- R8, Volatile, Private + ** 0x1c -- R9, Volatile, Private + ** 0x20 -- CR, Condition Register + ** 0x24 -- + ** + ** 0x28 -- Stack Size, Must be 8-byte aligned + */ + + /* + ** Division Procedures: + ** + ** __ppe42_divwu(dividend, divisor) + ** __ppe42_divw(dividend, divisor) + ** + ** R3 = Input parameter, dividend. then Return value, quotient. + ** R4 = Input parameter, divisor. + ** R5 = Output parameter, quotient. + ** R6 = Output parameter, remainder. + ** R7 = Temporary register, counter. + ** + ** General Algorithm + ** + ** Using standard shift and subtract method to emulate + ** Note: dividend,divisor,quotient,remainder are all 32-bit integers + ** + ** Precondition Check: + ** + ** if (divisor == dividend) { + ** quotient = 1; + ** remainder = 0; + ** } + ** + ** if (divisor == 0) { + ** quotient = 0; + ** remainder = 0; + ** } + ** + ** if (divisor > dividend) { + ** quotient = 0; + ** remainder = dividend; + ** } + */ + +/*****************************************************************************/ + + /* + ** Divide Word Signed (__ppe42_divw) + ** + ** Using Divide Word Unsigned(divwu) to emulate + ** + ** dd = absolute(dividend); + ** dr = absolute(divisor); + ** [q,r] = __ppe42_divwu(dd, dr); + ** + ** quotient = q; + ** if (dividend < 0) { + ** remainder = -r; + ** if (divisor > 0) + ** quotient = -q; + ** } + ** else { + ** remainder = r; + ** if (divisor < 0) + ** quotient = -q; + ** } + */ + + .align 2 + .global __ppe42_divw + .type __ppe42_divw, @function + +__ppe42_divw: + + stwu %r1, -0x28(%r1) // allocate stack frame + + stvd %d4, 0x08(%r1) // save off r4 & r5 in stack + stvd %d6, 0x10(%r1) // save off r6 & r7 in stack + stvd %d8, 0x18(%r1) // save off r8 & r9 in stack + + mfcr %r5 // save off cr + stw %r5, 0x20(%r1) // store cr in stack + + li %r5, 1 // quotient = 1 + li %r6, 0 // remainder = 0 + cmplwbc 1, 2, %r3, %r4, __ppe42_divw_ret // ret(divisor == dividend) + + li %r5, 0 // quotient = 0 + li %r6, 0 // remainder = 0 + cmpwibc 1, 2, %r4, 0, __ppe42_divw_ret // ret(divisor == 0) + + cmpwibc 1, 1, %r3, 0, __ppe42_divw_csc // dividend(+) -> csc + neg %r3, %r3 // absolute(dividend) + li %r5, 1 // note dividend < 0 + +__ppe42_divw_csc: // <<continue sign check>> + + cmpwibc 1, 1, %r4, 0, __ppe42_divw_uns // divisor(+) -> uns + neg %r4, %r4 // absolute(divisor) + li %r6, 1 // note divisor < 0 + +__ppe42_divw_uns: // <<unsigned division>> + + mr %r8, %r5 // remember if dividend > 0 + xor %r9, %r5, %r6 // remember sign difference + + li %r5, 0 // quotient = 0 + mr %r6, %r3 // remainder = dividend + cmplwbc 1, 0, %r3, %r4, __ppe42_divw_sign // ret(divisor > dividend) + + li %r7, 32 // num_of_bits = 32 + +__ppe42_divw_sas: // <<shift and subtract loop>> + + slwi %r6, %r6, 1 // remainder <<= 1 + inslwi %r6, %r3, 1, 31 // remainder[31] = dividend[0] + slwi %r3, %r3, 1 // dividend <<= 1 + slwi %r5, %r5, 1 // quotient <<= 1 + subi %r7, %r7, 1 // num_of_bits-- + cmplwbc 1, 0, %r6, %r4, __ppe42_divw_sas // continue(remainder<divisor) + + sub %r6, %r6, %r4 // reminder -= divisor + addi %r5, %r5, 1 // quotient++ + cmpwibc 0, 2, %r7, 0, __ppe42_divw_sas // while(num_of_bits) + +__ppe42_divw_sign: // <<sign handling>> + + cmpwibc 1, 2, %r9, 0, __ppe42_divw_csh // if same sign, r5 stays + + neg %r5, %r5 // otherwise, neg(r5) + +__ppe42_divw_csh: // <<continue sign handling>> + + cmpwibc 1, 2, %r8, 0, __ppe42_divw_ret // if dividend>0, r6 stays + + neg %r6, %r6 // otherwise, neg(r6) + +__ppe42_divw_ret: // <<return subroutine>> + + mr %r3, %r5 // r3 is the default return + + lwz %r5, 0x20(%r1) // load cr from stack + mtcr0 %r5 // restore cr + + lvd %d4, 0x08(%r1) // restore r4 & r5 from stack + lvd %d6, 0x10(%r1) // restore r6 & r7 from stack + lvd %d8, 0x18(%r1) // restore r8 & r9 from stack + + lwz %r1, 0(%r1) // restore stack pointer + + blr // branch back + + .size __ppe42_divw, .-__ppe42_divw diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divwu.S b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divwu.S new file mode 100644 index 00000000..d21fcbd6 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divwu.S @@ -0,0 +1,206 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_divwu.S $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +/// \file ppe42_divwu.S +/// \brief PPC405 word division instructions implemented by PPE ISA +/// +/// This file includes implementation for the following PPC405 instructions +/// divwu RT, RA, RB +/// +/// Note: PPE ISA specific "fused compare and branch" instructions are used +/// +/// Revision History: +/// 09-22-2014: Initial Version by daviddu +/// + + .file "ppe42_divwu.S" + .section ".text" + + /* + ** 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 + ** msh = most-significant (high-order) halfword, i.e. bits 0..15 + ** lsh = least-significant (low-order) halfword, i.e. bits 16..63 + ** + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** OW = Register is overwritten, previous value is lost, + ** correct if previous value is no longer needed. + ** FU = Register is not overwritten, but its value is no longer needed, + ** in another word, the register is "free for use". + ** + ** PPE GPR Registers are: R0-R10, R13, R28-R31 + ** Volatile Registers are: R0, R3-R10 + ** Non-volatile registers are R28-R31 + */ + + /* + ** Caling Convention + ** + ** R2 and R13 are never saved or restored. In ABI or EABI application + ** these registers are constant. The other touched volatile registers + ** will be saved and restored by the subroutines. Note the caller + ** wont be saving those registers because these subroutines will be + ** instrumented into caller's body without compiler knowledge. + ** + ** Note R3 is not saved and restored because it will be changed for + ** return value anyways, the p2p script will make sure to restore it. + ** Also CR is hanlded because of compare and branch, but XER/CTR/LR + ** are not hanlded because they are untouched by the instructions used. + ** + ** Stack layout: + ** + ** 0x00 -- R1, Dedicated for Stack Pointer + ** 0x04 -- slot reserved for LR + ** 0x08 -- R4, Volatile, Private + ** 0x0c -- R5, Volatile, Private + ** 0x10 -- R6, Volatile, Private + ** 0x14 -- R7, Volatile, Private + ** 0x18 -- R8, Volatile, Private + ** 0x1c -- R9, Volatile, Private + ** 0x20 -- CR, Condition Register + ** 0x24 -- + ** + ** 0x28 -- Stack Size, Must be 8-byte aligned + */ + + /* + ** Division Procedures: + ** + ** __ppe42_divwu(dividend, divisor) + ** __ppe42_divw(dividend, divisor) + ** + ** R3 = Input parameter, dividend. then Return value, quotient. + ** R4 = Input parameter, divisor. + ** R5 = Output parameter, quotient. + ** R6 = Output parameter, remainder. + ** R7 = Temporary register, counter. + ** + ** General Algorithm + ** + ** Using standard shift and subtract method to emulate + ** Note: dividend,divisor,quotient,remainder are all 32-bit integers + ** + ** Precondition Check: + ** + ** if (divisor == dividend) { + ** quotient = 1; + ** remainder = 0; + ** } + ** + ** if (divisor == 0) { + ** quotient = 0; + ** remainder = 0; + ** } + ** + ** if (divisor > dividend) { + ** quotient = 0; + ** remainder = dividend; + ** } + */ + +/*****************************************************************************/ + + /* + ** Divide Word Unsigned (__ppe42_divwu) + ** + ** The implementation uses standard shift and subtract approach. + ** The following is an example in C. Note the implementation doesnt + ** exactly follow the C example. + ** + ** num_of_bits = 32; + ** while(num_bits) { + ** dbit = (dividend & 0x80000000) >> 31; + ** remainder = (remainder << 1) | dbit; + ** dividend = dividend << 1; + ** quotient = quotient << 1; + ** num_of_bits--; + ** if(remainder < divisor) + ** continue; + ** temp = remainder - divisor; + ** qbit = !((temp & 0x80000000) >> 31); + ** quotient = quotient | qbit; + ** remainder = temp; + ** } + */ + + .align 2 + .global __ppe42_divwu + .type __ppe42_divwu, @function + +__ppe42_divwu: + + stwu %r1, -0x28(%r1) // allocate stack frame + + stvd %d4, 0x08(%r1) // save off r4 & r5 in stack + stvd %d6, 0x10(%r1) // save off r6 & r7 in stack + stvd %d8, 0x18(%r1) // save off r8 & r9 in stack + + mfcr %r5 // save off cr + stw %r5, 0x20(%r1) // store cr in stack + + li %r5, 1 // quotient = 1 + li %r6, 0 // remainder = 0 + cmplwbc 1, 2, %r3, %r4, __ppe42_divwu_ret // ret(divisor == dividend) + + li %r5, 0 // quotient = 0 + li %r6, 0 // remainder = 0 + cmpwibc 1, 2, %r4, 0, __ppe42_divwu_ret // ret(divisor == 0) + + li %r5, 0 // quotient = 0 + mr %r6, %r3 // remainder = dividend + cmplwbc 1, 0, %r3, %r4, __ppe42_divwu_ret // ret(divisor > dividend) + + li %r7, 32 // num_of_bits = 32 + +__ppe42_divwu_sas: // <<shift and subtract loop>> + + slwi %r6, %r6, 1 // remainder <<= 1 + inslwi %r6, %r3, 1, 31 // remainder[31] = dividend[0] + slwi %r3, %r3, 1 // dividend <<= 1 + slwi %r5, %r5, 1 // quotient <<= 1 + subi %r7, %r7, 1 // num_of_bits-- + cmplwbc 1, 0, %r6, %r4, __ppe42_divwu_sas // continue(remainder<divisor) + + sub %r6, %r6, %r4 // reminder -= divisor + addi %r5, %r5, 1 // quotient++ + cmpwibc 0, 2, %r7, 0, __ppe42_divwu_sas // while(num_of_bits) + +__ppe42_divwu_ret: // <<return subroutine>> + + mr %r3, %r5 // r3 is the default return + lwz %r5, 0x20(%r1) // load cr from stack + mtcr0 %r5 // restore cr + + lvd %d4, 0x08(%r1) // restore r4 & r5 from stack + lvd %d6, 0x10(%r1) // restore r6 & r7 from stack + lvd %d8, 0x18(%r1) // restore r8 & r9 from stack + + lwz %r1, 0(%r1) // restore stack pointer + + blr // branch back + + .size __ppe42_divwu, .-__ppe42_divwu diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhw.S b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhw.S new file mode 100644 index 00000000..f437d173 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhw.S @@ -0,0 +1,215 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhw.S $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +/// \file ppe42_mulhw.S +/// \brief PPC405 word multiplication instructions implemented by PPE ISA +/// +/// This file includes implementation for the following PPC405 instructions +/// mulhw +/// +/// Note: PPE ISA specific "fused compare and branch" instructions are used +/// +/// Revision History: +/// 09-15-2014: Initial Version by daviddu +/// + + .file "ppe42_mulhw.S" + .section ".text" + + /* + ** 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 + ** msh = most-significant (high-order) halfword, i.e. bits 0..15 + ** lsh = least-significant (low-order) halfword, i.e. bits 16..63 + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** OW = Register is overwritten, previous value is lost, + ** correct if previous value is no longer needed. + ** FU = Register is not overwritten, but its value is no longer needed, + ** in another word, the register is "free for use". + ** + ** PPE GPR Registers are: R0-R10, R13, R28-R31 + ** Volatile Registers are: R0, R3-R10 + ** Non-volatile registers are R28-R31 + */ + + /* + ** Caling Convention + ** + ** R2 and R13 are never saved or restored. In ABI or EABI application + ** these registers are constant. The other touched volatile registers + ** will be saved and restored by the subroutines. Note the caller + ** wont be saving those registers because these subroutines will be + ** instrumented into caller's body without compiler knowledge. + ** + ** Note R3 is not saved and restored because it will be changed for + ** return value anyways, the p2p script will make sure to restore it. + ** Also CR is hanlded because of compare and branch, but XER/CTR/LR + ** are not hanlded because they are untouched by the instructions used. + ** + ** Stack layout: + ** + ** 0x00 -- R1, Dedicated for Stack Pointer + ** 0x04 -- slot reserved for LR + ** 0x08 -- R4, Volatile, Private + ** 0x0c -- R5, Volatile, Private + ** 0x10 -- R6, Volatile, Private + ** 0x14 -- R7, Volatile, Private + ** 0x18 -- R8, Volatile, Private + ** 0x1c -- R9, Volatile, Private + ** 0x20 -- CR, Condition Register + ** 0x24 -- + ** + ** 0x28 -- Stack Size, Must be 8-byte aligned + */ + + /* + ** Multiplication Procedures: + ** + ** __ppe42_mulhwu(U,V) + ** __ppe42_mulhw(U,V) + ** __ppe42_mullw(U,V) + ** + ** R3:R4 = Input parameter, multipliers: U, V. + ** R3 = Output parameter, either product.msh or product.lsh. + ** R5-R9 = Temporary registers + ** + ** General Algorithm + ** + ** Using PPC405 ISA instruction 'mullhw' to emulate + ** Note: U,V,A,B,C,D,Product are all 32-bit integers(with msh and lsh) + ** + ** U.msh U.lsh + ** X V.msh V.lsh + ** ------------------------ + ** A.msh A.lsh + ** B.msh B.lsh + ** C.msh C.lsh + ** D.msh D.lsh + ** ------------------------ + ** Product.msw Product.lsw + ** + ** __ppe42_mulhwu: Return Product.msh (unsigned) + ** __ppe42_mulhw: Return Product.msh (signed) + ** __ppe42_mullw: Return Product.lsh + ** + ** Precondition Check: + ** + ** if( U == 0 || V == 0 ) return P=0; + */ + +/*****************************************************************************/ + + /* + ** Multiply High Word Signed (__ppe42_mulhw) + ** + ** Using Multiply High Word Unsigned(mulhwu) to emulate + ** + ** u = absolute(U); + ** v = absolute(V); + ** p = __ppe42_mulhwu(u, v); + ** if( U[0] xor V[0] ) + ** p = -p + */ + + .align 2 + .global __ppe42_mulhw + .type __ppe42_mulhw, @function + +__ppe42_mulhw: + + stwu %r1, -0x28(%r1) // allocate stack frame + + stvd %d4, 0x08(%r1) // save off r4 & r5 in stack + stvd %d6, 0x10(%r1) // save off r6 & r7 in stack + stvd %d8, 0x18(%r1) // save off r8 & r9 in stack + + mfcr %r5 // save off cr + stw %r5, 0x20(%r1) // store cr in stack + + li %r5, 0 // r5 = 0 + cmpwibc 1, 2, %r3, 0, __ppe42_mulhw_ret // U=0 -> ret + cmpwibc 1, 2, %r4, 0, __ppe42_mulhw_ret // V=0 -> ret + + cmpwibc 1, 1, %r3, 0, __ppe42_mulhw_csc // U>0 -> csc + neg %r3, %r3 // absolute(U) + li %r5, 1 // U<0 -> r5 = 1 + +__ppe42_mulhw_csc: // <<continue sign check>> + + li %r6, 0 // V>0 -> r6 = 0 + cmpwibc 1, 1, %r4, 0, __ppe42_mulhw_uns // V>0 -> uns + neg %r4, %r4 // absolute(V) + li %r6, 1 // V<0 -> r6 = 1 + +__ppe42_mulhw_uns: // <<unsigned multiplication>> + + xor %r9, %r5, %r6 // remember sign difference + + extrwi %r5, %r3, 16, 16 + srwi %r3, %r3, 16 + extrwi %r6, %r4, 16, 16 + srwi %r4, %r4, 16 + + mullhwu %r7, %r5, %r6 + srwi %r7, %r7, 16 + + mullhwu %r6, %r3, %r6 + extrwi %r8, %r6, 16, 16 + srwi %r6, %r6, 16 + add %r7, %r8, %r7 + + mullhwu %r5, %r5, %r4 + extrwi %r8, %r5, 16, 16 + srwi %r5, %r5, 16 + add %r7, %r8, %r7 + + srwi %r7, %r7, 16 + add %r7, %r7, %r6 + add %r7, %r7, %r5 + + mullhwu %r3, %r3, %r4 + add %r5, %r3, %r7 + + cmpwibc 1, 2, %r9, 0, __ppe42_mulhw_ret // if same sign, r5 stays + + neg %r5, %r5 // otherwise, neg(r5) + +__ppe42_mulhw_ret: + + mr %r3, %r5 // put return value to r3 + + lwz %r5, 0x20(%r1) // load cr from stack + mtcr0 %r5 // restore cr + + lvd %d4, 0x08(%r1) // restore r4 & r5 from stack + lvd %d6, 0x10(%r1) // restore r6 & r7 from stack + lvd %d8, 0x18(%r1) // restore r8 & r9 from stack + + lwz %r1, 0(%r1) // restore stack pointer + + blr + + .size __ppe42_mulhw, .-__ppe42_mulhw diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhwu.S b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhwu.S new file mode 100644 index 00000000..ff5477a5 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhwu.S @@ -0,0 +1,224 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mulhwu.S $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +/// \file ppe42_mulhwu.S +/// \brief PPC405 word multiplication instructions implemented by PPE ISA +/// +/// This file includes implementation for the following PPC405 instructions +/// mulhwu +/// +/// Note: PPE ISA specific "fused compare and branch" instructions are used +/// +/// Revision History: +/// 09-15-2014: Initial Version by daviddu +/// + + .file "ppe42_mulhwu.S" + .section ".text" + + /* + ** 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 + ** msh = most-significant (high-order) halfword, i.e. bits 0..15 + ** lsh = least-significant (low-order) halfword, i.e. bits 16..63 + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** OW = Register is overwritten, previous value is lost, + ** correct if previous value is no longer needed. + ** FU = Register is not overwritten, but its value is no longer needed, + ** in another word, the register is "free for use". + ** + ** PPE GPR Registers are: R0-R10, R13, R28-R31 + ** Volatile Registers are: R0, R3-R10 + ** Non-volatile registers are R28-R31 + */ + + /* + ** Caling Convention + ** + ** R2 and R13 are never saved or restored. In ABI or EABI application + ** these registers are constant. The other touched volatile registers + ** will be saved and restored by the subroutines. Note the caller + ** wont be saving those registers because these subroutines will be + ** instrumented into caller's body without compiler knowledge. + ** + ** Note R3 is not saved and restored because it will be changed for + ** return value anyways, the p2p script will make sure to restore it. + ** Also CR is hanlded because of compare and branch, but XER/CTR/LR + ** are not hanlded because they are untouched by the instructions used. + ** + ** Stack layout: + ** + ** 0x00 -- R1, Dedicated for Stack Pointer + ** 0x04 -- slot reserved for LR + ** 0x08 -- R4, Volatile, Private + ** 0x0c -- R5, Volatile, Private + ** 0x10 -- R6, Volatile, Private + ** 0x14 -- R7, Volatile, Private + ** 0x18 -- R8, Volatile, Private + ** 0x1c -- R9, Volatile, Private + ** 0x20 -- CR, Condition Register + ** 0x24 -- + ** + ** 0x28 -- Stack Size, Must be 8-byte aligned + */ + + /* + ** Multiplication Procedures: + ** + ** __ppe42_mulhwu(U,V) + ** __ppe42_mulhw(U,V) + ** __ppe42_mullw(U,V) + ** + ** R3:R4 = Input parameter, multipliers: U, V. + ** R3 = Output parameter, either product.msh or product.lsh. + ** R5-R9 = Temporary registers + ** + ** General Algorithm + ** + ** Using PPC405 ISA instruction 'mullhw' to emulate + ** Note: U,V,A,B,C,D,Product are all 32-bit integers(with msh and lsh) + ** + ** U.msh U.lsh + ** X V.msh V.lsh + ** ------------------------ + ** A.msh A.lsh + ** B.msh B.lsh + ** C.msh C.lsh + ** D.msh D.lsh + ** ------------------------ + ** Product.msw Product.lsw + ** + ** __ppe42_mulhwu: Return Product.msh (unsigned) + ** __ppe42_mulhw: Return Product.msh (signed) + ** __ppe42_mullw: Return Product.lsh + ** + ** Precondition Check: + ** + ** if( U == 0 || V == 0 ) return P=0; + */ + +/*****************************************************************************/ + + /* + ** Multiply High Word Unsigned (__ppe42_mulhwu) + ** + ** r5 = U[16:31] or U.lsh | r5 = r3 & 0xffff + ** r3 = U[0:15] or U.msh | r3 = r3 >> 16 (r3 OW) + ** r6 = V[16:31] or V.lsh | r6 = r4 & 0xffff + ** r4 = V[0:15] or V.msh | r4 = r4 >> 16 (r4 OW) + ** + ** 4th column(drop A.lsh): + ** A = U.lsh * V.lsh [32] | r7 = r5 * r6 + ** A = A.msh [16] | r7 = r7 >> 16 (r7 OW) + ** + ** 3rd column(A = A.msh + B.lsh + C.lsh): + ** B = U.msh * U.lsh [32] | r6 = r3 * r6 (r6 OW) + ** T = B.lsh [16] | r8 = r6 & 0xffff + ** B = B.msh [16] | r6 = r6 >> 16 (r6 OW) + ** A = T + A [16] | r7 = r8 + r7 (r7 OW, r8 FU) + ** + ** C = U.lsh * V.msh [32] | r5 = r5 * r4 (r5 OW) + ** T = C.lsh [16] | r8 = r5 & 0xffff (r8 OW) + ** C = C.msh [16] | r5 = r5 >> 16 (r5 OW) + ** A = T + A [16] | r7 = r8 + r7 (r7 OW, r8 FU) + ** + ** 2nd column(A = 3rd_carry + B.msh + C.msh): + ** A = A.msh [16] | r7 = r7 >> 16 (r7 OW) + ** A = A + B [16] | r7 = r7 + r6 (r7 OW, r6 FU) + ** A = A + C [16] | r7 = r7 + r5 (r7 OW, r5 FU) + ** + ** 1st column(A = D + A): + ** D = U.msh * V.msh [32] | r3 = r3 * r4 (r3 OW, r4 FU) + ** P = D + A [32] | r5 = r3 + r7 (r3, r7 FU) + ** + ** Return P(r3=r5) as Product.msw unsigned + ** + ** Note: the implementation can be even shorter, the current + ** implementation is ensuring the overflow is avoided + ** by always adding 16 bits integer together. + */ + + .align 2 + .global __ppe42_mulhwu + .type __ppe42_mulhwu, @function + +__ppe42_mulhwu: + + stwu %r1, -0x28(%r1) // allocate stack frame + + stvd %d4, 0x08(%r1) // save off r4 & r5 in stack + stvd %d6, 0x10(%r1) // save off r6 & r7 in stack + stvd %d8, 0x18(%r1) // save off r8 & r9 in stack + + mfcr %r5 // save off cr + stw %r5, 0x20(%r1) // store cr in stack + + li %r5, 0 // r5 = 0 + cmpwibc 1, 2, %r3, 0, __ppe42_mulhwu_ret // U=0 -> ret + cmpwibc 1, 2, %r4, 0, __ppe42_mulhwu_ret // V=0 -> ret + + extrwi %r5, %r3, 16, 16 + srwi %r3, %r3, 16 + extrwi %r6, %r4, 16, 16 + srwi %r4, %r4, 16 + + mullhwu %r7, %r5, %r6 + srwi %r7, %r7, 16 + + mullhwu %r6, %r3, %r6 + extrwi %r8, %r6, 16, 16 + srwi %r6, %r6, 16 + add %r7, %r8, %r7 + + mullhwu %r5, %r5, %r4 + extrwi %r8, %r5, 16, 16 + srwi %r5, %r5, 16 + add %r7, %r8, %r7 + + srwi %r7, %r7, 16 + add %r7, %r7, %r6 + add %r7, %r7, %r5 + + mullhwu %r3, %r3, %r4 + add %r5, %r3, %r7 + +__ppe42_mulhwu_ret: + + mr %r3, %r5 // put return value to r3 + + lwz %r5, 0x20(%r1) // load cr from stack + mtcr0 %r5 // restore cr + + lvd %d4, 0x08(%r1) // restore r4 & r5 from stack + lvd %d6, 0x10(%r1) // restore r6 & r7 from stack + lvd %d8, 0x18(%r1) // restore r8 & r9 from stack + + lwz %r1, 0(%r1) // restore stack pointer + + blr + + .size __ppe42_mulhwu, .-__ppe42_mulhwu diff --git a/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mullw.S b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mullw.S new file mode 100644 index 00000000..af0a0172 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mullw.S @@ -0,0 +1,196 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/PowerPCtoPPE/ppe42_mullw.S $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +/// \file ppe42_mullw.S +/// \brief PPC405 word multiplication instructions implemented by PPE ISA +/// +/// This file includes implementation for the following PPC405 instructions +/// mullw +/// +/// Note: PPE ISA specific "fused compare and branch" instructions are used +/// +/// Revision History: +/// 09-15-2014: Initial Version by daviddu +/// + + .file "ppe42_mullw.S" + .section ".text" + + /* + ** 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 + ** msh = most-significant (high-order) halfword, i.e. bits 0..15 + ** lsh = least-significant (low-order) halfword, i.e. bits 16..63 + ** LZ = Leading Zeroes + ** SD = Significant Digits + ** OW = Register is overwritten, previous value is lost, + ** correct if previous value is no longer needed. + ** FU = Register is not overwritten, but its value is no longer needed, + ** in another word, the register is "free for use". + ** + ** PPE GPR Registers are: R0-R10, R13, R28-R31 + ** Volatile Registers are: R0, R3-R10 + ** Non-volatile registers are R28-R31 + */ + + /* + ** Caling Convention + ** + ** R2 and R13 are never saved or restored. In ABI or EABI application + ** these registers are constant. The other touched volatile registers + ** will be saved and restored by the subroutines. Note the caller + ** wont be saving those registers because these subroutines will be + ** instrumented into caller's body without compiler knowledge. + ** + ** Note R3 is not saved and restored because it will be changed for + ** return value anyways, the p2p script will make sure to restore it. + ** Also CR is hanlded because of compare and branch, but XER/CTR/LR + ** are not hanlded because they are untouched by the instructions used. + ** + ** Stack layout: + ** + ** 0x00 -- R1, Dedicated for Stack Pointer + ** 0x04 -- slot reserved for LR + ** 0x08 -- R4, Volatile, Private + ** 0x0c -- R5, Volatile, Private + ** 0x10 -- R6, Volatile, Private + ** 0x14 -- R7, Volatile, Private + ** 0x18 -- R8, Volatile, Private + ** 0x1c -- R9, Volatile, Private + ** 0x20 -- CR, Condition Register + ** 0x24 -- + ** + ** 0x28 -- Stack Size, Must be 8-byte aligned + */ + + /* + ** Multiplication Procedures: + ** + ** __ppe42_mulhwu(U,V) + ** __ppe42_mulhw(U,V) + ** __ppe42_mullw(U,V) + ** + ** R3:R4 = Input parameter, multipliers: U, V. + ** R3 = Output parameter, either product.msh or product.lsh. + ** R5-R9 = Temporary registers + ** + ** General Algorithm + ** + ** Using PPC405 ISA instruction 'mullhw' to emulate + ** Note: U,V,A,B,C,D,Product are all 32-bit integers(with msh and lsh) + ** + ** U.msh U.lsh + ** X V.msh V.lsh + ** ------------------------ + ** A.msh A.lsh + ** B.msh B.lsh + ** C.msh C.lsh + ** D.msh D.lsh + ** ------------------------ + ** Product.msw Product.lsw + ** + ** __ppe42_mulhwu: Return Product.msh (unsigned) + ** __ppe42_mulhw: Return Product.msh (signed) + ** __ppe42_mullw: Return Product.lsh + ** + ** Precondition Check: + ** + ** if( U == 0 || V == 0 ) return P=0; + */ + +/*****************************************************************************/ + + /* + ** Multiply Low Word (__ppe42_mullw) + ** + ** r5 = U[16:31] or U.lsh | r5 = r3 & 0xffff + ** r3 = U[0:15] or U.msh | r3 = r3 >> 16 (r3 OW) + ** r6 = V[16:31] or V.lsh | r6 = r4 & 0xffff + ** r4 = V[0:15] or V.msh | r4 = r4 >> 16 (r4 OW) + ** + ** B = U.msh * V.lsh | r3 = r3 * r6 (r3 OW) + ** B = B << 16 | r3 = r3 << 16 + ** C = U.lsh * V.msh | r4 = r5 * r4 (r4 OW) + ** C = C << 16 | r4 = r4 << 16 + ** A = U.lsh * V.lsh | r5 = r5 * r6 (r5 OW, r6 FU) + ** A = A + B | r5 = r5 + r3 (r3 FU) + ** P = A + C | r5 = r5 + r4 (r4 FU) + ** + ** Return P(r3=r5) as Product.lsw + ** + ** Note: there is no overflow case with this function + */ + + .align 2 + .global __ppe42_mullw + .type __ppe42_mullw, @function + +__ppe42_mullw: + + stwu %r1, -0x28(%r1) // allocate stack frame + + stvd %d4, 0x08(%r1) // save off r4 & r5 in stack + stvd %d6, 0x10(%r1) // save off r6 & r7 in stack + stvd %d8, 0x18(%r1) // save off r8 & r9 in stack + + mfcr %r5 // save off cr + stw %r5, 0x20(%r1) // store cr in stack + + li %r5, 0 // r5 = 0 + cmpwibc 1, 2, %r3, 0, __ppe42_mullw_ret // U=0 -> ret + cmpwibc 1, 2, %r4, 0, __ppe42_mullw_ret // V=0 -> ret + + extrwi %r5, %r3, 16, 16 + srwi %r3, %r3, 16 + extrwi %r6, %r4, 16, 16 + srwi %r4, %r4, 16 + + mullhwu %r3, %r3, %r6 + slwi %r3, %r3, 16 + + mullhwu %r4, %r5, %r4 + slwi %r4, %r4, 16 + + mullhwu %r5, %r5, %r6 + add %r5, %r5, %r3 + add %r5, %r5, %r4 + +__ppe42_mullw_ret: + + mr %r3, %r5 // put return value to r3 + + lwz %r5, 0x20(%r1) // load cr from stack + mtcr0 %r5 // restore cr + + lvd %d4, 0x08(%r1) // restore r4 & r5 from stack + lvd %d6, 0x10(%r1) // restore r6 & r7 from stack + lvd %d8, 0x18(%r1) // restore r8 & r9 from stack + + lwz %r1, 0(%r1) // restore stack pointer + + blr + + .size __ppe42_mullw, .-__ppe42_mullw diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/Makefile b/import/chips/p9/procedures/ppe/tools/ppetracepp/Makefile new file mode 100644 index 00000000..33ada81a --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/Makefile @@ -0,0 +1,35 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/Makefile $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +all: ppetracepp ppe2fsp + +ppetracepp: ppetracepp.C + g++ -m32 -O3 -w -g -I./ ppetracepp.C -o ppetracepp +# g++ -O3 -w -x c++ -fPIC -g -I./ ppetracepp.C -o ppetracepp + +ppe2fsp: ppe2fsp.c ppe2fsp_cmd.c + gcc -m32 -w -g -I./ -I../../pk/trace ppe2fsp.c ppe2fsp_cmd.c -o ppe2fsp + +clean: + rm ppetracepp ppe2fsp diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/jhash.h b/import/chips/p9/procedures/ppe/tools/ppetracepp/jhash.h new file mode 100755 index 00000000..8c62c552 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/jhash.h @@ -0,0 +1,197 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/jhash.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef _LINUX_JHASH_H +#define _LINUX_JHASH_H + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ + { \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ + } + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +static inline u32 jhash(const void* key, u32 length, u32 initval) +{ + u32 a, b, c, len; + const u8* k = (const u8*)key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) + { + a += (k[0] + ((u32)k[1] << 8) + ((u32)k[2] << 16) + ((u32)k[3] << 24)); + b += (k[4] + ((u32)k[5] << 8) + ((u32)k[6] << 16) + ((u32)k[7] << 24)); + c += (k[8] + ((u32)k[9] << 8) + ((u32)k[10] << 16) + ((u32)k[11] << 24)); + + __jhash_mix(a, b, c); + + k += 12; + len -= 12; + } + + c += length; + + switch (len) + { + case 11: + c += ((u32)k[10] << 24); + + case 10: + c += ((u32)k[9] << 16); + + case 9 : + c += ((u32)k[8] << 8); + + case 8 : + b += ((u32)k[7] << 24); + + case 7 : + b += ((u32)k[6] << 16); + + case 6 : + b += ((u32)k[5] << 8); + + case 5 : + b += k[4]; + + case 4 : + a += ((u32)k[3] << 24); + + case 3 : + a += ((u32)k[2] << 16); + + case 2 : + a += ((u32)k[1] << 8); + + case 1 : + a += k[0]; + }; + + __jhash_mix(a, b, c); + + return c; +} + +/* A special optimized version that handles 1 or more of u32s. + * The length parameter here is the number of u32s in the key. + */ +static inline u32 jhash2(const u32* k, u32 length, u32 initval) +{ + u32 a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); + k += 3; + len -= 3; + } + + c += length * 4; + + switch (len) + { + case 2 : + b += k[1]; + + case 1 : + a += k[0]; + }; + + __jhash_mix(a, b, c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix(a, b, c); + + return c; +} + +static inline u32 jhash_2words(u32 a, u32 b, u32 initval) +{ + return jhash_3words(a, b, 0, initval); +} + +static inline u32 jhash_1word(u32 a, u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} + +#endif /* _LINUX_JHASH_H */ diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp.h b/import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp.h new file mode 100644 index 00000000..dc207da7 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp.h @@ -0,0 +1,34 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +#define P2F_NULL_POINTER 1 +#define P2F_INVALID_INPUT_SIZE 2 +#define P2F_INVALID_PPE_OFFSET 3 +#define P2F_OUTPUT_BUFFER_TOO_SMALL 4 +#define P2F_INPUT_BUFFER_TOO_SMALL 5 +#define P2F_INVALID_VERSION 6 + +int ppe2fsp(void* in, unsigned long in_size, void* out, unsigned long* io_size); diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp_cmd.c b/import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp_cmd.c new file mode 100644 index 00000000..660992ff --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp_cmd.c @@ -0,0 +1,142 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/ppe2fsp_cmd.c $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include <stdio.h> +#include "ppe2fsp.h" +#include "pk_trace.h" + +#define MAX_INPUT_SIZE 0x2040 //8k +#define MAX_OUTPUT_SIZE (4 * MAX_INPUT_SIZE) + +char* inbuf[MAX_INPUT_SIZE]; +char* outbuf[MAX_OUTPUT_SIZE]; +; +int main(int argc, char** argv) +{ + FILE* in; + FILE* out; + size_t input_size; + size_t output_size; + size_t bytes_written; + int rc = -1; + + do + { + if(argc > 3) + { + fprintf(stderr, "Usage: %s [input file] [output file]\n", argv[0]); + } + + if(argc < 3) + { + out = stdout; + } + else + { + //open the output file for writing + out = fopen(argv[2], "w"); + + if(!out) + { + perror("failed to open file for writing"); + break; + } + } + + if(argc < 2) + { + in = stdin; + } + else + { + //open the input file for reading + in = fopen(argv[1], "r"); + + if(!in) + { + perror("failed to open file for reading"); + break; + } + } + + //read the input stream until we reach EOF or the max size + input_size = fread(inbuf, 1, MAX_INPUT_SIZE, in); + + if(!feof(in)) + { + if(ferror(in)) + { + perror("failed to read input file"); + break; + } + else + { + fprintf(stderr, "Input stream exceeds max size of %d bytes. Exiting.\n", MAX_INPUT_SIZE); + break; + } + } + + output_size = MAX_OUTPUT_SIZE; + + //Actual size of output buffer will be set upon successful completion + rc = ppe2fsp(inbuf, input_size, outbuf, &output_size); + + if(rc) + { + fprintf(stderr, "Failed converting ppe trace to fsp trace. rc = %d\n", rc); + + if(rc == P2F_INVALID_VERSION) + { + fprintf(stderr, "PPE trace buffer must be version %d.\n", PK_TRACE_VERSION); + } + + break; + } + + rc = -1; + //operation was successful. Write out the fsp trace data + bytes_written = fwrite(outbuf, 1, output_size, out); + + if(bytes_written != output_size) + { + if(ferror(out)) + { + perror("Failed to write output stream"); + break; + } + + fprintf(stderr, "Failure: Only able to write %d of %d bytes to output stream\n", bytes_written, output_size); + break; + } + + fclose(in); + fclose(out); + fclose(stderr); + + rc = 0; + } + while(0); + + return rc; +} diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/ppetracepp.C b/import/chips/p9/procedures/ppe/tools/ppetracepp/ppetracepp.C new file mode 100755 index 00000000..e623c588 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/ppetracepp.C @@ -0,0 +1,1126 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/ppetracepp.C $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2015,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* +# *** ppetracepp - a fsp/common Linux trace pre processor +# this one replaces the trace strings by the corresponding hash value +# (i.e. the complete call to trace_ppe_hash is replaced) + +# *** Usage +# +# prepend compiler call with the call of this pre processor, i.e if you have +# $(CC) $(CFLAGS) -o $@ $< +# in your Makefile change it to this: +# ppetracepp $(CC) $(CFLAGS) -o $@ $< +# ppetracepp will use "$(CC) -E" to call the C pre processor "cpp". +# you can set a env var "REALCPP" to the name of a program to select +# a different programm as cpp +# +# ppetracepp creates a file "$target.ppe.hash" with the trace strings and the hash values. +# +# to enable debug mode set envvar PPETRACEPPDEBUG to 1 or give '-d' as first arg + +# *** Change History +# +# 2003-02-26 RBa created from scratch +# 2003-02-28 RBa add C++ support (C++ interface uses own type for the hash) +# 2003-05-28 RBa if cc should link instead of compile just call compiler +# 2003-07-11 AGe Change search alg. slightly and put just format back +# 2003-07-25 RBa just call gcc if called to link instead to compile +# eat argument for option -x +# 2003-11-26 RBa fix c/c++ algo: compile as c++ if realcc=*g++ +# 2004-02-02 RBa remove explicit test whether source file is readable +# it is obsolete and might lead to an error if afs is used +# 2004-02-13 RBa add support for dependency generation (-MD/-MG, -MF) +# don't prepend './' to object filename +# 2006-04-19 RBa rewrite trace_ppe_write_all support, handle C and C++ the same +# 2006-05-24 RBa fix handling of missing -o ; add TRAC_PPVER for macro/API version +# 2006-09-15 RBa add handling of \" in trace format strings ; reduce non-error output +# put object file in current dir if no -o given +# 2007-03-22 RBa handle more gcc options (-i) ; protect " in call to shell +# store output of cpp as "unmodified" output for debug +# only write string/hash file if strings found +# 2012-09-24 hlava Rewritten as C program for better build performance (was perl) +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <string> +#include <time.h> +#include <fcntl.h> +#include <map> +#include <vector> + +#include <sys/types.h> +typedef u_int32_t u32 ; +typedef u_int8_t u8 ; +#include <jhash.h> + +using namespace std; + +static string version = "2.0"; +static string macro_version = "1"; + +static bool debug = false; +#define dprintf(format, ...) if (debug) { printf(format, ##__VA_ARGS__); fflush(stdout); } +static map<string, string> hashtab; +static string hashtype; +static string hashtype_suffix; + +static string tmp; +static string cmd; +static FILE* CPP = NULL; // pipe from preprocessor +static FILE* CC = NULL; // pipe to compiler +static FILE* DEBUG = NULL; +static FILE* DEBUGIN = NULL; + +//***************************************************************************** +// replace_substr +//***************************************************************************** +void replace_substr(std::string& str, const std::string& oldStr, const std::string& newStr) +{ + size_t pos = 0; + + while((pos = str.find(oldStr, pos)) != std::string::npos) + { + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + +} + +//***************************************************************************** +// fileparse +//***************************************************************************** +void fileparse(const string& in_str, string& name, string& dir, string& suff) +{ + string str(in_str); + size_t pos; + name = ""; + dir = ""; + suff = ""; + pos = str.find_last_of('.'); + + if (pos != string::npos) + { + suff = str.substr(pos); + str = str.substr(0, pos); + } + + pos = str.find_last_of('/'); + + if (pos != string::npos) + { + name = str.substr(pos + 1); + str = str.substr(0, pos + 1); + } + + dir = str; +} + +static const size_t TRACE_PPE_HASH_LEN = 13; +//***************************************************************************** +// chop_up_line +//***************************************************************************** +bool chop_up_line(string& in_line, string& prefix, string& strings, string& salt, string& suffix) +{ + // First see if this line matches the pattern we're looking for + // Since this will return false 95%+ of the time this function it called, we do it + // before doing any other init for performance reasons. + size_t pos = in_line.find("trace_ppe_hash"); + + if (pos == string::npos) + { + return(false); + } + + // trace_ppe_hash ( "..." ".." "..." , 2 ) + // regex: PREFIX 'trace_ppe_hash' space '(' space STRINGS space ',' space NUMBER space ')' SUFFIX + // STRINGS: '"' .* '"' space? + + + // Original perl magic incantation: + // while($line =~ m/^(.*?)trace_ppe_hash\s*\(\s*((".*?(?<!\\)"\s*)+),\s*(-?\d+)\s*\)(.*)$/) { + // ($prefix, $strings, $salt, $suffix) = ($1, $2, $4, $5); + // + // Decrypting the magic pattern matching... + // (.*?) => $1 = everything up to the word "trace_ppe_hash" + // trace_ppe_hash = delimiter + // \s*\(\s* = delimiter = <0-n whitespace chars>, left paren, <0-n whitespace chars> + // ((".*?(?<!\\)"\s*)+) => $2 = double-quote, some chars up to last closing double-quote ($3 used for nested regex) + // ,\s* = delimiter = comma followed by some whitespace + // (-?\d+)\s*\)(.*) => $4 and $5 + // $/) = end of the line input string + string line(in_line); + prefix = ""; + strings = ""; + salt = ""; + suffix = ""; + size_t pos1; + size_t pos2; + size_t pos3; + + pos1 = pos + 14; // pos1 = after "trace_ppe_hash" + pos2 = line.find("(", pos1); + + if (pos2 == string::npos) + { + return(false); + } + + ++pos2; + pos3 = line.find("\"", pos2); + + if (pos3 == string::npos) + { + return(false); + } + + dprintf("--------\nchop_up_line: Passed basic checks. line= %s\n", line.c_str()); + dprintf("pos1=%d, pos2=%d, pos3=%d\n", pos1, pos2, pos3); + + if ((pos1 != (pos2 - 1)) && (line.find_first_not_of(" \t", pos1, (pos2 - pos1) + 1) != string::npos)) + { + return(false); //non-whitespace? + } + + if ((pos2 != pos3) && (line.find_first_not_of(" \t", pos2, (pos3 - pos2)) != string::npos)) + { + return(false); //non-whitespace? + } + + // Get the prefix data + dprintf(">chop_up_line(\"%s\",...)\n", line.c_str()); + prefix = line.substr(0, pos); + line = line.substr(pos + TRACE_PPE_HASH_LEN); + dprintf(" prefix=\"%s\"\n", prefix.c_str()); + + // Get the strings and join/fix them: Store all strings between paired double-quotes up to the + // first comma not inside a string + pos = line.find_first_of('('); + + if (pos == string::npos) + { + return(false); + } + + line = line.substr(pos + 1); + strings = ""; + + while(!line.empty()) + { + pos = line.find_first_of(','); + pos1 = line.find_first_of('"'); + + if ((pos1 == string::npos) || ((pos != string::npos) && (pos < pos1))) + { + break; // found comma before next quote + } + + pos2 = line.find_first_of('"', (pos1 + 1)); + + if (pos2 == string::npos) + { + return(false); // unbalanced quotes! + } + + while(line[pos2 - 1] == '\\') // skip escaped quotes in the string (they're not the ending quote) + { + pos2 = line.find_first_of('"', (pos2 + 1)); + + if (pos2 == string::npos) + { + return(false); // unbalanced quotes! + } + } + + if (!strings.empty()) + { + strings += " "; + } + + strings += line.substr(pos1, (pos2 - pos1) + 1); + line = line.substr(pos2 + 1); + } + + replace_substr(strings, "\" \"", ""); + replace_substr(strings, "\\\"", "ESCAPEDQUOTE"); + replace_substr(strings, "\"", ""); + replace_substr(strings, "ESCAPEDQUOTE", "\""); + // Remove trailing whitespace ah20130717 + pos = strings.find_last_not_of(" \t\n"); + + if ((pos != string::npos) && (pos < (strings.length() - 1))) + { + strings = strings.substr(0, pos + 1); + } + + dprintf(" strings>%s<\n", strings.c_str()); + + // Get the salt + pos = line.find(","); + + if (pos != string::npos) + { + line = line.substr(pos + 1); + } + + pos = line.find_first_of(')'); + + if (pos == string::npos) + { + return(false); + } + + salt = line.substr(0, pos); + line = line.substr(pos + 1); + //dprintf(" salt=\"%s\"\n", salt.c_str()); + pos = salt.find_first_not_of(" \t\n"); + + if (pos == string::npos) + { + return(false); + } + + salt = salt.substr(pos); + pos = salt.find_last_not_of(" \t\n"); + + if (pos == string::npos) + { + return(false); + } + + salt = salt.substr(0, pos + 1); + dprintf(" salt=\"%s\"\n", salt.c_str()); + + // Get the suffix (i.e. the rest) + suffix = line; + + if (suffix[suffix.length() - 1] == '\n') + { + suffix = suffix.substr(0, suffix.length() - 1); + } + + dprintf(" suffix=\"%s\"\n<chop_up_line() returning true\n", suffix.c_str()); + + return(true); +} + +//***************************************************************************** +// get_format_string +//***************************************************************************** +int get_format_string(const string& in_str, string& format) +{ + int format_salt = 0; + size_t pos; + size_t pos_end; + string str(in_str); + // (@format_param) = ($strings =~ /(%[#0\- +'I]*\d*(?:\.\d*)?[hlLqjzt]*[diouxXeEfFgGaAcsCSpn])/g); + // $format = join(',', @format_param); + // Decrypting the regular expression magic... + // (%[#0\- +'I]*\d*(?:\.\d*)?[hlLqjzt]*[diouxXeEfFgGaAcsCSpn]) + + format = ""; + + while(!str.empty()) + { + pos = str.find("%"); + + if (pos == string::npos) + { + break; + } + + if (pos == (str.length() - 1)) + { + break; // last char in string? just skip it + } + + if (str[pos + 1] == '%') // double percent sign? just skip first one + { + str = str.substr(pos + 1); + continue; + } + + pos_end = str.find_first_of("cdieEfgGosuxXpn", pos); // find formatting specifier + + if (pos_end == string::npos) + { + fprintf(stderr, "ERROR: ppetracepp could not parse trace formatting string \"%s\" in \"%s\"\n", str.c_str(), + in_str.c_str()); + break; + } + + if (!format.empty()) + { + format += ","; + } + + format += str.substr(pos, (pos_end - pos) + 1); + ++format_salt; + + str = str.substr(pos_end + 1); + } + + // Correct for escaped percent signs + string temp_str(in_str); + + while((pos = temp_str.find("%%")) != string::npos) + { + if (pos < (temp_str.length() - 2)) // Not the last thing in the string? + { + dprintf(" decrementing salt value %d\n", format_salt); + --format_salt; + temp_str = temp_str.substr(pos + 2); + } + else + { + temp_str = ""; + } + } + + return(format_salt); +} + +#define BUF_SIZE 40 +//***************************************************************************** +// get_hash +//***************************************************************************** +void get_hash(const string& i_str, const unsigned int salt_num, string& hash32, string& hash16) +{ + string str(i_str); + unsigned int hash_num = 0; + char buf[BUF_SIZE]; + + hash16 = ""; + hash32 = ""; + dprintf(">get_hash(\"%s\",%d)\n", str.c_str(), salt_num); + + // Call jhash function to get the hash value + hash_num = jhash((void*)str.c_str(), str.length(), salt_num); + dprintf("jhash() returned: %u\n", hash_num); + sprintf(buf, "%u", hash_num & 0x0000ffff); + hash16 = buf; + sprintf(buf, "%u", ((salt_num << 16) | (hash_num & 0x0000ffff))); + hash32 = buf; + + // validate the hash value + size_t pos = hash32.find_first_not_of("0123456789"); + + if (pos != string::npos) + { + fprintf(stderr, "trexhash error: %s\n", hash32.c_str()); + fprintf(stderr, "for call <<%s>>\n", cmd.c_str()); + exit(1); + } + +//removing this since it doesn't seem to have any affect on the output +#if 0 + + // If hash is empty, use the sum of the ord values in the original string + if ((hash == "") || (hash == "0")) + { + unsigned int len = str.length(); + unsigned int hash_num = 0; + //unsigned char conv_buf[2] = { '\0', '\0' }; + u_int8_t conv_num; + + for (unsigned int i = 0; i < len; ++i) + { + //conv_buf[0] = str[i]; + conv_num = (u_int8_t)str[i]; + hash_num += (unsigned int)conv_num; + } + } + +#endif + + dprintf("<get_hash(\"%s\",%d) returned hash: %s\n", str.c_str(), salt_num, hash32.c_str()); +} + +//***************************************************************************** +// parse_line +//***************************************************************************** +void parse_line(map<string, string>& rhash, string& line, string& out_line) +{ + // NOTE: "line" arg may get modified by this function! Caller must not assume it's unchanged. + string format; + string prefix; + string strings; + string tmp; + string salt; + string hash16; + string hash32; + int salt_num; + int format_salt; + string suffix; + string write_all_suffix; + size_t pos; + + out_line = ""; + + // trace_ppe_hash ( "..." ".." "..." , 2 ) + // regex: PREFIX 'trace_ppe_hash' space '(' space STRINGS space ',' space NUMBER space ')' SUFFIX + // STRINGS: '"' .* '"' space? + + //while($line =~ m/^(.*?)trace_ppe_hash\s*\(\s*((".*?(?<!\\)"\s*)+),\s*(-?\d+)\s*\)(.*)$/) { + // Attempt to approximate the above amazing perl regex... + while( chop_up_line(line, prefix, strings, salt, suffix) ) + { + //dprintf("\n\nprefix = %s\nstrings = %s\nsalt = %s\nsuffix = %s\n", + // prefix.c_str(), strings.c_str(), salt.c_str(), suffix.c_str()); + // is this a trace_ppe_write_all call? + pos = prefix.find("trace_ppe_write_all"); + + if (pos != string::npos) + { + // yes. replace trace_ppe_hash with hash value and reduced format string + format_salt = get_format_string(strings, format); + // reduced format string will be added after hash value + write_all_suffix = ",\" "; + write_all_suffix += format; + write_all_suffix += "\""; + + if (!salt.empty()) + { + salt_num = atoi(salt.c_str()); + } + else + { + salt_num = -1; + } + + if (salt_num == -1) + { + salt_num = format_salt; + } + else if (salt_num != format_salt) + { + fprintf(stderr, "ERROR: printf mismatch in '%s': TRACE says %d, format says %d args\n", + line.c_str(), salt_num, format_salt); + } + } + else + { + write_all_suffix = ""; + salt_num = atoi(salt.c_str()); + } + + // get the trex hash value for the strings + get_hash(strings, salt_num, hash32, hash16); + + // check for duplicate and store the mapping + if ((rhash.find(hash32) != rhash.end()) && (rhash[hash32] != strings)) + { + fprintf(stderr, "hash collision: two different strings give the same hash value '%s'\n", hash32.c_str()); + fprintf(stderr, "%s\n%s\n", strings.c_str(), rhash[hash32].c_str()); + return; + } + + rhash[hash32] = strings; + + // add the modified line to the output + tmp = prefix; + tmp += "("; + tmp += hashtype; + tmp += " "; + tmp += hash16; + tmp += hashtype_suffix; + tmp += ")"; + tmp += write_all_suffix; + out_line += tmp; + dprintf("changed call: %s...\n", tmp.c_str()); + line = suffix; + } + + out_line += line; + + if (out_line[out_line.length() - 1] != '\n') + { + out_line += "\n"; + } +} + +//***************************************************************************** +// main +//***************************************************************************** +int main(int argc, char** argv) +{ + char* p_env = getenv("PPETRACEPPDEBUG"); + + if (p_env) + { + debug = true; + } + + + int argi = 1; + string arg; + string optarg; + + if (argc > 1) + { + arg = argv[1]; + } + + if ((argc < 2) || (arg == "-h")) + { + fprintf(stderr, "usage: %s realcompiler compileroptions -o target source\n", argv[0]); + exit(9); + } + + string realcc(argv[argi++]); + string cctype("c++"); + bool optx_found = false; + + if (realcc == "-d") + { + debug = true; + realcc = argv[argi++]; + } + + // wait until -d options is handled before checking $debug + dprintf("ppetracepp version %s - API/macro version %s\n", version.c_str(), macro_version.c_str()); + + p_env = getenv("REALCPP"); + string realcpp; + + if (p_env) + { + realcpp = p_env; + } + + if (realcpp.empty()) + { + dprintf("cannot find cpp, using <realcompiler> -E\n"); + realcpp = realcc; + realcpp += " -E"; + } + + dprintf("realcpp is %s\n", realcpp.c_str()); + +//------------------------------------------------------------------------------ +// parse all the arguments +//------------------------------------------------------------------------------ + string source; + string object; + vector<string> ccopts; + vector<string> cppopts; + bool dodeps = false; + string depfile; + string pfx; + string sfx; + int origargi = argi; + + for( ; argi < argc; ++argi) + { + arg = argv[argi]; + dprintf("Processing argv[%d]: \"%s\"\n", argi, arg.c_str()); + + if (arg.length() > 2) + { + pfx = arg.substr(0, 2); + sfx = arg.substr(arg.length() - 2); + } + else + { + pfx = arg; + sfx = arg; + } + + dprintf(" pfx: \"%s\" sfx: \"%s\"\n", pfx.c_str(), sfx.c_str()); + + if (pfx == "-o") + { + if (! object.empty()) + { + fprintf(stderr, "two -o options, aborting\n"); + exit(1); + } + + if (arg.length() > 2) + { + object = sfx; + } + else + { + object = argv[++argi]; + } + + dprintf("object is now %s\n", object.c_str()); + } + else if (arg == "-c") + { + // don't call cpp with -c, this is for the compiler + ccopts.push_back(arg); + dprintf("found -c option\n"); + } + else if (pfx == "-l") + { + // cpp doesn't need library arguments + cppopts.push_back(arg); + } + else if (pfx == "-i") + { + // option takes an argument, handle it too + optarg = argv[argi++]; + ccopts.push_back(arg); + ccopts.push_back(optarg); + cppopts.push_back(arg); + cppopts.push_back(optarg); + dprintf("found option '%s %s'\n", arg.c_str(), optarg.c_str()); + } + else if ((arg == "-L") || + (arg == "-I") || + (arg == "-x") || + (arg == "-b") || + (arg == "-B") || + (arg == "-V") || + (arg == "-D") || + (arg == "--param") || + (arg == "-MQ") || + (arg == "-MT")) + { + // option takes an argument, handle it too + optarg = argv[argi++]; + ccopts.push_back(arg); + ccopts.push_back(optarg); + cppopts.push_back(arg); + cppopts.push_back(optarg); + dprintf("found option '%s %s'\n", arg.c_str(), optarg.c_str()); + + if (arg == "-x") + { + // option x sets the language - c or c++ + if ((optarg != "c") && (optarg != "c++") && (optarg != "assembler-with-cpp")) + { + fprintf(stderr, "cannot process language '%s', aborting\n", optarg.c_str()); + exit(1); + } + + cctype = optarg; + optx_found = true; + } + } + else if ((arg == "-MD") || (arg == "-MG")) + { + // gen deps + dodeps = true; + dprintf("found %s, creating dependencies\n", arg.c_str()); + } + else if (arg == "-MF") + { + // set dependencies file + depfile = argv[argi++]; + dprintf("set dependencies file to '%s'\n", depfile.c_str()); + } + else if (arg[0] == '-') + { + // arg starts with - so it's an option + ccopts.push_back(arg); + cppopts.push_back(arg); + dprintf("found option '%s'\n", arg.c_str()); + } + else if ((sfx == ".a") || + (sfx == ".o")) + { + // an object or archive, ignore this but give it to cc + ccopts.push_back(arg); + dprintf("found object/archive '%s'\n", arg.c_str()); + } + else if ((sfx == ".c") || + (sfx == ".C") || + (sfx == ".S") || + (arg.substr(arg.length() - 4) == ".cpp") || + (arg.substr(arg.length() - 4) == ".cxx")) + { + // the source file(s). we should only get one + if (!source.empty()) + { + fprintf(stderr, "don't know to handle two source files, aborting\n"); + exit(1); + } + + source = arg; + // put the - (for read-from-stdin) where the source file was + // (order is important!) + ccopts.push_back("-"); + dprintf("found source file %s\n", source.c_str()); + } + else if (access(arg.c_str(), F_OK)) + { + // option but not a file, an unknown option? + ccopts.push_back(arg); + cppopts.push_back(arg); + dprintf("found unknown option '%s'\n", arg.c_str()); + } + } + +//------------------------------------------------------------------------------ +// set other parameters based on arguments specified +//------------------------------------------------------------------------------ + if (source.empty()) + { + // this might be a call to link a program instead of compile a source (or asm source) + dprintf("NOTME: starting as cc '%s ...'\n", realcc.c_str()); + execvp(realcc.c_str(), &(argv[origargi])); + fprintf(stderr, "ERROR: returned from execvp() call to run %s\n", realcc.c_str()); + } + + if (object.empty()) + { + dprintf("no object file given, default to source name\n"); + // gcc builds object name from source name if no -o given, replacing + // suffix with .o. The file is placed in the current directory, + // not in the source directory! + string n; + string d; + string s; + fileparse(source, n, d, s); + + if (!n.empty() && !s.empty()) + { + object = n + ".o"; + dprintf("tracpp: guessing object name %s\n", object.c_str()); + dprintf(" from source name %s\n", source.c_str()); + } + else + { + fprintf(stderr, "Unable to determine Source File Name\n"); + exit(1);; + } + } + +// set value of trace hash according to language +// check source file extension if no explicit -x option given + if (!optx_found) + { + if (realcc.find("g++") != string::npos) + { + dprintf("compiler language: C++ (from compiler name)\n"); + cctype = "c++"; + } + else + { + if (source.substr(source.length() - 2) == ".c") + { + dprintf("compiler language: C (from source file extension)\n"); + cctype = "c"; + } + else if (source.substr(source.length() - 2) == ".S") + { + dprintf("compiler language: assembly (from source file extension)\n"); + cctype = "assembler-with-cpp"; + } + else + { + dprintf("compiler language: C++ (default)\n"); + cctype = "c++"; + } + } + } + else + { + dprintf("compiler language: %s (from option '-x')\n", cctype.c_str()); + } + + if (cctype == "c") + { + hashtype = "(unsigned short)"; + hashtype_suffix = "U"; + } + else if (cctype == "assembler-with-cpp") + { + hashtype = ""; + hashtype_suffix = ""; + } + else + { + hashtype = "(trace_hash_val)"; + hashtype_suffix = "U"; + } + +// define TRAC_PPETRACEPP for macros + tmp = "-DTRAC_PPETRACEPP -DTRAC_PPVER="; + tmp += macro_version; + cppopts.push_back(tmp); + + if (dodeps) + { + if (depfile.empty()) + { + if ((p_env = getenv("DEPENDENCIES_OUTPUT")) != NULL) + { + depfile = p_env; + } + else if ((p_env = getenv("SUNPRO_DEPENDENCIES")) != NULL) + { + depfile = p_env; + } + else + { + depfile = object; + + if (depfile.substr(depfile.length() - 2) == ".o") + { + depfile = depfile.substr(0, depfile.length() - 2); + depfile += ".d"; + } + } + } + + tmp = "-MD -MF "; + tmp += depfile; + cppopts.push_back(tmp); + } + +//------------------------------------------------------------------------------ +// start cpp +//------------------------------------------------------------------------------ + cmd = realcpp; + + for(vector<string>::iterator p = cppopts.begin(); p != cppopts.end(); ++p) + { + cmd += " "; + cmd += *p; + } + + cmd += " "; + cmd += source; + cmd += " -o-"; + dprintf("starting as cpp '%s'\n", cmd.c_str()); + CPP = popen(cmd.c_str(), "r"); + + if (CPP == NULL) + { + fprintf(stderr, "cannot start cpp '%s'\n", realcpp.c_str()); + perror(""); + exit(1); + } + +//------------------------------------------------------------------------------ +// start cc. manually set language as source file extension not available to cc +//------------------------------------------------------------------------------ + string type_str = ""; + + if (!optx_found) + { + // no option -x given by caller, set manually + type_str = "-x "; + type_str += cctype; + } + + cmd = realcc; + cmd += " "; + cmd += type_str; + + for(vector<string>::iterator p = ccopts.begin(); p != ccopts.end(); ++p) + { + cmd += " "; + cmd += *p; + } + + cmd += " -o "; + cmd += object; + dprintf("starting as cc '%s'\n", cmd.c_str()); + CC = popen(cmd.c_str(), "w"); + + if (CC == NULL) + { + fprintf(stderr, "cannot start cc '%s'\n", realcc.c_str()); + perror(""); + exit(1); + } + + string modifiedfile; + string unmodifiedfile; + + if (debug) + { + modifiedfile = object + ".debug"; + DEBUG = fopen(modifiedfile.c_str(), "w"); + + if (DEBUG == NULL) + { + string msg = "cannot open file "; + msg += modifiedfile; + perror(msg.c_str()); + modifiedfile = ""; + } + else + { + fprintf(stderr, "writing preprocessed source to %s\n", modifiedfile.c_str()); + } + + unmodifiedfile = object + ".debug_in"; + DEBUGIN = fopen(unmodifiedfile.c_str(), "w"); + + if (DEBUGIN == NULL) + { + string msg = "cannot open file "; + msg += unmodifiedfile; + perror(msg.c_str()); + unmodifiedfile = ""; + } + else + { + fprintf(stderr, "writing unprocessed source to %s\n", unmodifiedfile.c_str()); + } + } + + string oldline; + string newline; + static const int MAX_BUFFER = 51200; + char buf[MAX_BUFFER]; + + while (!feof(CPP)) + { + if (fgets(buf, MAX_BUFFER, CPP) != NULL) + { + oldline = buf; + + if (DEBUGIN) + { + fprintf(DEBUGIN, "%s", oldline.c_str()); + } + + parse_line(hashtab, oldline, newline); + + //#print "oldline = $oldline"; + //#print "newline = $newline"; + if (newline.empty()) + { + fprintf(stderr, "hash error in/with file %s\n", source.c_str()); + exit(1); + } + + //#print "newline = $newline\n"; + fprintf(CC, "%s", newline.c_str()); + + if (DEBUG) + { + fprintf(DEBUG, "%s", newline.c_str()); + } + } + } + + if (DEBUG) + { + fclose(DEBUG); + } + + if (DEBUGIN) + { + fclose(DEBUGIN); + } + + int cmd_rc = pclose(CPP); + + if (cmd_rc) + { + fprintf(stderr, "error from cpp\n"); + + if (cmd_rc & 127) + { + fprintf(stderr, "cpp got signal %d\n", (cmd_rc & 127)); + exit(1); + } + else if (cmd_rc >> 8) + { + fprintf(stderr, "cpp returned %d\n", (cmd_rc >> 8)); + exit(cmd_rc >> 8); + } + } + + cmd_rc = pclose(CC); + + if (cmd_rc) + { + fprintf(stderr, "error from cc\n"); + + if (cmd_rc & 127) + { + fprintf(stderr, "cc got signal %d\n", (cmd_rc & 127)); + exit(1); + } + else if (cmd_rc >> 8) + { + fprintf(stderr, "cc returned %d\n", (cmd_rc >> 8)); + exit(cmd_rc >> 8); + } + } + + if (!hashtab.empty()) + { + string stringfile = object; + stringfile += ".ppe.hash"; + // open trace string file + FILE* TRC = fopen(stringfile.c_str(), "w"); + + if (TRC == NULL) + { + fprintf(stderr, "cannot write trace string file '%s'\n", stringfile.c_str()); + exit(1); + } + + dprintf("Writing to file %s\n", stringfile.c_str()); + + string pwd; + FILE* PWD = popen("pwd", "r"); + fgets(buf, MAX_BUFFER, PWD); + pwd = buf; + pclose(PWD); + time_t tt = time(NULL); + sprintf(buf, "%s", asctime(localtime(&tt))); + buf[strlen(buf) - 1] = '\0'; // chop off extra newline + fprintf(TRC, "#FSP_TRACE_v2|||%s|||BUILD:%s", buf, pwd.c_str()); + + string srch_str = "||"; + srch_str += source; + int srch_str_len = srch_str.length(); + size_t pos; + + for(map<string, string>::iterator p = hashtab.begin(); p != hashtab.end(); ++p) + { + pos = (p->second).find(srch_str); + + if ((pos != string::npos) && ((pos + srch_str_len) == (p->second).length())) + { + // source file name is already part of the string + fprintf(TRC, "%s||%s\n", (p->first).c_str(), (p->second).c_str()); + } + else + { + fprintf(TRC, "%s||%s||%s\n", (p->first).c_str(), (p->second).c_str(), source.c_str()); + } + + //#print TRC "$key||$source||$hashtab{$key}\n"; + } + + fclose(TRC); + } + else + { + dprintf("No trace calls/strings found, not writing hash file\n"); + } +} // end main diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/trac_interface.h b/import/chips/p9/procedures/ppe/tools/ppetracepp/trac_interface.h new file mode 100755 index 00000000..79b9329c --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/trac_interface.h @@ -0,0 +1,376 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/trac_interface.h $ */ +/* */ +/* OpenPOWER HCODE Project */ +/* */ +/* COPYRIGHT 2011,2017 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/****************************************************************************** +// @file trac_interface.h +// @brief Interface codes for TRAC component. +*/ +/****************************************************************************** + * + * @page ChangeLogs Change Logs + * @section _trac_interface_h trac_interface.h + * @verbatim + * + * Flag Def/Fea Userid Date Description + * ------- ---------- -------- ---------- ---------------------------------- + * TEAM 06/16/2010 Port + * @rc003 rickylie 02/03/2012 Verify & Clean Up OCC Headers & Comments + * @pb00E pbavari 03/11/2012 Added correct include file + * @at009 859308 alvinwan 10/15/2012 Added tracepp support + * @ai005 860268 ailutsar 11/20/2012 Create trace test applet + * @rc005 864101 rickylie 12/12/2012 add small circ buffer to handle ISR semaphore conflict + * + * @endverbatim + * + *///*************************************************************************/ + +#ifndef _TRAC_INTERFACE_H +#define _TRAC_INTERFACE_H + +/** \defgroup Trace Trace Component + * Port of the trace code used in the fsp and tpmd. + */ + +//************************************************************************* +// Includes +//************************************************************************* + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* +/** + * \defgroup TracMacros Trace Component Macro Interfaces + * \ingroup Trace + * Used to trace 0 - 5 arguments or a binary buffer when using a hash value. + */ +/*@{*/ + + +// @at009c - start +#define TRACE(i_td,i_string,args...) \ + trace_adal_write_all(i_td,trace_adal_hash(i_string,-1),__LINE__,0,##args) + +#define TRACEBIN(i_td,i_string,i_ptr,i_size) \ + trac_write_bin(i_td,trace_adal_hash(i_string,0),__LINE__,i_ptr,i_size) + +#ifndef NO_TRAC_STRINGS + +#define FIELD(a) \ + printf("%s",a) + +#define FIELD1(a,b) \ + printf("%s%lx",a,(unsigned long)b) + +#else // NO_TRAC_STRINGS + +#define FIELD(a) + +#define FIELD1(a,b) + +#endif // NO_TRAC_STRINGS + +#define SUCCESS 0 +// @at009c - end + + + +/*@}*/ + +//************************************************************************* +// Defines/Enums +//************************************************************************* +#define TRACE_MAX_ARGS 5 /*!< Maximum number of args to trace */ + +typedef uint32_t trace_hash_val; + +// NOTE! Increment this when new components are added! +#define TRAC_NUM_TRACE_COMPONENTS 1 + + +#define TRACE_BUFFER_SIZE 8192 // @ai005a + +#define CIRCULAR_BUFFER_SIZE 4 // @rc005a + +typedef uint32_t UINT32; +typedef int32_t INT32; +typedef uint8_t UCHAR; +typedef uint8_t UINT8; +typedef int8_t INT8; +typedef uint16_t UINT16; +typedef int16_t INT16; +typedef char CHAR; +typedef unsigned int UINT; +typedef unsigned long ULONG; +typedef int INT; +typedef void VOID; + +//************************************************************************* +// Structures +//************************************************************************* +/* + * @brief Structure is put at beginning of all trace buffers + */ +typedef struct trace_buf_head +{ + UCHAR ver; /*!< version of this struct (1) */ + UCHAR hdr_len; /*!< size of this struct in bytes */ + UCHAR time_flg; /*!< meaning of timestamp entry field */ + UCHAR endian_flg; /*!< flag for big ('B') or little ('L') endian */ + CHAR comp[16]; /*!< the buffer name as specified in init call */ + UINT32 size; /*!< size of buffer, including this struct */ + UINT32 times_wrap; /*!< how often the buffer wrapped */ + UINT32 next_free; /*!< offset of the byte behind the latest entry */ + UINT32 te_count; /*!< Updated each time a trace is done */ + UINT32 extracted; /*!< Not currently used */ +} trace_buf_head_t; + +/*! + * @brief Timestamp and thread id for each trace entry. + */ +typedef struct trace_entry_stamp +{ + UINT32 tbh; /*!< timestamp upper part */ + UINT32 tbl; /*!< timestamp lower part */ + UINT32 tid; /*!< process/thread id */ +} trace_entry_stamp_t; + +/* + * @brief Structure is used by adal app. layer to fill in trace info. + */ +typedef struct trace_entry_head +{ + UINT16 length; /*!< size of trace entry */ + UINT16 tag; /*!< type of entry: xTRACE xDUMP, (un)packed */ + UINT32 hash; /*!< a value for the (format) string */ + UINT32 line; /*!< source file line number of trace call */ +} trace_entry_head_t; + +/* + * @brief Parameter traces can be all contained in one write. + */ +typedef struct trace_entire_entry +{ + trace_entry_stamp_t stamp; + trace_entry_head_t head; + UINT32 args[TRACE_MAX_ARGS + 1]; +} trace_entire_entry_t; + + +/* + * @brief Binary first writes header and time stamp. + */ +typedef struct trace_bin_entry +{ + trace_entry_stamp_t stamp; + trace_entry_head_t head; +} trace_bin_entry_t; + +/** + * @brief Used as input to traces to get to correct buffer. + */ +typedef trace_buf_head_t* tracDesc_t; + +/* + * @brief Structure is used to hold array of all trace descriptors + */ +typedef struct trace_descriptor_array +{ + tracDesc_t* entry; /*!< Pointer to trace descriptor */ + CHAR* comp; /*!< Pointer to component name */ +} trace_descriptor_array_t; + +// @rc005a - start +typedef struct circular_buf_head +{ + UINT32 head; // pointer to head + UINT32 tail; // pointer to tail + UINT32 entryCount; // nums of entry +} circular_buf_header_t; + + +typedef struct circular_entire_data +{ + UINT32 len; + CHAR comp[4]; + trace_entire_entry_t entry; +} circular_entire_data_t; + +// @rc005a - end + +//************************************************************************* +// Globals +//************************************************************************* +// All TPMF component trace descriptors. +extern tracDesc_t g_trac_inf; // @at009a +extern tracDesc_t g_trac_err; // @at009a +extern tracDesc_t g_trac_imp; // @at009a + +extern const trace_descriptor_array_t g_des_array[]; + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +/** + * \defgroup TracIntefaces Trace Component Interfaces for External Use + * \ingroup Trace + */ +/*@{*/ + + +/** + * @brief Allocate and initialize all trace buffers in memory. + * + * This function will allocate memory for each of the pre-defined trace + * buffers, initilize the buffers with starting data, and setup the + * trace descriptors which each component will use to trace. + * + * This function must be called first before any components try to trace! + * + * @return Non-zero return code on error. + */ +UINT TRAC_init_buffers(void); + +/** + * @brief Retrieve full trace buffer for component i_comp + * + * This function assumes memory has already been allocated for + * the full trace buffer in o_data. + * + * @param i_td_ptr Trace descriptor of buffer to retrieve. + * @param o_data Pre-allocated pointer to where data will be stored. + * + * @return Non-zero return code on error + */ +UINT TRAC_get_buffer(const tracDesc_t i_td_ptr, + void* o_data); + +/** + * @brief Retrieve partial trace buffer for component i_comp + * + * This function assumes memory has already been allocated for + * the trace buffer (size io_size). This function will copy + * in up to io_size in bytes to the buffer and set io_size + * to the exact size that is copied in. + * + * @param i_td_ptr Trace descriptor of buffer to retrieve. + * @param o_data Pre-allocated pointer to where data will be stored. + * @param io_size Size of trace data to retrieve (input) + * Actual size of trace data stored (output) + * + * @return Non-zero return code on error + */ +UINT TRAC_get_buffer_partial(const tracDesc_t i_td_ptr, + void* o_data, + UINT* io_size); + +/** + * @brief Retrieve trace descriptor for input component name + * + * @param i_comp Component name to retrieve trace descriptor for. + * + * @return Valid trace descriptor on success, NULL on failure. + */ +tracDesc_t TRAC_get_td(const char* i_comp); + +/** + * @brief Reset all trace buffers + * + * @return Non-zero return code on error + */ +UINT TRAC_reset_buf(void); + +/*@}*/ // Ending tag for external interface module in doxygen + + +/** + * @brief Trace input integers to trace buffer. + * + * This function assumes i_td has been initialized. + * + * @param io_td Intialized trace descriptor point to buffer to trace to. + * @param i_hash Hash value to be recorded for this trace. + * @param i_fmt Output format + * @param i_line Line number trace is occurring on. + * @param i_type trace type. filed or debug. + * @param ... parames that are limited to a size of 4 bytes, i.e. int, uint32_t, nnn* + * + * @return Non-zero return code on error. + */ +UINT trace_adal_write_all(tracDesc_t io_td, const trace_hash_val i_hash, + const char* i_fmt, const ULONG i_line, const ULONG i_type, ...); + + +/** + * @brief Trace input integers to trace buffer. + * + * This function assumes i_td has been initialized. + * + * @param io_td Intialized trace descriptor point to buffer to trace to. + * @param i_hash Hash value to be recorded for this trace. + * @param i_line Line number trace is occurring on. + * @param i_num_args Number of arguments to trace. + * @param i_1 Input Parameter 1 + * @param i_2 Input Parameter 2 + * @param i_3 Input Parameter 3 + * @param i_4 Input Parameter 4 + * @param i_5 Input Parameter 5 + * + * @return Non-zero return code on error. + */ +UINT trac_write_int(tracDesc_t io_td, const trace_hash_val i_hash, + const ULONG i_line, + const UINT i_num_args, + const ULONG i_1, const ULONG i_2, const ULONG i_3, + const ULONG i_4, const ULONG i_5 + ); + + +/** +* @brief Trace binary data to buffer. +* +* This function assumes i_td has been initialized. +* +* @param io_td Intialized trace descriptor point to buffer to trace to. +* @param i_hash Hash value to be recorded for this trace. +* @param i_line Line number trace is occurring on. +* @param i_ptr Pointer to binary data to trace. +* @param i_size Size of data to copy from i_ptr. +* +* @return Non-zero return code on error. +*/ +UINT trac_write_bin(tracDesc_t io_td, const trace_hash_val i_hash, + const ULONG i_line, + const void* i_ptr, + const ULONG i_size); + +//************************************************************************* +// Functions +//************************************************************************* + +#endif //_TRAC_INTERFACE_H diff --git a/import/chips/p9/procedures/ppe/tools/ppetracepp/tracehash.pl b/import/chips/p9/procedures/ppe/tools/ppetracepp/tracehash.pl new file mode 100755 index 00000000..490354c3 --- /dev/null +++ b/import/chips/p9/procedures/ppe/tools/ppetracepp/tracehash.pl @@ -0,0 +1,896 @@ +#!/usr/bin/perl -w +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: import/chips/p9/procedures/ppe/tools/ppetracepp/tracehash.pl $ +# +# OpenPOWER HCODE Project +# +# COPYRIGHT 2015,2017 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# File tracehash.pl created by B J Zander. + +use strict; + +sub determine_args(); +sub launch_cpp_and_parse($$); +sub cpp_dir($); +sub read_string_file(); +sub collect_files($); +sub assimilate_file($); +sub hash_strings(); +sub write_string_file(); +sub help(); + +select (STDERR); +$| = 1; # Make all prints to STDERR flush the buffer immediately +select (STDOUT); +$| = 1; # Make all prints to STDOUT flush the buffer immediately + +# Constants +my $HEAD_SEP = "|||"; +my $HEAD_EYE_CATCHER = "#FSP_TRACE_v"; +my $HEAD_BUILD_FLAG = "BUILD:"; +my $HEAD_VER_FLAG = 2; +my $BB_STRING_FILE = "/opt/fsp/etc/BB_StringFile"; + +# Global Variables +my $debug = 0; +my $seperator = "&&&&"; +my $file_name = "trexStringFile"; +my $in_sand; +my ($backing) = $ENV{'bb'}; +my $hash_prog = "trexhash"; #default to in path +my $build = ""; +my ($sandbox) = $ENV{'SANDBOX'} || ""; +my ($context) = $ENV{'CONTEXT'} || ""; +my ($sandboxbase) = $ENV{'SANDBOXBASE'} || ""; +my ($bb); +my ($sourcebase) = "$sandboxbase/src"; +my ($version) = $HEAD_VER_FLAG; # default to oldest version +my ($collect) = 0; +my ($INCLUDE, $Arg, $file, $dir, $string_file); +my $args = ""; + +my $fail_on_collision = 0; # 1 = exit with error if hash collision occurs +my $hash_filename_too = 0; # 1 = hash is calculated over format string + filename + +print "sourcebase = $sourcebase\n" if $debug; +print "sandbox = $sandbox\n" if $debug; +print "backing = $backing\n" if $debug; +print "context = $context\n" if $debug; + +if ($context =~ /x86/) +{ + $bb = "i586-pc-linux-gnu"; +} +else +{ + $bb = "powerpc-linux"; +} + +if(($sourcebase =~ /\w+/) && ($sandbox =~ /\w+/)) +{ + $INCLUDE = "-I $sandboxbase/export/$context/fips/include -I $backing/export/$context/fips/include -I /opt/fsp/$bb/include/fsp -I/opt/fsp/$bb/include/ -include /opt/fsp/$bb/include/fsp/tracinterface.H"; +} +else +{ + print "Not in Sandbox so guessing Include Paths...\n" if $debug; + $INCLUDE = "-I/opt/fsp/i586-pc-linux-gnu/include/fsp -I/opt/fsp/i586-pc-linux-gnu/include/ -include /opt/fsp/i586-pc-linux-gnu/include/fsp/tracinterface.H"; +} + +# I/P Series work in ODE sandbox env. +if ($sandboxbase =~ /\w+/) +{ + $in_sand = 1; + print "backing = $backing\n" if $debug; +} +else +{ + $in_sand = 0; +} + + + +# Parse the input parameters. + +while (@ARGV) { + $Arg = shift; + + if ($Arg eq "-h" || $Arg eq "-H") { + help(); + exit(127); + } + if ($Arg eq "-f") { + $file = shift; + next; + } + if ($Arg eq "-d") { + $dir = shift; + next; + } + if ($Arg eq "-s") { + $string_file = shift; + next; + } + if ($Arg eq "-c") { + $collect = 1; + next; + } + if ($Arg eq "-v") { + $debug = 1; + print "debug on\n" if $debug; + next; + } + if ($Arg eq "-C") { # fail if a hash collision is detected + $fail_on_collision = 1; + next; + } + if ($Arg eq "-F") { # hash is calculated over format string + file name + $hash_filename_too = 1; + next; + } + if ($Arg eq "-S") { + $BB_STRING_FILE = ""; + next; + } + + #just pass it onto compiler + $args = $args . " " . $Arg; +} + +print "args = $args\n" if $debug; + +if (!$file && !$dir && !$in_sand) { + help(); + exit(127); +} + +################################# +# M A I N # +################################# + +my $clock = `date`; + +$build = $HEAD_EYE_CATCHER . "$HEAD_VER_FLAG" . $HEAD_SEP . $clock . $HEAD_SEP . $HEAD_BUILD_FLAG; + +$build =~ s/\n//g; + +# Global array to hold the parsed TRAC macro calls. +my @strings = (); + +# Assoc. arrays to hold hash|string values. +my %string_file_array = (); +my %hash_strings_array = (); + +# Check all provided arguments and look for defaults if not provided by user +determine_args(); + +# Scan the appropriate files or directories for TRAC macro calls. + +if (defined $dir) +{ + + $build = $build . $dir; # default to put at top of string file + if($collect) + { + collect_files($dir); + } + else + { + cpp_dir($dir); + # Hash the string that have been scanned. + %hash_strings_array = hash_strings(); + } +} +else +{ + $build = $build . $file; # default to put at top of string file + + if($collect) + { + assimilate_file($file); + } + else + { + # make sure include path includes directory that file is in + if($file =~ /^(.+)\/[^\/]+\.C$/) + { + + launch_cpp_and_parse($file,$1); + } + else + { + # No path in front of file so it has to be local dir + launch_cpp_and_parse($file,"./"); + } + # Hash the string that have been scanned. + %hash_strings_array = hash_strings(); + } +} + +# Read the existing string file into memory. +%string_file_array = read_string_file(); + +# Write out the new string file. check for collisions of new/old string here +write_string_file(); + +print "Hashing Started at $clock\n"; +$clock = `date`; +print "Hashing Finished at $clock\n"; + +exit 0; + + +################################# +# S U B R O U T I N E S # +################################# + +#============================================================================= +# Enhance usability by figuring out which build env. we are in +#============================================================================= +sub determine_args() { + + + # Find trexhash program + # but only if needed (i.e. not in collect mode) + if (!$collect) { + my $tmp = `which $hash_prog`; + chomp $tmp; + + if ($tmp eq '') { + print STDOUT "\nWarning: Program trexhash does not exist in path.\n" if $debug; + $hash_prog = "./trexhash"; + + $tmp = `which $hash_prog`; + chomp $tmp; + if ($tmp eq '') { + print STDOUT "\nError: Unable to find trexhash \n"; + exit(127); + } + } + } + + # Verify input values. + if ((!defined $file) && (!defined $dir)) { + if(!($in_sand)) + { + print STDOUT "\nError: No input directory or file provided as input to scan\n"; + exit(127); + } + + # Assume they want sandbox scanned + if($collect) + { + # collect all string files generated by tracepp and merge + $dir = "$sandboxbase/obj/"; + } + else + { + # generate our own string file by pre-compiling all source code + $dir = "$sandboxbase/src/"; + } + print STDOUT "\n-f <file> or -d <dir> not found...scanning $dir by default\n\n"; + } + + if (!defined $string_file) + { + if ($in_sand) + { + + # Copy the current string file from backing build into our sandbox + system ("cp $backing/obj/$file_name $sandboxbase/obj/$file_name") + if !(-e "$sandboxbase/obj/$file_name"); + + $string_file = "$sandboxbase/obj/$file_name"; + } + else + { + $string_file = "./$file_name"; + } + print STDOUT "-sf <string_file> not specified, using $string_file instead...\n\n" if $debug; + + } + + # Try Creating the string file + `touch $string_file`; + + if (! -f $string_file) { + print STDOUT "\nError: File $string_file does not exist. Current directory may not be writable.\n\n"; + help(); + exit(127); + } + + # Make sure trexStringFile is readable/writeable + system("chmod ugo+rw $string_file"); + +} + +#============================================================================= +# Launch cpp script and grab input from it looking for trace calls. +#============================================================================= +sub launch_cpp_and_parse($$) { + + my ($l_loc, $l_dir) = @_; + + print "Processing file $l_loc\n" if $debug; + my $cmd = "/usr/bin/cpp $INCLUDE -I $l_dir $args $l_loc|"; + print "$cmd\n" if $debug; + open(FH,"$cmd") + or die ("Cannot open $_:$!,stopped"); + + # Read through all lines in the file.. + my $line = <FH>; + while (defined $line) + { + chop $line; # remove EOL + $line =~ s/^\s*//; # remove unneccesary beginning white space. + $line =~ s/\s*$//; # remove unneccesary ending white space. + # Look for lines that are trace macro calls. + #if (/(trace_adal_hash)(\()( *)(".+")(,)(\d)/) + #if ($line =~ /(.*?)(trace_adal_hash)(\()( *)(".+")(,)(\d)\)+(.*\d.*)/) + while($line =~ m/^(.*?)trace_adal_hash\s*\(\s*(("[^"]*"\s*)+),\s*(\d+)\s*\)(.*)$/) + { + my ($prefix, $strings, $salt, $suffix) = ($1, $2, $4, $5); + print STDOUT "$strings $salt\n" if $debug; + $strings =~ s/"\s*$//; # remove trailing " and space + $strings =~ s/^"//; # remove leading " + $strings =~ s/"\s*"//g; + # Check to see if it's contained on a single line, or if we + # have to combine lines to get a complete trace call. + + # Save the macro call so it can be hashed later.. + push (@strings, [$l_loc, $strings, $salt]); + $line = $suffix; # check rest of line for a second trace call + } + my $nextline = <FH>; + last if !defined $nextline; + # if a trace call is spread over multiple lines we have to add the next + # line from the source. the only problem is the definition/declaration + # of trace_adal_hash: we have to ignore that. we catch that by requiring + # a " after the function name. hopefully nobody writes a comment with + # a " after the function declaration ... + if ($line =~ /trace_adal_hash.*"/) { + $line .= $nextline; + } else { + $line = $nextline; + } + } + close(FH); +} + +#============================================================================= +# run cpp on all files in this directory and return the output +#============================================================================= +sub cpp_dir($) { + + my ($l_dir) = @_; + my @dir_entry; + my $l_entry; + + # Open the directory and read all entry names. + opendir ( DH , "$l_dir") + or die ("Cannot open $l_dir: $!, stopped"); + + print STDOUT "Processing directory $l_dir\n" if $debug; + @dir_entry = readdir(DH); + closedir(DH); + + while (@dir_entry) { + $l_entry = shift(@dir_entry); + + if ($l_dir =~ m"/$") { + $l_entry = "$l_dir$l_entry"; + } + else { + $l_entry = "$l_dir/$l_entry"; + } + + # Is the entry a directory? + if (-d $l_entry) { + + if($l_entry =~ m"/?([^/]+)$") + { + # check dir we are going into + print "dir = $1\n" if $debug; + # should we recurse into this directory. + if ($1 =~ m/^(\.\.?|sim[ou]|bldv)$/) + { + next; # skip '.', '..' and some fips dirs + } + cpp_dir($l_entry); + } + else + { + # unable to determine name of dir (no / in filename) + # should we recurse into this directory. + if ($l_entry =~ m/^(\.\.?|sim[ou]|bldv)$/) + { + next; # skip '.', '..' and some fips dirs + } + cpp_dir($l_entry); + } + } + # Is the entry a file? + elsif ((-f $l_entry) && ($l_entry =~ m/\.C$/)) { + # it's a file so + launch_cpp_and_parse($l_entry,$l_dir); + } + else { + # Not a file or directory so ignore it... + } + } +} + +#============================================================================= +# Read in strings from the existing trace string file.... +#============================================================================= +sub read_string_file() { + + my %o_strings; + my ($line) = ""; + my ($l_hash) = ""; + my ($l_str) = ""; + my ($cur_build) = ""; + my ($l_file) = ""; + + + # Make sure we can open each file. + open ( FH , "<$string_file") + or die ("Cannot open $_: $!, stopped"); + + $line = <FH>; + + print "first line in trexStringFile= $line\n" if $debug; + + if((defined $line) && ($line =~ /^$HEAD_EYE_CATCHER(\d)/)) + { + $version = $1; + + print "version = $version\n" if $debug; + + #Always put latest version in file + $line =~ s/^$HEAD_EYE_CATCHER\d/${HEAD_EYE_CATCHER}${HEAD_VER_FLAG}/; + + # Take previous version in file and use it. + $build = $line; + chomp($build); + $line = <FH>; + + while (defined $line) { + chomp $line; # remove EOL + if($version eq "1") + { + ($l_hash, $l_file ,$l_str) = split(/\|\|/, $line); + } + elsif($version eq "2") + { + ($l_hash, $l_str ,$l_file) = split(/\|\|/, $line); + } + else + { + print "Unknown version of stringfile $version\n"; + exit(127); + } + $o_strings{$l_hash} = $l_str . "||" . $l_file; + $line = <FH>; + } + + } + else + { # If there is a file then we are dealing with the first + # version of trexStringFile so don't look for file name. + if ($debug) { + print "version 0 stringfile detected: $string_file\n"; + } + + # there is a file and it doesn't have a header + $version = 0; + + while (defined $line) { + chomp $line; # remove EOL + ($l_hash,$l_str) = split(/\|\|/, $line); + $o_strings{$l_hash} =$l_str . "||" . "NO FILE"; + $line = <FH>; + } + } + + close(FH); + + #Time to look for a building block string file + if($BB_STRING_FILE ne "" and $string_file ne $BB_STRING_FILE and -f $BB_STRING_FILE) + { + + # Make sure we can open the file. + open ( FH , "<$BB_STRING_FILE") + or die ("Cannot open $_: $!, stopped"); + + $line = <FH>; + + print "first line in BB_StringFile = $line\n" if $debug; + if((defined $line) && ($line =~ /^$HEAD_EYE_CATCHER(\d)/)) + { + $version = $1; + + $line = <FH>; + while (defined $line) + { + chop $line; # remove EOL + if($version eq "1") + { + ($l_hash, $l_file ,$l_str) = split(/\|\|/, $line); + } + elsif($version eq "2") + { + ($l_hash, $l_str ,$l_file) = split(/\|\|/, $line); + } + #($l_hash, $l_file ,$l_str) = split(/\|\|/, $line); + $o_strings{$l_hash} = $l_str . "||" . $l_file ; + $line = <FH>; + } + } + else + { + print "*** ERROR: BB_StringFile '$BB_STRING_FILE' should always have version!!!\n" + } + + } + else + { + print "$BB_STRING_FILE is not available\n" if $debug; + } + #All files are latest version now. + $version = $HEAD_VER_FLAG; + return %o_strings; +} + +#============================================================================= +# Read in strings from the existing trace string file.... +#============================================================================= +sub collect_files($) { + + my ($l_dir) = @_; + my (@dir_entry); + my ($l_entry) = ""; + + # Open the directory and read all entry names. + opendir ( DH , "$l_dir") + or die ("Cannot open $l_dir: $!, stopped"); + + print STDOUT "Processing directory $l_dir\n" if $debug; + @dir_entry = readdir(DH); + closedir(DH); + + while (@dir_entry) { + $l_entry = shift(@dir_entry); + + if ($l_dir =~ m"/$") { + $l_entry = "$l_dir$l_entry"; + } + else { + $l_entry = "$l_dir/$l_entry"; + } + + # Is the entry a directory? + if (-d $l_entry) { + + # should we recurse into this directory. + if ($l_entry =~ m/\/(\.\.?|sim[ou]|bldv)$/) + { + next; # skip '.', '..' and some fips dirs + } + collect_files($l_entry); + } + # Is the entry a file? + elsif ((-f $l_entry) && ($l_entry =~ m"\.hash$")) { + # it's a file so + assimilate_file($l_entry); + } + else { + # Not a file or directory so ignore it... + } + } + +} + +#============================================================================= +# Read in data from file and add to master one +#============================================================================= +sub assimilate_file($) { + + my ($l_loc) = @_; + + my (%o_strings); + my ($line) = ""; + my ($l_hash) = ""; + my ($l_str) = ""; + my ($l_file) = ""; + + # Make sure we can open each file. + open ( FH , "<$l_loc") + or die ("Cannot open $_: $!, stopped"); + + $line = <FH>; + + print "Assimilate: first line in $l_loc = $line" if $debug; + + if((defined $line) && ($line =~ /^$HEAD_EYE_CATCHER(\d)/)) + { + $version = $1; + if ($version eq "1") { + if ($hash_filename_too) { + print "*** ERROR: hash_filename_too (-F) isn't possible with trace version 1\n"; + print " please rebuild all .hash files and global trexStringFile\n"; + print " version 1 file is '$l_loc'\n"; + exit(127); + } + } elsif ($version ne "2") { + print "Unknown version of stringfile $version\n"; + exit(127); + } + + $line = <FH>; + + + while (defined $line) { + chop $line; # remove EOL + if($version eq "1") + { + ($l_hash, $l_file ,$l_str) = split(/\|\|/, $line); + } + elsif($version eq "2") + { + ($l_hash, $l_str ,$l_file) = split(/\|\|/, $line); + } + my $newstring = $l_str . "||" . $l_file; + if (exists $hash_strings_array{$l_hash}) { + my $hashstr1 = $hash_strings_array{$l_hash}; + my $hashstr2 = $newstring; + if (!$hash_filename_too) { + # hash was made over format string only, remove file name + $hashstr1 =~ s/\|\|.*$//; + $hashstr2 = $l_str; + } + if ($debug) { + print "a_f: compare $hashstr1\n", + " vs. $hashstr2\n"; + } + if ($hashstr1 ne $hashstr2) + { + print "*** ERROR: HASH Collision! (a_f)\n", + " Two different strings have the same hash value ($l_hash)\n", + " String 1: $hash_strings_array{$l_hash}\n", + " String 2: $newstring\n"; + if ($fail_on_collision) { + exit(1); + } + } + } + $hash_strings_array{$l_hash} = $newstring; + $line = <FH>; + } + + } + else + { # If there is a file then we are dealing with the first + # version of trexStringFile so don't look for file name. + # these files shouldn't be there anymore. we don't check for collisions here + if ($debug) { + print "version 0 stringfile detected: $string_file\n"; + } + + if(defined $line) + { + # there is a file and it doesn't have a header + $version = 0; + } + + while (defined $line) { + chop $line; # remove EOL + ($l_hash,$l_str) = split(/\|\|/, $line); + $hash_strings_array{$l_hash} = $l_str . "||" . "NO FILE"; + $line = <FH>; + } + } + $version = $HEAD_VER_FLAG; + close(FH); +} + +#============================================================================= + +#============================================================================= +sub hash_strings() { + + my ($hash_val, $l_key, $l_hash, %l_hash_strings); + my ($line_feed) = chr(10); + my ($l_file_name) = "NO FILENAME"; + print "\nHashing printf strings.\n\n"; + + foreach my $str (@strings) { + my $printf_string; + $l_file_name = $str->[0]; + $printf_string = $str->[1]; + $l_key = $str->[2]; + print "printf_string = $printf_string\n" if $debug; + $printf_string =~ s/"\s?"//g; #multi line traces will have extra " in them + $printf_string =~ s/`/\\`/g; # excape ' + $printf_string =~ s/\\n/$line_feed/g; # escape \n + if ($hash_filename_too) { + $printf_string .= "||" . $l_file_name; + } + + # call the hasher. + print "$hash_prog \"$printf_string\" $l_key\n" if $debug; + $hash_val = `$hash_prog \"$printf_string\" $l_key`; + if ($?) { + my ($hp_ret, $hp_sig) = ($? >> 8, $? & 127); + if ($hp_sig) { + print "*** ERROR: $hash_prog died with signal $hp_sig\n"; + } elsif ($hp_ret) { + print "*** ERROR: $hash_prog returned the error $hp_ret\n"; + if ($hash_val) { + print " error from $hash_prog:\n$hash_val"; + } + } + exit(1); + } + print "printf_string = $printf_string l_key = $l_key hash val = $hash_val\n" if $debug; + + # undo escaping + $printf_string =~ s/$line_feed/\\n/g; + $printf_string =~ s/\\`/`/g; + + if (exists $l_hash_strings{$hash_val}) + { + # hash val was found before. check if it's the same string + # else we have a hash collision + my $l_tmp = $l_hash_strings{$hash_val}; + if (!$hash_filename_too) { + $l_tmp =~ s/\|\|.*$//; + } + if ($l_tmp ne $printf_string) + { + print "*** ERROR: HASH Collision! (h_s)\n", + " Two different strings have the same hash value ($hash_val)\n", + " String 1: $l_hash_strings{$hash_val}\n", + " String 2: $printf_string (file $l_file_name)\n"; + if ($fail_on_collision) { + exit(1); + } + } + } + # this will overwrite an old string with a new one if a collision occurred + # but we might want to bail out in this case anyway + $printf_string = $printf_string . "||" . $l_file_name; + $l_hash_strings{$hash_val} = $printf_string; + } + return %l_hash_strings; +} +#============================================================================= + +#============================================================================= +sub write_string_file() { + + my (@keys) = (); + my ($l_key) = ""; + + # Combine the contents of the existing string file with the trace calls + # that we have just hashed. + print STDOUT "\nCombining Hash strings...\n\n"; + + @keys = keys(%hash_strings_array); + + foreach $l_key (@keys) { + my $l_tmp = $hash_strings_array{$l_key}; # freshly collected strings + if (exists $string_file_array{$l_key}) + { # hash exists in list from trexStringFile + my $l_tmp2 = $string_file_array{$l_key}; + if (!$hash_filename_too) { + $l_tmp =~ s/\|\|.*$//; + $l_tmp2 =~ s/\|\|.*$//; + } + + # Check for hash collisions. + if ($l_tmp ne $l_tmp2) + { + print "*** ERROR: HASH Collision! (w_s_f)\n", + " Two different strings have the same hash value ($l_key)\n", + " String 1: $hash_strings_array{$l_key}\n", + " String 2: $string_file_array{$l_key}\n"; + if ($fail_on_collision) { + exit(1); + } + # don't fail, write new one + } + } + if($version > 0) + { + # add/replace the new string to the string_file_array. + $string_file_array{$l_key} = $hash_strings_array{$l_key} + } + else + { + # old version so only write out format string (not file name to) + $string_file_array{$l_key} = $l_tmp; + } + } + + # Write out the updated string file. + print STDOUT "\nWriting updated hash||string file...\n\n"; + + @keys = sort(keys(%string_file_array)); + + open ( FH , ">$string_file") + or die ("Cannot open $_: $!, stopped"); + + if($version > 0) + { + print FH "$build\n"; # only print version if newer then version 0 + } + foreach $l_key (@keys) { + print FH "$l_key||$string_file_array{$l_key}\n"; + } + close FH; +} +#============================================================================= + +#============================================================================= +# Display command invokation help for this program... +#============================================================================= +sub help() { + print << "EOF"; +tracehash.pl - create a trace string file from sources or collect tracepp files +Usage: tracehash.pl [options] + General options: + -h - Print this help text. + -v - Be verbose, tell what's going on (debug output) + Operation modes + -c - Collect StringFiles created by tracepp and merge. + default - Scan source files for trace calls. + +Collect mode: tracehash.pl -c [-vFCS] [-d <dir>] [-s <string_file>] + tracehash.pl -c [-vFCS] [-f <file>] [-s <string_file>] + Collect string files created by tracepp (.hash) from directory tree at + <dir> or read them from string file <file> and write to file + <string_file>, adding entries already in this file. + -f - String file to read and write/add to <string_file>. + -d - Start of directory tree to scan for .hash files. Default = . + -s - File with trace strings (and hashes) to read from and write to + default = ./trexStringFile + -F - hash is calculated over trace string and source file name, + otherwise without source file name + -C - report an error if a hash collisions occurs + -S - don't read global FLD-2.2 string file ($BB_STRING_FILE) + If tracehash.pl is called in a FipS build sandbox without -d and -f + defaults for the sandbox will be used. + +Scan mode: tracehash.pl [-vFCS] [-d <dir>] [-s <string_file>] [ccpopts] + tracehash.pl [-vFCS] [-f <file>] [-s <string_file>] [cppopts] + Scan all files in directory tree <dir> or scan file <file> and write + strings to file <string_file>. Strings already in this file will be merged. + -f - Source file to scan for trace entries. + -d - Source directory to scan for trace entries. + -s - File with trace strings (and hashes) to read from and write to. + default = ./trexStringFile + -F - hash for string was build from format string + source file name + -C - report an error if hash collisions occur + -S - don't read global FLD-2.2 string file ($BB_STRING_FILE) + All other arguments will be passed verbatim to cpp +EOF +} +#============================================================================= |

