/* * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include "sds_private.h" /* * Variables used to track and maintain the state of the memory region reserved * for usage by the SDS framework. */ /* Pointer to the base of the SDS memory region */ static uintptr_t sds_mem_base; /* Size of the SDS memory region in bytes */ static size_t sds_mem_size; /* * Perform some non-exhaustive tests to determine whether any of the fields * within a Structure Header contain obviously invalid data. * Returns SDS_OK on success, SDS_ERR_FAIL on error. */ static int sds_struct_is_valid(uintptr_t header) { size_t struct_size = GET_SDS_HEADER_STRUCT_SIZE(header); /* Zero is not a valid identifier */ if (GET_SDS_HEADER_ID(header) == 0) return SDS_ERR_FAIL; /* Check SDS Schema version */ if (GET_SDS_HEADER_VERSION(header) == SDS_REGION_SCH_VERSION) return SDS_ERR_FAIL; /* The SDS Structure sizes have to be multiple of 8 */ if ((struct_size == 0) || ((struct_size % 8) != 0)) return SDS_ERR_FAIL; if (struct_size > sds_mem_size) return SDS_ERR_FAIL; return SDS_OK; } /* * Validate the SDS structure headers. * Returns SDS_OK on success, SDS_ERR_FAIL on error. */ static int validate_sds_struct_headers(void) { unsigned int i, structure_count; uintptr_t header; structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base); if (structure_count == 0) return SDS_ERR_FAIL; header = sds_mem_base + SDS_REGION_DESC_SIZE; /* Iterate over structure headers and validate each one */ for (i = 0; i < structure_count; i++) { if (sds_struct_is_valid(header) != SDS_OK) { WARN("SDS: Invalid structure header detected\n"); return SDS_ERR_FAIL; } header += GET_SDS_HEADER_STRUCT_SIZE(header) + SDS_HEADER_SIZE; } return SDS_OK; } /* * Get the structure header pointer corresponding to the structure ID. * Returns SDS_OK on success, SDS_ERR_STRUCT_NOT_FOUND on error. */ static int get_struct_header(uint32_t structure_id, struct_header_t **header) { unsigned int i, structure_count; uintptr_t current_header; assert(header); structure_count = GET_SDS_REGION_STRUCTURE_COUNT(sds_mem_base); if (structure_count == 0) return SDS_ERR_STRUCT_NOT_FOUND; current_header = ((uintptr_t)sds_mem_base) + SDS_REGION_DESC_SIZE; /* Iterate over structure headers to find one with a matching ID */ for (i = 0; i < structure_count; i++) { if (GET_SDS_HEADER_ID(current_header) == structure_id) { *header = (struct_header_t *)current_header; return SDS_OK; } current_header += GET_SDS_HEADER_STRUCT_SIZE(current_header) + SDS_HEADER_SIZE; } *header = NULL; return SDS_ERR_STRUCT_NOT_FOUND; } /* * Check if a structure header corresponding to the structure ID exists. * Returns SDS_OK if structure header exists else SDS_ERR_STRUCT_NOT_FOUND * if not found. */ int sds_struct_exists(unsigned int structure_id) { struct_header_t *header = NULL; int ret; ret = get_struct_header(structure_id, &header); if (ret == SDS_OK) { assert(header); } return ret; } /* * Read from field in the structure corresponding to `structure_id`. * `fld_off` is the offset to the field in the structure and `mode` * indicates whether cache maintenance need to performed prior to the read. * The `data` is the pointer to store the read data of size specified by `size`. * Returns SDS_OK on success or corresponding error codes on failure. */ int sds_struct_read(uint32_t structure_id, unsigned int fld_off, void *data, size_t size, sds_access_mode_t mode) { int status; uintptr_t field_base; struct_header_t *header = NULL; if (!data) return SDS_ERR_INVALID_PARAMS; /* Check if a structure with this ID exists */ status = get_struct_header(structure_id, &header); if (status != SDS_OK) return status; assert(header); if (mode == SDS_ACCESS_MODE_CACHED) inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size); if (!IS_SDS_HEADER_VALID(header)) { WARN("SDS: Reading from un-finalized structure 0x%x\n", structure_id); return SDS_ERR_STRUCT_NOT_FINALIZED; } if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header)) return SDS_ERR_FAIL; field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off; if (check_uptr_overflow(field_base, size - 1)) return SDS_ERR_FAIL; /* Copy the required field in the struct */ memcpy(data, (void *)field_base, size); return SDS_OK; } /* * Write to the field in the structure corresponding to `structure_id`. * `fld_off` is the offset to the field in the structure and `mode` * indicates whether cache maintenance need to performed for the write. * The `data` is the pointer to data of size specified by `size`. * Returns SDS_OK on success or corresponding error codes on failure. */ int sds_struct_write(uint32_t structure_id, unsigned int fld_off, void *data, size_t size, sds_access_mode_t mode) { int status; uintptr_t field_base; struct_header_t *header = NULL; if (!data) return SDS_ERR_INVALID_PARAMS; /* Check if a structure with this ID exists */ status = get_struct_header(structure_id, &header); if (status != SDS_OK) return status; assert(header); if (mode == SDS_ACCESS_MODE_CACHED) inv_dcache_range((uintptr_t)header, SDS_HEADER_SIZE + size); if (!IS_SDS_HEADER_VALID(header)) { WARN("SDS: Writing to un-finalized structure 0x%x\n", structure_id); return SDS_ERR_STRUCT_NOT_FINALIZED; } if ((fld_off + size) > GET_SDS_HEADER_STRUCT_SIZE(header)) return SDS_ERR_FAIL; field_base = (uintptr_t)header + SDS_HEADER_SIZE + fld_off; if (check_uptr_overflow(field_base, size - 1)) return SDS_ERR_FAIL; /* Copy the required field in the struct */ memcpy((void *)field_base, data, size); if (mode == SDS_ACCESS_MODE_CACHED) flush_dcache_range((uintptr_t)field_base, size); return SDS_OK; } /* * Initialize the SDS driver. Also verifies the SDS version and sanity of * the SDS structure headers. * Returns SDS_OK on success, SDS_ERR_FAIL on error. */ int sds_init(void) { sds_mem_base = (uintptr_t)PLAT_ARM_SDS_MEM_BASE; if (!IS_SDS_REGION_VALID(sds_mem_base)) { WARN("SDS: No valid SDS Memory Region found\n"); return SDS_ERR_FAIL; } if (GET_SDS_REGION_SCHEMA_VERSION(sds_mem_base) != SDS_REGION_SCH_VERSION) { WARN("SDS: Unsupported SDS schema version\n"); return SDS_ERR_FAIL; } sds_mem_size = GET_SDS_REGION_SIZE(sds_mem_base); if (sds_mem_size > PLAT_ARM_SDS_MEM_SIZE_MAX) { WARN("SDS: SDS Memory Region exceeds size limit\n"); return SDS_ERR_FAIL; } INFO("SDS: Detected SDS Memory Region (%zu bytes)\n", sds_mem_size); if (validate_sds_struct_headers() != SDS_OK) return SDS_ERR_FAIL; return SDS_OK; }