blob: e2fac54f371374e07ecb314069ba507fe2101a6e [file] [log] [blame]
Soby Mathewf6f2b7e2017-06-12 12:13:04 +01001/*
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 */
23static uintptr_t sds_mem_base;
24
25/* Size of the SDS memory region in bytes */
26static 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 */
33static 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 */
59static 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 */
86static 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 */
118int 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 */
138int 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 */
184int 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 */
231int 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}