1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4  */
5 
6 #include <assert.h>
7 #include <buffer.h>
8 #include <debug.h>
9 #include <granule.h>
10 #include <mmio.h>
11 #include <platform_api.h>
12 #include <smc.h>
13 #include <status.h>
14 #include <stddef.h>
15 #include <string.h>
16 #include <utils_def.h>
17 
18 static struct granule granules[RMM_MAX_GRANULES];
19 
20 /*
21  * Takes a valid pointer to a struct granule, and returns the granule physical
22  * address.
23  *
24  * This is purely a lookup, and provides no guarantees about the attributes of
25  * the granule (i.e. whether it is locked, its state or its reference count).
26  */
granule_addr(struct granule * g)27 unsigned long granule_addr(struct granule *g)
28 {
29 	unsigned long idx;
30 
31 	assert(g != NULL);
32 
33 	idx = g - &granules[0];
34 
35 	return plat_granule_idx_to_addr(idx);
36 }
37 
38 /*
39  * Takes a granule index, and returns a pointer to the struct granule.
40  *
41  * This is purely a lookup, and provides no guarantees about the attributes of
42  * the granule (i.e. whether it is locked, its state or its reference count).
43  */
granule_from_idx(unsigned long idx)44 static struct granule *granule_from_idx(unsigned long idx)
45 {
46 	assert(idx < RMM_MAX_GRANULES);
47 	return &granules[idx];
48 }
49 
50 /*
51  * Takes an aligned granule address, and returns a pointer to the corresponding
52  * struct granule.
53  *
54  * This is purely a lookup, and provides no guarantees about the attributes of
55  * the granule (i.e. whether it is locked, its state or its reference count).
56  */
addr_to_granule(unsigned long addr)57 struct granule *addr_to_granule(unsigned long addr)
58 {
59 	unsigned long idx;
60 
61 	assert(GRANULE_ALIGNED(addr));
62 
63 	idx = plat_granule_addr_to_idx(addr);
64 	return granule_from_idx(idx);
65 }
66 
67 /*
68  * Verifies whether @addr is a valid granule physical address, and returns a
69  * pointer to the corresponding struct granule.
70  *
71  * This is purely a lookup, and provides no guarantees w.r.t the state of the
72  * granule (e.g. locking).
73  *
74  * Returns:
75  *     Pointer to the struct granule if @addr is a valid granule physical
76  *     address.
77  *     NULL if any of:
78  *     - @addr is not aligned to the size of a granule.
79  *     - @addr is out of range.
80  */
find_granule(unsigned long addr)81 struct granule *find_granule(unsigned long addr)
82 {
83 	unsigned long idx;
84 
85 	if (!GRANULE_ALIGNED(addr)) {
86 		return NULL;
87 	}
88 
89 	idx = plat_granule_addr_to_idx(addr);
90 
91 	if (idx >= RMM_MAX_GRANULES) {
92 		return NULL;
93 	}
94 
95 	return granule_from_idx(idx);
96 }
97 
98 /*
99  * Obtain a pointer to a locked granule at @addr if @addr is a valid granule
100  * physical address and the state of the granule at @addr is @expected_state.
101  *
102  * Returns:
103  *	A valid granule pointer if @addr is a valid granule physical address.
104  *	NULL if any of:
105  *	- @addr is not aligned to the size of a granule.
106  *	- @addr is out of range.
107  *	- if the state of the granule at @addr is not
108  *	@expected_state.
109  */
find_lock_granule(unsigned long addr,enum granule_state expected_state)110 struct granule *find_lock_granule(unsigned long addr,
111 				  enum granule_state expected_state)
112 {
113 	struct granule *g;
114 
115 	g = find_granule(addr);
116 	if (g == NULL) {
117 		return NULL;
118 	}
119 
120 	if (!granule_lock_on_state_match(g, expected_state)) {
121 		return NULL;
122 	}
123 
124 	return g;
125 }
126 
127 struct granule_set {
128 	unsigned int idx;
129 	unsigned long addr;
130 	enum granule_state state;
131 	struct granule *g;
132 	struct granule **g_ret;
133 };
134 
135 /*
136  * Sort a set of granules by their address.
137  */
sort_granules(struct granule_set * granules,unsigned long n)138 static void sort_granules(struct granule_set *granules,
139 			unsigned long n)
140 {
141 	unsigned long i;
142 
143 	for (i = 1UL; i < n; i++) {
144 		struct granule_set temp = granules[i];
145 		unsigned long j = i;
146 
147 		while ((j > 0UL) && (granules[j - 1].addr > temp.addr)) {
148 			granules[j] = granules[j - 1];
149 			j--;
150 		}
151 		if (i != j) {
152 			granules[j] = temp;
153 		}
154 	}
155 }
156 
157 /*
158  * Find a set of granules and lock them in order of their address.
159  *
160  * @granules: Pointer to array of @n items.  Each item must be pre-populated
161  *		with ->addr set to the granule's address, and ->state set to
162  *		the expected state of the granule, and ->g_ret pointing to
163  *		a valid 'struct granule *'.
164  *		This function sorts the supplied array in place.
165  * @n: Number of struct granule_set in array pointed to by @granules
166  *
167  * Returns:
168  *     True if all granules in @granules were successfully locked.
169  *
170  *     False if any two entries in @granules have the same ->addr, or
171  *     if, for any entry in @granules, any of the following is true:
172  *       - entry->addr is not aligned to the size of a granule
173  *       - entry->addr is out of range
174  *       - the state of the granule at entry->addr is not entry->state
175  *
176  * Locking only succeeds if the granules are in their expected states as per the
177  * locking rules in granule_types.h.
178  *
179  * If the function succeeds, for all items in @granules, ->g points to a locked
180  * granule in ->state and *->g_ret is set to the pointer value.
181  *
182  * If the function fails, no lock is held and no *->g_ret pointers are
183  * modified.
184  */
find_lock_granules(struct granule_set * granules,unsigned long n)185 static bool find_lock_granules(struct granule_set *granules,
186 				unsigned long n)
187 {
188 	long i;
189 
190 	for (i = 0L; i < n; i++) {
191 		granules[i].idx = i;
192 	}
193 
194 	sort_granules(granules, n);
195 
196 	for (i = 0L; i < n; i++) {
197 		/* Check for duplicates */
198 		if ((i > 0L) && (granules[i].addr == granules[i - 1].addr)) {
199 			goto out_err;
200 		}
201 
202 		granules[i].g = find_lock_granule(granules[i].addr,
203 						granules[i].state);
204 		if (granules[i].g == NULL) {
205 			goto out_err;
206 		}
207 	}
208 
209 	for (i = 0L; i < n; i++) {
210 		*granules[i].g_ret = granules[i].g;
211 	}
212 
213 	return true;
214 
215 out_err:
216 	for (i = i - 1; i >= 0L; i--) {
217 		granule_unlock(granules[i].g);
218 	}
219 
220 	return false;
221 }
222 
223 /*
224  * Find two granules and lock them in order of their address.
225  *
226  * See find_lock_granules().
227  */
find_lock_two_granules(unsigned long addr1,enum granule_state expected_state1,struct granule ** g1,unsigned long addr2,enum granule_state expected_state2,struct granule ** g2)228 bool find_lock_two_granules(
229 			unsigned long addr1,
230 			enum granule_state expected_state1,
231 			struct granule **g1,
232 			unsigned long addr2,
233 			enum granule_state expected_state2,
234 			struct granule **g2)
235 {
236 	struct granule_set granules[] = {
237 		{0U, addr1, expected_state1, NULL, g1},
238 		{1U, addr2, expected_state2, NULL, g2}
239 	};
240 
241 	assert((g1 != NULL) && (g2 != NULL));
242 
243 	return find_lock_granules(granules, ARRAY_SIZE(granules));
244 }
245 
granule_memzero(struct granule * g,enum buffer_slot slot)246 void granule_memzero(struct granule *g, enum buffer_slot slot)
247 {
248 	unsigned long *buf;
249 
250 	assert(g != NULL);
251 
252 	buf = granule_map(g, slot);
253 	(void)memset(buf, 0, GRANULE_SIZE);
254 	buffer_unmap(buf);
255 }
256 
granule_memzero_mapped(void * buf)257 void granule_memzero_mapped(void *buf)
258 {
259 	(void)memset(buf, 0, GRANULE_SIZE);
260 }
261