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