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