1 /*
2  * Copyright (c) 2017, 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 /*
19  * Variables used to track and maintain the state of the memory region reserved
20  * for usage by the SDS framework.
21  */
22 
23 /* Pointer to the base of the SDS memory region */
24 static uintptr_t sds_mem_base;
25 
26 /* Size of the SDS memory region in bytes */
27 static size_t sds_mem_size;
28 
29 /*
30  * Perform some non-exhaustive tests to determine whether any of the fields
31  * within a Structure Header contain obviously invalid data.
32  * Returns SDS_OK on success, SDS_ERR_FAIL on error.
33  */
sds_struct_is_valid(uintptr_t header)34 static int sds_struct_is_valid(uintptr_t header)
35 {
36 	size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header);
37 
38 	/* Zero is not a valid identifier */
39 	if (GET_SDS_HEADER_ID(header) == 0)
40 		return SDS_ERR_FAIL;
41 
42 	/* Check SDS Schema version */
43 	if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION)
44 		return SDS_ERR_FAIL;
45 
46 	/* The SDS Structure sizes have to be multiple of 8 */
47 	if ((struct_size == 0) || ((struct_size % 8) != 0))
48 		return SDS_ERR_FAIL;
49 
50 	if (struct_size > sds_mem_size)
51 		return SDS_ERR_FAIL;
52 
53 	return SDS_OK;
54 }
55 
56 /*
57  * Validate the SDS structure headers.
58  * Returns SDS_OK on success, SDS_ERR_FAIL on error.
59  */
validate_sds_struct_headers(void)60 static int validate_sds_struct_headers(void)
61 {
62 	unsigned int i, structure_count;
63 	uintptr_t header;
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(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(uint32_t structure_id,struct_header_t ** header)87 static int get_struct_header(uint32_t structure_id, struct_header_t **header)
88 {
89 	unsigned int i, structure_count;
90 	uintptr_t current_header;
91 
92 	assert(header);
93 
94 	structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base);
95 	if (structure_count == 0)
96 		return SDS_ERR_STRUCT_NOT_FOUND;
97 
98 	current_header = ((uintptr_t)sds_mem_base) + SDS_REGION_DESC_SIZE;
99 
100 	/* Iterate over structure headers to find one with a matching ID */
101 	for (i = 0; i < structure_count; i++) {
102 		if (GET_SDS_HEADER_ID(current_header) == structure_id) {
103 			*header = (struct_header_t *)current_header;
104 			return SDS_OK;
105 		}
106 		current_header += GET_SDS_HEADER_STRUCT_SIZE(current_header) +
107 						SDS_HEADER_SIZE;
108 	}
109 
110 	*header = NULL;
111 	return SDS_ERR_STRUCT_NOT_FOUND;
112 }
113 
114 /*
115  * Check if a structure header corresponding to the structure ID exists.
116  * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND
117  * if not found.
118  */
sds_struct_exists(unsigned int structure_id)119 int sds_struct_exists(unsigned int structure_id)
120 {
121 	struct_header_t *header = NULL;
122 	int ret;
123 
124 	ret = get_struct_header(structure_id, &header);
125 	if (ret == SDS_OK) {
126 		assert(header);
127 	}
128 
129 	return ret;
130 }
131 
132 /*
133  * Read from field in the structure corresponding to `structure_id`.
134  * `fld_off` is the offset to the field in the structure and `mode`
135  * indicates whether cache maintenance need to performed prior to the read.
136  * The `data` is the pointer to store the read data of size specified by `size`.
137  * Returns SDS_OK on success or corresponding error codes on failure.
138  */
sds_struct_read(uint32_t structure_id,unsigned int fld_off,void * data,size_t size,sds_access_mode_t mode)139 int sds_struct_read(uint32_t structure_id, unsigned int fld_off,
140 		void *data, size_t size, sds_access_mode_t mode)
141 {
142 	int status;
143 	uintptr_t field_base;
144 	struct_header_t *header = NULL;
145 
146 	if (!data)
147 		return SDS_ERR_INVALID_PARAMS;
148 
149 	/* Check if a structure with this ID exists */
150 	status = get_struct_header(structure_id, &header);
151 	if (status != SDS_OK)
152 		return status;
153 
154 	assert(header);
155 
156 	if (mode == SDS_ACCESS_MODE_CACHED)
157 		inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
158 
159 	if (!IS_SDS_HEADER_VALID(header)) {
160 		WARN("SDS: Reading from un-finalized structure 0x%x\n",
161 				structure_id);
162 		return SDS_ERR_STRUCT_NOT_FINALIZED;
163 	}
164 
165 	if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
166 		return SDS_ERR_FAIL;
167 
168 	field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
169 	if (check_uptr_overflow(field_base, size - 1))
170 		return SDS_ERR_FAIL;
171 
172 	/* Copy the required field in the struct */
173 	memcpy(data, (void *)field_base, size);
174 
175 	return SDS_OK;
176 }
177 
178 /*
179  * Write to the field in the structure corresponding to `structure_id`.
180  * `fld_off` is the offset to the field in the structure and `mode`
181  * indicates whether cache maintenance need to performed for the write.
182  * The `data` is the pointer to data of size specified by `size`.
183  * Returns SDS_OK on success or corresponding error codes on failure.
184  */
sds_struct_write(uint32_t structure_id,unsigned int fld_off,void * data,size_t size,sds_access_mode_t mode)185 int sds_struct_write(uint32_t structure_id, unsigned int fld_off,
186 		void *data, size_t size, sds_access_mode_t mode)
187 {
188 	int status;
189 	uintptr_t field_base;
190 	struct_header_t *header = NULL;
191 
192 	if (!data)
193 		return SDS_ERR_INVALID_PARAMS;
194 
195 	/* Check if a structure with this ID exists */
196 	status = get_struct_header(structure_id, &header);
197 	if (status != SDS_OK)
198 		return status;
199 
200 	assert(header);
201 
202 	if (mode == SDS_ACCESS_MODE_CACHED)
203 		inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
204 
205 	if (!IS_SDS_HEADER_VALID(header)) {
206 		WARN("SDS: Writing to un-finalized structure 0x%x\n",
207 				structure_id);
208 		return SDS_ERR_STRUCT_NOT_FINALIZED;
209 	}
210 
211 	if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
212 		return SDS_ERR_FAIL;
213 
214 	field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
215 	if (check_uptr_overflow(field_base, size - 1))
216 		return SDS_ERR_FAIL;
217 
218 	/* Copy the required field in the struct */
219 	memcpy((void *)field_base, data, size);
220 
221 	if (mode == SDS_ACCESS_MODE_CACHED)
222 		flush_dcache_range((uintptr_t)field_base, size);
223 
224 	return SDS_OK;
225 }
226 
227 /*
228  * Initialize the SDS driver. Also verifies the SDS version and sanity of
229  * the SDS structure headers.
230  * Returns SDS_OK on success, SDS_ERR_FAIL on error.
231  */
sds_init(void)232 int sds_init(void)
233 {
234 	sds_mem_base = (uintptr_t)PLAT_ARM_SDS_MEM_BASE;
235 
236 	if (!IS_SDS_REGION_VALID(sds_mem_base)) {
237 		WARN("SDS: No valid SDS Memory Region found\n");
238 		return SDS_ERR_FAIL;
239 	}
240 
241 	if (GET_SDS_REGION_SCHEMA_VERSION(sds_mem_base)
242 				!= SDS_REGION_SCH_VERSION) {
243 		WARN("SDS: Unsupported SDS schema version\n");
244 		return SDS_ERR_FAIL;
245 	}
246 
247 	sds_mem_size = GET_SDS_REGION_SIZE(sds_mem_base);
248 	if (sds_mem_size > PLAT_ARM_SDS_MEM_SIZE_MAX) {
249 		WARN("SDS: SDS Memory Region exceeds size limit\n");
250 		return SDS_ERR_FAIL;
251 	}
252 
253 	INFO("SDS: Detected SDS Memory Region (%zu bytes)\n", sds_mem_size);
254 
255 	if (validate_sds_struct_headers() != SDS_OK)
256 		return SDS_ERR_FAIL;
257 
258 	return SDS_OK;
259 }
260