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                                                &region_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