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