mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-18 10:34:19 +00:00

Change-Id: Ib7c2529b85bb5930d44907edfc8ead13d3b1ef4d Co-authored-by: Sandrine Bailleux <sandrine.bailleux@arm.com> Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
436 lines
13 KiB
C
436 lines
13 KiB
C
/*
|
|
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <arch.h>
|
|
#include <arch_helpers.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <object_pool.h>
|
|
#include <platform_def.h>
|
|
#include <platform.h>
|
|
#include <sp_res_desc.h>
|
|
#include <spm_svc.h>
|
|
#include <string.h>
|
|
#include <utils.h>
|
|
#include <utils_def.h>
|
|
#include <xlat_tables_v2.h>
|
|
|
|
#include "spm_private.h"
|
|
#include "spm_shim_private.h"
|
|
|
|
/*******************************************************************************
|
|
* Instantiation of translation table context
|
|
******************************************************************************/
|
|
|
|
/* Lock used for SP_MEMORY_ATTRIBUTES_GET and SP_MEMORY_ATTRIBUTES_SET */
|
|
static spinlock_t mem_attr_smc_lock;
|
|
|
|
/* Place translation tables by default along with the ones used by BL31. */
|
|
#ifndef PLAT_SP_IMAGE_XLAT_SECTION_NAME
|
|
#define PLAT_SP_IMAGE_XLAT_SECTION_NAME "xlat_table"
|
|
#endif
|
|
|
|
/*
|
|
* Allocate elements of the translation contexts for the Secure Partitions.
|
|
*/
|
|
|
|
/* Allocate an array of mmap_region per partition. */
|
|
static struct mmap_region sp_mmap_regions[PLAT_SP_IMAGE_MMAP_REGIONS + 1]
|
|
[PLAT_SPM_MAX_PARTITIONS];
|
|
static OBJECT_POOL(sp_mmap_regions_pool, sp_mmap_regions,
|
|
sizeof(mmap_region_t) * (PLAT_SP_IMAGE_MMAP_REGIONS + 1),
|
|
PLAT_SPM_MAX_PARTITIONS);
|
|
|
|
/* Allocate individual translation tables. */
|
|
static uint64_t sp_xlat_tables[XLAT_TABLE_ENTRIES]
|
|
[(PLAT_SP_IMAGE_MAX_XLAT_TABLES + 1) * PLAT_SPM_MAX_PARTITIONS]
|
|
__aligned(XLAT_TABLE_SIZE) __section(PLAT_SP_IMAGE_XLAT_SECTION_NAME);
|
|
static OBJECT_POOL(sp_xlat_tables_pool, sp_xlat_tables,
|
|
XLAT_TABLE_ENTRIES * sizeof(uint64_t),
|
|
(PLAT_SP_IMAGE_MAX_XLAT_TABLES + 1) * PLAT_SPM_MAX_PARTITIONS);
|
|
|
|
/* Allocate base translation tables. */
|
|
static uint64_t sp_xlat_base_tables
|
|
[GET_NUM_BASE_LEVEL_ENTRIES(PLAT_VIRT_ADDR_SPACE_SIZE)]
|
|
[PLAT_SPM_MAX_PARTITIONS]
|
|
__aligned(GET_NUM_BASE_LEVEL_ENTRIES(PLAT_VIRT_ADDR_SPACE_SIZE)
|
|
* sizeof(uint64_t))
|
|
__section(PLAT_SP_IMAGE_XLAT_SECTION_NAME);
|
|
static OBJECT_POOL(sp_xlat_base_tables_pool, sp_xlat_base_tables,
|
|
GET_NUM_BASE_LEVEL_ENTRIES(PLAT_VIRT_ADDR_SPACE_SIZE) * sizeof(uint64_t),
|
|
PLAT_SPM_MAX_PARTITIONS);
|
|
|
|
/* Allocate arrays. */
|
|
static int sp_xlat_mapped_regions[PLAT_SP_IMAGE_MAX_XLAT_TABLES]
|
|
[PLAT_SPM_MAX_PARTITIONS];
|
|
static OBJECT_POOL(sp_xlat_mapped_regions_pool, sp_xlat_mapped_regions,
|
|
sizeof(int) * PLAT_SP_IMAGE_MAX_XLAT_TABLES, PLAT_SPM_MAX_PARTITIONS);
|
|
|
|
/* Allocate individual contexts. */
|
|
static xlat_ctx_t sp_xlat_ctx[PLAT_SPM_MAX_PARTITIONS];
|
|
static OBJECT_POOL(sp_xlat_ctx_pool, sp_xlat_ctx, sizeof(xlat_ctx_t),
|
|
PLAT_SPM_MAX_PARTITIONS);
|
|
|
|
/* Get handle of Secure Partition translation context */
|
|
xlat_ctx_t *spm_sp_xlat_context_alloc(void)
|
|
{
|
|
xlat_ctx_t *ctx = pool_alloc(&sp_xlat_ctx_pool);
|
|
|
|
struct mmap_region *mmap = pool_alloc(&sp_mmap_regions_pool);
|
|
|
|
uint64_t *base_table = pool_alloc(&sp_xlat_base_tables_pool);
|
|
uint64_t **tables = pool_alloc_n(&sp_xlat_tables_pool,
|
|
PLAT_SP_IMAGE_MAX_XLAT_TABLES);
|
|
|
|
int *mapped_regions = pool_alloc(&sp_xlat_mapped_regions_pool);
|
|
|
|
xlat_setup_dynamic_ctx(ctx, PLAT_PHY_ADDR_SPACE_SIZE - 1,
|
|
PLAT_VIRT_ADDR_SPACE_SIZE - 1, mmap,
|
|
PLAT_SP_IMAGE_MMAP_REGIONS, tables,
|
|
PLAT_SP_IMAGE_MAX_XLAT_TABLES, base_table,
|
|
EL1_EL0_REGIME, mapped_regions);
|
|
|
|
return ctx;
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* Functions to allocate memory for regions.
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* The region with base PLAT_SPM_HEAP_BASE and size PLAT_SPM_HEAP_SIZE is
|
|
* reserved for SPM to use as heap to allocate memory regions of Secure
|
|
* Partitions. This is only done at boot.
|
|
*/
|
|
static OBJECT_POOL(spm_heap_mem, (void *)PLAT_SPM_HEAP_BASE, 1U,
|
|
PLAT_SPM_HEAP_SIZE);
|
|
|
|
static uintptr_t spm_alloc_heap(size_t size)
|
|
{
|
|
return (uintptr_t)pool_alloc_n(&spm_heap_mem, size);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Functions to map memory regions described in the resource description.
|
|
******************************************************************************/
|
|
static unsigned int rdmem_attr_to_mmap_attr(uint32_t attr)
|
|
{
|
|
unsigned int index = attr & RD_MEM_MASK;
|
|
|
|
const unsigned int mmap_attr_arr[8] = {
|
|
MT_DEVICE | MT_RW | MT_SECURE, /* RD_MEM_DEVICE */
|
|
MT_CODE | MT_SECURE, /* RD_MEM_NORMAL_CODE */
|
|
MT_MEMORY | MT_RW | MT_SECURE, /* RD_MEM_NORMAL_DATA */
|
|
MT_MEMORY | MT_RW | MT_SECURE, /* RD_MEM_NORMAL_BSS */
|
|
MT_RO_DATA | MT_SECURE, /* RD_MEM_NORMAL_RODATA */
|
|
MT_MEMORY | MT_RW | MT_SECURE, /* RD_MEM_NORMAL_SPM_SP_SHARED_MEM */
|
|
MT_MEMORY | MT_RW | MT_SECURE, /* RD_MEM_NORMAL_CLIENT_SHARED_MEM */
|
|
MT_MEMORY | MT_RW | MT_SECURE /* RD_MEM_NORMAL_MISCELLANEOUS */
|
|
};
|
|
|
|
if (index >= ARRAY_SIZE(mmap_attr_arr)) {
|
|
ERROR("Unsupported RD memory attributes 0x%x\n", attr);
|
|
panic();
|
|
}
|
|
|
|
return mmap_attr_arr[index];
|
|
}
|
|
|
|
/*
|
|
* The data provided in the resource description structure is not directly
|
|
* compatible with a mmap_region structure. This function handles the conversion
|
|
* and maps it.
|
|
*/
|
|
static void map_rdmem(sp_context_t *sp_ctx, struct sp_rd_sect_mem_region *rdmem)
|
|
{
|
|
int rc;
|
|
mmap_region_t mmap;
|
|
|
|
/* Location of the SP image */
|
|
uintptr_t sp_size = sp_ctx->image_size;
|
|
uintptr_t sp_base_va = sp_ctx->rd.attribute.load_address;
|
|
unsigned long long sp_base_pa = sp_ctx->image_base;
|
|
|
|
/* Location of the memory region to map */
|
|
size_t rd_size = rdmem->size;
|
|
uintptr_t rd_base_va = rdmem->base;
|
|
unsigned long long rd_base_pa;
|
|
|
|
unsigned int memtype = rdmem->attr & RD_MEM_MASK;
|
|
|
|
VERBOSE("Adding memory region '%s'\n", rdmem->name);
|
|
|
|
mmap.granularity = REGION_DEFAULT_GRANULARITY;
|
|
|
|
/* Check if the RD region is inside of the SP image or not */
|
|
int is_outside = (rd_base_va + rd_size <= sp_base_va) ||
|
|
(sp_base_va + sp_size <= rd_base_va);
|
|
|
|
/* Set to 1 if it is needed to zero this region */
|
|
int zero_region = 0;
|
|
|
|
switch (memtype) {
|
|
case RD_MEM_DEVICE:
|
|
/* Device regions are mapped 1:1 */
|
|
rd_base_pa = rd_base_va;
|
|
break;
|
|
|
|
case RD_MEM_NORMAL_CODE:
|
|
case RD_MEM_NORMAL_RODATA:
|
|
{
|
|
if (is_outside == 1) {
|
|
ERROR("Code and rodata sections must be fully contained in the image.");
|
|
panic();
|
|
}
|
|
|
|
/* Get offset into the image */
|
|
rd_base_pa = sp_base_pa + rd_base_va - sp_base_va;
|
|
break;
|
|
}
|
|
case RD_MEM_NORMAL_DATA:
|
|
{
|
|
if (is_outside == 1) {
|
|
ERROR("Data sections must be fully contained in the image.");
|
|
panic();
|
|
}
|
|
|
|
rd_base_pa = spm_alloc_heap(rd_size);
|
|
|
|
/* Get offset into the image */
|
|
void *img_pa = (void *)(sp_base_pa + rd_base_va - sp_base_va);
|
|
|
|
VERBOSE(" Copying data from %p to 0x%llx\n", img_pa, rd_base_pa);
|
|
|
|
/* Map destination */
|
|
rc = mmap_add_dynamic_region(rd_base_pa, rd_base_pa,
|
|
rd_size, MT_MEMORY | MT_RW | MT_SECURE);
|
|
if (rc != 0) {
|
|
ERROR("Unable to map data region at EL3: %d\n", rc);
|
|
panic();
|
|
}
|
|
|
|
/* Copy original data to destination */
|
|
memcpy((void *)rd_base_pa, img_pa, rd_size);
|
|
|
|
/* Unmap destination region */
|
|
rc = mmap_remove_dynamic_region(rd_base_pa, rd_size);
|
|
if (rc != 0) {
|
|
ERROR("Unable to remove data region at EL3: %d\n", rc);
|
|
panic();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case RD_MEM_NORMAL_MISCELLANEOUS:
|
|
/* Allow SPM to change the attributes of the region. */
|
|
mmap.granularity = PAGE_SIZE;
|
|
rd_base_pa = spm_alloc_heap(rd_size);
|
|
zero_region = 1;
|
|
break;
|
|
|
|
case RD_MEM_NORMAL_SPM_SP_SHARED_MEM:
|
|
if ((sp_ctx->spm_sp_buffer_base != 0) ||
|
|
(sp_ctx->spm_sp_buffer_size != 0)) {
|
|
ERROR("A partition must have only one SPM<->SP buffer.\n");
|
|
panic();
|
|
}
|
|
rd_base_pa = spm_alloc_heap(rd_size);
|
|
zero_region = 1;
|
|
/* Save location of this buffer, it is needed by SPM */
|
|
sp_ctx->spm_sp_buffer_base = rd_base_pa;
|
|
sp_ctx->spm_sp_buffer_size = rd_size;
|
|
break;
|
|
|
|
case RD_MEM_NORMAL_CLIENT_SHARED_MEM:
|
|
/* Fallthrough */
|
|
case RD_MEM_NORMAL_BSS:
|
|
rd_base_pa = spm_alloc_heap(rd_size);
|
|
zero_region = 1;
|
|
break;
|
|
|
|
default:
|
|
panic();
|
|
}
|
|
|
|
mmap.base_pa = rd_base_pa;
|
|
mmap.base_va = rd_base_va;
|
|
mmap.size = rd_size;
|
|
|
|
/* Only S-EL0 mappings supported for now */
|
|
mmap.attr = rdmem_attr_to_mmap_attr(rdmem->attr) | MT_USER;
|
|
|
|
VERBOSE(" VA: 0x%lx PA: 0x%llx (0x%lx, attr: 0x%x)\n",
|
|
mmap.base_va, mmap.base_pa, mmap.size, mmap.attr);
|
|
|
|
/* Map region in the context of the Secure Partition */
|
|
mmap_add_region_ctx(sp_ctx->xlat_ctx_handle, &mmap);
|
|
|
|
if (zero_region == 1) {
|
|
VERBOSE(" Zeroing region...\n");
|
|
|
|
rc = mmap_add_dynamic_region(mmap.base_pa, mmap.base_pa,
|
|
mmap.size, MT_MEMORY | MT_RW | MT_SECURE);
|
|
if (rc != 0) {
|
|
ERROR("Unable to map memory at EL3 to zero: %d\n",
|
|
rc);
|
|
panic();
|
|
}
|
|
|
|
zeromem((void *)mmap.base_pa, mmap.size);
|
|
|
|
/*
|
|
* Unmap destination region unless it is the SPM<->SP buffer,
|
|
* which must be used by SPM.
|
|
*/
|
|
if (memtype != RD_MEM_NORMAL_SPM_SP_SHARED_MEM) {
|
|
rc = mmap_remove_dynamic_region(rd_base_pa, rd_size);
|
|
if (rc != 0) {
|
|
ERROR("Unable to remove region at EL3: %d\n", rc);
|
|
panic();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void sp_map_memory_regions(sp_context_t *sp_ctx)
|
|
{
|
|
/* This region contains the exception vectors used at S-EL1. */
|
|
const mmap_region_t sel1_exception_vectors =
|
|
MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START,
|
|
SPM_SHIM_EXCEPTIONS_SIZE,
|
|
MT_CODE | MT_SECURE | MT_PRIVILEGED);
|
|
|
|
mmap_add_region_ctx(sp_ctx->xlat_ctx_handle,
|
|
&sel1_exception_vectors);
|
|
|
|
struct sp_rd_sect_mem_region *rdmem;
|
|
|
|
for (rdmem = sp_ctx->rd.mem_region; rdmem != NULL; rdmem = rdmem->next) {
|
|
map_rdmem(sp_ctx, rdmem);
|
|
}
|
|
|
|
init_xlat_tables_ctx(sp_ctx->xlat_ctx_handle);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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 tf_attr = 0U;
|
|
|
|
unsigned int access = (attributes & SP_MEMORY_ATTRIBUTES_ACCESS_MASK)
|
|
>> SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT;
|
|
|
|
if (access == SP_MEMORY_ATTRIBUTES_ACCESS_RW) {
|
|
tf_attr |= MT_RW | MT_USER;
|
|
} else if (access == SP_MEMORY_ATTRIBUTES_ACCESS_RO) {
|
|
tf_attr |= MT_RO | MT_USER;
|
|
} else {
|
|
/* Other values are reserved. */
|
|
assert(access == SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS);
|
|
/* The only requirement is that there's no access from EL0 */
|
|
tf_attr |= MT_RO | MT_PRIVILEGED;
|
|
}
|
|
|
|
if ((attributes & SP_MEMORY_ATTRIBUTES_NON_EXEC) == 0) {
|
|
tf_attr |= MT_EXECUTE;
|
|
} else {
|
|
tf_attr |= MT_EXECUTE_NEVER;
|
|
}
|
|
|
|
return tf_attr;
|
|
}
|
|
|
|
/*
|
|
* This function converts attributes from the Trusted Firmware format into the
|
|
* SMC interface format.
|
|
*/
|
|
static unsigned int smc_mmap_to_smc_attr(unsigned int attr)
|
|
{
|
|
unsigned int smc_attr = 0U;
|
|
|
|
unsigned int data_access;
|
|
|
|
if ((attr & MT_USER) == 0) {
|
|
/* No access from EL0. */
|
|
data_access = SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS;
|
|
} else {
|
|
if ((attr & MT_RW) != 0) {
|
|
assert(MT_TYPE(attr) != MT_DEVICE);
|
|
data_access = SP_MEMORY_ATTRIBUTES_ACCESS_RW;
|
|
} else {
|
|
data_access = SP_MEMORY_ATTRIBUTES_ACCESS_RO;
|
|
}
|
|
}
|
|
|
|
smc_attr |= (data_access & SP_MEMORY_ATTRIBUTES_ACCESS_MASK)
|
|
<< SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT;
|
|
|
|
if ((attr & MT_EXECUTE_NEVER) != 0U) {
|
|
smc_attr |= SP_MEMORY_ATTRIBUTES_NON_EXEC;
|
|
}
|
|
|
|
return smc_attr;
|
|
}
|
|
|
|
int32_t spm_memory_attributes_get_smc_handler(sp_context_t *sp_ctx,
|
|
uintptr_t base_va)
|
|
{
|
|
uint32_t attributes;
|
|
|
|
spin_lock(&mem_attr_smc_lock);
|
|
|
|
int rc = xlat_get_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
|
|
base_va, &attributes);
|
|
|
|
spin_unlock(&mem_attr_smc_lock);
|
|
|
|
/* Convert error codes of xlat_get_mem_attributes_ctx() into SPM. */
|
|
assert((rc == 0) || (rc == -EINVAL));
|
|
|
|
if (rc == 0) {
|
|
return (int32_t) smc_mmap_to_smc_attr(attributes);
|
|
} else {
|
|
return SPM_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx,
|
|
u_register_t page_address,
|
|
u_register_t pages_count,
|
|
u_register_t smc_attributes)
|
|
{
|
|
uintptr_t base_va = (uintptr_t) page_address;
|
|
size_t size = (size_t) (pages_count * PAGE_SIZE);
|
|
uint32_t attributes = (uint32_t) smc_attributes;
|
|
|
|
INFO(" Start address : 0x%lx\n", base_va);
|
|
INFO(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size);
|
|
INFO(" Attributes : 0x%x\n", attributes);
|
|
|
|
spin_lock(&mem_attr_smc_lock);
|
|
|
|
int ret = xlat_change_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
|
|
base_va, size,
|
|
smc_attr_to_mmap_attr(attributes));
|
|
|
|
spin_unlock(&mem_attr_smc_lock);
|
|
|
|
/* Convert error codes of xlat_change_mem_attributes_ctx() into SPM. */
|
|
assert((ret == 0) || (ret == -EINVAL));
|
|
|
|
return (ret == 0) ? SPM_SUCCESS : SPM_INVALID_PARAMETER;
|
|
}
|