/* * Copyright (c) 2018-2019, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include "spm_private.h" /******************************************************************************* * Functions to manipulate memory regions ******************************************************************************/ /* * Attributes are encoded using a different format in the SMC interface than in * the Trusted Firmware, where the mmap_attr_t enum type is used. This function * converts an attributes value from the SMC format to the mmap_attr_t format by * setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and MT_EXECUTE/MT_EXECUTE_NEVER. * The other fields are left as 0 because they are ignored by the function * xlat_change_mem_attributes_ctx(). */ static unsigned int smc_attr_to_mmap_attr(unsigned int attributes) { unsigned int perm = attributes & SPRT_MEMORY_PERM_ATTR_MASK; if (perm == SPRT_MEMORY_PERM_ATTR_RW) { return MT_RW | MT_EXECUTE_NEVER | MT_USER; } else if (perm == SPRT_MEMORY_PERM_ATTR_RO) { return MT_RO | MT_EXECUTE_NEVER | MT_USER; } else if (perm == SPRT_MEMORY_PERM_ATTR_RO_EXEC) { return MT_RO | MT_USER; } else { return UINT_MAX; } } /* * This function converts attributes from the Trusted Firmware format into the * SMC interface format. */ static unsigned int mmap_attr_to_smc_attr(unsigned int attr) { unsigned int perm; /* No access from EL0. */ if ((attr & MT_USER) == 0U) return UINT_MAX; if ((attr & MT_RW) != 0) { assert(MT_TYPE(attr) != MT_DEVICE); perm = SPRT_MEMORY_PERM_ATTR_RW; } else { if ((attr & MT_EXECUTE_NEVER) != 0U) { perm = SPRT_MEMORY_PERM_ATTR_RO; } else { perm = SPRT_MEMORY_PERM_ATTR_RO_EXEC; } } return perm << SPRT_MEMORY_PERM_ATTR_SHIFT; } static int32_t sprt_memory_perm_attr_get(sp_context_t *sp_ctx, uintptr_t base_va) { uint32_t attributes; spin_lock(&(sp_ctx->xlat_ctx_lock)); int ret = xlat_get_mem_attributes_ctx(sp_ctx->xlat_ctx_handle, base_va, &attributes); spin_unlock(&(sp_ctx->xlat_ctx_lock)); /* Convert error codes of xlat_get_mem_attributes_ctx() into SPM. */ assert((ret == 0) || (ret == -EINVAL)); if (ret != 0) return SPRT_INVALID_PARAMETER; unsigned int perm = mmap_attr_to_smc_attr(attributes); if (perm == UINT_MAX) return SPRT_INVALID_PARAMETER; return SPRT_SUCCESS | perm; } static int32_t sprt_memory_perm_attr_set(sp_context_t *sp_ctx, u_register_t page_address, u_register_t pages_count, u_register_t smc_attributes) { int ret; uintptr_t base_va = (uintptr_t) page_address; size_t size = pages_count * PAGE_SIZE; VERBOSE(" Start address : 0x%lx\n", base_va); VERBOSE(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size); VERBOSE(" Attributes : 0x%lx\n", smc_attributes); uint32_t mmap_attr = smc_attr_to_mmap_attr(smc_attributes); if (mmap_attr == UINT_MAX) { WARN("%s: Invalid memory attributes: 0x%lx\n", __func__, smc_attributes); return SPRT_INVALID_PARAMETER; } /* * Perform some checks before actually trying to change the memory * attributes. */ spin_lock(&(sp_ctx->xlat_ctx_lock)); uint32_t attributes; ret = xlat_get_mem_attributes_ctx(sp_ctx->xlat_ctx_handle, base_va, &attributes); if (ret != 0) { spin_unlock(&(sp_ctx->xlat_ctx_lock)); return SPRT_INVALID_PARAMETER; } if ((attributes & MT_USER) == 0U) { /* Prohibit changing attributes of S-EL1 regions */ spin_unlock(&(sp_ctx->xlat_ctx_lock)); return SPRT_INVALID_PARAMETER; } ret = xlat_change_mem_attributes_ctx(sp_ctx->xlat_ctx_handle, base_va, size, mmap_attr); spin_unlock(&(sp_ctx->xlat_ctx_lock)); /* Convert error codes of xlat_change_mem_attributes_ctx() into SPM. */ assert((ret == 0) || (ret == -EINVAL)); return (ret == 0) ? SPRT_SUCCESS : SPRT_INVALID_PARAMETER; } /******************************************************************************* * This function handles all SMCs in the range reserved for SPRT. ******************************************************************************/ static uintptr_t sprt_smc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, u_register_t x4, void *cookie, void *handle, u_register_t flags) { /* SPRT only supported from the Secure world */ if (is_caller_non_secure(flags) == SMC_FROM_NON_SECURE) { SMC_RET1(handle, SMC_UNK); } assert(handle == cm_get_context(SECURE)); /* * Only S-EL0 partitions are supported for now. Make the next ERET into * the partition jump directly to S-EL0 instead of S-EL1. */ cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1()); switch (smc_fid) { case SPRT_VERSION: SMC_RET1(handle, SPRT_VERSION_COMPILED); case SPRT_PUT_RESPONSE_AARCH64: /* * Registers x1-x3 aren't saved by default to the context, * but they are needed after spm_sp_synchronous_exit() because * they hold return values. */ SMC_SET_GP(handle, CTX_GPREG_X1, x1); SMC_SET_GP(handle, CTX_GPREG_X2, x2); SMC_SET_GP(handle, CTX_GPREG_X3, x3); spm_sp_synchronous_exit(SPRT_PUT_RESPONSE_AARCH64); case SPRT_YIELD_AARCH64: spm_sp_synchronous_exit(SPRT_YIELD_AARCH64); case SPRT_MEMORY_PERM_ATTR_GET_AARCH64: { /* Get context of the SP in use by this CPU. */ unsigned int linear_id = plat_my_core_pos(); sp_context_t *sp_ctx = spm_cpu_get_sp_ctx(linear_id); SMC_RET1(handle, sprt_memory_perm_attr_get(sp_ctx, x1)); } case SPRT_MEMORY_PERM_ATTR_SET_AARCH64: { /* Get context of the SP in use by this CPU. */ unsigned int linear_id = plat_my_core_pos(); sp_context_t *sp_ctx = spm_cpu_get_sp_ctx(linear_id); SMC_RET1(handle, sprt_memory_perm_attr_set(sp_ctx, x1, x2, x3)); } default: break; } WARN("SPRT: Unsupported call 0x%08x\n", smc_fid); SMC_RET1(handle, SPRT_NOT_SUPPORTED); } DECLARE_RT_SVC( sprt_handler, OEN_SPRT_START, OEN_SPRT_END, SMC_TYPE_FAST, NULL, sprt_smc_handler );