1 /*
2 * Copyright (c) 2023, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <assert.h>
12
13 #include "host_base_address.h"
14 #include "rse_comms_atu.h"
15 #include "sds.h"
16 #include "tfm_plat_defs.h"
17 #include "utilities.h"
18
19 /*!
20 * \brief Aligns a value to the next multiple.
21 *
22 * \param VALUE Value to be aligned.
23 * \param INTERVAL Multiple to align to.
24 *
25 * \return The nearest multiple which is greater than or equal to VALUE.
26 */
27 #define ALIGN_NEXT(VALUE, INTERVAL) ((\
28 ((VALUE) + (INTERVAL) - 1) / (INTERVAL)) * (INTERVAL))
29
30 /* Arbitrary, 16 bit value that indicates a valid SDS Memory Region */
31 #define REGION_SIGNATURE 0xAA7A
32 /* The minor version of the SDS schema supported by this implementation */
33 #define SUPPORTED_VERSION_MINOR 0x0
34 /* The major version of the SDS schema supported by this implementation */
35 #define SUPPORTED_VERSION_MAJOR 0x1
36
37 /*! Position of the major version field. */
38 #define SDS_ID_VERSION_MAJOR_POS 24
39
40 /* Minimum required byte alignment for fields within structures */
41 #define MIN_FIELD_ALIGNMENT 4
42 /* Minimum required byte alignment for structures within the region */
43 #define MIN_STRUCT_ALIGNMENT 8
44 /* Minimum structure size in bytes */
45 #define MIN_STRUCT_SIZE 4
46 /* Minimum structure size (including padding) in bytes */
47 #define MIN_ALIGNED_STRUCT_SIZE \
48 ALIGN_NEXT(MIN_STRUCT_SIZE, MIN_STRUCT_ALIGNMENT)
49 /* Minimum region size in bytes */
50 #define MIN_REGION_SIZE (sizeof(struct region_descriptor) + \
51 MIN_ALIGNED_STRUCT_SIZE)
52
53 /* Header containing Shared Data Structure metadata */
54 struct structure_header {
55 /*
56 * Compound identifier value consisting of a linear structure ID, a major
57 * version, and a minor version. Together these uniquely identify the
58 * structure.
59 */
60 uint32_t id;
61
62 /*
63 * Flag indicating whether the structure's content is valid. Always false
64 * initially, the flag is set to true when the structure is finalized.
65 */
66 bool valid : 1;
67
68 /*
69 * Size, in bytes, of the structure. The size of the header is not included,
70 * only the structure content. If padding was used to maintain alignment
71 * during the structure's creation then the additional space used for the
72 * padding will be reflected in this value.
73 */
74 uint32_t size : 23;
75
76 /* Reserved */
77 uint32_t reserved : 8;
78 };
79
80 /* Region Descriptor describing the SDS Memory Region */
81 struct region_descriptor {
82 /*
83 * Field used to determine the presence, or absence, of an SDS Memory Region
84 * and Region Descriptor. When the region is initialized the signature field
85 * is given a fixed value according to the REGION_SIGNATURE definition.
86 */
87 uint16_t signature;
88
89 /*
90 * The total number of structures, finalized or otherwise, within the SDS
91 * Memory Region.
92 */
93 uint16_t structure_count : 8;
94
95 /* The minor version component of the implemented SDS specification */
96 uint16_t version_minor : 4;
97
98 /* The major version component of the implemented SDS specification */
99 uint16_t version_major : 4;
100
101 /*
102 * The total size of the SDS Memory Region, in bytes. The value includes the
103 * size of the Region Descriptor because the descriptor is located within
104 * the region that it describes. This is in contrast to structure headers,
105 * which are considered to be prepended to the memory allocated for the
106 * structure content.
107 */
108 uint32_t region_size;
109 };
110
111
112 struct sds_region {
113 uint64_t host_addr;
114 void *mapped_addr;
115 size_t size;
116 uint8_t free_mem_offset;
117 size_t free_mem_size;
118 bool is_init;
119 };
120
121 static struct sds_region region_config = {.host_addr = PLAT_RSE_AP_SDS_BASE,
122 .size = PLAT_RSE_AP_SDS_SIZE,
123 .is_init = false};
124
125 /*
126 * Perform some tests to determine whether any of the fields within a Structure
127 * Header contain obviously invalid data.
128 */
header_is_valid(const volatile struct region_descriptor * region,const volatile struct structure_header * header)129 static bool header_is_valid(const volatile struct region_descriptor *region,
130 const volatile struct structure_header *header)
131 {
132 const volatile char *region_tail, *struct_tail;
133
134 if (header->id == 0) {
135 return false; /* Zero is not a valid identifier */
136 }
137
138 if ((header->id >> SDS_ID_VERSION_MAJOR_POS) == 0) {
139 return false; /* 0 is not a valid major version */
140 }
141
142 if (header->size < MIN_ALIGNED_STRUCT_SIZE) {
143 return false; /* Padded structure size is less than the minimum */
144 }
145
146 region_tail = (const volatile char *)region + region->region_size;
147 struct_tail = (const volatile char *)header
148 + sizeof(struct structure_header)
149 + header->size;
150 if (struct_tail > region_tail) {
151 /* Structure exceeds the capacity of the SDS Memory Region */
152 return false;
153 }
154
155 if ((header->size % MIN_STRUCT_ALIGNMENT) != 0) {
156 return false; /* Structure does not meet alignment requirements */
157 }
158
159 return true;
160 }
161
get_structure_info(uint32_t structure_id,struct structure_header * header,uint8_t ** structure_base)162 static int get_structure_info(uint32_t structure_id,
163 struct structure_header *header,
164 uint8_t **structure_base)
165 {
166 volatile struct structure_header *current_header;
167 size_t offset, region_size, struct_count, struct_idx;
168 struct region_descriptor *region_desc;
169
170 region_desc = (struct region_descriptor *)region_config.mapped_addr;
171 region_size = region_desc->region_size;
172 struct_count = region_desc->structure_count;
173 offset = sizeof(struct region_descriptor);
174
175 /* Iterate over structure headers to find one with a matching ID */
176 for (struct_idx = 0; struct_idx < struct_count; struct_idx++) {
177 current_header =
178 (struct structure_header *)(region_config.mapped_addr + offset);
179 if (!header_is_valid(region_desc, current_header)) {
180 return -1;
181 }
182 if (current_header->id == structure_id) {
183 if (structure_base != NULL) {
184 *structure_base = ((uint8_t *)current_header
185 + sizeof(struct structure_header));
186 }
187
188 *header = *current_header;
189 return 0;
190 }
191
192 offset += sizeof(struct structure_header);
193 offset += current_header->size;
194 if (offset >= region_size) {
195 return -1;
196 }
197 }
198
199 /* Structure with structure_id does not exist yet */
200 return -1;
201 }
202
203 /*
204 * Search the SDS Memory Region to determine if a structure with the given ID
205 * is present.
206 */
structure_exists(uint32_t structure_id)207 static bool structure_exists(uint32_t structure_id)
208 {
209 int status;
210 struct structure_header header;
211
212 status = get_structure_info(structure_id, &header, NULL);
213 return status == 0;
214 }
215
sds_region_map(uint8_t * atu_region)216 static enum tfm_plat_err_t sds_region_map(uint8_t *atu_region)
217 {
218 enum tfm_plat_err_t err;
219
220 /* TODO: might replace the comms_atu_* calls with native ATU driver calls */
221 err = comms_atu_alloc_region(region_config.host_addr,
222 region_config.size,
223 atu_region);
224 if (err != TFM_PLAT_ERR_SUCCESS) {
225 return err;
226 }
227
228 /* TODO: might replace the comms_atu_* calls with native ATU driver calls */
229 err = comms_atu_get_rse_ptr_from_host_addr(*atu_region,
230 region_config.host_addr,
231 ®ion_config.mapped_addr);
232 if (err != TFM_PLAT_ERR_SUCCESS) {
233 return err;
234 }
235
236 return TFM_PLAT_ERR_SUCCESS;
237 }
238
sds_region_unmap(uint8_t atu_region)239 static enum tfm_plat_err_t sds_region_unmap(uint8_t atu_region)
240 {
241 enum tfm_plat_err_t err;
242
243 /* TODO: might replace the comms_atu_* calls with native ATU driver calls */
244 err = comms_atu_free_region(atu_region);
245 if (err != TFM_PLAT_ERR_SUCCESS) {
246 return err;
247 }
248 }
249
sds_region_init(void)250 static void sds_region_init(void)
251 {
252 assert(PLAT_RSE_AP_SDS_SIZE > MIN_REGION_SIZE);
253
254 struct region_descriptor *region_desc =
255 (struct region_descriptor *)region_config.mapped_addr;
256
257 if (region_config.is_init == false) {
258 memset(region_config.mapped_addr, 0, region_config.size);
259
260 region_desc->signature = REGION_SIGNATURE;
261 region_desc->structure_count = 0;
262 region_desc->version_major = SUPPORTED_VERSION_MAJOR;
263 region_desc->version_minor = SUPPORTED_VERSION_MINOR;
264 region_desc->region_size = region_config.size;
265
266 region_config.free_mem_offset = sizeof(*region_desc);
267 region_config.free_mem_size = region_config.size - sizeof(*region_desc);
268
269 region_config.is_init = true;
270 }
271 }
272
273 static enum tfm_plat_err_t
sds_struct_add_internal(const struct sds_structure_desc * struct_desc)274 sds_struct_add_internal(const struct sds_structure_desc *struct_desc)
275 {
276 struct structure_header *header;
277 struct region_descriptor *region_desc =
278 (struct region_descriptor *)region_config.mapped_addr;
279 size_t padded_size = ALIGN_NEXT(struct_desc->size, MIN_STRUCT_ALIGNMENT);
280
281 if (padded_size + sizeof(*header) > region_config.free_mem_size) {
282 return TFM_PLAT_ERR_INVALID_INPUT;
283 }
284
285 if (struct_desc->payload == NULL) {
286 return TFM_PLAT_ERR_INVALID_INPUT;
287 }
288
289 /* Create the Structure Header */
290 header = (struct structure_header *)(region_config.mapped_addr +
291 region_config.free_mem_offset);
292 header->id = struct_desc->id;
293 header->size = padded_size;
294 header->valid = true; /* Set to always true */
295
296 /* Adjust free memory data with the size of structure_header */
297 region_config.free_mem_offset += sizeof(*header);
298 region_config.free_mem_size -= sizeof(*header);
299
300 /* Copy data */
301 memcpy(region_config.mapped_addr + region_config.free_mem_offset,
302 struct_desc->payload, struct_desc->size);
303
304 /* Adjust free memory data with padded_size to maintain alignment. */
305 region_config.free_mem_offset += padded_size;
306 region_config.free_mem_size -= padded_size;
307
308 /* Increment the structure count within the region descriptor */
309 region_desc->structure_count++;
310
311 return TFM_PLAT_ERR_SUCCESS;
312 }
313
314 enum tfm_plat_err_t
sds_struct_add(const struct sds_structure_desc * struct_desc)315 sds_struct_add(const struct sds_structure_desc *struct_desc)
316 {
317 enum tfm_plat_err_t err, err2;
318 uint8_t atu_region;
319
320 err = sds_region_map(&atu_region);
321 if (err != TFM_PLAT_ERR_SUCCESS) {
322 return err;
323 }
324
325 sds_region_init();
326
327 /* If the structure does not already exist, add it to the SDS region. */
328 if (!structure_exists(struct_desc->id)) {
329 err = sds_struct_add_internal(struct_desc);
330 } else {
331 /* Already added or other error */
332 err = TFM_PLAT_ERR_INVALID_INPUT;
333 }
334
335 err2 = sds_region_unmap(1);
336 if (err2 != TFM_PLAT_ERR_SUCCESS) {
337 return err2;
338 }
339
340 return err;
341 }
342