blob: a5e638939c09776d8131e0f39e58281218ffacea [file] [log] [blame]
Soby Mathewf6f2b7e2017-06-12 12:13:04 +01001/*
Tamas Ban896c7342023-05-08 13:38:27 +02002 * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.
Soby Mathewf6f2b7e2017-06-12 12:13:04 +01003 *
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
Tamas Ban896c7342023-05-08 13:38:27 +020018/* Array of SDS memory region descriptions */
19static sds_region_desc_t *sds_regions;
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010020
Tamas Ban896c7342023-05-08 13:38:27 +020021/* Total count of SDS memory regions */
22static unsigned int sds_region_cnt;
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010023
24/*
25 * Perform some non-exhaustive tests to determine whether any of the fields
26 * within a Structure Header contain obviously invalid data.
27 * Returns SDS_OK on success, SDS_ERR_FAIL on error.
28 */
Tamas Ban896c7342023-05-08 13:38:27 +020029static int sds_struct_is_valid(unsigned int region_id, uintptr_t header)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010030{
31 size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header);
32
33 /* Zero is not a valid identifier */
Tamas Ban896c7342023-05-08 13:38:27 +020034 if (GET_SDS_HEADER_ID(header) == 0) {
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010035 return SDS_ERR_FAIL;
Tamas Ban896c7342023-05-08 13:38:27 +020036 }
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010037
38 /* Check SDS Schema version */
Tamas Ban896c7342023-05-08 13:38:27 +020039 if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) {
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010040 return SDS_ERR_FAIL;
Tamas Ban896c7342023-05-08 13:38:27 +020041 }
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010042
43 /* The SDS Structure sizes have to be multiple of 8 */
Tamas Ban896c7342023-05-08 13:38:27 +020044 if ((struct_size == 0) || ((struct_size % 8) != 0)) {
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010045 return SDS_ERR_FAIL;
Tamas Ban896c7342023-05-08 13:38:27 +020046 }
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010047
Tamas Ban896c7342023-05-08 13:38:27 +020048 if (struct_size > sds_regions[region_id].size) {
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010049 return SDS_ERR_FAIL;
Tamas Ban896c7342023-05-08 13:38:27 +020050 }
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010051
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 */
Tamas Ban896c7342023-05-08 13:38:27 +020059static int validate_sds_struct_headers(unsigned int region_id)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010060{
61 unsigned int i, structure_count;
62 uintptr_t header;
Tamas Ban896c7342023-05-08 13:38:27 +020063 uintptr_t sds_mem_base = sds_regions[region_id].base;
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010064
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++) {
Tamas Ban896c7342023-05-08 13:38:27 +020074 if (sds_struct_is_valid(region_id, header) != SDS_OK) {
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010075 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 */
Tamas Ban896c7342023-05-08 13:38:27 +020087static int get_struct_header(unsigned int region_id, uint32_t structure_id,
88 struct_header_t **header)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010089{
90 unsigned int i, structure_count;
91 uintptr_t current_header;
Tamas Ban896c7342023-05-08 13:38:27 +020092 uintptr_t sds_mem_base = sds_regions[region_id].base;
Soby Mathewf6f2b7e2017-06-12 12:13:04 +010093
94 assert(header);
95
96 structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base);
97 if (structure_count == 0)
98 return SDS_ERR_STRUCT_NOT_FOUND;
99
100 current_header = ((uintptr_t)sds_mem_base) + SDS_REGION_DESC_SIZE;
101
102 /* Iterate over structure headers to find one with a matching ID */
103 for (i = 0; i < structure_count; i++) {
104 if (GET_SDS_HEADER_ID(current_header) == structure_id) {
105 *header = (struct_header_t *)current_header;
106 return SDS_OK;
107 }
108 current_header += GET_SDS_HEADER_STRUCT_SIZE(current_header) +
109 SDS_HEADER_SIZE;
110 }
111
112 *header = NULL;
113 return SDS_ERR_STRUCT_NOT_FOUND;
114}
115
116/*
117 * Check if a structure header corresponding to the structure ID exists.
118 * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND
119 * if not found.
120 */
Tamas Ban896c7342023-05-08 13:38:27 +0200121int sds_struct_exists(unsigned int region_id, unsigned int structure_id)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100122{
123 struct_header_t *header = NULL;
124 int ret;
125
Tamas Ban896c7342023-05-08 13:38:27 +0200126 assert(region_id < sds_region_cnt);
127
128 ret = get_struct_header(region_id, structure_id, &header);
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100129 if (ret == SDS_OK) {
130 assert(header);
131 }
132
133 return ret;
134}
135
136/*
137 * Read from field in the structure corresponding to `structure_id`.
138 * `fld_off` is the offset to the field in the structure and `mode`
139 * indicates whether cache maintenance need to performed prior to the read.
140 * The `data` is the pointer to store the read data of size specified by `size`.
141 * Returns SDS_OK on success or corresponding error codes on failure.
142 */
Tamas Ban896c7342023-05-08 13:38:27 +0200143int sds_struct_read(unsigned int region_id, uint32_t structure_id,
144 unsigned int fld_off, void *data, size_t size,
145 sds_access_mode_t mode)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100146{
147 int status;
148 uintptr_t field_base;
149 struct_header_t *header = NULL;
150
Tamas Ban896c7342023-05-08 13:38:27 +0200151 assert(region_id < sds_region_cnt);
152
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100153 if (!data)
154 return SDS_ERR_INVALID_PARAMS;
155
156 /* Check if a structure with this ID exists */
Tamas Ban896c7342023-05-08 13:38:27 +0200157 status = get_struct_header(region_id, structure_id, &header);
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100158 if (status != SDS_OK)
159 return status;
160
161 assert(header);
162
163 if (mode == SDS_ACCESS_MODE_CACHED)
164 inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
165
166 if (!IS_SDS_HEADER_VALID(header)) {
167 WARN("SDS: Reading from un-finalized structure 0x%x\n",
168 structure_id);
169 return SDS_ERR_STRUCT_NOT_FINALIZED;
170 }
171
172 if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
173 return SDS_ERR_FAIL;
174
175 field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
176 if (check_uptr_overflow(field_base, size - 1))
177 return SDS_ERR_FAIL;
178
179 /* Copy the required field in the struct */
180 memcpy(data, (void *)field_base, size);
181
182 return SDS_OK;
183}
184
185/*
186 * Write to the field in the structure corresponding to `structure_id`.
187 * `fld_off` is the offset to the field in the structure and `mode`
188 * indicates whether cache maintenance need to performed for the write.
189 * The `data` is the pointer to data of size specified by `size`.
190 * Returns SDS_OK on success or corresponding error codes on failure.
191 */
Tamas Ban896c7342023-05-08 13:38:27 +0200192int sds_struct_write(unsigned int region_id, uint32_t structure_id,
193 unsigned int fld_off, void *data, size_t size,
194 sds_access_mode_t mode)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100195{
196 int status;
197 uintptr_t field_base;
198 struct_header_t *header = NULL;
199
Tamas Ban896c7342023-05-08 13:38:27 +0200200 assert(region_id < sds_region_cnt);
201
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100202 if (!data)
203 return SDS_ERR_INVALID_PARAMS;
204
205 /* Check if a structure with this ID exists */
Tamas Ban896c7342023-05-08 13:38:27 +0200206 status = get_struct_header(region_id, structure_id, &header);
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100207 if (status != SDS_OK)
208 return status;
209
210 assert(header);
211
212 if (mode == SDS_ACCESS_MODE_CACHED)
213 inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
214
215 if (!IS_SDS_HEADER_VALID(header)) {
216 WARN("SDS: Writing to un-finalized structure 0x%x\n",
217 structure_id);
218 return SDS_ERR_STRUCT_NOT_FINALIZED;
219 }
220
221 if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
222 return SDS_ERR_FAIL;
223
224 field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
225 if (check_uptr_overflow(field_base, size - 1))
226 return SDS_ERR_FAIL;
227
228 /* Copy the required field in the struct */
229 memcpy((void *)field_base, data, size);
230
231 if (mode == SDS_ACCESS_MODE_CACHED)
232 flush_dcache_range((uintptr_t)field_base, size);
233
234 return SDS_OK;
235}
236
237/*
238 * Initialize the SDS driver. Also verifies the SDS version and sanity of
Tamas Ban896c7342023-05-08 13:38:27 +0200239 * the SDS structure headers in the given SDS region.
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100240 * Returns SDS_OK on success, SDS_ERR_FAIL on error.
241 */
Tamas Ban896c7342023-05-08 13:38:27 +0200242int sds_init(unsigned int region_id)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100243{
Tamas Ban896c7342023-05-08 13:38:27 +0200244 if (sds_regions == NULL) {
245 sds_regions = plat_sds_get_regions(&sds_region_cnt);
246 }
247
248 assert(region_id < sds_region_cnt);
249
250 uintptr_t sds_mem_base = sds_regions[region_id].base;
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100251
252 if (!IS_SDS_REGION_VALID(sds_mem_base)) {
253 WARN("SDS: No valid SDS Memory Region found\n");
254 return SDS_ERR_FAIL;
255 }
256
257 if (GET_SDS_REGION_SCHEMA_VERSION(sds_mem_base)
258 != SDS_REGION_SCH_VERSION) {
259 WARN("SDS: Unsupported SDS schema version\n");
260 return SDS_ERR_FAIL;
261 }
262
Tamas Ban896c7342023-05-08 13:38:27 +0200263 sds_regions[region_id].size = GET_SDS_REGION_SIZE(sds_mem_base);
264 if (sds_regions[region_id].size > PLAT_ARM_SDS_MEM_SIZE_MAX) {
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100265 WARN("SDS: SDS Memory Region exceeds size limit\n");
266 return SDS_ERR_FAIL;
267 }
268
Tamas Ban896c7342023-05-08 13:38:27 +0200269 INFO("SDS: Detected SDS Memory Region (%zu bytes)\n",
270 sds_regions[region_id].size);
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100271
Tamas Ban896c7342023-05-08 13:38:27 +0200272 if (validate_sds_struct_headers(region_id) != SDS_OK)
Soby Mathewf6f2b7e2017-06-12 12:13:04 +0100273 return SDS_ERR_FAIL;
274
275 return SDS_OK;
276}