xref: /rk3399_ARM-atf/drivers/arm/css/sds/sds.c (revision e7d14fa83f3dd50d2ee2aa1e269851eb9e351951)
15932d194SAntonio Nino Diaz /*
28d1a04bdSTamas Ban  * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved.
35932d194SAntonio Nino Diaz  *
45932d194SAntonio Nino Diaz  * SPDX-License-Identifier: BSD-3-Clause
55932d194SAntonio Nino Diaz  */
65932d194SAntonio Nino Diaz 
75932d194SAntonio Nino Diaz #include <assert.h>
85932d194SAntonio Nino Diaz #include <stdint.h>
95932d194SAntonio Nino Diaz #include <string.h>
105932d194SAntonio Nino Diaz 
115932d194SAntonio Nino Diaz #include <arch_helpers.h>
125932d194SAntonio Nino Diaz #include <common/debug.h>
135932d194SAntonio Nino Diaz #include <drivers/arm/css/sds.h>
145932d194SAntonio Nino Diaz #include <platform_def.h>
155932d194SAntonio Nino Diaz 
165932d194SAntonio Nino Diaz #include "sds_private.h"
175932d194SAntonio Nino Diaz 
188d1a04bdSTamas Ban /* Array of SDS memory region descriptions */
198d1a04bdSTamas Ban static sds_region_desc_t *sds_regions;
205932d194SAntonio Nino Diaz 
218d1a04bdSTamas Ban /* Total count of SDS memory regions */
228d1a04bdSTamas Ban static unsigned int sds_region_cnt;
235932d194SAntonio Nino Diaz 
245932d194SAntonio Nino Diaz /*
255932d194SAntonio Nino Diaz  * Perform some non-exhaustive tests to determine whether any of the fields
265932d194SAntonio Nino Diaz  * within a Structure Header contain obviously invalid data.
275932d194SAntonio Nino Diaz  * Returns SDS_OK on success, SDS_ERR_FAIL on error.
285932d194SAntonio Nino Diaz  */
sds_struct_is_valid(unsigned int region_id,uintptr_t header)298d1a04bdSTamas Ban static int sds_struct_is_valid(unsigned int region_id, uintptr_t header)
305932d194SAntonio Nino Diaz {
315932d194SAntonio Nino Diaz 	size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header);
325932d194SAntonio Nino Diaz 
335932d194SAntonio Nino Diaz 	/* Zero is not a valid identifier */
348d1a04bdSTamas Ban 	if (GET_SDS_HEADER_ID(header) == 0) {
355932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
368d1a04bdSTamas Ban 	}
375932d194SAntonio Nino Diaz 
385932d194SAntonio Nino Diaz 	/* Check SDS Schema version */
398d1a04bdSTamas Ban 	if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) {
405932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
418d1a04bdSTamas Ban 	}
425932d194SAntonio Nino Diaz 
435932d194SAntonio Nino Diaz 	/* The SDS Structure sizes have to be multiple of 8 */
448d1a04bdSTamas Ban 	if ((struct_size == 0) || ((struct_size % 8) != 0)) {
455932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
468d1a04bdSTamas Ban 	}
475932d194SAntonio Nino Diaz 
488d1a04bdSTamas Ban 	if (struct_size > sds_regions[region_id].size) {
495932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
508d1a04bdSTamas Ban 	}
515932d194SAntonio Nino Diaz 
525932d194SAntonio Nino Diaz 	return SDS_OK;
535932d194SAntonio Nino Diaz }
545932d194SAntonio Nino Diaz 
555932d194SAntonio Nino Diaz /*
565932d194SAntonio Nino Diaz  * Validate the SDS structure headers.
575932d194SAntonio Nino Diaz  * Returns SDS_OK on success, SDS_ERR_FAIL on error.
585932d194SAntonio Nino Diaz  */
validate_sds_struct_headers(unsigned int region_id)598d1a04bdSTamas Ban static int validate_sds_struct_headers(unsigned int region_id)
605932d194SAntonio Nino Diaz {
615932d194SAntonio Nino Diaz 	unsigned int i, structure_count;
625932d194SAntonio Nino Diaz 	uintptr_t header;
638d1a04bdSTamas Ban 	uintptr_t sds_mem_base = sds_regions[region_id].base;
645932d194SAntonio Nino Diaz 
655932d194SAntonio Nino Diaz 	structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base);
665932d194SAntonio Nino Diaz 
675932d194SAntonio Nino Diaz 	if (structure_count == 0)
685932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
695932d194SAntonio Nino Diaz 
705932d194SAntonio Nino Diaz 	header = sds_mem_base + SDS_REGION_DESC_SIZE;
715932d194SAntonio Nino Diaz 
725932d194SAntonio Nino Diaz 	/* Iterate over structure headers and validate each one */
735932d194SAntonio Nino Diaz 	for (i = 0; i < structure_count; i++) {
748d1a04bdSTamas Ban 		if (sds_struct_is_valid(region_id, header) != SDS_OK) {
755932d194SAntonio Nino Diaz 			WARN("SDS: Invalid structure header detected\n");
765932d194SAntonio Nino Diaz 			return SDS_ERR_FAIL;
775932d194SAntonio Nino Diaz 		}
785932d194SAntonio Nino Diaz 		header += GET_SDS_HEADER_STRUCT_SIZE(header) + SDS_HEADER_SIZE;
795932d194SAntonio Nino Diaz 	}
805932d194SAntonio Nino Diaz 	return SDS_OK;
815932d194SAntonio Nino Diaz }
825932d194SAntonio Nino Diaz 
835932d194SAntonio Nino Diaz /*
845932d194SAntonio Nino Diaz  * Get the structure header pointer corresponding to the structure ID.
855932d194SAntonio Nino Diaz  * Returns SDS_OK on success, SDS_ERR_STRUCT_NOT_FOUND on error.
865932d194SAntonio Nino Diaz  */
get_struct_header(unsigned int region_id,uint32_t structure_id,struct_header_t ** header)878d1a04bdSTamas Ban static int get_struct_header(unsigned int region_id, uint32_t structure_id,
888d1a04bdSTamas Ban 			struct_header_t **header)
895932d194SAntonio Nino Diaz {
905932d194SAntonio Nino Diaz 	unsigned int i, structure_count;
915932d194SAntonio Nino Diaz 	uintptr_t current_header;
928d1a04bdSTamas Ban 	uintptr_t sds_mem_base = sds_regions[region_id].base;
935932d194SAntonio Nino Diaz 
945932d194SAntonio Nino Diaz 	assert(header);
955932d194SAntonio Nino Diaz 
965932d194SAntonio Nino Diaz 	structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base);
975932d194SAntonio Nino Diaz 	if (structure_count == 0)
985932d194SAntonio Nino Diaz 		return SDS_ERR_STRUCT_NOT_FOUND;
995932d194SAntonio Nino Diaz 
1005932d194SAntonio Nino Diaz 	current_header = ((uintptr_t)sds_mem_base) + SDS_REGION_DESC_SIZE;
1015932d194SAntonio Nino Diaz 
1025932d194SAntonio Nino Diaz 	/* Iterate over structure headers to find one with a matching ID */
1035932d194SAntonio Nino Diaz 	for (i = 0; i < structure_count; i++) {
1045932d194SAntonio Nino Diaz 		if (GET_SDS_HEADER_ID(current_header) == structure_id) {
1055932d194SAntonio Nino Diaz 			*header = (struct_header_t *)current_header;
1065932d194SAntonio Nino Diaz 			return SDS_OK;
1075932d194SAntonio Nino Diaz 		}
1085932d194SAntonio Nino Diaz 		current_header += GET_SDS_HEADER_STRUCT_SIZE(current_header) +
1095932d194SAntonio Nino Diaz 						SDS_HEADER_SIZE;
1105932d194SAntonio Nino Diaz 	}
1115932d194SAntonio Nino Diaz 
1125932d194SAntonio Nino Diaz 	*header = NULL;
1135932d194SAntonio Nino Diaz 	return SDS_ERR_STRUCT_NOT_FOUND;
1145932d194SAntonio Nino Diaz }
1155932d194SAntonio Nino Diaz 
1165932d194SAntonio Nino Diaz /*
1175932d194SAntonio Nino Diaz  * Check if a structure header corresponding to the structure ID exists.
1185932d194SAntonio Nino Diaz  * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND
1195932d194SAntonio Nino Diaz  * if not found.
1205932d194SAntonio Nino Diaz  */
sds_struct_exists(unsigned int region_id,unsigned int structure_id)1218d1a04bdSTamas Ban int sds_struct_exists(unsigned int region_id, unsigned int structure_id)
1225932d194SAntonio Nino Diaz {
1235932d194SAntonio Nino Diaz 	struct_header_t *header = NULL;
1245932d194SAntonio Nino Diaz 	int ret;
1255932d194SAntonio Nino Diaz 
1268d1a04bdSTamas Ban 	assert(region_id < sds_region_cnt);
1278d1a04bdSTamas Ban 
1288d1a04bdSTamas Ban 	ret = get_struct_header(region_id, structure_id, &header);
1295932d194SAntonio Nino Diaz 	if (ret == SDS_OK) {
1305932d194SAntonio Nino Diaz 		assert(header);
1315932d194SAntonio Nino Diaz 	}
1325932d194SAntonio Nino Diaz 
1335932d194SAntonio Nino Diaz 	return ret;
1345932d194SAntonio Nino Diaz }
1355932d194SAntonio Nino Diaz 
1365932d194SAntonio Nino Diaz /*
1375932d194SAntonio Nino Diaz  * Read from field in the structure corresponding to `structure_id`.
1385932d194SAntonio Nino Diaz  * `fld_off` is the offset to the field in the structure and `mode`
1395932d194SAntonio Nino Diaz  * indicates whether cache maintenance need to performed prior to the read.
1405932d194SAntonio Nino Diaz  * The `data` is the pointer to store the read data of size specified by `size`.
1415932d194SAntonio Nino Diaz  * Returns SDS_OK on success or corresponding error codes on failure.
1425932d194SAntonio Nino Diaz  */
sds_struct_read(unsigned int region_id,uint32_t structure_id,unsigned int fld_off,void * data,size_t size,sds_access_mode_t mode)1438d1a04bdSTamas Ban int sds_struct_read(unsigned int region_id, uint32_t structure_id,
1448d1a04bdSTamas Ban 		unsigned int fld_off, void *data, size_t size,
1458d1a04bdSTamas Ban 		sds_access_mode_t mode)
1465932d194SAntonio Nino Diaz {
1475932d194SAntonio Nino Diaz 	int status;
1485932d194SAntonio Nino Diaz 	uintptr_t field_base;
1495932d194SAntonio Nino Diaz 	struct_header_t *header = NULL;
1505932d194SAntonio Nino Diaz 
1518d1a04bdSTamas Ban 	assert(region_id < sds_region_cnt);
1528d1a04bdSTamas Ban 
1535932d194SAntonio Nino Diaz 	if (!data)
1545932d194SAntonio Nino Diaz 		return SDS_ERR_INVALID_PARAMS;
1555932d194SAntonio Nino Diaz 
1565932d194SAntonio Nino Diaz 	/* Check if a structure with this ID exists */
1578d1a04bdSTamas Ban 	status = get_struct_header(region_id, structure_id, &header);
1585932d194SAntonio Nino Diaz 	if (status != SDS_OK)
1595932d194SAntonio Nino Diaz 		return status;
1605932d194SAntonio Nino Diaz 
1615932d194SAntonio Nino Diaz 	assert(header);
1625932d194SAntonio Nino Diaz 
1635932d194SAntonio Nino Diaz 	if (mode == SDS_ACCESS_MODE_CACHED)
1645932d194SAntonio Nino Diaz 		inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
1655932d194SAntonio Nino Diaz 
1665932d194SAntonio Nino Diaz 	if (!IS_SDS_HEADER_VALID(header)) {
1675932d194SAntonio Nino Diaz 		WARN("SDS: Reading from un-finalized structure 0x%x\n",
1685932d194SAntonio Nino Diaz 				structure_id);
1695932d194SAntonio Nino Diaz 		return SDS_ERR_STRUCT_NOT_FINALIZED;
1705932d194SAntonio Nino Diaz 	}
1715932d194SAntonio Nino Diaz 
1725932d194SAntonio Nino Diaz 	if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
1735932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
1745932d194SAntonio Nino Diaz 
1755932d194SAntonio Nino Diaz 	field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
1765932d194SAntonio Nino Diaz 	if (check_uptr_overflow(field_base, size - 1))
1775932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
1785932d194SAntonio Nino Diaz 
1795932d194SAntonio Nino Diaz 	/* Copy the required field in the struct */
1805932d194SAntonio Nino Diaz 	memcpy(data, (void *)field_base, size);
1815932d194SAntonio Nino Diaz 
1825932d194SAntonio Nino Diaz 	return SDS_OK;
1835932d194SAntonio Nino Diaz }
1845932d194SAntonio Nino Diaz 
1855932d194SAntonio Nino Diaz /*
1865932d194SAntonio Nino Diaz  * Write to the field in the structure corresponding to `structure_id`.
1875932d194SAntonio Nino Diaz  * `fld_off` is the offset to the field in the structure and `mode`
1885932d194SAntonio Nino Diaz  * indicates whether cache maintenance need to performed for the write.
1895932d194SAntonio Nino Diaz  * The `data` is the pointer to data of size specified by `size`.
1905932d194SAntonio Nino Diaz  * Returns SDS_OK on success or corresponding error codes on failure.
1915932d194SAntonio Nino Diaz  */
sds_struct_write(unsigned int region_id,uint32_t structure_id,unsigned int fld_off,void * data,size_t size,sds_access_mode_t mode)1928d1a04bdSTamas Ban int sds_struct_write(unsigned int region_id, uint32_t structure_id,
1938d1a04bdSTamas Ban 		unsigned int fld_off, void *data, size_t size,
1948d1a04bdSTamas Ban 		sds_access_mode_t mode)
1955932d194SAntonio Nino Diaz {
1965932d194SAntonio Nino Diaz 	int status;
1975932d194SAntonio Nino Diaz 	uintptr_t field_base;
1985932d194SAntonio Nino Diaz 	struct_header_t *header = NULL;
1995932d194SAntonio Nino Diaz 
2008d1a04bdSTamas Ban 	assert(region_id < sds_region_cnt);
2018d1a04bdSTamas Ban 
2025932d194SAntonio Nino Diaz 	if (!data)
2035932d194SAntonio Nino Diaz 		return SDS_ERR_INVALID_PARAMS;
2045932d194SAntonio Nino Diaz 
2055932d194SAntonio Nino Diaz 	/* Check if a structure with this ID exists */
2068d1a04bdSTamas Ban 	status = get_struct_header(region_id, structure_id, &header);
2075932d194SAntonio Nino Diaz 	if (status != SDS_OK)
2085932d194SAntonio Nino Diaz 		return status;
2095932d194SAntonio Nino Diaz 
2105932d194SAntonio Nino Diaz 	assert(header);
2115932d194SAntonio Nino Diaz 
2125932d194SAntonio Nino Diaz 	if (mode == SDS_ACCESS_MODE_CACHED)
2135932d194SAntonio Nino Diaz 		inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size);
2145932d194SAntonio Nino Diaz 
2155932d194SAntonio Nino Diaz 	if (!IS_SDS_HEADER_VALID(header)) {
2165932d194SAntonio Nino Diaz 		WARN("SDS: Writing to un-finalized structure 0x%x\n",
2175932d194SAntonio Nino Diaz 				structure_id);
2185932d194SAntonio Nino Diaz 		return SDS_ERR_STRUCT_NOT_FINALIZED;
2195932d194SAntonio Nino Diaz 	}
2205932d194SAntonio Nino Diaz 
2215932d194SAntonio Nino Diaz 	if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header))
2225932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
2235932d194SAntonio Nino Diaz 
2245932d194SAntonio Nino Diaz 	field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off;
2255932d194SAntonio Nino Diaz 	if (check_uptr_overflow(field_base, size - 1))
2265932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
2275932d194SAntonio Nino Diaz 
2285932d194SAntonio Nino Diaz 	/* Copy the required field in the struct */
2295932d194SAntonio Nino Diaz 	memcpy((void *)field_base, data, size);
2305932d194SAntonio Nino Diaz 
2315932d194SAntonio Nino Diaz 	if (mode == SDS_ACCESS_MODE_CACHED)
2325932d194SAntonio Nino Diaz 		flush_dcache_range((uintptr_t)field_base, size);
2335932d194SAntonio Nino Diaz 
2345932d194SAntonio Nino Diaz 	return SDS_OK;
2355932d194SAntonio Nino Diaz }
2365932d194SAntonio Nino Diaz 
2375932d194SAntonio Nino Diaz /*
2385932d194SAntonio Nino Diaz  * Initialize the SDS driver. Also verifies the SDS version and sanity of
2398d1a04bdSTamas Ban  * the SDS structure headers in the given SDS region.
2405932d194SAntonio Nino Diaz  * Returns SDS_OK on success, SDS_ERR_FAIL on error.
2415932d194SAntonio Nino Diaz  */
sds_init(unsigned int region_id)2428d1a04bdSTamas Ban int sds_init(unsigned int region_id)
2435932d194SAntonio Nino Diaz {
2448d1a04bdSTamas Ban 	if (sds_regions == NULL) {
2458d1a04bdSTamas Ban 		sds_regions = plat_sds_get_regions(&sds_region_cnt);
2468d1a04bdSTamas Ban 	}
2478d1a04bdSTamas Ban 
2488d1a04bdSTamas Ban 	assert(region_id < sds_region_cnt);
2498d1a04bdSTamas Ban 
2508d1a04bdSTamas Ban 	uintptr_t sds_mem_base = sds_regions[region_id].base;
2515932d194SAntonio Nino Diaz 
2525932d194SAntonio Nino Diaz 	if (!IS_SDS_REGION_VALID(sds_mem_base)) {
253*33f29b8aSDavid Vincze 		VERBOSE("SDS: No valid SDS Memory Region found\n");
2545932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
2555932d194SAntonio Nino Diaz 	}
2565932d194SAntonio Nino Diaz 
2575932d194SAntonio Nino Diaz 	if (GET_SDS_REGION_SCHEMA_VERSION(sds_mem_base)
2585932d194SAntonio Nino Diaz 				!= SDS_REGION_SCH_VERSION) {
2595932d194SAntonio Nino Diaz 		WARN("SDS: Unsupported SDS schema version\n");
2605932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
2615932d194SAntonio Nino Diaz 	}
2625932d194SAntonio Nino Diaz 
2638d1a04bdSTamas Ban 	sds_regions[region_id].size = GET_SDS_REGION_SIZE(sds_mem_base);
2648d1a04bdSTamas Ban 	if (sds_regions[region_id].size > PLAT_ARM_SDS_MEM_SIZE_MAX) {
2655932d194SAntonio Nino Diaz 		WARN("SDS: SDS Memory Region exceeds size limit\n");
2665932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
2675932d194SAntonio Nino Diaz 	}
2685932d194SAntonio Nino Diaz 
2698d1a04bdSTamas Ban 	INFO("SDS: Detected SDS Memory Region (%zu bytes)\n",
2708d1a04bdSTamas Ban 		sds_regions[region_id].size);
2715932d194SAntonio Nino Diaz 
2728d1a04bdSTamas Ban 	if (validate_sds_struct_headers(region_id) != SDS_OK)
2735932d194SAntonio Nino Diaz 		return SDS_ERR_FAIL;
2745932d194SAntonio Nino Diaz 
2755932d194SAntonio Nino Diaz 	return SDS_OK;
2765932d194SAntonio Nino Diaz }
277