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