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