blob: 3d8922f33b9e4d8aa76438709f5c8fc94a0a8103 [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>
Soby Mathewb4c6df42022-11-09 11:13:29 +00008#include <debug.h>
9#include <granule.h>
10#include <mmio.h>
11#include <platform_api.h>
Soby Mathewb4c6df42022-11-09 11:13:29 +000012#include <stddef.h>
Mate Toth-Pal2755d692023-09-28 17:15:58 +020013/* According to the C standard, the memset function used in this file is declared in string.h */
14/* coverity[unnecessary_header: SUPPRESS] */
Soby Mathewb4c6df42022-11-09 11:13:29 +000015#include <string.h>
16#include <utils_def.h>
17
Mate Toth-Pal0e936512023-10-19 15:02:20 +020018IF_NCBMC(static) struct granule granules[RMM_MAX_GRANULES];
Soby Mathewb4c6df42022-11-09 11:13:29 +000019
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 */
Shruti Gupta9debb132022-12-13 14:38:49 +000027unsigned long granule_addr(const struct granule *g)
Soby Mathewb4c6df42022-11-09 11:13:29 +000028{
29 unsigned long idx;
30
31 assert(g != NULL);
Soby Mathewe02e0bd2023-01-18 10:57:18 +010032 assert(ALIGNED_TO_ARRAY(g, granules));
Soby Mathewb4c6df42022-11-09 11:13:29 +000033
AlexeiFedorov7c8bf6e2023-08-29 17:02:28 +010034 idx = ((unsigned long)g - (unsigned long)granules) /
35 sizeof(struct granule);
Soby Mathewb4c6df42022-11-09 11:13:29 +000036
37 return plat_granule_idx_to_addr(idx);
38}
39
40/*
41 * Takes a granule index, and returns a pointer to the struct granule.
42 *
43 * This is purely a lookup, and provides no guarantees about the attributes of
44 * the granule (i.e. whether it is locked, its state or its reference count).
45 */
46static struct granule *granule_from_idx(unsigned long idx)
47{
48 assert(idx < RMM_MAX_GRANULES);
49 return &granules[idx];
50}
51
52/*
53 * Takes an aligned granule address, and returns a pointer to the corresponding
54 * struct granule.
55 *
56 * This is purely a lookup, and provides no guarantees about the attributes of
57 * the granule (i.e. whether it is locked, its state or its reference count).
58 */
59struct granule *addr_to_granule(unsigned long addr)
60{
61 unsigned long idx;
62
63 assert(GRANULE_ALIGNED(addr));
64
65 idx = plat_granule_addr_to_idx(addr);
66 return granule_from_idx(idx);
67}
68
69/*
70 * Verifies whether @addr is a valid granule physical address, and returns a
71 * pointer to the corresponding struct granule.
72 *
73 * This is purely a lookup, and provides no guarantees w.r.t the state of the
74 * granule (e.g. locking).
75 *
76 * Returns:
77 * Pointer to the struct granule if @addr is a valid granule physical
78 * address.
79 * NULL if any of:
80 * - @addr is not aligned to the size of a granule.
81 * - @addr is out of range.
82 */
83struct granule *find_granule(unsigned long addr)
84{
85 unsigned long idx;
86
87 if (!GRANULE_ALIGNED(addr)) {
88 return NULL;
89 }
90
91 idx = plat_granule_addr_to_idx(addr);
92
93 if (idx >= RMM_MAX_GRANULES) {
94 return NULL;
95 }
96
97 return granule_from_idx(idx);
98}
99
100/*
101 * Obtain a pointer to a locked granule at @addr if @addr is a valid granule
102 * physical address and the state of the granule at @addr is @expected_state.
103 *
104 * Returns:
105 * A valid granule pointer if @addr is a valid granule physical address.
106 * NULL if any of:
107 * - @addr is not aligned to the size of a granule.
108 * - @addr is out of range.
109 * - if the state of the granule at @addr is not
110 * @expected_state.
111 */
112struct granule *find_lock_granule(unsigned long addr,
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000113 unsigned char expected_state)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000114{
AlexeiFedorov745499d2024-04-25 16:52:44 +0100115 struct granule *g = find_granule(addr);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000116
Soby Mathewb4c6df42022-11-09 11:13:29 +0000117 if (g == NULL) {
118 return NULL;
119 }
120
121 if (!granule_lock_on_state_match(g, expected_state)) {
122 return NULL;
123 }
124
125 return g;
126}
127
128struct granule_set {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000129 unsigned long addr;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000130 struct granule *g;
131 struct granule **g_ret;
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000132 unsigned char state;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000133};
134
135/*
136 * Sort a set of granules by their address.
137 */
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100138static void sort_granules(struct granule_set *gs, unsigned long n)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000139{
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100140 for (unsigned long i = 1UL; i < n; i++) {
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100141 struct granule_set temp = gs[i];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000142 unsigned long j = i;
143
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100144 while ((j > 0UL) && (gs[j - 1UL].addr > temp.addr)) {
145 gs[j] = gs[j - 1UL];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000146 j--;
147 }
148 if (i != j) {
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100149 gs[j] = temp;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000150 }
151 }
152}
153
154/*
155 * Find a set of granules and lock them in order of their address.
156 *
157 * @granules: Pointer to array of @n items. Each item must be pre-populated
158 * with ->addr set to the granule's address, and ->state set to
159 * the expected state of the granule, and ->g_ret pointing to
160 * a valid 'struct granule *'.
161 * This function sorts the supplied array in place.
162 * @n: Number of struct granule_set in array pointed to by @granules
163 *
164 * Returns:
165 * True if all granules in @granules were successfully locked.
166 *
167 * False if any two entries in @granules have the same ->addr, or
168 * if, for any entry in @granules, any of the following is true:
169 * - entry->addr is not aligned to the size of a granule
170 * - entry->addr is out of range
171 * - the state of the granule at entry->addr is not entry->state
172 *
173 * Locking only succeeds if the granules are in their expected states as per the
174 * locking rules in granule_types.h.
175 *
176 * If the function succeeds, for all items in @granules, ->g points to a locked
177 * granule in ->state and *->g_ret is set to the pointer value.
178 *
179 * If the function fails, no lock is held and no *->g_ret pointers are
180 * modified.
181 */
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100182static bool find_lock_granules(struct granule_set *gs, unsigned long n)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000183{
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100184 unsigned long i;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000185
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100186 sort_granules(gs, n);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000187
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100188 for (i = 0UL; i < n; i++) {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000189 /* Check for duplicates */
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100190 if ((i != 0UL) &&
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100191 (gs[i].addr == gs[i - 1UL].addr)) {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000192 goto out_err;
193 }
194
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100195 gs[i].g = find_lock_granule(gs[i].addr, gs[i].state);
196 if (gs[i].g == NULL) {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000197 goto out_err;
198 }
199 }
200
AlexeiFedorov93f5ec52023-08-31 14:26:53 +0100201 for (i = 0UL; i < n; i++) {
AlexeiFedorov56e1a8e2023-09-01 17:06:13 +0100202 *gs[i].g_ret = gs[i].g;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000203 }
204
205 return true;
206
207out_err:
AlexeiFedorove956db32024-04-04 14:36:21 +0100208 while (i != 0UL) {
209 granule_unlock(gs[--i].g);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000210 }
211
212 return false;
213}
214
215/*
216 * Find two granules and lock them in order of their address.
217 *
218 * See find_lock_granules().
219 */
220bool find_lock_two_granules(
221 unsigned long addr1,
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000222 unsigned char expected_state1,
Soby Mathewb4c6df42022-11-09 11:13:29 +0000223 struct granule **g1,
224 unsigned long addr2,
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000225 unsigned char expected_state2,
Soby Mathewb4c6df42022-11-09 11:13:29 +0000226 struct granule **g2)
227{
Shruti Gupta9debb132022-12-13 14:38:49 +0000228 struct granule_set gs[] = {
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000229 {addr1, NULL, g1, expected_state1},
230 {addr2, NULL, g2, expected_state2}
Soby Mathewb4c6df42022-11-09 11:13:29 +0000231 };
232
233 assert((g1 != NULL) && (g2 != NULL));
234
Shruti Gupta9debb132022-12-13 14:38:49 +0000235 return find_lock_granules(gs, ARRAY_SIZE(gs));
Soby Mathewb4c6df42022-11-09 11:13:29 +0000236}
237
Soby Mathewb4c6df42022-11-09 11:13:29 +0000238void granule_memzero_mapped(void *buf)
239{
AlexeiFedorov862f96c2024-03-01 16:26:48 +0000240 unsigned long dczid_el0 = read_dczid_el0();
241 uintptr_t addr = (uintptr_t)buf;
242 unsigned int log2_size;
243 unsigned int block_size;
244 unsigned int cnt;
245
246 /* Check that use of DC ZVA instructions is permitted */
247 assert((dczid_el0 & DCZID_EL0_DZP_BIT) == 0UL);
248
249 /*
250 * Log2 of the block size in words.
251 * The maximum size supported is 2KB, indicated by value 0b1001.
252 */
253 log2_size = (unsigned int)EXTRACT(DCZID_EL0_BS, dczid_el0) + 2U;
254 block_size = U(1) << log2_size;
255
256 /* Number of iterations */
257 cnt = U(1) << (GRANULE_SHIFT - log2_size);
258
259 for (unsigned int i = 0U; i < cnt; i++) {
260 dczva(addr);
261 addr += block_size;
262 }
AlexeiFedorovd9aa2e72024-03-08 11:43:54 +0000263
264 dsb(ish);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000265}
Arunachalam Ganapathy40b3bf02023-06-12 12:19:55 +0100266
Jean-Philippe Brucker8b4361d2025-01-22 18:11:26 +0000267/* Must be called with granule lock held */
268void granule_set_state(struct granule *g, unsigned char state)
269{
270 unsigned short val;
271
272 assert((g != NULL) && LOCKED(g));
273
274 /* NOLINTNEXTLINE(clang-analyzer-core.NullDereference) */
275 val = g->descriptor & STATE_MASK;
276
277 /* cppcheck-suppress misra-c2012-10.3 */
278 val ^= (unsigned short)state << GRN_STATE_SHIFT;
279
280 /*
281 * Atomically EOR val while keeping the bits for refcount and
282 * bitlock as 0 which would preserve their values in memory.
283 */
284 (void)atomic_eor_16(&g->descriptor, val);
285}