1 /* 2 * Copyright (c) 2017-2024, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <stdint.h> 9 #include <string.h> 10 11 #include <arch_helpers.h> 12 #include <common/debug.h> 13 #include <drivers/arm/css/sds.h> 14 #include <platform_def.h> 15 16 #include "sds_private.h" 17 18 /* Array of SDS memory region descriptions */ 19 static sds_region_desc_t *sds_regions; 20 21 /* Total count of SDS memory regions */ 22 static unsigned int sds_region_cnt; 23 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 */ 29 static int sds_struct_is_valid(unsigned int region_id, uintptr_t header) 30 { 31 size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header); 32 33 /* Zero is not a valid identifier */ 34 if (GET_SDS_HEADER_ID(header) == 0) { 35 return SDS_ERR_FAIL; 36 } 37 38 /* Check SDS Schema version */ 39 if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) { 40 return SDS_ERR_FAIL; 41 } 42 43 /* The SDS Structure sizes have to be multiple of 8 */ 44 if ((struct_size == 0) || ((struct_size % 8) != 0)) { 45 return SDS_ERR_FAIL; 46 } 47 48 if (struct_size > sds_regions[region_id].size) { 49 return SDS_ERR_FAIL; 50 } 51 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 */ 59 static int validate_sds_struct_headers(unsigned int region_id) 60 { 61 unsigned int i, structure_count; 62 uintptr_t header; 63 uintptr_t sds_mem_base = sds_regions[region_id].base; 64 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++) { 74 if (sds_struct_is_valid(region_id, header) != SDS_OK) { 75 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 */ 87 static int get_struct_header(unsigned int region_id, uint32_t structure_id, 88 struct_header_t **header) 89 { 90 unsigned int i, structure_count; 91 uintptr_t current_header; 92 uintptr_t sds_mem_base = sds_regions[region_id].base; 93 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 */ 121 int sds_struct_exists(unsigned int region_id, unsigned int structure_id) 122 { 123 struct_header_t *header = NULL; 124 int ret; 125 126 assert(region_id < sds_region_cnt); 127 128 ret = get_struct_header(region_id, structure_id, &header); 129 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 */ 143 int 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) 146 { 147 int status; 148 uintptr_t field_base; 149 struct_header_t *header = NULL; 150 151 assert(region_id < sds_region_cnt); 152 153 if (!data) 154 return SDS_ERR_INVALID_PARAMS; 155 156 /* Check if a structure with this ID exists */ 157 status = get_struct_header(region_id, structure_id, &header); 158 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 */ 192 int 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) 195 { 196 int status; 197 uintptr_t field_base; 198 struct_header_t *header = NULL; 199 200 assert(region_id < sds_region_cnt); 201 202 if (!data) 203 return SDS_ERR_INVALID_PARAMS; 204 205 /* Check if a structure with this ID exists */ 206 status = get_struct_header(region_id, structure_id, &header); 207 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 239 * the SDS structure headers in the given SDS region. 240 * Returns SDS_OK on success, SDS_ERR_FAIL on error. 241 */ 242 int sds_init(unsigned int region_id) 243 { 244 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; 251 252 if (!IS_SDS_REGION_VALID(sds_mem_base)) { 253 VERBOSE("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 263 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) { 265 WARN("SDS: SDS Memory Region exceeds size limit\n"); 266 return SDS_ERR_FAIL; 267 } 268 269 INFO("SDS: Detected SDS Memory Region (%zu bytes)\n", 270 sds_regions[region_id].size); 271 272 if (validate_sds_struct_headers(region_id) != SDS_OK) 273 return SDS_ERR_FAIL; 274 275 return SDS_OK; 276 } 277