blob: ced6289f7f0dd88d554760f02535a914d8d14619 [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
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
18static 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 */
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);
Javier Almansa Sobrino68a593a2022-07-25 09:35:32 +010032 assert(g >= &granules[0]);
Soby Mathewb4c6df42022-11-09 11:13:29 +000033
34 idx = g - &granules[0];
35
36 return plat_granule_idx_to_addr(idx);
37}
38
39/*
40 * Takes a granule index, and returns a pointer to the struct granule.
41 *
42 * This is purely a lookup, and provides no guarantees about the attributes of
43 * the granule (i.e. whether it is locked, its state or its reference count).
44 */
45static struct granule *granule_from_idx(unsigned long idx)
46{
47 assert(idx < RMM_MAX_GRANULES);
48 return &granules[idx];
49}
50
51/*
52 * Takes an aligned granule address, and returns a pointer to the corresponding
53 * struct granule.
54 *
55 * This is purely a lookup, and provides no guarantees about the attributes of
56 * the granule (i.e. whether it is locked, its state or its reference count).
57 */
58struct granule *addr_to_granule(unsigned long addr)
59{
60 unsigned long idx;
61
62 assert(GRANULE_ALIGNED(addr));
63
64 idx = plat_granule_addr_to_idx(addr);
65 return granule_from_idx(idx);
66}
67
68/*
69 * Verifies whether @addr is a valid granule physical address, and returns a
70 * pointer to the corresponding struct granule.
71 *
72 * This is purely a lookup, and provides no guarantees w.r.t the state of the
73 * granule (e.g. locking).
74 *
75 * Returns:
76 * Pointer to the struct granule if @addr is a valid granule physical
77 * address.
78 * NULL if any of:
79 * - @addr is not aligned to the size of a granule.
80 * - @addr is out of range.
81 */
82struct granule *find_granule(unsigned long addr)
83{
84 unsigned long idx;
85
86 if (!GRANULE_ALIGNED(addr)) {
87 return NULL;
88 }
89
90 idx = plat_granule_addr_to_idx(addr);
91
92 if (idx >= RMM_MAX_GRANULES) {
93 return NULL;
94 }
95
96 return granule_from_idx(idx);
97}
98
99/*
100 * Obtain a pointer to a locked granule at @addr if @addr is a valid granule
101 * physical address and the state of the granule at @addr is @expected_state.
102 *
103 * Returns:
104 * A valid granule pointer if @addr is a valid granule physical address.
105 * NULL if any of:
106 * - @addr is not aligned to the size of a granule.
107 * - @addr is out of range.
108 * - if the state of the granule at @addr is not
109 * @expected_state.
110 */
111struct granule *find_lock_granule(unsigned long addr,
112 enum granule_state expected_state)
113{
114 struct granule *g;
115
116 g = find_granule(addr);
117 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 {
129 unsigned int idx;
130 unsigned long addr;
131 enum granule_state state;
132 struct granule *g;
133 struct granule **g_ret;
134};
135
136/*
137 * Sort a set of granules by their address.
138 */
139static void sort_granules(struct granule_set *granules,
140 unsigned long n)
141{
142 unsigned long i;
143
144 for (i = 1UL; i < n; i++) {
145 struct granule_set temp = granules[i];
146 unsigned long j = i;
147
148 while ((j > 0UL) && (granules[j - 1].addr > temp.addr)) {
149 granules[j] = granules[j - 1];
150 j--;
151 }
152 if (i != j) {
153 granules[j] = temp;
154 }
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 */
186static bool find_lock_granules(struct granule_set *granules,
187 unsigned long n)
188{
189 long i;
190
191 for (i = 0L; i < n; i++) {
192 granules[i].idx = i;
193 }
194
195 sort_granules(granules, n);
196
197 for (i = 0L; i < n; i++) {
198 /* Check for duplicates */
199 if ((i > 0L) && (granules[i].addr == granules[i - 1].addr)) {
200 goto out_err;
201 }
202
203 granules[i].g = find_lock_granule(granules[i].addr,
204 granules[i].state);
205 if (granules[i].g == NULL) {
206 goto out_err;
207 }
208 }
209
210 for (i = 0L; i < n; i++) {
211 *granules[i].g_ret = granules[i].g;
212 }
213
214 return true;
215
216out_err:
217 for (i = i - 1; i >= 0L; i--) {
218 granule_unlock(granules[i].g);
219 }
220
221 return false;
222}
223
224/*
225 * Find two granules and lock them in order of their address.
226 *
227 * See find_lock_granules().
228 */
229bool find_lock_two_granules(
230 unsigned long addr1,
231 enum granule_state expected_state1,
232 struct granule **g1,
233 unsigned long addr2,
234 enum granule_state expected_state2,
235 struct granule **g2)
236{
Shruti Gupta9debb132022-12-13 14:38:49 +0000237 struct granule_set gs[] = {
Soby Mathewb4c6df42022-11-09 11:13:29 +0000238 {0U, addr1, expected_state1, NULL, g1},
239 {1U, addr2, expected_state2, NULL, g2}
240 };
241
242 assert((g1 != NULL) && (g2 != NULL));
243
Shruti Gupta9debb132022-12-13 14:38:49 +0000244 return find_lock_granules(gs, ARRAY_SIZE(gs));
Soby Mathewb4c6df42022-11-09 11:13:29 +0000245}
246
247void granule_memzero(struct granule *g, enum buffer_slot slot)
248{
249 unsigned long *buf;
250
251 assert(g != NULL);
252
253 buf = granule_map(g, slot);
254 (void)memset(buf, 0, GRANULE_SIZE);
255 buffer_unmap(buf);
256}
257
258void granule_memzero_mapped(void *buf)
259{
260 (void)memset(buf, 0, GRANULE_SIZE);
261}