Home | History | Annotate | Download | only in sds
      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