1a31d8983SYatharth Kochar /* 29f85f9e3SJoel Hutton * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. 3a31d8983SYatharth Kochar * 482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 5a31d8983SYatharth Kochar */ 6*09d40e0eSAntonio Nino Diaz 7*09d40e0eSAntonio Nino Diaz #include <assert.h> 8*09d40e0eSAntonio Nino Diaz #include <errno.h> 9*09d40e0eSAntonio Nino Diaz #include <string.h> 10*09d40e0eSAntonio Nino Diaz 11a31d8983SYatharth Kochar #include <arch.h> 12a31d8983SYatharth Kochar #include <arch_helpers.h> 13*09d40e0eSAntonio Nino Diaz #include <common/debug.h> 14*09d40e0eSAntonio Nino Diaz #include <lib/pmf/pmf.h> 15*09d40e0eSAntonio Nino Diaz #include <lib/utils_def.h> 16*09d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 17a31d8983SYatharth Kochar 18a31d8983SYatharth Kochar /******************************************************************************* 19a31d8983SYatharth Kochar * The 'pmf_svc_descs' array holds the PMF service descriptors exported by 20a31d8983SYatharth Kochar * services by placing them in the 'pmf_svc_descs' linker section. 21a31d8983SYatharth Kochar * The 'pmf_svc_descs_indices' array holds the index of a descriptor in the 22a31d8983SYatharth Kochar * 'pmf_svc_descs' array. The TIF[15:10] bits in the time-stamp id are used 23a31d8983SYatharth Kochar * to get an index into the 'pmf_svc_descs_indices' array. This gives the 24a31d8983SYatharth Kochar * index of the descriptor in the 'pmf_svc_descs' array which contains the 25a31d8983SYatharth Kochar * service function pointers. 26a31d8983SYatharth Kochar ******************************************************************************/ 279f85f9e3SJoel Hutton 289f85f9e3SJoel Hutton IMPORT_SYM(uintptr_t, __PMF_SVC_DESCS_START__, PMF_SVC_DESCS_START); 299f85f9e3SJoel Hutton IMPORT_SYM(uintptr_t, __PMF_SVC_DESCS_END__, PMF_SVC_DESCS_END); 30931f7c61SSoby Mathew IMPORT_SYM(uintptr_t, __PMF_PERCPU_TIMESTAMP_END__, PMF_PERCPU_TIMESTAMP_END); 31195e363fSAntonio Nino Diaz IMPORT_SYM(uintptr_t, __PMF_TIMESTAMP_START__, PMF_TIMESTAMP_ARRAY_START); 32931f7c61SSoby Mathew 33931f7c61SSoby Mathew #define PMF_PERCPU_TIMESTAMP_SIZE (PMF_PERCPU_TIMESTAMP_END - PMF_TIMESTAMP_ARRAY_START) 34a31d8983SYatharth Kochar 35a31d8983SYatharth Kochar #define PMF_SVC_DESCS_MAX 10 36a31d8983SYatharth Kochar 37a31d8983SYatharth Kochar /* 38a31d8983SYatharth Kochar * This is used to traverse through registered PMF services. 39a31d8983SYatharth Kochar */ 40a31d8983SYatharth Kochar static pmf_svc_desc_t *pmf_svc_descs; 41a31d8983SYatharth Kochar 42a31d8983SYatharth Kochar /* 43a31d8983SYatharth Kochar * This array is used to store registered PMF services in sorted order. 44a31d8983SYatharth Kochar */ 45a31d8983SYatharth Kochar static int pmf_svc_descs_indices[PMF_SVC_DESCS_MAX]; 46a31d8983SYatharth Kochar 47a31d8983SYatharth Kochar /* 48a31d8983SYatharth Kochar * This is used to track total number of successfully registered PMF services. 49a31d8983SYatharth Kochar */ 50a31d8983SYatharth Kochar static int pmf_num_services; 51a31d8983SYatharth Kochar 52a31d8983SYatharth Kochar /* 53a31d8983SYatharth Kochar * This is the main PMF function that initialize registered 54a31d8983SYatharth Kochar * PMF services and also sort them in ascending order. 55a31d8983SYatharth Kochar */ 56a31d8983SYatharth Kochar int pmf_setup(void) 57a31d8983SYatharth Kochar { 58a31d8983SYatharth Kochar int rc, ii, jj = 0; 59a31d8983SYatharth Kochar int pmf_svc_descs_num, temp_val; 60a31d8983SYatharth Kochar 61a31d8983SYatharth Kochar /* If no PMF services are registered then simply bail out */ 62a31d8983SYatharth Kochar pmf_svc_descs_num = (PMF_SVC_DESCS_END - PMF_SVC_DESCS_START)/ 63a31d8983SYatharth Kochar sizeof(pmf_svc_desc_t); 64a31d8983SYatharth Kochar if (pmf_svc_descs_num == 0) 65a31d8983SYatharth Kochar return 0; 66a31d8983SYatharth Kochar 67a31d8983SYatharth Kochar assert(pmf_svc_descs_num < PMF_SVC_DESCS_MAX); 68a31d8983SYatharth Kochar 69a31d8983SYatharth Kochar pmf_svc_descs = (pmf_svc_desc_t *) PMF_SVC_DESCS_START; 70a31d8983SYatharth Kochar for (ii = 0; ii < pmf_svc_descs_num; ii++) { 71a31d8983SYatharth Kochar 72195e363fSAntonio Nino Diaz assert(pmf_svc_descs[ii].get_ts != NULL); 73a31d8983SYatharth Kochar 74a31d8983SYatharth Kochar /* 75a31d8983SYatharth Kochar * Call the initialization routine for this 76a31d8983SYatharth Kochar * PMF service, if it is defined. 77a31d8983SYatharth Kochar */ 78195e363fSAntonio Nino Diaz if (pmf_svc_descs[ii].init != NULL) { 79a31d8983SYatharth Kochar rc = pmf_svc_descs[ii].init(); 80195e363fSAntonio Nino Diaz if (rc != 0) { 81a31d8983SYatharth Kochar WARN("Could not initialize PMF" 82a31d8983SYatharth Kochar "service %s - skipping \n", 83a31d8983SYatharth Kochar pmf_svc_descs[ii].name); 84a31d8983SYatharth Kochar continue; 85a31d8983SYatharth Kochar } 86a31d8983SYatharth Kochar } 87a31d8983SYatharth Kochar 88a31d8983SYatharth Kochar /* Update the pmf_svc_descs_indices array */ 89a31d8983SYatharth Kochar pmf_svc_descs_indices[jj++] = ii; 90a31d8983SYatharth Kochar } 91a31d8983SYatharth Kochar 92a31d8983SYatharth Kochar pmf_num_services = jj; 93a31d8983SYatharth Kochar 94a31d8983SYatharth Kochar /* 95a31d8983SYatharth Kochar * Sort the successfully registered PMF services 96a31d8983SYatharth Kochar * according to service ID 97a31d8983SYatharth Kochar */ 98a31d8983SYatharth Kochar for (ii = 1; ii < pmf_num_services; ii++) { 99a31d8983SYatharth Kochar for (jj = 0; jj < (pmf_num_services - ii); jj++) { 100a31d8983SYatharth Kochar if ((pmf_svc_descs[jj].svc_config & PMF_SVC_ID_MASK) > 101a31d8983SYatharth Kochar (pmf_svc_descs[jj + 1].svc_config & 102a31d8983SYatharth Kochar PMF_SVC_ID_MASK)) { 103a31d8983SYatharth Kochar temp_val = pmf_svc_descs_indices[jj]; 104a31d8983SYatharth Kochar pmf_svc_descs_indices[jj] = 105a31d8983SYatharth Kochar pmf_svc_descs_indices[jj+1]; 106a31d8983SYatharth Kochar pmf_svc_descs_indices[jj+1] = temp_val; 107a31d8983SYatharth Kochar } 108a31d8983SYatharth Kochar } 109a31d8983SYatharth Kochar } 110a31d8983SYatharth Kochar 111a31d8983SYatharth Kochar return 0; 112a31d8983SYatharth Kochar } 113a31d8983SYatharth Kochar 114a31d8983SYatharth Kochar /* 115a31d8983SYatharth Kochar * This function implements binary search to find registered 116a31d8983SYatharth Kochar * PMF service based on Service ID provided in `tid` argument. 117a31d8983SYatharth Kochar */ 118a31d8983SYatharth Kochar static pmf_svc_desc_t *get_service(unsigned int tid) 119a31d8983SYatharth Kochar { 120a31d8983SYatharth Kochar int low = 0; 121a31d8983SYatharth Kochar int mid; 122a31d8983SYatharth Kochar int high = pmf_num_services; 123a31d8983SYatharth Kochar unsigned int svc_id = tid & PMF_SVC_ID_MASK; 124a31d8983SYatharth Kochar int index; 125a31d8983SYatharth Kochar unsigned int desc_svc_id; 126a31d8983SYatharth Kochar 127a31d8983SYatharth Kochar if (pmf_num_services == 0) 128a31d8983SYatharth Kochar return NULL; 129a31d8983SYatharth Kochar 130195e363fSAntonio Nino Diaz assert(pmf_svc_descs != NULL); 131a31d8983SYatharth Kochar 132a31d8983SYatharth Kochar do { 133a31d8983SYatharth Kochar mid = (low + high) / 2; 134a31d8983SYatharth Kochar index = pmf_svc_descs_indices[mid]; 135a31d8983SYatharth Kochar 136a31d8983SYatharth Kochar desc_svc_id = pmf_svc_descs[index].svc_config & PMF_SVC_ID_MASK; 137a31d8983SYatharth Kochar if (svc_id < desc_svc_id) 138a31d8983SYatharth Kochar high = mid - 1; 139a31d8983SYatharth Kochar if (svc_id > desc_svc_id) 140a31d8983SYatharth Kochar low = mid + 1; 141a31d8983SYatharth Kochar } while ((svc_id != desc_svc_id) && (low <= high)); 142a31d8983SYatharth Kochar 143a31d8983SYatharth Kochar /* 144a31d8983SYatharth Kochar * Make sure the Service found supports the tid range. 145a31d8983SYatharth Kochar */ 146a31d8983SYatharth Kochar if ((svc_id == desc_svc_id) && ((tid & PMF_TID_MASK) < 147a31d8983SYatharth Kochar (pmf_svc_descs[index].svc_config & PMF_TID_MASK))) 148a31d8983SYatharth Kochar return (pmf_svc_desc_t *)&pmf_svc_descs[index]; 149a31d8983SYatharth Kochar 150a31d8983SYatharth Kochar return NULL; 151a31d8983SYatharth Kochar } 152a31d8983SYatharth Kochar 153a31d8983SYatharth Kochar /* 154a31d8983SYatharth Kochar * This function gets the time-stamp value for the PMF services 155a31d8983SYatharth Kochar * registered for SMC interface based on `tid` and `mpidr`. 156a31d8983SYatharth Kochar */ 157a31d8983SYatharth Kochar int pmf_get_timestamp_smc(unsigned int tid, 158a31d8983SYatharth Kochar u_register_t mpidr, 159a31d8983SYatharth Kochar unsigned int flags, 160a31d8983SYatharth Kochar unsigned long long *ts_value) 161a31d8983SYatharth Kochar { 162a31d8983SYatharth Kochar pmf_svc_desc_t *svc_desc; 163195e363fSAntonio Nino Diaz assert(ts_value != NULL); 164a31d8983SYatharth Kochar 165a31d8983SYatharth Kochar /* Search for registered service. */ 166a31d8983SYatharth Kochar svc_desc = get_service(tid); 167a31d8983SYatharth Kochar 168a31d8983SYatharth Kochar if ((svc_desc == NULL) || (plat_core_pos_by_mpidr(mpidr) < 0)) { 169a31d8983SYatharth Kochar *ts_value = 0; 170a31d8983SYatharth Kochar return -EINVAL; 171a31d8983SYatharth Kochar } else { 172a31d8983SYatharth Kochar /* Call the service time-stamp handler. */ 173a31d8983SYatharth Kochar *ts_value = svc_desc->get_ts(tid, mpidr, flags); 174a31d8983SYatharth Kochar return 0; 175a31d8983SYatharth Kochar } 176a31d8983SYatharth Kochar } 177a31d8983SYatharth Kochar 178a31d8983SYatharth Kochar /* 179a31d8983SYatharth Kochar * This function can be used to dump `ts` value for given `tid`. 180a31d8983SYatharth Kochar * Assumption is that the console is already initialized. 181a31d8983SYatharth Kochar */ 182a31d8983SYatharth Kochar void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts) 183a31d8983SYatharth Kochar { 18439b6cc66SAntonio Nino Diaz printf("PMF:cpu %u tid %u ts %llu\n", 185a31d8983SYatharth Kochar plat_my_core_pos(), tid, ts); 186a31d8983SYatharth Kochar } 187a31d8983SYatharth Kochar 188a31d8983SYatharth Kochar /* 189a31d8983SYatharth Kochar * This function calculate the address identified by 190a31d8983SYatharth Kochar * `base_addr`, `tid` and `cpuid`. 191a31d8983SYatharth Kochar */ 192a31d8983SYatharth Kochar static inline uintptr_t calc_ts_addr(uintptr_t base_addr, 193a31d8983SYatharth Kochar unsigned int tid, 194a31d8983SYatharth Kochar unsigned int cpuid) 195a31d8983SYatharth Kochar { 196a31d8983SYatharth Kochar assert(cpuid < PLATFORM_CORE_COUNT); 197a31d8983SYatharth Kochar assert(base_addr >= PMF_TIMESTAMP_ARRAY_START); 198a31d8983SYatharth Kochar assert(base_addr < ((PMF_TIMESTAMP_ARRAY_START + 199a31d8983SYatharth Kochar PMF_PERCPU_TIMESTAMP_SIZE) - ((tid & PMF_TID_MASK) * 200a31d8983SYatharth Kochar sizeof(unsigned long long)))); 201a31d8983SYatharth Kochar 202a31d8983SYatharth Kochar base_addr += ((cpuid * PMF_PERCPU_TIMESTAMP_SIZE) + 203a31d8983SYatharth Kochar ((tid & PMF_TID_MASK) * sizeof(unsigned long long))); 204a31d8983SYatharth Kochar 205a31d8983SYatharth Kochar return base_addr; 206a31d8983SYatharth Kochar } 207a31d8983SYatharth Kochar 208a31d8983SYatharth Kochar /* 209a31d8983SYatharth Kochar * This function stores the `ts` value to the storage identified by 210a31d8983SYatharth Kochar * `base_addr`, `tid` and current cpu id. 211a31d8983SYatharth Kochar * Note: The timestamp addresses are cache line aligned per cpu 212a31d8983SYatharth Kochar * and only the owning CPU would ever write into it. 213a31d8983SYatharth Kochar */ 214a31d8983SYatharth Kochar void __pmf_store_timestamp(uintptr_t base_addr, 215a31d8983SYatharth Kochar unsigned int tid, 216a31d8983SYatharth Kochar unsigned long long ts) 217a31d8983SYatharth Kochar { 218a31d8983SYatharth Kochar unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr, 219a31d8983SYatharth Kochar tid, plat_my_core_pos()); 220a31d8983SYatharth Kochar *ts_addr = ts; 221a31d8983SYatharth Kochar } 222a31d8983SYatharth Kochar 223a31d8983SYatharth Kochar /* 224a31d8983SYatharth Kochar * This is the cached version of `pmf_store_my_timestamp` 225a31d8983SYatharth Kochar * Note: The timestamp addresses are cache line aligned per cpu 226a31d8983SYatharth Kochar * and only the owning CPU would ever write into it. 227a31d8983SYatharth Kochar */ 228a31d8983SYatharth Kochar void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr, 229a31d8983SYatharth Kochar unsigned int tid, 230a31d8983SYatharth Kochar unsigned long long ts) 231a31d8983SYatharth Kochar { 232a31d8983SYatharth Kochar unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr, 233a31d8983SYatharth Kochar tid, plat_my_core_pos()); 234a31d8983SYatharth Kochar *ts_addr = ts; 235a31d8983SYatharth Kochar flush_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long)); 236a31d8983SYatharth Kochar } 237a31d8983SYatharth Kochar 238a31d8983SYatharth Kochar /* 239a31d8983SYatharth Kochar * This function retrieves the `ts` value from the storage identified by 240a31d8983SYatharth Kochar * `base_addr`, `tid` and `cpuid`. 241a31d8983SYatharth Kochar * Note: The timestamp addresses are cache line aligned per cpu. 242a31d8983SYatharth Kochar */ 243a31d8983SYatharth Kochar unsigned long long __pmf_get_timestamp(uintptr_t base_addr, 244a31d8983SYatharth Kochar unsigned int tid, 245a31d8983SYatharth Kochar unsigned int cpuid, 246a31d8983SYatharth Kochar unsigned int flags) 247a31d8983SYatharth Kochar { 248a31d8983SYatharth Kochar assert(cpuid < PLATFORM_CORE_COUNT); 249a31d8983SYatharth Kochar unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr, 250a31d8983SYatharth Kochar tid, cpuid); 251a31d8983SYatharth Kochar 252195e363fSAntonio Nino Diaz if ((flags & PMF_CACHE_MAINT) != 0U) 253a31d8983SYatharth Kochar inv_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long)); 254a31d8983SYatharth Kochar 255a31d8983SYatharth Kochar return *ts_addr; 256a31d8983SYatharth Kochar } 257