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