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