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