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