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