xref: /arm-trusted-firmware/lib/pmf/pmf_main.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
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