blob: 1fb196c708274511bead6aead37f84b48b013b0d [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
Soby Mathewf6f2b7e2017-06-12 12:13:04 +01007#include <assert.h>
Soby Mathewf6f2b7e2017-06-12 12:13:04 +01008#include <stdint.h>
9#include <string.h>
10
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000011#include <arch_helpers.h>
12#include <common/debug.h>
Antonio Nino Diaz09d58762019-01-23 19:06:55 +000013#include <drivers/arm/css/sds.h>
Antonio Nino Diaza320ecd2019-01-15 14:19:50 +000014#include <platform_def.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000015
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010016#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 */
24static uintptr_t sds_mem_base;
25
26/* Size of the SDS memory region in bytes */
27static 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 */
34static 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 */
60static 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 */
87static 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 */
119int 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 */
139int 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 */
185int 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 */
232int 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}