1 /* 2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of ARM nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 #include <arch.h> 31 #include <arch_helpers.h> 32 #include <assert.h> 33 #include <debug.h> 34 #include <errno.h> 35 #include <platform.h> 36 #include <pmf.h> 37 #include <string.h> 38 39 /******************************************************************************* 40 * The 'pmf_svc_descs' array holds the PMF service descriptors exported by 41 * services by placing them in the 'pmf_svc_descs' linker section. 42 * The 'pmf_svc_descs_indices' array holds the index of a descriptor in the 43 * 'pmf_svc_descs' array. The TIF[15:10] bits in the time-stamp id are used 44 * to get an index into the 'pmf_svc_descs_indices' array. This gives the 45 * index of the descriptor in the 'pmf_svc_descs' array which contains the 46 * service function pointers. 47 ******************************************************************************/ 48 extern uintptr_t __PMF_SVC_DESCS_START__; 49 extern uintptr_t __PMF_SVC_DESCS_END__; 50 #define PMF_SVC_DESCS_START ((uintptr_t)(&__PMF_SVC_DESCS_START__)) 51 #define PMF_SVC_DESCS_END ((uintptr_t)(&__PMF_SVC_DESCS_END__)) 52 extern void *__PERCPU_TIMESTAMP_SIZE__; 53 #define PMF_PERCPU_TIMESTAMP_SIZE ((uintptr_t)&__PERCPU_TIMESTAMP_SIZE__) 54 extern uintptr_t __PMF_TIMESTAMP_START__; 55 #define PMF_TIMESTAMP_ARRAY_START ((uintptr_t)&__PMF_TIMESTAMP_START__) 56 extern uintptr_t __PMF_TIMESTAMP_END__; 57 #define PMF_TIMESTAMP_ARRAY_END ((uintptr_t)&__PMF_TIMESTAMP_END__) 58 59 #define PMF_SVC_DESCS_MAX 10 60 61 /* 62 * This is used to traverse through registered PMF services. 63 */ 64 static pmf_svc_desc_t *pmf_svc_descs; 65 66 /* 67 * This array is used to store registered PMF services in sorted order. 68 */ 69 static int pmf_svc_descs_indices[PMF_SVC_DESCS_MAX]; 70 71 /* 72 * This is used to track total number of successfully registered PMF services. 73 */ 74 static int pmf_num_services; 75 76 /* 77 * This is the main PMF function that initialize registered 78 * PMF services and also sort them in ascending order. 79 */ 80 int pmf_setup(void) 81 { 82 int rc, ii, jj = 0; 83 int pmf_svc_descs_num, temp_val; 84 85 /* If no PMF services are registered then simply bail out */ 86 pmf_svc_descs_num = (PMF_SVC_DESCS_END - PMF_SVC_DESCS_START)/ 87 sizeof(pmf_svc_desc_t); 88 if (pmf_svc_descs_num == 0) 89 return 0; 90 91 assert(pmf_svc_descs_num < PMF_SVC_DESCS_MAX); 92 93 pmf_svc_descs = (pmf_svc_desc_t *) PMF_SVC_DESCS_START; 94 for (ii = 0; ii < pmf_svc_descs_num; ii++) { 95 96 assert(pmf_svc_descs[ii].get_ts); 97 98 /* 99 * Call the initialization routine for this 100 * PMF service, if it is defined. 101 */ 102 if (pmf_svc_descs[ii].init) { 103 rc = pmf_svc_descs[ii].init(); 104 if (rc) { 105 WARN("Could not initialize PMF" 106 "service %s - skipping \n", 107 pmf_svc_descs[ii].name); 108 continue; 109 } 110 } 111 112 /* Update the pmf_svc_descs_indices array */ 113 pmf_svc_descs_indices[jj++] = ii; 114 } 115 116 pmf_num_services = jj; 117 118 /* 119 * Sort the successfully registered PMF services 120 * according to service ID 121 */ 122 for (ii = 1; ii < pmf_num_services; ii++) { 123 for (jj = 0; jj < (pmf_num_services - ii); jj++) { 124 if ((pmf_svc_descs[jj].svc_config & PMF_SVC_ID_MASK) > 125 (pmf_svc_descs[jj + 1].svc_config & 126 PMF_SVC_ID_MASK)) { 127 temp_val = pmf_svc_descs_indices[jj]; 128 pmf_svc_descs_indices[jj] = 129 pmf_svc_descs_indices[jj+1]; 130 pmf_svc_descs_indices[jj+1] = temp_val; 131 } 132 } 133 } 134 135 return 0; 136 } 137 138 /* 139 * This function implements binary search to find registered 140 * PMF service based on Service ID provided in `tid` argument. 141 */ 142 static pmf_svc_desc_t *get_service(unsigned int tid) 143 { 144 int low = 0; 145 int mid; 146 int high = pmf_num_services; 147 unsigned int svc_id = tid & PMF_SVC_ID_MASK; 148 int index; 149 unsigned int desc_svc_id; 150 151 if (pmf_num_services == 0) 152 return NULL; 153 154 assert(pmf_svc_descs); 155 156 do { 157 mid = (low + high) / 2; 158 index = pmf_svc_descs_indices[mid]; 159 160 desc_svc_id = pmf_svc_descs[index].svc_config & PMF_SVC_ID_MASK; 161 if (svc_id < desc_svc_id) 162 high = mid - 1; 163 if (svc_id > desc_svc_id) 164 low = mid + 1; 165 } while ((svc_id != desc_svc_id) && (low <= high)); 166 167 /* 168 * Make sure the Service found supports the tid range. 169 */ 170 if ((svc_id == desc_svc_id) && ((tid & PMF_TID_MASK) < 171 (pmf_svc_descs[index].svc_config & PMF_TID_MASK))) 172 return (pmf_svc_desc_t *)&pmf_svc_descs[index]; 173 174 return NULL; 175 } 176 177 /* 178 * This function gets the time-stamp value for the PMF services 179 * registered for SMC interface based on `tid` and `mpidr`. 180 */ 181 int pmf_get_timestamp_smc(unsigned int tid, 182 u_register_t mpidr, 183 unsigned int flags, 184 unsigned long long *ts_value) 185 { 186 pmf_svc_desc_t *svc_desc; 187 assert(ts_value); 188 189 /* Search for registered service. */ 190 svc_desc = get_service(tid); 191 192 if ((svc_desc == NULL) || (plat_core_pos_by_mpidr(mpidr) < 0)) { 193 *ts_value = 0; 194 return -EINVAL; 195 } else { 196 /* Call the service time-stamp handler. */ 197 *ts_value = svc_desc->get_ts(tid, mpidr, flags); 198 return 0; 199 } 200 } 201 202 /* 203 * This function can be used to dump `ts` value for given `tid`. 204 * Assumption is that the console is already initialized. 205 */ 206 void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts) 207 { 208 tf_printf("PMF:cpu %u tid %u ts %llu\n", 209 plat_my_core_pos(), tid, ts); 210 } 211 212 /* 213 * This function calculate the address identified by 214 * `base_addr`, `tid` and `cpuid`. 215 */ 216 static inline uintptr_t calc_ts_addr(uintptr_t base_addr, 217 unsigned int tid, 218 unsigned int cpuid) 219 { 220 assert(cpuid < PLATFORM_CORE_COUNT); 221 assert(base_addr >= PMF_TIMESTAMP_ARRAY_START); 222 assert(base_addr < ((PMF_TIMESTAMP_ARRAY_START + 223 PMF_PERCPU_TIMESTAMP_SIZE) - ((tid & PMF_TID_MASK) * 224 sizeof(unsigned long long)))); 225 226 base_addr += ((cpuid * PMF_PERCPU_TIMESTAMP_SIZE) + 227 ((tid & PMF_TID_MASK) * sizeof(unsigned long long))); 228 229 return base_addr; 230 } 231 232 /* 233 * This function stores the `ts` value to the storage identified by 234 * `base_addr`, `tid` and current cpu id. 235 * Note: The timestamp addresses are cache line aligned per cpu 236 * and only the owning CPU would ever write into it. 237 */ 238 void __pmf_store_timestamp(uintptr_t base_addr, 239 unsigned int tid, 240 unsigned long long ts) 241 { 242 unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr, 243 tid, plat_my_core_pos()); 244 *ts_addr = ts; 245 } 246 247 /* 248 * This is the cached version of `pmf_store_my_timestamp` 249 * Note: The timestamp addresses are cache line aligned per cpu 250 * and only the owning CPU would ever write into it. 251 */ 252 void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr, 253 unsigned int tid, 254 unsigned long long ts) 255 { 256 unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr, 257 tid, plat_my_core_pos()); 258 *ts_addr = ts; 259 flush_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long)); 260 } 261 262 /* 263 * This function retrieves the `ts` value from the storage identified by 264 * `base_addr`, `tid` and `cpuid`. 265 * Note: The timestamp addresses are cache line aligned per cpu. 266 */ 267 unsigned long long __pmf_get_timestamp(uintptr_t base_addr, 268 unsigned int tid, 269 unsigned int cpuid, 270 unsigned int flags) 271 { 272 assert(cpuid < PLATFORM_CORE_COUNT); 273 unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr, 274 tid, cpuid); 275 276 if (flags & PMF_CACHE_MAINT) 277 inv_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long)); 278 279 return *ts_addr; 280 } 281