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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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