blob: 6b6014191cafaf9e3013097115df7d6166d3a19f [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2018-2019, Linaro Limited
*/
#include <assert.h>
#include <crypto/crypto.h>
#include <kernel/handle.h>
#include <kernel/huk_subkey.h>
#include <kernel/misc.h>
#include <kernel/msg_param.h>
#include <kernel/pseudo_ta.h>
#include <kernel/user_ta.h>
#include <kernel/user_ta_store.h>
#include <ldelf.h>
#include <mm/file.h>
#include <mm/fobj.h>
#include <mm/tee_mmu.h>
#include <pta_system.h>
#include <string.h>
#include <tee_api_defines_extensions.h>
#include <tee_api_defines.h>
#include <util.h>
#define MAX_ENTROPY_IN 32u
struct bin_handle {
const struct user_ta_store_ops *op;
struct user_ta_store_handle *h;
struct file *f;
size_t offs_bytes;
size_t size_bytes;
};
struct system_ctx {
struct handle_db db;
const struct user_ta_store_ops *store_op;
};
static unsigned int system_pnum;
static TEE_Result system_rng_reseed(struct tee_ta_session *s __unused,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
size_t entropy_sz;
uint8_t *entropy_input;
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
entropy_input = params[0].memref.buffer;
entropy_sz = params[0].memref.size;
/* Fortuna PRNG requires seed <= 32 bytes */
if (!entropy_sz)
return TEE_ERROR_BAD_PARAMETERS;
entropy_sz = MIN(entropy_sz, MAX_ENTROPY_IN);
crypto_rng_add_event(CRYPTO_RNG_SRC_NONSECURE, &system_pnum,
entropy_input, entropy_sz);
return TEE_SUCCESS;
}
static TEE_Result system_derive_ta_unique_key(struct tee_ta_session *s,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
size_t data_len = sizeof(TEE_UUID);
TEE_Result res = TEE_ERROR_GENERIC;
uint8_t *data = NULL;
uint32_t access_flags = 0;
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
struct user_ta_ctx *utc = NULL;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
if (params[0].memref.size > TA_DERIVED_EXTRA_DATA_MAX_SIZE ||
params[1].memref.size < TA_DERIVED_KEY_MIN_SIZE ||
params[1].memref.size > TA_DERIVED_KEY_MAX_SIZE)
return TEE_ERROR_BAD_PARAMETERS;
utc = to_user_ta_ctx(s->ctx);
/*
* The derived key shall not end up in non-secure memory by
* mistake.
*
* Note that we're allowing shared memory as long as it's
* secure. This is needed because a TA always uses shared memory
* when communicating with another TA.
*/
access_flags = TEE_MEMORY_ACCESS_WRITE | TEE_MEMORY_ACCESS_ANY_OWNER |
TEE_MEMORY_ACCESS_SECURE;
res = tee_mmu_check_access_rights(&utc->uctx, access_flags,
(uaddr_t)params[1].memref.buffer,
params[1].memref.size);
if (res != TEE_SUCCESS)
return TEE_ERROR_SECURITY;
/* Take extra data into account. */
if (ADD_OVERFLOW(data_len, params[0].memref.size, &data_len))
return TEE_ERROR_SECURITY;
data = calloc(data_len, 1);
if (!data)
return TEE_ERROR_OUT_OF_MEMORY;
memcpy(data, &s->ctx->uuid, sizeof(TEE_UUID));
/* Append the user provided data */
memcpy(data + sizeof(TEE_UUID), params[0].memref.buffer,
params[0].memref.size);
res = huk_subkey_derive(HUK_SUBKEY_UNIQUE_TA, data, data_len,
params[1].memref.buffer,
params[1].memref.size);
free(data);
return res;
}
static TEE_Result system_map_zi(struct tee_ta_session *s, uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INOUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_NONE);
struct user_ta_ctx *utc = to_user_ta_ctx(s->ctx);
uint32_t prot = TEE_MATTR_URW | TEE_MATTR_PRW;
TEE_Result res = TEE_ERROR_GENERIC;
struct mobj *mobj = NULL;
uint32_t pad_begin = 0;
uint32_t vm_flags = 0;
struct fobj *f = NULL;
uint32_t pad_end = 0;
size_t num_bytes = 0;
vaddr_t va = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
if (params[0].value.b & ~PTA_SYSTEM_MAP_FLAG_SHAREABLE)
return TEE_ERROR_BAD_PARAMETERS;
if (params[0].value.b & PTA_SYSTEM_MAP_FLAG_SHAREABLE)
vm_flags |= VM_FLAG_SHAREABLE;
num_bytes = params[0].value.a;
va = reg_pair_to_64(params[1].value.a, params[1].value.b);
pad_begin = params[2].value.a;
pad_end = params[2].value.b;
f = fobj_ta_mem_alloc(ROUNDUP(num_bytes, SMALL_PAGE_SIZE) /
SMALL_PAGE_SIZE);
if (!f)
return TEE_ERROR_OUT_OF_MEMORY;
mobj = mobj_with_fobj_alloc(f, NULL);
fobj_put(f);
if (!mobj)
return TEE_ERROR_OUT_OF_MEMORY;
res = vm_map_pad(&utc->uctx, &va, num_bytes, prot, vm_flags,
mobj, 0, pad_begin, pad_end);
mobj_put(mobj);
if (!res)
reg_pair_from_64(va, &params[1].value.a, &params[1].value.b);
return res;
}
static TEE_Result system_unmap(struct tee_ta_session *s, uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
struct user_ta_ctx *utc = to_user_ta_ctx(s->ctx);
TEE_Result res = TEE_SUCCESS;
uint32_t vm_flags = 0;
vaddr_t va = 0;
size_t sz = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
if (params[0].value.b)
return TEE_ERROR_BAD_PARAMETERS;
va = reg_pair_to_64(params[1].value.a, params[1].value.b);
sz = ROUNDUP(params[0].value.a, SMALL_PAGE_SIZE);
res = vm_get_flags(&utc->uctx, va, sz, &vm_flags);
if (res)
return res;
if (vm_flags & VM_FLAG_PERMANENT)
return TEE_ERROR_ACCESS_DENIED;
return vm_unmap(&to_user_ta_ctx(s->ctx)->uctx, va, sz);
}
static void ta_bin_close(void *ptr)
{
struct bin_handle *binh = ptr;
if (binh) {
if (binh->op && binh->h)
binh->op->close(binh->h);
file_put(binh->f);
}
free(binh);
}
static TEE_Result system_open_ta_binary(struct system_ctx *ctx,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
TEE_Result res = TEE_SUCCESS;
struct bin_handle *binh = NULL;
int h = 0;
TEE_UUID *uuid = NULL;
uint8_t tag[FILE_TAG_SIZE] = { 0 };
unsigned int tag_len = sizeof(tag);
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_VALUE_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
if (params[0].memref.size != sizeof(*uuid))
return TEE_ERROR_BAD_PARAMETERS;
uuid = params[0].memref.buffer;
binh = calloc(1, sizeof(*binh));
if (!binh)
return TEE_ERROR_OUT_OF_MEMORY;
SCATTERED_ARRAY_FOREACH(binh->op, ta_stores, struct user_ta_store_ops) {
DMSG("Lookup user TA ELF %pUl (%s)",
(void *)uuid, binh->op->description);
res = binh->op->open(uuid, &binh->h);
DMSG("res=0x%x", res);
if (res != TEE_ERROR_ITEM_NOT_FOUND &&
res != TEE_ERROR_STORAGE_NOT_AVAILABLE)
break;
}
if (res)
goto err;
res = binh->op->get_size(binh->h, &binh->size_bytes);
if (res)
goto err;
res = binh->op->get_tag(binh->h, tag, &tag_len);
if (res)
goto err;
binh->f = file_get_by_tag(tag, tag_len);
if (!binh->f)
goto err_oom;
h = handle_get(&ctx->db, binh);
if (h < 0)
goto err_oom;
return TEE_SUCCESS;
err_oom:
res = TEE_ERROR_OUT_OF_MEMORY;
err:
ta_bin_close(binh);
return res;
}
static TEE_Result system_close_ta_binary(struct system_ctx *ctx,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
TEE_Result res = TEE_SUCCESS;
struct bin_handle *binh = NULL;
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
if (params[0].value.b)
return TEE_ERROR_BAD_PARAMETERS;
binh = handle_put(&ctx->db, params[0].value.a);
if (!binh)
return TEE_ERROR_BAD_PARAMETERS;
if (binh->offs_bytes < binh->size_bytes)
res = binh->op->read(binh->h, NULL,
binh->size_bytes - binh->offs_bytes);
ta_bin_close(binh);
return res;
}
static TEE_Result binh_copy_to(struct bin_handle *binh, vaddr_t va,
size_t offs_bytes, size_t num_bytes)
{
TEE_Result res = TEE_SUCCESS;
size_t l = num_bytes;
if (offs_bytes < binh->offs_bytes)
return TEE_ERROR_BAD_STATE;
if (offs_bytes > binh->offs_bytes) {
res = binh->op->read(binh->h, NULL,
offs_bytes - binh->offs_bytes);
if (res)
return res;
binh->offs_bytes = offs_bytes;
}
if (binh->offs_bytes + l > binh->size_bytes) {
size_t rb = binh->size_bytes - binh->offs_bytes;
res = binh->op->read(binh->h, (void *)va, rb);
if (res)
return res;
memset((uint8_t *)va + rb, 0, l - rb);
binh->offs_bytes = binh->size_bytes;
} else {
res = binh->op->read(binh->h, (void *)va, l);
if (res)
return res;
binh->offs_bytes += l;
}
return TEE_SUCCESS;
}
static TEE_Result system_map_ta_binary(struct system_ctx *ctx,
struct tee_ta_session *s,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
const uint32_t accept_flags = PTA_SYSTEM_MAP_FLAG_SHAREABLE |
PTA_SYSTEM_MAP_FLAG_WRITEABLE |
PTA_SYSTEM_MAP_FLAG_EXECUTABLE;
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INOUT,
TEE_PARAM_TYPE_VALUE_INPUT);
struct user_ta_ctx *utc = to_user_ta_ctx(s->ctx);
struct bin_handle *binh = NULL;
TEE_Result res = TEE_SUCCESS;
struct file_slice *fs = NULL;
bool file_is_locked = false;
struct mobj *mobj = NULL;
uint32_t offs_bytes = 0;
uint32_t offs_pages = 0;
uint32_t num_bytes = 0;
uint32_t pad_begin = 0;
uint32_t pad_end = 0;
size_t num_pages = 0;
uint32_t flags = 0;
uint32_t prot = 0;
vaddr_t va = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
binh = handle_lookup(&ctx->db, params[0].value.a);
if (!binh)
return TEE_ERROR_BAD_PARAMETERS;
flags = params[0].value.b;
offs_bytes = params[1].value.a;
num_bytes = params[1].value.b;
va = reg_pair_to_64(params[2].value.a, params[2].value.b);
pad_begin = params[3].value.a;
pad_end = params[3].value.b;
if ((flags & accept_flags) != flags)
return TEE_ERROR_BAD_PARAMETERS;
if ((flags & PTA_SYSTEM_MAP_FLAG_SHAREABLE) &&
(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE))
return TEE_ERROR_BAD_PARAMETERS;
if ((flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE) &&
(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE))
return TEE_ERROR_BAD_PARAMETERS;
if (offs_bytes & SMALL_PAGE_MASK)
return TEE_ERROR_BAD_PARAMETERS;
prot = TEE_MATTR_UR | TEE_MATTR_PR;
if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)
prot |= TEE_MATTR_UW | TEE_MATTR_PW;
if (flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE)
prot |= TEE_MATTR_UX;
offs_pages = offs_bytes >> SMALL_PAGE_SHIFT;
num_pages = ROUNDUP(num_bytes, SMALL_PAGE_SIZE) / SMALL_PAGE_SIZE;
if (!file_trylock(binh->f)) {
/*
* Before we can block on the file lock we must make all
* our page tables available for reclaiming in order to
* avoid a dead-lock with the other thread (which already
* is holding the file lock) mapping lots of memory below.
*/
tee_mmu_set_ctx(NULL);
file_lock(binh->f);
tee_mmu_set_ctx(s->ctx);
}
file_is_locked = true;
fs = file_find_slice(binh->f, offs_pages);
if (fs) {
/* If there's registered slice it has to match */
if (fs->page_offset != offs_pages ||
num_pages > fs->fobj->num_pages) {
res = TEE_ERROR_BAD_PARAMETERS;
goto err;
}
/* If there's a slice we must be mapping shareable */
if (!(flags & PTA_SYSTEM_MAP_FLAG_SHAREABLE)) {
res = TEE_ERROR_BAD_PARAMETERS;
goto err;
}
mobj = mobj_with_fobj_alloc(fs->fobj, binh->f);
if (!mobj) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto err;
}
res = vm_map_pad(&utc->uctx, &va, num_pages * SMALL_PAGE_SIZE,
prot, VM_FLAG_READONLY,
mobj, 0, pad_begin, pad_end);
mobj_put(mobj);
if (res)
goto err;
} else {
struct fobj *f = fobj_ta_mem_alloc(num_pages);
struct file *file = NULL;
uint32_t vm_flags = 0;
if (!f) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto err;
}
if (!(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) {
file = binh->f;
vm_flags |= VM_FLAG_READONLY;
}
mobj = mobj_with_fobj_alloc(f, file);
fobj_put(f);
if (!mobj) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto err;
}
res = vm_map_pad(&utc->uctx, &va, num_pages * SMALL_PAGE_SIZE,
TEE_MATTR_PRW, vm_flags, mobj, 0,
pad_begin, pad_end);
mobj_put(mobj);
if (res)
goto err;
res = binh_copy_to(binh, va, offs_bytes, num_bytes);
if (res)
goto err_unmap_va;
res = vm_set_prot(&utc->uctx, va, num_pages * SMALL_PAGE_SIZE,
prot);
if (res)
goto err_unmap_va;
/*
* The context currently is active set it again to update
* the mapping.
*/
tee_mmu_set_ctx(s->ctx);
if (!(flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)) {
res = file_add_slice(binh->f, f, offs_pages);
if (res)
goto err_unmap_va;
}
}
file_unlock(binh->f);
reg_pair_from_64(va, &params[2].value.a, &params[2].value.b);
return TEE_SUCCESS;
err_unmap_va:
if (vm_unmap(&utc->uctx, va, num_pages * SMALL_PAGE_SIZE))
panic();
/*
* The context currently is active set it again to update
* the mapping.
*/
tee_mmu_set_ctx(s->ctx);
err:
if (file_is_locked)
file_unlock(binh->f);
return res;
}
static TEE_Result system_copy_from_ta_binary(struct system_ctx *ctx,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
struct bin_handle *binh = NULL;
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
binh = handle_lookup(&ctx->db, params[0].value.a);
if (!binh)
return TEE_ERROR_BAD_PARAMETERS;
return binh_copy_to(binh, (vaddr_t)params[1].memref.buffer,
params[0].value.b, params[1].memref.size);
}
static TEE_Result system_set_prot(struct tee_ta_session *s,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
const uint32_t accept_flags = PTA_SYSTEM_MAP_FLAG_WRITEABLE |
PTA_SYSTEM_MAP_FLAG_EXECUTABLE;
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
struct user_ta_ctx *utc = to_user_ta_ctx(s->ctx);
uint32_t prot = TEE_MATTR_UR | TEE_MATTR_PR;
TEE_Result res = TEE_SUCCESS;
uint32_t vm_flags = 0;
uint32_t flags = 0;
vaddr_t va = 0;
size_t sz = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
flags = params[0].value.b;
if ((flags & accept_flags) != flags)
return TEE_ERROR_BAD_PARAMETERS;
if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE)
prot |= TEE_MATTR_UW | TEE_MATTR_PW;
if (flags & PTA_SYSTEM_MAP_FLAG_EXECUTABLE)
prot |= TEE_MATTR_UX;
va = reg_pair_to_64(params[1].value.a, params[1].value.b),
sz = ROUNDUP(params[0].value.a, SMALL_PAGE_SIZE);
res = vm_get_flags(&utc->uctx, va, sz, &vm_flags);
if (res)
return res;
if (vm_flags & VM_FLAG_PERMANENT)
return TEE_ERROR_ACCESS_DENIED;
/*
* If the segment is a mapping of a part of a file (vm_flags &
* VM_FLAG_READONLY) it cannot be made writeable as all mapped
* files are mapped read-only.
*/
if ((vm_flags & VM_FLAG_READONLY) &&
(prot & (TEE_MATTR_UW | TEE_MATTR_PW)))
return TEE_ERROR_ACCESS_DENIED;
return vm_set_prot(&utc->uctx, va, sz, prot);
}
static TEE_Result system_remap(struct tee_ta_session *s, uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INOUT,
TEE_PARAM_TYPE_VALUE_INPUT);
struct user_ta_ctx *utc = to_user_ta_ctx(s->ctx);
TEE_Result res = TEE_SUCCESS;
uint32_t num_bytes = 0;
uint32_t pad_begin = 0;
uint32_t vm_flags = 0;
uint32_t pad_end = 0;
vaddr_t old_va = 0;
vaddr_t new_va = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
num_bytes = params[0].value.a;
old_va = reg_pair_to_64(params[1].value.a, params[1].value.b);
new_va = reg_pair_to_64(params[2].value.a, params[2].value.b);
pad_begin = params[3].value.a;
pad_end = params[3].value.b;
res = vm_get_flags(&utc->uctx, old_va, num_bytes, &vm_flags);
if (res)
return res;
if (vm_flags & VM_FLAG_PERMANENT)
return TEE_ERROR_ACCESS_DENIED;
res = vm_remap(&utc->uctx, &new_va, old_va, num_bytes, pad_begin,
pad_end);
if (!res)
reg_pair_from_64(new_va, &params[2].value.a,
&params[2].value.b);
return res;
}
/* ldelf has the same architecture/register width as the kernel */
#ifdef ARM32
static const bool is_arm32 = true;
#else
static const bool is_arm32;
#endif
static TEE_Result call_ldelf_dlopen(struct user_ta_ctx *utc, TEE_UUID *uuid,
uint32_t flags)
{
uaddr_t usr_stack = utc->ldelf_stack_ptr;
TEE_Result res = TEE_ERROR_GENERIC;
struct dl_entry_arg *arg = NULL;
uint32_t panic_code = 0;
uint32_t panicked = 0;
assert(uuid);
usr_stack -= ROUNDUP(sizeof(*arg), STACK_ALIGNMENT);
arg = (struct dl_entry_arg *)usr_stack;
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_READ |
TEE_MEMORY_ACCESS_WRITE |
TEE_MEMORY_ACCESS_ANY_OWNER,
(uaddr_t)arg, sizeof(*arg));
if (res) {
EMSG("ldelf stack is inaccessible!");
return res;
}
memset(arg, 0, sizeof(*arg));
arg->cmd = LDELF_DL_ENTRY_DLOPEN;
arg->dlopen.uuid = *uuid;
arg->dlopen.flags = flags;
res = thread_enter_user_mode((vaddr_t)arg, 0, 0, 0,
usr_stack, utc->dl_entry_func,
is_arm32, &panicked, &panic_code);
if (panicked) {
EMSG("ldelf dl_entry function panicked");
abort_print_current_ta();
res = TEE_ERROR_TARGET_DEAD;
}
if (!res)
res = arg->ret;
return res;
}
static TEE_Result call_ldelf_dlsym(struct user_ta_ctx *utc, TEE_UUID *uuid,
const char *sym, size_t maxlen, vaddr_t *val)
{
uaddr_t usr_stack = utc->ldelf_stack_ptr;
TEE_Result res = TEE_ERROR_GENERIC;
struct dl_entry_arg *arg = NULL;
uint32_t panic_code = 0;
uint32_t panicked = 0;
size_t len = strnlen(sym, maxlen);
if (len == maxlen)
return TEE_ERROR_BAD_PARAMETERS;
usr_stack -= ROUNDUP(sizeof(*arg) + len + 1, STACK_ALIGNMENT);
arg = (struct dl_entry_arg *)usr_stack;
res = tee_mmu_check_access_rights(&utc->uctx,
TEE_MEMORY_ACCESS_READ |
TEE_MEMORY_ACCESS_WRITE |
TEE_MEMORY_ACCESS_ANY_OWNER,
(uaddr_t)arg, sizeof(*arg) + len + 1);
if (res) {
EMSG("ldelf stack is inaccessible!");
return res;
}
memset(arg, 0, sizeof(*arg));
arg->cmd = LDELF_DL_ENTRY_DLSYM;
arg->dlsym.uuid = *uuid;
memcpy(arg->dlsym.symbol, sym, len);
arg->dlsym.symbol[len] = '\0';
res = thread_enter_user_mode((vaddr_t)arg, 0, 0, 0,
usr_stack, utc->dl_entry_func,
is_arm32, &panicked, &panic_code);
if (panicked) {
EMSG("ldelf dl_entry function panicked");
abort_print_current_ta();
res = TEE_ERROR_TARGET_DEAD;
}
if (!res) {
res = arg->ret;
if (!res)
*val = arg->dlsym.val;
}
return res;
}
static TEE_Result system_dlopen(struct tee_ta_session *cs, uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
TEE_Result res = TEE_ERROR_GENERIC;
struct tee_ta_session *s = NULL;
struct user_ta_ctx *utc = NULL;
TEE_UUID *uuid = NULL;
uint32_t flags = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
uuid = params[0].memref.buffer;
if (!uuid || params[0].memref.size != sizeof(*uuid))
return TEE_ERROR_BAD_PARAMETERS;
flags = params[1].value.a;
utc = to_user_ta_ctx(cs->ctx);
s = tee_ta_pop_current_session();
res = call_ldelf_dlopen(utc, uuid, flags);
tee_ta_push_current_session(s);
return res;
}
static TEE_Result system_dlsym(struct tee_ta_session *cs, uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_VALUE_OUTPUT,
TEE_PARAM_TYPE_NONE);
TEE_Result res = TEE_ERROR_GENERIC;
struct tee_ta_session *s = NULL;
struct user_ta_ctx *utc = NULL;
const char *sym = NULL;
TEE_UUID *uuid = NULL;
size_t maxlen = 0;
vaddr_t va = 0;
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
uuid = params[0].memref.buffer;
if (uuid && params[0].memref.size != sizeof(*uuid))
return TEE_ERROR_BAD_PARAMETERS;
sym = params[1].memref.buffer;
if (!sym)
return TEE_ERROR_BAD_PARAMETERS;
maxlen = params[1].memref.size;
utc = to_user_ta_ctx(cs->ctx);
s = tee_ta_pop_current_session();
res = call_ldelf_dlsym(utc, uuid, sym, maxlen, &va);
tee_ta_push_current_session(s);
if (!res)
reg_pair_from_64(va, &params[2].value.a, &params[2].value.b);
return res;
}
static TEE_Result open_session(uint32_t param_types __unused,
TEE_Param params[TEE_NUM_PARAMS] __unused,
void **sess_ctx)
{
struct tee_ta_session *s = NULL;
struct system_ctx *ctx = NULL;
/* Check that we're called from a user TA */
s = tee_ta_get_calling_session();
if (!s)
return TEE_ERROR_ACCESS_DENIED;
if (!is_user_ta_ctx(s->ctx))
return TEE_ERROR_ACCESS_DENIED;
ctx = calloc(1, sizeof(*ctx));
if (!ctx)
return TEE_ERROR_OUT_OF_MEMORY;
*sess_ctx = ctx;
return TEE_SUCCESS;
}
static void close_session(void *sess_ctx)
{
struct system_ctx *ctx = sess_ctx;
handle_db_destroy(&ctx->db, ta_bin_close);
free(ctx);
}
static TEE_Result invoke_command(void *sess_ctx, uint32_t cmd_id,
uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
struct tee_ta_session *s = tee_ta_get_calling_session();
switch (cmd_id) {
case PTA_SYSTEM_ADD_RNG_ENTROPY:
return system_rng_reseed(s, param_types, params);
case PTA_SYSTEM_DERIVE_TA_UNIQUE_KEY:
return system_derive_ta_unique_key(s, param_types, params);
case PTA_SYSTEM_MAP_ZI:
return system_map_zi(s, param_types, params);
case PTA_SYSTEM_UNMAP:
return system_unmap(s, param_types, params);
case PTA_SYSTEM_OPEN_TA_BINARY:
return system_open_ta_binary(sess_ctx, param_types, params);
case PTA_SYSTEM_CLOSE_TA_BINARY:
return system_close_ta_binary(sess_ctx, param_types, params);
case PTA_SYSTEM_MAP_TA_BINARY:
return system_map_ta_binary(sess_ctx, s, param_types, params);
case PTA_SYSTEM_COPY_FROM_TA_BINARY:
return system_copy_from_ta_binary(sess_ctx, param_types,
params);
case PTA_SYSTEM_SET_PROT:
return system_set_prot(s, param_types, params);
case PTA_SYSTEM_REMAP:
return system_remap(s, param_types, params);
case PTA_SYSTEM_DLOPEN:
return system_dlopen(s, param_types, params);
case PTA_SYSTEM_DLSYM:
return system_dlsym(s, param_types, params);
default:
break;
}
return TEE_ERROR_NOT_IMPLEMENTED;
}
pseudo_ta_register(.uuid = PTA_SYSTEM_UUID, .name = "system.pta",
.flags = PTA_DEFAULT_FLAGS | TA_FLAG_CONCURRENT,
.open_session_entry_point = open_session,
.close_session_entry_point = close_session,
.invoke_command_entry_point = invoke_command);