blob: bba612918c78b0b7d0fee70a5dc360b61cd9b364 [file] [log] [blame]
Soby Mathewb4c6df42022-11-09 11:13:29 +00001/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4 */
5
AlexeiFedorov862f96c2024-03-01 16:26:48 +00006#include <arch_helpers.h>
Soby Mathewb4c6df42022-11-09 11:13:29 +00007#include <assert.h>
8#include <buffer.h>
9#include <debug.h>
10#include <granule.h>
11#include <mmio.h>
12#include <platform_api.h>
13#include <smc.h>
14#include <status.h>
15#include <stddef.h>
Mate Toth-Pal2755d692023-09-28 17:15:58 +020016/* According to the C standard, the memset function used in this file is declared in string.h */
17/* coverity[unnecessary_header: SUPPRESS] */
Soby Mathewb4c6df42022-11-09 11:13:29 +000018#include <string.h>
19#include <utils_def.h>
20
Mate Toth-Pal0e936512023-10-19 15:02:20 +020021IF_NCBMC(static) struct granule granules[RMM_MAX_GRANULES];
Soby Mathewb4c6df42022-11-09 11:13:29 +000022
23/*
24 * Takes a valid pointer to a struct granule, and returns the granule physical
25 * address.
26 *
27 * This is purely a lookup, and provides no guarantees about the attributes of
28 * the granule (i.e. whether it is locked, its state or its reference count).
29 */
Shruti Gupta9debb132022-12-13 14:38:49 +000030unsigned long granule_addr(const struct granule *g)
Soby Mathewb4c6df42022-11-09 11:13:29 +000031{
32 unsigned long idx;
33
34 assert(g != NULL);
Soby Mathewe02e0bd2023-01-18 10:57:18 +010035 assert(ALIGNED_TO_ARRAY(g, granules));
Soby Mathewb4c6df42022-11-09 11:13:29 +000036
AlexeiFedorov7c8bf6e2023-08-29 17:02:28 +010037 idx = ((unsigned long)g - (unsigned long)granules) /
38 sizeof(struct granule);
Soby Mathewb4c6df42022-11-09 11:13:29 +000039
40 return plat_granule_idx_to_addr(idx);
41}
42
43/*
44 * Takes a granule index, and returns a pointer to the struct granule.
45 *
46 * This is purely a lookup, and provides no guarantees about the attributes of
47 * the granule (i.e. whether it is locked, its state or its reference count).
48 */
49static struct granule *granule_from_idx(unsigned long idx)
50{
51 assert(idx < RMM_MAX_GRANULES);
52 return &granules[idx];
53}
54
55/*
56 * Takes an aligned granule address, and returns a pointer to the corresponding
57 * struct granule.
58 *
59 * This is purely a lookup, and provides no guarantees about the attributes of
60 * the granule (i.e. whether it is locked, its state or its reference count).
61 */
62struct granule *addr_to_granule(unsigned long addr)
63{
64 unsigned long idx;
65
66 assert(GRANULE_ALIGNED(addr));
67
68 idx = plat_granule_addr_to_idx(addr);
69 return granule_from_idx(idx);
70}
71
72/*
73 * Verifies whether @addr is a valid granule physical address, and returns a
74 * pointer to the corresponding struct granule.
75 *
76 * This is purely a lookup, and provides no guarantees w.r.t the state of the
77 * granule (e.g. locking).
78 *
79 * Returns:
80 * Pointer to the struct granule if @addr is a valid granule physical
81 * address.
82 * NULL if any of:
83 * - @addr is not aligned to the size of a granule.
84 * - @addr is out of range.
85 */
86struct granule *find_granule(unsigned long addr)
87{
88 unsigned long idx;
89
90 if (!GRANULE_ALIGNED(addr)) {
91 return NULL;
92 }
93
94 idx = plat_granule_addr_to_idx(addr);
95
96 if (idx >= RMM_MAX_GRANULES) {
97 return NULL;
98 }
99
100 return granule_from_idx(idx);
101}
102
103/*
104 * Obtain a pointer to a locked granule at @addr if @addr is a valid granule
105 * physical address and the state of the granule at @addr is @expected_state.
106 *
107 * Returns:
108 * A valid granule pointer if @addr is a valid granule physical address.
109 * NULL if any of:
110 * - @addr is not aligned to the size of a granule.
111 * - @addr is out of range.
112 * - if the state of the granule at @addr is not
113 * @expected_state.
114 */
115struct granule *find_lock_granule(unsigned long addr,
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000116 unsigned char expected_state)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000117{
118 struct granule *g;
119
120 g = find_granule(addr);
121 if (g == NULL) {
122 return NULL;
123 }
124
125 if (!granule_lock_on_state_match(g, expected_state)) {
126 return NULL;
127 }
128
129 return g;
130}
131
132struct granule_set {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000133 unsigned long addr;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000134 struct granule *g;
135 struct granule **g_ret;
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000136 unsigned char state;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000137};
138
139/*
140 * Sort a set of granules by their address.
141 */
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100142static void sort_granules(struct granule_set *gs, unsigned long n)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000143{
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100144 for (unsigned long i = 1UL; i < n; i++) {
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100145 struct granule_set temp = gs[i];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000146 unsigned long j = i;
147
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100148 while ((j > 0UL) && (gs[j - 1UL].addr > temp.addr)) {
149 gs[j] = gs[j - 1UL];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000150 j--;
151 }
152 if (i != j) {
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100153 gs[j] = temp;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000154 }
155 }
156}
157
158/*
159 * Find a set of granules and lock them in order of their address.
160 *
161 * @granules: Pointer to array of @n items. Each item must be pre-populated
162 * with ->addr set to the granule's address, and ->state set to
163 * the expected state of the granule, and ->g_ret pointing to
164 * a valid 'struct granule *'.
165 * This function sorts the supplied array in place.
166 * @n: Number of struct granule_set in array pointed to by @granules
167 *
168 * Returns:
169 * True if all granules in @granules were successfully locked.
170 *
171 * False if any two entries in @granules have the same ->addr, or
172 * if, for any entry in @granules, any of the following is true:
173 * - entry->addr is not aligned to the size of a granule
174 * - entry->addr is out of range
175 * - the state of the granule at entry->addr is not entry->state
176 *
177 * Locking only succeeds if the granules are in their expected states as per the
178 * locking rules in granule_types.h.
179 *
180 * If the function succeeds, for all items in @granules, ->g points to a locked
181 * granule in ->state and *->g_ret is set to the pointer value.
182 *
183 * If the function fails, no lock is held and no *->g_ret pointers are
184 * modified.
185 */
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100186static bool find_lock_granules(struct granule_set *gs, unsigned long n)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000187{
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100188 unsigned long i;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000189
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100190 sort_granules(gs, n);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000191
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100192 for (i = 0UL; i < n; i++) {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000193 /* Check for duplicates */
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100194 if ((i != 0UL) &&
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100195 (gs[i].addr == gs[i - 1UL].addr)) {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000196 goto out_err;
197 }
198
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100199 gs[i].g = find_lock_granule(gs[i].addr, gs[i].state);
200 if (gs[i].g == NULL) {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000201 goto out_err;
202 }
203 }
204
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100205 for (i = 0UL; i < n; i++) {
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100206 *gs[i].g_ret = gs[i].g;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000207 }
208
209 return true;
210
211out_err:
AlexeiFedorove956db32024-04-04 14:36:21 +0100212 while (i != 0UL) {
213 granule_unlock(gs[--i].g);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000214 }
215
216 return false;
217}
218
219/*
220 * Find two granules and lock them in order of their address.
221 *
222 * See find_lock_granules().
223 */
224bool find_lock_two_granules(
225 unsigned long addr1,
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000226 unsigned char expected_state1,
Soby Mathewb4c6df42022-11-09 11:13:29 +0000227 struct granule **g1,
228 unsigned long addr2,
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000229 unsigned char expected_state2,
Soby Mathewb4c6df42022-11-09 11:13:29 +0000230 struct granule **g2)
231{
Shruti Gupta9debb132022-12-13 14:38:49 +0000232 struct granule_set gs[] = {
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000233 {addr1, NULL, g1, expected_state1},
234 {addr2, NULL, g2, expected_state2}
Soby Mathewb4c6df42022-11-09 11:13:29 +0000235 };
236
237 assert((g1 != NULL) && (g2 != NULL));
238
Shruti Gupta9debb132022-12-13 14:38:49 +0000239 return find_lock_granules(gs, ARRAY_SIZE(gs));
Soby Mathewb4c6df42022-11-09 11:13:29 +0000240}
241
242void granule_memzero(struct granule *g, enum buffer_slot slot)
243{
244 unsigned long *buf;
245
246 assert(g != NULL);
247
248 buf = granule_map(g, slot);
AlexeiFedorov9a9062c2023-08-21 15:41:48 +0100249 assert(buf != NULL);
250
AlexeiFedorov862f96c2024-03-01 16:26:48 +0000251 granule_memzero_mapped(buf);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000252 buffer_unmap(buf);
253}
254
255void granule_memzero_mapped(void *buf)
256{
AlexeiFedorov862f96c2024-03-01 16:26:48 +0000257 unsigned long dczid_el0 = read_dczid_el0();
258 uintptr_t addr = (uintptr_t)buf;
259 unsigned int log2_size;
260 unsigned int block_size;
261 unsigned int cnt;
262
263 /* Check that use of DC ZVA instructions is permitted */
264 assert((dczid_el0 & DCZID_EL0_DZP_BIT) == 0UL);
265
266 /*
267 * Log2 of the block size in words.
268 * The maximum size supported is 2KB, indicated by value 0b1001.
269 */
270 log2_size = (unsigned int)EXTRACT(DCZID_EL0_BS, dczid_el0) + 2U;
271 block_size = U(1) << log2_size;
272
273 /* Number of iterations */
274 cnt = U(1) << (GRANULE_SHIFT - log2_size);
275
276 for (unsigned int i = 0U; i < cnt; i++) {
277 dczva(addr);
278 addr += block_size;
279 }
AlexeiFedorovd9aa2e72024-03-08 11:43:54 +0000280
281 dsb(ish);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000282}
Arunachalam Ganapathy40b3bf02023-06-12 12:19:55 +0100283
284/*
285 * The parent REC granules lock is expected to be acquired before functions
286 * aux_granules_map() and aux_granules_unmap() are called.
287 */
288void *aux_granules_map(struct granule *rec_aux_pages[], unsigned int num_aux)
289{
290 void *rec_aux = NULL;
291
292 assert(rec_aux_pages != NULL);
293 assert(num_aux <= MAX_REC_AUX_GRANULES);
294
295 for (unsigned int i = 0U; i < num_aux; i++) {
296 void *aux = granule_map(rec_aux_pages[i],
297 (enum buffer_slot)((unsigned int)
298 SLOT_REC_AUX0 + i));
299
300 assert(aux != NULL);
301
302 if (i == 0UL) {
303 rec_aux = aux;
304 }
305 }
306 return rec_aux;
307}
308
309void aux_granules_unmap(void *rec_aux, unsigned int num_aux)
310{
311 unsigned char *rec_aux_vaddr = (unsigned char *)rec_aux;
312
313 assert(rec_aux != NULL);
314 assert(num_aux <= MAX_REC_AUX_GRANULES);
315
316 for (unsigned int i = 0U; i < num_aux; i++) {
AlexeiFedorov3f5d6272023-10-23 16:27:37 +0100317 buffer_unmap((void *)((uintptr_t)rec_aux_vaddr + (i * GRANULE_SIZE)));
Arunachalam Ganapathy40b3bf02023-06-12 12:19:55 +0100318 }
319}