summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--bl31/bl31.ld.S28
-rw-r--r--bl31/bl31.mk6
-rw-r--r--docs/user-guide.md3
-rw-r--r--include/lib/pmf.h199
-rw-r--r--include/lib/pmf_helpers.h166
-rw-r--r--lib/pmf/pmf_main.c280
-rw-r--r--lib/pmf/pmf_smc.c91
8 files changed, 775 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index ab9b1ade..c7930b12 100644
--- a/Makefile
+++ b/Makefile
@@ -104,7 +104,8 @@ COLD_BOOT_SINGLE_CPU := 0
SPIN_ON_BL1_EXIT := 0
# Build PL011 UART driver in minimal generic UART mode
PL011_GENERIC_UART := 0
-
+# Flag to enable Performance Measurement Framework
+ENABLE_PMF := 0
################################################################################
# Checkpatch script options
@@ -410,6 +411,7 @@ $(eval $(call assert_boolean,ERROR_DEPRECATED))
$(eval $(call assert_boolean,ENABLE_PLAT_COMPAT))
$(eval $(call assert_boolean,SPIN_ON_BL1_EXIT))
$(eval $(call assert_boolean,PL011_GENERIC_UART))
+$(eval $(call assert_boolean,ENABLE_PMF))
################################################################################
@@ -437,6 +439,7 @@ $(eval $(call add_define,ERROR_DEPRECATED))
$(eval $(call add_define,ENABLE_PLAT_COMPAT))
$(eval $(call add_define,SPIN_ON_BL1_EXIT))
$(eval $(call add_define,PL011_GENERIC_UART))
+$(eval $(call add_define,ENABLE_PMF))
# Define the EL3_PAYLOAD_BASE flag only if it is provided.
ifdef EL3_PAYLOAD_BASE
$(eval $(call add_define,EL3_PAYLOAD_BASE))
diff --git a/bl31/bl31.ld.S b/bl31/bl31.ld.S
index dc6e7a46..33cbe4b7 100644
--- a/bl31/bl31.ld.S
+++ b/bl31/bl31.ld.S
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -58,6 +58,14 @@ SECTIONS
KEEP(*(rt_svc_descs))
__RT_SVC_DESCS_END__ = .;
+#if ENABLE_PMF
+ /* Ensure 8-byte alignment for descriptors and ensure inclusion */
+ . = ALIGN(8);
+ __PMF_SVC_DESCS_START__ = .;
+ KEEP(*(pmf_svc_descs))
+ __PMF_SVC_DESCS_END__ = .;
+#endif /* ENABLE_PMF */
+
/*
* Ensure 8-byte alignment for cpu_ops so that its fields are also
* aligned. Also ensure cpu_ops inclusion.
@@ -132,6 +140,24 @@ SECTIONS
"PLAT_PERCPU_BAKERY_LOCK_SIZE does not match bakery lock requirements");
#endif
#endif
+
+#if ENABLE_PMF
+ /*
+ * Time-stamps are stored in normal .bss memory
+ *
+ * The compiler will allocate enough memory for one CPU's time-stamps,
+ * the remaining memory for other CPU's is allocated by the
+ * linker script
+ */
+ . = ALIGN(CACHE_WRITEBACK_GRANULE);
+ __PMF_TIMESTAMP_START__ = .;
+ KEEP(*(pmf_timestamp_array))
+ . = ALIGN(CACHE_WRITEBACK_GRANULE);
+ __PMF_PERCPU_TIMESTAMP_END__ = .;
+ __PERCPU_TIMESTAMP_SIZE__ = ABSOLUTE(. - __PMF_TIMESTAMP_START__);
+ . = . + (__PERCPU_TIMESTAMP_SIZE__ * (PLATFORM_CORE_COUNT - 1));
+ __PMF_TIMESTAMP_END__ = .;
+#endif /* ENABLE_PMF */
__BSS_END__ = .;
} >RAM
diff --git a/bl31/bl31.mk b/bl31/bl31.mk
index 0f14bfa0..0491a1a8 100644
--- a/bl31/bl31.mk
+++ b/bl31/bl31.mk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -59,6 +59,10 @@ else
BL31_SOURCES += lib/locks/bakery/bakery_lock_normal.c
endif
+ifeq (${ENABLE_PMF}, 1)
+BL31_SOURCES += lib/pmf/pmf_main.c
+endif
+
BL31_LINKERFILE := bl31/bl31.ld.S
# Flag used to indicate if Crash reporting via console should be included
diff --git a/docs/user-guide.md b/docs/user-guide.md
index 0bdedf90..f7fcf89b 100644
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -408,6 +408,9 @@ performed.
* `HANDLE_EA_EL3_FIRST`: When defined External Aborts and SError Interrupts
will be always trapped in EL3 i.e. in BL31 at runtime.
+* `ENABLE_PMF`: Boolean option to enable support for optional Performance
+ Measurement Framework(PMF). Default is 0.
+
#### ARM development platform specific build options
* `ARM_TSP_RAM_LOCATION`: location of the TSP binary. Options:
diff --git a/include/lib/pmf.h b/include/lib/pmf.h
new file mode 100644
index 00000000..5f953b5a
--- /dev/null
+++ b/include/lib/pmf.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PMF_H__
+#define __PMF_H__
+
+#include <cassert.h>
+#include <pmf_helpers.h>
+
+/*
+ * Constants used for/by PMF services.
+ */
+#define PMF_ARM_TIF_IMPL_ID (0x41000000)
+#define PMF_TID_SHIFT 0
+#define PMF_TID_MASK (0xFF << PMF_TID_SHIFT)
+#define PMF_SVC_ID_SHIFT 10
+#define PMF_SVC_ID_MASK (0x3F << PMF_SVC_ID_SHIFT)
+#define PMF_IMPL_ID_SHIFT 24
+#define PMF_IMPL_ID_MASK (0xFF << PMF_IMPL_ID_SHIFT)
+
+/*
+ * Flags passed to PMF_REGISTER_SERVICE
+ */
+#define PMF_STORE_ENABLE (1 << 0)
+#define PMF_DUMP_ENABLE (1 << 1)
+
+/*
+ * Flags passed to PMF_GET_TIMESTAMP_XXX
+ * and PMF_CAPTURE_TIMESTAMP
+ */
+#define PMF_CACHE_MAINT (1 << 0)
+#define PMF_NO_CACHE_MAINT 0
+
+/*
+ * Defines for PMF SMC function ids.
+ */
+#define PMF_SMC_GET_TIMESTAMP_32 0x82000010
+#define PMF_SMC_GET_TIMESTAMP_64 0xC2000010
+#define PMF_NUM_SMC_CALLS 2
+
+/*
+ * The macros below are used to identify
+ * PMF calls from the SMC function ID.
+ */
+#define PMF_FID_MASK 0xffe0u
+#define PMF_FID_VALUE 0u
+#define is_pmf_fid(_fid) (((_fid) & PMF_FID_MASK) == PMF_FID_VALUE)
+
+/* Following are the supported PMF service IDs */
+#define PMF_PSCI_STAT_SVC_ID 0
+
+#if ENABLE_PMF
+/*
+ * Convenience macros for capturing time-stamp.
+ */
+#define PMF_DECLARE_CAPTURE_TIMESTAMP(_name) \
+ void pmf_capture_timestamp_with_cache_maint_ ## _name( \
+ unsigned int tid, \
+ unsigned long long ts); \
+ void pmf_capture_timestamp_ ## _name( \
+ unsigned int tid, \
+ unsigned long long ts);
+
+#define PMF_CAPTURE_TIMESTAMP(_name, _tid, _flags) \
+ do { \
+ unsigned long long ts = read_cntpct_el0(); \
+ if ((_flags) & PMF_CACHE_MAINT) \
+ pmf_capture_timestamp_with_cache_maint_ ## _name((_tid), ts);\
+ else \
+ pmf_capture_timestamp_ ## _name((_tid), ts); \
+ } while (0)
+
+#define PMF_CAPTURE_AND_GET_TIMESTAMP(_name, _tid, _flags, _tsval) \
+ do { \
+ (_tsval) = read_cntpct_el0(); \
+ CASSERT(sizeof(_tsval) == sizeof(unsigned long long), invalid_tsval_size);\
+ if ((_flags) & PMF_CACHE_MAINT) \
+ pmf_capture_timestamp_with_cache_maint_ ## _name((_tid), (_tsval));\
+ else \
+ pmf_capture_timestamp_ ## _name((_tid), (_tsval));\
+ } while (0)
+
+#define PMF_WRITE_TIMESTAMP(_name, _tid, _flags, _wrval) \
+ do { \
+ CASSERT(sizeof(_wrval) == sizeof(unsigned long long), invalid_wrval_size);\
+ if ((_flags) & PMF_CACHE_MAINT) \
+ pmf_capture_timestamp_with_cache_maint_ ## _name((_tid), (_wrval));\
+ else \
+ pmf_capture_timestamp_ ## _name((_tid), (_wrval));\
+ } while (0)
+
+/*
+ * Convenience macros for retrieving time-stamp.
+ */
+#define PMF_DECLARE_GET_TIMESTAMP(_name) \
+ unsigned long long pmf_get_timestamp_by_index_ ## _name(\
+ unsigned int tid, \
+ unsigned int cpuid, \
+ unsigned int flags); \
+ unsigned long long pmf_get_timestamp_by_mpidr_ ## _name(\
+ unsigned int tid, \
+ u_register_t mpidr, \
+ unsigned int flags);
+
+#define PMF_GET_TIMESTAMP_BY_MPIDR(_name, _tid, _mpidr, _flags, _tsval)\
+ _tsval = pmf_get_timestamp_by_mpidr_ ## _name(_tid, _mpidr, _flags)
+
+#define PMF_GET_TIMESTAMP_BY_INDEX(_name, _tid, _cpuid, _flags, _tsval)\
+ _tsval = pmf_get_timestamp_by_index_ ## _name(_tid, _cpuid, _flags)
+
+/* Convenience macros to register a PMF service.*/
+/*
+ * This macro is used to register a PMF Service. It allocates PMF memory
+ * and defines default service-specific PMF functions.
+ */
+#define PMF_REGISTER_SERVICE(_name, _svcid, _totalid, _flags) \
+ PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _totalid) \
+ PMF_DEFINE_CAPTURE_TIMESTAMP(_name, _flags) \
+ PMF_DEFINE_GET_TIMESTAMP(_name)
+
+/*
+ * This macro is used to register a PMF service, including an
+ * SMC interface to that service.
+ */
+#define PMF_REGISTER_SERVICE_SMC(_name, _svcid, _totalid, _flags)\
+ PMF_REGISTER_SERVICE(_name, _svcid, _totalid, _flags) \
+ PMF_DEFINE_SERVICE_DESC(_name, PMF_ARM_TIF_IMPL_ID, \
+ _svcid, _totalid, NULL, \
+ pmf_get_timestamp_by_mpidr_ ## _name)
+
+/*
+ * This macro is used to register a PMF service that has an SMC interface
+ * but provides its own service-specific PMF functions.
+ */
+#define PMF_REGISTER_SERVICE_SMC_OWN(_name, _implid, _svcid, _totalid, \
+ _init, _getts) \
+ PMF_DEFINE_SERVICE_DESC(_name, _implid, _svcid, _totalid, \
+ _init, _getts)
+
+#else
+
+#define PMF_REGISTER_SERVICE(_name, _svcid, _totalid, _flags)
+#define PMF_REGISTER_SERVICE_SMC(_name, _svcid, _totalid, _flags)
+#define PMF_REGISTER_SERVICE_SMC_OWN(_name, _implid, _svcid, _totalid, \
+ _init, _getts)
+#define PMF_DECLARE_CAPTURE_TIMESTAMP(_name)
+#define PMF_DECLARE_GET_TIMESTAMP(_name)
+#define PMF_CAPTURE_TIMESTAMP(_name, _tid, _flags)
+#define PMF_GET_TIMESTAMP_BY_MPIDR(_name, _tid, _mpidr, _flags, _tsval)
+#define PMF_GET_TIMESTAMP_BY_INDEX(_name, _tid, _cpuid, _flags, _tsval)
+
+#endif /* ENABLE_PMF */
+
+/*******************************************************************************
+ * Function & variable prototypes
+ ******************************************************************************/
+/* PMF common functions */
+int pmf_get_timestamp_smc(unsigned int tid,
+ u_register_t mpidr,
+ unsigned int flags,
+ unsigned long long *ts);
+int pmf_setup(void);
+uintptr_t pmf_smc_handler(unsigned int smc_fid,
+ u_register_t x1,
+ u_register_t x2,
+ u_register_t x3,
+ u_register_t x4,
+ void *cookie,
+ void *handle,
+ u_register_t flags);
+
+#endif /* __PMF_H__ */
diff --git a/include/lib/pmf_helpers.h b/include/lib/pmf_helpers.h
new file mode 100644
index 00000000..1b8c5077
--- /dev/null
+++ b/include/lib/pmf_helpers.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PMF_HELPERS_H__
+#define __PMF_HELPERS_H__
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <platform.h>
+#include <pmf.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * Prototype for PMF service functions.
+ */
+typedef int (*pmf_svc_init_t)(void);
+typedef unsigned long long (*pmf_svc_get_ts_t)(unsigned int tid,
+ u_register_t mpidr,
+ unsigned int flags);
+
+/*
+ * This is the definition of PMF service desc.
+ */
+typedef struct pmf_svc_desc {
+ /* Structure version information */
+ param_header_t h;
+
+ /* Name of the PMF service */
+ const char *name;
+
+ /* PMF service config: Implementer id, Service id and total id*/
+ unsigned int svc_config;
+
+ /* PMF service initialization handler */
+ pmf_svc_init_t init;
+
+ /* PMF service time-stamp retrieval handler */
+ pmf_svc_get_ts_t get_ts;
+} pmf_svc_desc_t;
+
+/*
+ * Convenience macro to allocate memory for a PMF service.
+ */
+#define PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _total_id) \
+ static unsigned long long pmf_ts_mem_ ## _name[_total_id] \
+ __section("pmf_timestamp_array") __used;
+
+/*
+ * Convenience macro to validate tid index for the given TS array.
+ */
+#define PMF_VALIDATE_TID(_name, _tid) \
+ assert((_tid & PMF_TID_MASK) < (ARRAY_SIZE(pmf_ts_mem_ ## _name)))
+
+/*
+ * Convenience macros for capturing time-stamp.
+ */
+#define PMF_DEFINE_CAPTURE_TIMESTAMP(_name, _flags) \
+ void pmf_capture_timestamp_ ## _name( \
+ unsigned int tid, \
+ unsigned long long ts) \
+ { \
+ CASSERT(_flags, select_proper_config); \
+ PMF_VALIDATE_TID(_name, tid); \
+ uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
+ if ((_flags) & PMF_STORE_ENABLE) \
+ __pmf_store_timestamp(base_addr, tid, ts); \
+ if ((_flags) & PMF_DUMP_ENABLE) \
+ __pmf_dump_timestamp(tid, ts); \
+ } \
+ void pmf_capture_timestamp_with_cache_maint_ ## _name( \
+ unsigned int tid, \
+ unsigned long long ts) \
+ { \
+ CASSERT(_flags, select_proper_config); \
+ PMF_VALIDATE_TID(_name, tid); \
+ uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
+ if ((_flags) & PMF_STORE_ENABLE) \
+ __pmf_store_timestamp_with_cache_maint(base_addr, tid, ts);\
+ if ((_flags) & PMF_DUMP_ENABLE) \
+ __pmf_dump_timestamp(tid, ts); \
+ }
+
+/*
+ * Convenience macros for retrieving time-stamp.
+ */
+#define PMF_DEFINE_GET_TIMESTAMP(_name) \
+ unsigned long long pmf_get_timestamp_by_index_ ## _name( \
+ unsigned int tid, unsigned int cpuid, unsigned int flags)\
+ { \
+ PMF_VALIDATE_TID(_name, tid); \
+ uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
+ return __pmf_get_timestamp(base_addr, tid, cpuid, flags);\
+ } \
+ unsigned long long pmf_get_timestamp_by_mpidr_ ## _name( \
+ unsigned int tid, u_register_t mpidr, unsigned int flags)\
+ { \
+ PMF_VALIDATE_TID(_name, tid); \
+ uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
+ return __pmf_get_timestamp(base_addr, tid, \
+ plat_core_pos_by_mpidr(mpidr), flags); \
+ }
+
+/*
+ * Convenience macro to register a PMF service.
+ * This is needed for services that require SMC handling.
+ */
+#define PMF_DEFINE_SERVICE_DESC(_name, _implid, _svcid, _totalid, \
+ _init, _getts_by_mpidr) \
+ static const pmf_svc_desc_t __pmf_desc_ ## _name \
+ __section("pmf_svc_descs") __used = { \
+ .h.type = PARAM_EP, \
+ .h.version = VERSION_1, \
+ .h.size = sizeof(pmf_svc_desc_t), \
+ .h.attr = 0, \
+ .name = #_name, \
+ .svc_config = ((((_implid) << PMF_IMPL_ID_SHIFT) & \
+ PMF_IMPL_ID_MASK) | \
+ (((_svcid) << PMF_SVC_ID_SHIFT) & \
+ PMF_SVC_ID_MASK) | \
+ (((_totalid) << PMF_TID_SHIFT) & \
+ PMF_TID_MASK)), \
+ .init = _init, \
+ .get_ts = _getts_by_mpidr \
+ };
+
+/* PMF internal functions */
+void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts);
+void __pmf_store_timestamp(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned long long ts);
+void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned long long ts);
+unsigned long long __pmf_get_timestamp(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned int cpuid,
+ unsigned int flags);
+#endif /* __PMF_HELPERS_H__ */
diff --git a/lib/pmf/pmf_main.c b/lib/pmf/pmf_main.c
new file mode 100644
index 00000000..7ce1a9d8
--- /dev/null
+++ b/lib/pmf/pmf_main.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <platform.h>
+#include <pmf.h>
+#include <string.h>
+
+/*******************************************************************************
+ * The 'pmf_svc_descs' array holds the PMF service descriptors exported by
+ * services by placing them in the 'pmf_svc_descs' linker section.
+ * The 'pmf_svc_descs_indices' array holds the index of a descriptor in the
+ * 'pmf_svc_descs' array. The TIF[15:10] bits in the time-stamp id are used
+ * to get an index into the 'pmf_svc_descs_indices' array. This gives the
+ * index of the descriptor in the 'pmf_svc_descs' array which contains the
+ * service function pointers.
+ ******************************************************************************/
+extern uintptr_t __PMF_SVC_DESCS_START__;
+extern uintptr_t __PMF_SVC_DESCS_END__;
+#define PMF_SVC_DESCS_START ((uintptr_t)(&__PMF_SVC_DESCS_START__))
+#define PMF_SVC_DESCS_END ((uintptr_t)(&__PMF_SVC_DESCS_END__))
+extern void *__PERCPU_TIMESTAMP_SIZE__;
+#define PMF_PERCPU_TIMESTAMP_SIZE ((uintptr_t)&__PERCPU_TIMESTAMP_SIZE__)
+extern uintptr_t __PMF_TIMESTAMP_START__;
+#define PMF_TIMESTAMP_ARRAY_START ((uintptr_t)&__PMF_TIMESTAMP_START__)
+extern uintptr_t __PMF_TIMESTAMP_END__;
+#define PMF_TIMESTAMP_ARRAY_END ((uintptr_t)&__PMF_TIMESTAMP_END__)
+
+#define PMF_SVC_DESCS_MAX 10
+
+/*
+ * This is used to traverse through registered PMF services.
+ */
+static pmf_svc_desc_t *pmf_svc_descs;
+
+/*
+ * This array is used to store registered PMF services in sorted order.
+ */
+static int pmf_svc_descs_indices[PMF_SVC_DESCS_MAX];
+
+/*
+ * This is used to track total number of successfully registered PMF services.
+ */
+static int pmf_num_services;
+
+/*
+ * This is the main PMF function that initialize registered
+ * PMF services and also sort them in ascending order.
+ */
+int pmf_setup(void)
+{
+ int rc, ii, jj = 0;
+ int pmf_svc_descs_num, temp_val;
+
+ /* If no PMF services are registered then simply bail out */
+ pmf_svc_descs_num = (PMF_SVC_DESCS_END - PMF_SVC_DESCS_START)/
+ sizeof(pmf_svc_desc_t);
+ if (pmf_svc_descs_num == 0)
+ return 0;
+
+ assert(pmf_svc_descs_num < PMF_SVC_DESCS_MAX);
+
+ pmf_svc_descs = (pmf_svc_desc_t *) PMF_SVC_DESCS_START;
+ for (ii = 0; ii < pmf_svc_descs_num; ii++) {
+
+ assert(pmf_svc_descs[ii].get_ts);
+
+ /*
+ * Call the initialization routine for this
+ * PMF service, if it is defined.
+ */
+ if (pmf_svc_descs[ii].init) {
+ rc = pmf_svc_descs[ii].init();
+ if (rc) {
+ WARN("Could not initialize PMF"
+ "service %s - skipping \n",
+ pmf_svc_descs[ii].name);
+ continue;
+ }
+ }
+
+ /* Update the pmf_svc_descs_indices array */
+ pmf_svc_descs_indices[jj++] = ii;
+ }
+
+ pmf_num_services = jj;
+
+ /*
+ * Sort the successfully registered PMF services
+ * according to service ID
+ */
+ for (ii = 1; ii < pmf_num_services; ii++) {
+ for (jj = 0; jj < (pmf_num_services - ii); jj++) {
+ if ((pmf_svc_descs[jj].svc_config & PMF_SVC_ID_MASK) >
+ (pmf_svc_descs[jj + 1].svc_config &
+ PMF_SVC_ID_MASK)) {
+ temp_val = pmf_svc_descs_indices[jj];
+ pmf_svc_descs_indices[jj] =
+ pmf_svc_descs_indices[jj+1];
+ pmf_svc_descs_indices[jj+1] = temp_val;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function implements binary search to find registered
+ * PMF service based on Service ID provided in `tid` argument.
+ */
+static pmf_svc_desc_t *get_service(unsigned int tid)
+{
+ int low = 0;
+ int mid;
+ int high = pmf_num_services;
+ unsigned int svc_id = tid & PMF_SVC_ID_MASK;
+ int index;
+ unsigned int desc_svc_id;
+
+ if (pmf_num_services == 0)
+ return NULL;
+
+ assert(pmf_svc_descs);
+
+ do {
+ mid = (low + high) / 2;
+ index = pmf_svc_descs_indices[mid];
+
+ desc_svc_id = pmf_svc_descs[index].svc_config & PMF_SVC_ID_MASK;
+ if (svc_id < desc_svc_id)
+ high = mid - 1;
+ if (svc_id > desc_svc_id)
+ low = mid + 1;
+ } while ((svc_id != desc_svc_id) && (low <= high));
+
+ /*
+ * Make sure the Service found supports the tid range.
+ */
+ if ((svc_id == desc_svc_id) && ((tid & PMF_TID_MASK) <
+ (pmf_svc_descs[index].svc_config & PMF_TID_MASK)))
+ return (pmf_svc_desc_t *)&pmf_svc_descs[index];
+
+ return NULL;
+}
+
+/*
+ * This function gets the time-stamp value for the PMF services
+ * registered for SMC interface based on `tid` and `mpidr`.
+ */
+int pmf_get_timestamp_smc(unsigned int tid,
+ u_register_t mpidr,
+ unsigned int flags,
+ unsigned long long *ts_value)
+{
+ pmf_svc_desc_t *svc_desc;
+ assert(ts_value);
+
+ /* Search for registered service. */
+ svc_desc = get_service(tid);
+
+ if ((svc_desc == NULL) || (plat_core_pos_by_mpidr(mpidr) < 0)) {
+ *ts_value = 0;
+ return -EINVAL;
+ } else {
+ /* Call the service time-stamp handler. */
+ *ts_value = svc_desc->get_ts(tid, mpidr, flags);
+ return 0;
+ }
+}
+
+/*
+ * This function can be used to dump `ts` value for given `tid`.
+ * Assumption is that the console is already initialized.
+ */
+void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts)
+{
+ tf_printf("PMF:cpu %u tid %u ts %llu\n",
+ plat_my_core_pos(), tid, ts);
+}
+
+/*
+ * This function calculate the address identified by
+ * `base_addr`, `tid` and `cpuid`.
+ */
+static inline uintptr_t calc_ts_addr(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned int cpuid)
+{
+ assert(cpuid < PLATFORM_CORE_COUNT);
+ assert(base_addr >= PMF_TIMESTAMP_ARRAY_START);
+ assert(base_addr < ((PMF_TIMESTAMP_ARRAY_START +
+ PMF_PERCPU_TIMESTAMP_SIZE) - ((tid & PMF_TID_MASK) *
+ sizeof(unsigned long long))));
+
+ base_addr += ((cpuid * PMF_PERCPU_TIMESTAMP_SIZE) +
+ ((tid & PMF_TID_MASK) * sizeof(unsigned long long)));
+
+ return base_addr;
+}
+
+/*
+ * This function stores the `ts` value to the storage identified by
+ * `base_addr`, `tid` and current cpu id.
+ * Note: The timestamp addresses are cache line aligned per cpu
+ * and only the owning CPU would ever write into it.
+ */
+void __pmf_store_timestamp(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned long long ts)
+{
+ unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
+ tid, plat_my_core_pos());
+ *ts_addr = ts;
+}
+
+/*
+ * This is the cached version of `pmf_store_my_timestamp`
+ * Note: The timestamp addresses are cache line aligned per cpu
+ * and only the owning CPU would ever write into it.
+ */
+void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned long long ts)
+{
+ unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
+ tid, plat_my_core_pos());
+ *ts_addr = ts;
+ flush_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long));
+}
+
+/*
+ * This function retrieves the `ts` value from the storage identified by
+ * `base_addr`, `tid` and `cpuid`.
+ * Note: The timestamp addresses are cache line aligned per cpu.
+ */
+unsigned long long __pmf_get_timestamp(uintptr_t base_addr,
+ unsigned int tid,
+ unsigned int cpuid,
+ unsigned int flags)
+{
+ assert(cpuid < PLATFORM_CORE_COUNT);
+ unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
+ tid, cpuid);
+
+ if (flags & PMF_CACHE_MAINT)
+ inv_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long));
+
+ return *ts_addr;
+}
diff --git a/lib/pmf/pmf_smc.c b/lib/pmf/pmf_smc.c
new file mode 100644
index 00000000..14190b77
--- /dev/null
+++ b/lib/pmf/pmf_smc.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <assert.h>
+#include <debug.h>
+#include <platform.h>
+#include <pmf.h>
+#include <smcc_helpers.h>
+
+/*
+ * This function is responsible for handling all PMF SMC calls.
+ */
+uintptr_t pmf_smc_handler(unsigned int smc_fid,
+ u_register_t x1,
+ u_register_t x2,
+ u_register_t x3,
+ u_register_t x4,
+ void *cookie,
+ void *handle,
+ u_register_t flags)
+{
+ int rc;
+ unsigned long long ts_value;
+
+ if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) {
+
+ x1 = (uint32_t)x1;
+ x2 = (uint32_t)x2;
+ x3 = (uint32_t)x3;
+
+ switch (smc_fid) {
+ case PMF_SMC_GET_TIMESTAMP_32:
+ /*
+ * Return error code and the captured
+ * time-stamp to the caller.
+ * x0 --> error code.
+ * x1 - x2 --> time-stamp value.
+ */
+ rc = pmf_get_timestamp_smc(x1, x2, x3, &ts_value);
+ SMC_RET3(handle, rc, (uint32_t)ts_value,
+ (uint32_t)(ts_value >> 32));
+
+ default:
+ break;
+ }
+ } else {
+ switch (smc_fid) {
+ case PMF_SMC_GET_TIMESTAMP_64:
+ /*
+ * Return error code and the captured
+ * time-stamp to the caller.
+ * x0 --> error code.
+ * x1 --> time-stamp value.
+ */
+ rc = pmf_get_timestamp_smc(x1, x2, x3, &ts_value);
+ SMC_RET2(handle, rc, ts_value);
+
+ default:
+ break;
+ }
+ }
+
+ WARN("Unimplemented PMF Call: 0x%x \n", smc_fid);
+ SMC_RET1(handle, SMC_UNK);
+}