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