1 /*
2 * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8 #include <stdint.h>
9 #include <string.h>
10
11 #include <arch_helpers.h>
12 #include <common/debug.h>
13 #include <drivers/arm/css/sds.h>
14 #include <platform_def.h>
15
16 #include "sds_private.h"
17
18 /* Array of SDS memory region descriptions */
19 static sds_region_desc_t *sds_regions;
20
21 /* Total count of SDS memory regions */
22 static unsigned int sds_region_cnt;
23
24 /*
25 * Perform some non-exhaustive tests to determine whether any of the fields
26 * within a Structure Header contain obviously invalid data.
27 * Returns SDS_OK on success, SDS_ERR_FAIL on error.
28 */
sds_struct_is_valid(unsigned int region_id,uintptr_t header)29 static int sds_struct_is_valid(unsigned int region_id, uintptr_t header)
30 {
31 size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header);
32
33 /* Zero is not a valid identifier */
34 if (GET_SDS_HEADER_ID(header) == 0) {
35 return SDS_ERR_FAIL;
36 }
37
38 /* Check SDS Schema version */
39 if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) {
40 return SDS_ERR_FAIL;
41 }
42
43 /* The SDS Structure sizes have to be multiple of 8 */
44 if ((struct_size == 0) || ((struct_size % 8) != 0)) {
45 return SDS_ERR_FAIL;
46 }
47
48 if (struct_size > sds_regions[region_id].size) {
49 return SDS_ERR_FAIL;
50 }
51
52 return SDS_OK;
53 }
54
55 /*
56 * Validate the SDS structure headers.
57 * Returns SDS_OK on success, SDS_ERR_FAIL on error.
58 */
validate_sds_struct_headers(unsigned int region_id)59 static int validate_sds_struct_headers(unsigned int region_id)
60 {
61 unsigned int i, structure_count;
62 uintptr_t header;
63 uintptr_t sds_mem_base = sds_regions[region_id].base;
64
65 structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base);
66
67 if (structure_count == 0)
68 return SDS_ERR_FAIL;
69
70 header = sds_mem_base + SDS_REGION_DESC_SIZE;
71
72 /* Iterate over structure headers and validate each one */
73 for (i = 0; i < structure_count; i++) {
74 if (sds_struct_is_valid(region_id, header) != SDS_OK) {
75 WARN("SDS: Invalid structure header detected\n");
76 return SDS_ERR_FAIL;
77 }
78 header += GET_SDS_HEADER_STRUCT_SIZE(header) + SDS_HEADER_SIZE;
79 }
80 return SDS_OK;
81 }
82
83 /*
84 * Get the structure header pointer corresponding to the structure ID.
85 * Returns SDS_OK on success, SDS_ERR_STRUCT_NOT_FOUND on error.
86 */
get_struct_header(unsigned int region_id,uint32_t structure_id,struct_header_t ** header)87 static int get_struct_header(unsigned int region_id, uint32_t structure_id,
88 struct_header_t **header)
89 {
90 unsigned int i, structure_count;
91 uintptr_t current_header;
92 uintptr_t sds_mem_base = sds_regions[region_id].base;
93
94 assert(header);
95
96 structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base);
97 if (structure_count == 0)
98 return SDS_ERR_STRUCT_NOT_FOUND;
99
100 current_header = ((uintptr_t)sds_mem_base) + SDS_REGION_DESC_SIZE;
101
102 /* Iterate over structure headers to find one with a matching ID */
103 for (i = 0; i < structure_count; i++) {
104 if (GET_SDS_HEADER_ID(current_header) == structure_id) {
105 *header = (struct_header_t *)current_header;
106 return SDS_OK;
107 }
108 current_header += GET_SDS_HEADER_STRUCT_SIZE(current_header) +
109 SDS_HEADER_SIZE;
110 }
111
112 *header = NULL;
113 return SDS_ERR_STRUCT_NOT_FOUND;
114 }
115
116 /*
117 * Check if a structure header corresponding to the structure ID exists.
118 * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND
119 * if not found.
120 */
sds_struct_exists(unsigned int region_id,unsigned int structure_id)121 int sds_struct_exists(unsigned int region_id, unsigned int structure_id)
122 {
123 struct_header_t *header = NULL;
124 int ret;
125
126 assert(region_id < sds_region_cnt);
127
128 ret = get_struct_header(region_id, structure_id, &header);
129 if (ret == SDS_OK) {
130 assert(header);
131 }
132
133 return ret;
134 }
135
136 /*
137 * Read from field in the structure corresponding to `structure_id`.
138 * `fld_off` is the offset to the field in the structure and `mode`
139 * indicates whether cache maintenance need to performed prior to the read.
140 * The `data` is the pointer to store the read data of size specified by `size`.
141 * Returns SDS_OK on success or corresponding error codes on failure.
142 */
sds_struct_read(unsigned int region_id,uint32_t structure_id,unsigned int fld_off,void * data,size_t size,sds_access_mode_t mode)143 int sds_struct_read(unsigned int region_id, uint32_t structure_id,
144 unsigned int fld_off, void *data, size_t size,
145 sds_access_mode_t mode)
146 {
147 int status;
148 uintptr_t field_base;
149 struct_header_t *header = NULL;
150
151 assert(region_id < sds_region_cnt);
152
153 if (!data)
154 return SDS_ERR_INVALID_PARAMS;
155
156 /* Check if a structure with this ID exists */
157 status = get_struct_header(region_id, structure_id, &header);
158 if (status != SDS_OK)
159 return status;
160
161 assert(header);
162
163 if (mode == SDS_ACCESS_MODE_CACHED)
164 inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
165
166 if (!IS_SDS_HEADER_VALID(header)) {
167 WARN("SDS: Reading from un-finalized structure 0x%x\n",
168 structure_id);
169 return SDS_ERR_STRUCT_NOT_FINALIZED;
170 }
171
172 if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
173 return SDS_ERR_FAIL;
174
175 field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
176 if (check_uptr_overflow(field_base, size - 1))
177 return SDS_ERR_FAIL;
178
179 /* Copy the required field in the struct */
180 memcpy(data, (void *)field_base, size);
181
182 return SDS_OK;
183 }
184
185 /*
186 * Write to the field in the structure corresponding to `structure_id`.
187 * `fld_off` is the offset to the field in the structure and `mode`
188 * indicates whether cache maintenance need to performed for the write.
189 * The `data` is the pointer to data of size specified by `size`.
190 * Returns SDS_OK on success or corresponding error codes on failure.
191 */
sds_struct_write(unsigned int region_id,uint32_t structure_id,unsigned int fld_off,void * data,size_t size,sds_access_mode_t mode)192 int sds_struct_write(unsigned int region_id, uint32_t structure_id,
193 unsigned int fld_off, void *data, size_t size,
194 sds_access_mode_t mode)
195 {
196 int status;
197 uintptr_t field_base;
198 struct_header_t *header = NULL;
199
200 assert(region_id < sds_region_cnt);
201
202 if (!data)
203 return SDS_ERR_INVALID_PARAMS;
204
205 /* Check if a structure with this ID exists */
206 status = get_struct_header(region_id, structure_id, &header);
207 if (status != SDS_OK)
208 return status;
209
210 assert(header);
211
212 if (mode == SDS_ACCESS_MODE_CACHED)
213 inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
214
215 if (!IS_SDS_HEADER_VALID(header)) {
216 WARN("SDS: Writing to un-finalized structure 0x%x\n",
217 structure_id);
218 return SDS_ERR_STRUCT_NOT_FINALIZED;
219 }
220
221 if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
222 return SDS_ERR_FAIL;
223
224 field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
225 if (check_uptr_overflow(field_base, size - 1))
226 return SDS_ERR_FAIL;
227
228 /* Copy the required field in the struct */
229 memcpy((void *)field_base, data, size);
230
231 if (mode == SDS_ACCESS_MODE_CACHED)
232 flush_dcache_range((uintptr_t)field_base, size);
233
234 return SDS_OK;
235 }
236
237 /*
238 * Initialize the SDS driver. Also verifies the SDS version and sanity of
239 * the SDS structure headers in the given SDS region.
240 * Returns SDS_OK on success, SDS_ERR_FAIL on error.
241 */
sds_init(unsigned int region_id)242 int sds_init(unsigned int region_id)
243 {
244 if (sds_regions == NULL) {
245 sds_regions = plat_sds_get_regions(&sds_region_cnt);
246 }
247
248 assert(region_id < sds_region_cnt);
249
250 uintptr_t sds_mem_base = sds_regions[region_id].base;
251
252 if (!IS_SDS_REGION_VALID(sds_mem_base)) {
253 VERBOSE("SDS: No valid SDS Memory Region found\n");
254 return SDS_ERR_FAIL;
255 }
256
257 if (GET_SDS_REGION_SCHEMA_VERSION(sds_mem_base)
258 != SDS_REGION_SCH_VERSION) {
259 WARN("SDS: Unsupported SDS schema version\n");
260 return SDS_ERR_FAIL;
261 }
262
263 sds_regions[region_id].size = GET_SDS_REGION_SIZE(sds_mem_base);
264 if (sds_regions[region_id].size > PLAT_ARM_SDS_MEM_SIZE_MAX) {
265 WARN("SDS: SDS Memory Region exceeds size limit\n");
266 return SDS_ERR_FAIL;
267 }
268
269 INFO("SDS: Detected SDS Memory Region (%zu bytes)\n",
270 sds_regions[region_id].size);
271
272 if (validate_sds_struct_headers(region_id) != SDS_OK)
273 return SDS_ERR_FAIL;
274
275 return SDS_OK;
276 }
277