blob: b38ad71028437e2c0d2af020e6cc372ed87c52a4 [file] [log] [blame]
/*
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved
* Copyright (c) 2013, Google, Inc. All rights reserved
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <arch.h>
#include <assert.h>
#include <compiler.h>
#include <debug.h>
#include "elf.h"
#include <err.h>
#include <kernel/event.h>
#include <kernel/mutex.h>
#include <kernel/thread.h>
#include <malloc.h>
#include <platform.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <lk/init.h>
#include <trace.h>
#include <lib/trusty/trusty_app.h>
#define LOCAL_TRACE 0
/*
* Layout of .trusty_app.manifest section in the trusted application is the
* required UUID followed by an abitrary number of configuration options.
*
* Note: Ensure that the manifest definition is kept in sync with the
* one userspace uses to build the trusty apps.
*/
enum {
TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE = 1,
TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE = 2,
TRUSTY_APP_CONFIG_KEY_MAP_MEM = 3,
TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS = 4,
};
enum trusty_app_mgmt_flags {
TRUSTY_APP_MGMT_FLAGS_NONE = 0x0,
/* Restart application on exit */
TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT = 0x1,
};
#define DEFAULT_MGMT_FLAGS TRUSTY_APP_MGMT_FLAGS_NONE
typedef struct trusty_app_manifest {
uuid_t uuid;
uint32_t config_options[];
} trusty_app_manifest_t;
#define TRUSTY_APP_START_ADDR 0x8000
#define TRUSTY_APP_STACK_TOP 0x1000000 /* 16MB */
#ifndef DEFAULT_HEAP_SIZE
#define DEFAULT_HEAP_SIZE (4 * PAGE_SIZE)
#endif
#define PAGE_MASK (PAGE_SIZE - 1)
static u_int trusty_next_app_id;
static struct list_node trusty_app_list = LIST_INITIAL_VALUE(trusty_app_list);
/* These symbols are linker defined and are declared as unsized arrays to prevent
* compiler(clang) optimizations that break when the list is empty and the symbols alias
*/
extern struct trusty_app_img __trusty_app_list_start[];
extern struct trusty_app_img __trusty_app_list_end[];
static bool apps_started;
static mutex_t apps_lock = MUTEX_INITIAL_VALUE(apps_lock);
static struct list_node app_notifier_list = LIST_INITIAL_VALUE(app_notifier_list);
uint als_slot_cnt;
static event_t app_mgr_event = EVENT_INITIAL_VALUE(app_mgr_event, 0,
EVENT_FLAG_AUTOUNSIGNAL);
#define PRINT_TRUSTY_APP_UUID(tid,u) \
dprintf(SPEW, \
"trusty_app %d uuid: 0x%x 0x%x 0x%x 0x%x%x 0x%x%x%x%x%x%x\n",\
tid, \
(u)->time_low, (u)->time_mid, \
(u)->time_hi_and_version, \
(u)->clock_seq_and_node[0], \
(u)->clock_seq_and_node[1], \
(u)->clock_seq_and_node[2], \
(u)->clock_seq_and_node[3], \
(u)->clock_seq_and_node[4], \
(u)->clock_seq_and_node[5], \
(u)->clock_seq_and_node[6], \
(u)->clock_seq_and_node[7]);
static bool address_range_within_bounds(const void *range_start,
size_t range_size,
const void *lower_bound,
const void *upper_bound)
{
const void *range_end = range_start + range_size;
if (upper_bound < lower_bound) {
LTRACEF("upper bound(%p) is below upper bound(%p)\n",
upper_bound, lower_bound);
return false;
}
if (range_end < range_start) {
LTRACEF("Range overflows. start:%p size:%zd end:%p\n", range_start,
range_size, range_end);
return false;
}
if (range_start < lower_bound) {
LTRACEF("Range starts(%p) before lower bound(%p)\n", range_start,
lower_bound);
return false;
}
if (range_end > upper_bound) {
LTRACEF("Range ends(%p) past upper bound(%p)\n", range_end,
upper_bound);
return false;
}
return true;
}
static inline bool address_range_within_img(const void *range_start,
size_t range_size,
const struct trusty_app_img *appimg)
{
return address_range_within_bounds(range_start, range_size,
(const void *)appimg->img_start,
(const void *)appimg->img_end);
}
static bool compare_section_name(Elf32_Shdr *shdr, const char *name,
char *shstbl, uint32_t shstbl_size)
{
return shstbl_size - shdr->sh_name > strlen(name) &&
!strcmp(shstbl + shdr->sh_name, name);
}
static void finalize_registration(void)
{
mutex_acquire(&apps_lock);
apps_started = true;
mutex_release(&apps_lock);
}
status_t trusty_register_app_notifier(trusty_app_notifier_t *n)
{
status_t ret = NO_ERROR;
mutex_acquire(&apps_lock);
if (!apps_started)
list_add_tail(&app_notifier_list, &n->node);
else
ret = ERR_ALREADY_STARTED;
mutex_release(&apps_lock);
return ret;
}
int trusty_als_alloc_slot(void)
{
int ret;
mutex_acquire(&apps_lock);
if (!apps_started)
ret = ++als_slot_cnt;
else
ret = ERR_ALREADY_STARTED;
mutex_release(&apps_lock);
return ret;
}
static int trusty_thread_startup(void *arg)
{
struct trusty_thread *trusty_thread = current_trusty_thread();
vmm_set_active_aspace(trusty_thread->app->aspace);
arch_enter_uspace(trusty_thread->entry,
ROUNDDOWN(trusty_thread->stack_start, 8),
ARCH_ENTER_USPACE_FLAG_32BIT, 0);
__UNREACHABLE;
}
static status_t trusty_thread_start(struct trusty_thread *trusty_thread)
{
DEBUG_ASSERT(trusty_thread && trusty_thread->thread);
return thread_resume(trusty_thread->thread);
}
void __NO_RETURN trusty_thread_exit(int retcode)
{
struct trusty_thread *trusty_thread = current_trusty_thread();
vaddr_t stack_bot;
ASSERT(trusty_thread);
stack_bot = trusty_thread->stack_start - trusty_thread->stack_size;
vmm_free_region(trusty_thread->app->aspace, stack_bot);
thread_exit(retcode);
}
static struct trusty_thread *
trusty_thread_create(const char *name, vaddr_t entry, int priority,
vaddr_t stack_start, size_t stack_size,
trusty_app_t *trusty_app)
{
struct trusty_thread *trusty_thread;
status_t err;
vaddr_t stack_bot = stack_start - stack_size;
trusty_thread = calloc(1, sizeof(struct trusty_thread));
if (!trusty_thread)
return NULL;
err = vmm_alloc(trusty_app->aspace, "stack", stack_size,
(void **)&stack_bot, PAGE_SIZE_SHIFT,
VMM_FLAG_VALLOC_SPECIFIC,
ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_NO_EXECUTE);
if (err != NO_ERROR) {
dprintf(CRITICAL, "failed(%d) to create thread stack(0x%lx) for app %u\n",
err, stack_bot, trusty_app->app_id);
goto err_stack;
}
ASSERT(stack_bot == stack_start - stack_size);
trusty_thread->thread = thread_create(name, trusty_thread_startup, NULL,
priority, DEFAULT_STACK_SIZE);
if (!trusty_thread->thread)
goto err_thread;
trusty_thread->app = trusty_app;
trusty_thread->entry = entry;
trusty_thread->stack_start = stack_start;
trusty_thread->stack_size = stack_size;
trusty_thread->thread->tls[TLS_ENTRY_TRUSTY] = (uintptr_t)trusty_thread;
return trusty_thread;
err_thread:
vmm_free_region(trusty_app->aspace, stack_bot);
err_stack:
free(trusty_thread);
return NULL;
}
static status_t load_app_config_options(trusty_app_t *trusty_app, Elf32_Shdr *shdr)
{
char *manifest_data;
u_int *config_blob, config_blob_size;
u_int i;
/* have to at least have a valid UUID */
if (shdr->sh_size < sizeof(uuid_t)) {
dprintf(CRITICAL, "app %u manifest too small %u\n", trusty_app->app_id,
shdr->sh_size);
return ERR_NOT_VALID;
}
/* init default config options before parsing manifest */
trusty_app->props.min_heap_size = DEFAULT_HEAP_SIZE;
trusty_app->props.min_stack_size = DEFAULT_STACK_SIZE;
trusty_app->props.mgmt_flags = DEFAULT_MGMT_FLAGS;
manifest_data = (char *)(trusty_app->app_img->img_start + shdr->sh_offset);
if (!address_range_within_img(manifest_data, shdr->sh_size,
trusty_app->app_img)) {
dprintf(CRITICAL, "app %u manifest data out of bounds\n",
trusty_app->app_id);
return ERR_NOT_VALID;
}
memcpy(&trusty_app->props.uuid, (uuid_t *)manifest_data, sizeof(uuid_t));
PRINT_TRUSTY_APP_UUID(trusty_app->app_id, &trusty_app->props.uuid);
manifest_data += sizeof(trusty_app->props.uuid);
config_blob = (u_int *)manifest_data;
config_blob_size = (shdr->sh_size - sizeof(uuid_t));
trusty_app->props.config_entry_cnt = config_blob_size / sizeof (u_int);
/* if no config options we're done */
if (trusty_app->props.config_entry_cnt == 0) {
return NO_ERROR;
}
/* save off configuration blob start so it can be accessed later */
trusty_app->props.config_blob = config_blob;
/*
* Step thru configuration blob.
*
* Save off some configuration data while we are here but
* defer processing of other data until it is needed later.
*/
for (i = 0; i < trusty_app->props.config_entry_cnt; i++) {
switch (config_blob[i]) {
case TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE:
/* MIN_STACK_SIZE takes 1 data value */
if ((trusty_app->props.config_entry_cnt - i) < 2) {
dprintf(CRITICAL, "app %u manifest missing MIN_STACK_SIZE value\n",
trusty_app->app_id);
return ERR_NOT_VALID;
}
trusty_app->props.min_stack_size = ROUNDUP(config_blob[++i], 4096);
if (trusty_app->props.min_stack_size == 0) {
dprintf(CRITICAL, "app %u manifest MIN_STACK_SIZE is 0\n",
trusty_app->app_id);
return ERR_NOT_VALID;
}
break;
case TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE:
/* MIN_HEAP_SIZE takes 1 data value */
if ((trusty_app->props.config_entry_cnt - i) < 2) {
dprintf(CRITICAL, "app %u manifest missing MIN_HEAP_SIZE value\n",
trusty_app->app_id);
return ERR_NOT_VALID;
}
trusty_app->props.min_heap_size = config_blob[++i];
break;
case TRUSTY_APP_CONFIG_KEY_MAP_MEM:
/* MAP_MEM takes 3 data values */
if ((trusty_app->props.config_entry_cnt - i) < 4) {
dprintf(CRITICAL, "app %u manifest missing MAP_MEM value\n",
trusty_app->app_id);
return ERR_NOT_VALID;
}
trusty_app->props.map_io_mem_cnt++;
i += 3;
break;
case TRUSTY_APP_CONFIG_KEY_MGMT_FLAGS:
/* MGMT_FLAGS takes 1 data value */
if (trusty_app->props.config_entry_cnt - i < 2) {
dprintf(CRITICAL, "app %u manifest missing MGMT_FLAGS value\n",
trusty_app->app_id);
return ERR_NOT_VALID;
}
trusty_app->props.mgmt_flags = config_blob[++i];
break;
default:
dprintf(CRITICAL, "app %u manifest contains unknown config key %u\n",
trusty_app->app_id, config_blob[i]);
return ERR_NOT_VALID;
}
}
LTRACEF("trusty_app %p: stack_sz=0x%x\n", trusty_app,
trusty_app->props.min_stack_size);
LTRACEF("trusty_app %p: heap_sz=0x%x\n", trusty_app,
trusty_app->props.min_heap_size);
LTRACEF("trusty_app %p: num_io_mem=%d\n", trusty_app,
trusty_app->props.map_io_mem_cnt);
return NO_ERROR;
}
static status_t init_brk(trusty_app_t *trusty_app, vaddr_t hint)
{
status_t status;
uint arch_mmu_flags;
vaddr_t start_brk;
vaddr_t hint_page_end;
size_t remaining;
status = arch_mmu_query(&trusty_app->aspace->arch_aspace, hint, NULL,
&arch_mmu_flags);
if(status != NO_ERROR) {
dprintf(CRITICAL, "app %u heap hint page is not mapped %u\n",
trusty_app->app_id, status);
return ERR_NOT_VALID;
}
hint_page_end = ROUNDUP(hint, PAGE_SIZE);
if (!(arch_mmu_flags & ARCH_MMU_FLAG_PERM_RO)) {
start_brk = ROUNDUP(hint, CACHE_LINE);
remaining = hint_page_end - start_brk;
} else {
start_brk = ROUNDUP(hint, PAGE_SIZE);
remaining = 0;
}
if (remaining < trusty_app->props.min_heap_size) {
status = vmm_alloc(trusty_app->aspace, "heap",
trusty_app->props.min_heap_size - remaining,
(void**)&hint_page_end,
PAGE_SIZE_SHIFT, VMM_FLAG_VALLOC_SPECIFIC,
ARCH_MMU_FLAG_PERM_USER |
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
if (status != NO_ERROR) {
dprintf(CRITICAL, "failed(%d) to create heap(0x%lx) for app %u\n",
status, hint_page_end, trusty_app->app_id);
return ERR_NO_MEMORY;
}
ASSERT(hint_page_end == ROUNDUP(hint, PAGE_SIZE));
}
trusty_app->start_brk = start_brk;
trusty_app->cur_brk = trusty_app->start_brk;
trusty_app->end_brk = trusty_app->start_brk +
trusty_app->props.min_heap_size;
return NO_ERROR;
}
static status_t alloc_address_map(trusty_app_t *trusty_app)
{
Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *)trusty_app->app_img->img_start;
void *trusty_app_image;
Elf32_Phdr *prg_hdr;
u_int i;
status_t ret;
vaddr_t start_code = ~0;
vaddr_t start_data = 0;
vaddr_t end_code = 0;
vaddr_t end_data = 0;
vaddr_t last_mem = 0;
trusty_app_image = (void *)trusty_app->app_img->img_start;
prg_hdr = (Elf32_Phdr *)(trusty_app_image + elf_hdr->e_phoff);
if (!address_range_within_img(prg_hdr,
sizeof(Elf32_Phdr) * elf_hdr->e_phnum,
trusty_app->app_img)) {
dprintf(CRITICAL, "ELF program headers table out of bounds\n");
return ERR_NOT_VALID;
}
/* create mappings for PT_LOAD sections */
for (i = 0; i < elf_hdr->e_phnum; i++, prg_hdr++) {
vaddr_t first, last;
LTRACEF("trusty_app %d: ELF type 0x%x, vaddr 0x%08x, paddr 0x%08x"
" rsize 0x%08x, msize 0x%08x, flags 0x%08x\n",
trusty_app->app_id, prg_hdr->p_type, prg_hdr->p_vaddr,
prg_hdr->p_paddr, prg_hdr->p_filesz, prg_hdr->p_memsz,
prg_hdr->p_flags);
if (prg_hdr->p_type != PT_LOAD)
continue;
/* skip PT_LOAD if it's below trusty_app start or above .bss */
if ((prg_hdr->p_vaddr < TRUSTY_APP_START_ADDR) ||
(prg_hdr->p_vaddr >= trusty_app->end_bss))
continue;
/* check for overlap into user stack range */
vaddr_t stack_bot = TRUSTY_APP_STACK_TOP -
trusty_app->props.min_stack_size;
if (stack_bot < prg_hdr->p_vaddr + prg_hdr->p_memsz) {
dprintf(CRITICAL,
"failed to load trusty_app: (overlaps user stack 0x%lx)\n",
stack_bot);
return ERR_TOO_BIG;
}
vaddr_t vaddr = prg_hdr->p_vaddr;
vaddr_t img_kvaddr = (vaddr_t)(trusty_app_image + prg_hdr->p_offset);
size_t mapping_size;
if (vaddr & PAGE_MASK) {
dprintf(CRITICAL, "app %u segment %u load address 0x%lx in not page aligned\n",
trusty_app->app_id, i, vaddr);
return ERR_NOT_VALID;
}
if (img_kvaddr & PAGE_MASK) {
dprintf(CRITICAL, "app %u segment %u image address 0x%lx in not page aligned\n",
trusty_app->app_id, i, img_kvaddr);
return ERR_NOT_VALID;
}
uint arch_mmu_flags = ARCH_MMU_FLAG_PERM_USER;
if (!(prg_hdr->p_flags & PF_X)) {
arch_mmu_flags += ARCH_MMU_FLAG_PERM_NO_EXECUTE;
}
if (prg_hdr->p_flags & PF_W) {
paddr_t upaddr;
void *load_kvaddr;
size_t copy_size;
size_t file_size;
mapping_size = ROUNDUP(prg_hdr->p_memsz, PAGE_SIZE);
if (!address_range_within_img((void *)img_kvaddr, prg_hdr->p_filesz,
trusty_app->app_img)) {
dprintf(CRITICAL, "ELF Program segment %u out of bounds\n", i);
return ERR_NOT_VALID;
}
ret = vmm_alloc(trusty_app->aspace, "elfseg", mapping_size,
(void **)&vaddr, PAGE_SIZE_SHIFT,
VMM_FLAG_VALLOC_SPECIFIC,
arch_mmu_flags);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed(%d) to allocate data segment(0x%lx) %u for app %u\n",
ret, vaddr, i, trusty_app->app_id);
return ret;
}
ASSERT(vaddr == prg_hdr->p_vaddr);
file_size = prg_hdr->p_filesz;
while (file_size > 0) {
ret = arch_mmu_query(&trusty_app->aspace->arch_aspace, vaddr,
&upaddr, NULL);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "Could not copy data segment: %d\n", ret);
return ret;
}
load_kvaddr = paddr_to_kvaddr(upaddr);
ASSERT(load_kvaddr);
copy_size = MIN(file_size,PAGE_SIZE);
memcpy(load_kvaddr, (void *)img_kvaddr, copy_size);
file_size -= copy_size;
vaddr += copy_size;
img_kvaddr += copy_size;
}
} else {
mapping_size = ROUNDUP(prg_hdr->p_filesz, PAGE_SIZE);
if (!address_range_within_img((void *)img_kvaddr, mapping_size,
trusty_app->app_img)) {
dprintf(CRITICAL, "ELF Program segment %u out of bounds\n", i);
return ERR_NOT_VALID;
}
paddr_t paddr = vaddr_to_paddr((void *)img_kvaddr);
ASSERT(paddr && !(paddr & PAGE_MASK));
arch_mmu_flags += ARCH_MMU_FLAG_PERM_RO;
ret = vmm_alloc_physical(trusty_app->aspace,
"elfseg", mapping_size, (void **)&vaddr,
PAGE_SIZE_SHIFT, paddr,
VMM_FLAG_VALLOC_SPECIFIC,
arch_mmu_flags);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed(%d) to map RO segment(0x%lx) %u for app %u\n",
ret, vaddr, i, trusty_app->app_id);
return ret;
}
ASSERT(vaddr == prg_hdr->p_vaddr);
}
LTRACEF("trusty_app %d: load vaddr 0x%08lx, paddr 0x%08lx,"
" rsize 0x%08zx, msize 0x%08x, access r%c%c,"
" flags 0x%x\n",
trusty_app->app_id, vaddr, vaddr_to_paddr((void *)vaddr),
mapping_size, prg_hdr->p_memsz,
arch_mmu_flags & ARCH_MMU_FLAG_PERM_RO ? '-' : 'w',
arch_mmu_flags & ARCH_MMU_FLAG_PERM_NO_EXECUTE ? '-' : 'x',
arch_mmu_flags);
/* start of code/data */
first = prg_hdr->p_vaddr;
if (first < start_code)
start_code = first;
if (start_data < first)
start_data = first;
/* end of code/data */
last = prg_hdr->p_vaddr + prg_hdr->p_filesz;
if ((prg_hdr->p_flags & PF_X) && end_code < last)
end_code = last;
if (end_data < last)
end_data = last;
/* hint for start of brk */
last_mem = MAX(last_mem, prg_hdr->p_vaddr + prg_hdr->p_memsz);
}
ret = init_brk(trusty_app, last_mem);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed to load trusty_app: trusty_app heap creation error\n");
return ret;
}
dprintf(SPEW, "trusty_app %d: code: start 0x%08lx end 0x%08lx\n",
trusty_app->app_id, start_code, end_code);
dprintf(SPEW, "trusty_app %d: data: start 0x%08lx end 0x%08lx\n",
trusty_app->app_id, start_data, end_data);
dprintf(SPEW, "trusty_app %d: bss: end 0x%08lx\n",
trusty_app->app_id, trusty_app->end_bss);
dprintf(SPEW, "trusty_app %d: brk: start 0x%08lx end 0x%08lx\n",
trusty_app->app_id, trusty_app->start_brk, trusty_app->end_brk);
dprintf(SPEW, "trusty_app %d: entry 0x%08x\n", trusty_app->app_id,
elf_hdr->e_entry);
return NO_ERROR;
}
/*
* Create a trusty_app from its memory image and add it to the global list of
* apps
*/
static status_t trusty_app_create(struct trusty_app_img *app_img)
{
Elf32_Ehdr *ehdr;
Elf32_Shdr *shdr;
Elf32_Shdr *bss_shdr, *manifest_shdr;
char *shstbl;
uint32_t shstbl_size;
trusty_app_t *trusty_app;
u_int i;
status_t ret;
if (app_img->img_start & PAGE_MASK || app_img->img_end & PAGE_MASK) {
dprintf(CRITICAL, "app image is not page aligned start 0x%lx end 0x%lx\n",
app_img->img_start, app_img->img_end);
return ERR_NOT_VALID;
}
dprintf(SPEW, "trusty_app: start %p size 0x%08lx end %p\n",
(void *)app_img->img_start,
app_img->img_end - app_img->img_start,
(void *)app_img->img_end);
trusty_app = (trusty_app_t *) calloc(1, sizeof(trusty_app_t));
if (!trusty_app) {
dprintf(CRITICAL, "trusty_app: failed to allocate memory for trusty app\n");
return ERR_NO_MEMORY;
}
ehdr = (Elf32_Ehdr *)app_img->img_start;
if (!address_range_within_img(ehdr, sizeof(Elf32_Ehdr), app_img)) {
dprintf(CRITICAL, "trusty_app_create: ELF header out of bounds\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
if (strncmp((char *)ehdr->e_ident, ELFMAG, SELFMAG)) {
dprintf(CRITICAL, "trusty_app_create: ELF header not found\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
shdr = (Elf32_Shdr *) ((intptr_t)ehdr + ehdr->e_shoff);
if (!address_range_within_img(shdr, sizeof(Elf32_Shdr) * ehdr->e_shnum,
app_img)) {
dprintf(CRITICAL, "trusty_app_create: ELF section headers out of bounds\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
if (ehdr->e_shstrndx >= ehdr->e_shnum) {
dprintf(CRITICAL, "trusty_app_create: ELF names table section header out of bounds\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
shstbl = (char *)((intptr_t)ehdr + shdr[ehdr->e_shstrndx].sh_offset);
shstbl_size = shdr[ehdr->e_shstrndx].sh_size;
if (!address_range_within_img(shstbl, shstbl_size, app_img)) {
dprintf(CRITICAL, "trusty_app_create: ELF section names out of bounds\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
bss_shdr = manifest_shdr = NULL;
for (i = 0; i < ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_NULL)
continue;
LTRACEF("trusty_app: sect %d, off 0x%08x, size 0x%08x, flags 0x%02x, name %s\n",
i, shdr[i].sh_offset, shdr[i].sh_size, shdr[i].sh_flags,
shstbl + shdr[i].sh_name);
/* track bss and manifest sections */
if (compare_section_name(shdr + i, ".bss", shstbl, shstbl_size)) {
bss_shdr = shdr + i;
trusty_app->end_bss = bss_shdr->sh_addr + bss_shdr->sh_size;
}
else if (compare_section_name(shdr + i, ".trusty_app.manifest",
shstbl, shstbl_size)) {
manifest_shdr = shdr + i;
}
}
/* we need these sections */
if (!bss_shdr) {
dprintf(CRITICAL, "bss section header not found\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
if (!manifest_shdr) {
dprintf(CRITICAL, "manifest section header not found\n");
ret = ERR_NOT_VALID;
goto err_hdr;
}
trusty_app->app_id = trusty_next_app_id++;
trusty_app->app_img = app_img;
trusty_app->state = APP_NOT_RUNNING;
ret = load_app_config_options(trusty_app, manifest_shdr);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "manifest processing failed\n");
goto err_load;
}
list_add_tail(&trusty_app_list, &trusty_app->node);
return NO_ERROR;
err_load:
trusty_next_app_id--;
err_hdr:
free(trusty_app);
return ret;
}
status_t trusty_app_setup_mmio(trusty_app_t *trusty_app, u_int mmio_id,
vaddr_t *vaddr, uint32_t map_size)
{
status_t ret;
u_int i;
u_int id, offset, size;
/* step thru configuration blob looking for I/O mapping requests */
for (i = 0; i < trusty_app->props.config_entry_cnt; i++) {
if (trusty_app->props.config_blob[i] == TRUSTY_APP_CONFIG_KEY_MAP_MEM) {
id = trusty_app->props.config_blob[++i];
offset = trusty_app->props.config_blob[++i];
size = ROUNDUP(trusty_app->props.config_blob[++i],
PAGE_SIZE);
if (id != mmio_id)
continue;
map_size = ROUNDUP(map_size, PAGE_SIZE);
if (map_size > size)
return ERR_INVALID_ARGS;
ret = vmm_alloc_physical(trusty_app->aspace, "mmio",
map_size, (void **)vaddr,
PAGE_SIZE_SHIFT, offset,
0,
ARCH_MMU_FLAG_UNCACHED_DEVICE |
ARCH_MMU_FLAG_PERM_USER);
dprintf(SPEW, "mmio: vaddr 0x%lx, paddr 0x%x, ret %d\n",
*vaddr, offset, ret);
return ret;
} else {
/* all other config options take 1 data value */
i++;
}
}
return ERR_NOT_FOUND;
}
static status_t trusty_app_start(trusty_app_t *trusty_app)
{
char name[32];
struct trusty_thread *trusty_thread;
struct trusty_app_notifier *n;
Elf32_Ehdr *elf_hdr;
int ret;
DEBUG_ASSERT(trusty_app->state == APP_STARTING);
snprintf(name, sizeof(name), "trusty_app_%d_%08x-%04x-%04x",
trusty_app->app_id,
trusty_app->props.uuid.time_low,
trusty_app->props.uuid.time_mid,
trusty_app->props.uuid.time_hi_and_version);
ret = vmm_create_aspace(&trusty_app->aspace, name, 0);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "Failed(%d) to allocate address space for %s\n", ret,
name);
goto err_aspace;
}
ret = alloc_address_map(trusty_app);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed(%d) to load address map for %s\n", ret, name);
goto err_map;
}
/* attach als_cnt */
trusty_app->als = calloc(1, als_slot_cnt * sizeof(void*));
if (!trusty_app->als) {
dprintf(CRITICAL, "failed to allocate local storage for %s\n", name );
ret = ERR_NO_MEMORY;
/* alloc_address_map gets cleaned up by destroying the address space */
goto err_alloc;
}
/* call all registered startup notifiers */
list_for_every_entry(&app_notifier_list, n, struct trusty_app_notifier,
node) {
if (!n->startup)
continue;
ret = n->startup(trusty_app);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed(%d) to invoke startup notifier for %s\n",
ret, name);
goto err_notifier;
}
}
elf_hdr = (Elf32_Ehdr *)trusty_app->app_img->img_start;
trusty_thread = trusty_thread_create(name, elf_hdr->e_entry,
DEFAULT_PRIORITY,
TRUSTY_APP_STACK_TOP,
trusty_app->props.min_stack_size,
trusty_app);
if (!trusty_thread) {
dprintf(CRITICAL, "failed to allocate trusty thread for %s\n", name);
ret = ERR_NO_MEMORY;
goto err_thread;
}
trusty_app->thread = trusty_thread;
trusty_app->state = APP_RUNNING;
ret = trusty_thread_start(trusty_app->thread);
ASSERT(ret == NO_ERROR);
return ret;
err_thread:
err_notifier:
for (n = list_prev_type(&app_notifier_list, &n->node,
struct trusty_app_notifier, node);
n != NULL;
n = list_prev_type(&app_notifier_list, &n->node,
struct trusty_app_notifier, node)) {
if (!n->shutdown)
continue;
if (n->shutdown(trusty_app) != NO_ERROR)
panic("failed to invoke shutdown notifier for %s\n",
name);
}
free(trusty_app->als);
err_alloc:
err_map:
vmm_free_aspace(trusty_app->aspace);
err_aspace:
return ret;
}
void trusty_app_exit(int status)
{
status_t ret;
struct trusty_app *app;
struct trusty_app_notifier *notifier;
app = current_trusty_app();
DEBUG_ASSERT(app->state == APP_RUNNING);
LTRACEF("app %u exiting...\n", app->app_id);
app->state = APP_TERMINATING;
list_for_every_entry(&app_notifier_list, notifier,
struct trusty_app_notifier, node) {
if(!notifier->shutdown)
continue;
ret = notifier->shutdown(app);
if (ret != NO_ERROR)
panic("shutdown notifier for app %u failed(%d)\n",
app->app_id, ret);
}
free(app->als);
event_signal(&app_mgr_event, false);
trusty_thread_exit(status);
}
static status_t app_mgr_handle_starting(struct trusty_app *app)
{
status_t ret;
DEBUG_ASSERT(app->state == APP_STARTING);
LTRACEF("starting app %u\n", app->app_id);
ret = trusty_app_start(app);
if (ret != NO_ERROR)
app->state = APP_NOT_RUNNING;
return ret;
}
static status_t app_mgr_handle_terminating(struct trusty_app *app)
{
status_t ret;
int retcode;
DEBUG_ASSERT(app->state == APP_TERMINATING);
LTRACEF("waiting for app %u to exit \n", app->app_id);
ret = thread_join(app->thread->thread, &retcode, INFINITE_TIME);
ASSERT(ret == NO_ERROR);
free(app->thread);
ret = vmm_free_aspace(app->aspace);
if (app->props.mgmt_flags & TRUSTY_APP_MGMT_FLAGS_RESTART_ON_EXIT) {
app->state = APP_STARTING;
event_signal(&app_mgr_event, false);
}
else {
app->state = APP_NOT_RUNNING;
}
return ret;
}
static int app_mgr(void *arg)
{
status_t ret;
struct trusty_app *app;
while (true) {
LTRACEF("app manager waiting for events\n");
event_wait(&app_mgr_event);
list_for_every_entry(&trusty_app_list, app, struct trusty_app, node) {
switch (app->state) {
case APP_TERMINATING:
ret = app_mgr_handle_terminating(app);
if (ret != NO_ERROR)
panic("failed(%d) to terminate app %u\n", ret, app->app_id);
break;
case APP_NOT_RUNNING:
break;
case APP_STARTING:
ret = app_mgr_handle_starting(app);
if (ret != NO_ERROR)
panic("failed(%d) to start app %u\n", ret, app->app_id);
break;
case APP_RUNNING:
break;
default:
panic("app %u in unknown state %u\n", app->app_id,
app->state);
}
}
}
}
static void app_mgr_init(void)
{
status_t err;
thread_t *app_mgr_thread;
LTRACEF("Creating app manager thread\n");
app_mgr_thread = thread_create("app manager", &app_mgr, NULL,
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
if (!app_mgr_thread)
panic("Failed to create app manager thread\n");
err = thread_resume(app_mgr_thread);
if (err != NO_ERROR)
panic("Failed to start app manager thread\n");
}
static status_t trusty_app_request_start(struct trusty_app *app)
{
DEBUG_ASSERT(app->state == APP_NOT_RUNNING);
app->state = APP_STARTING;
return event_signal(&app_mgr_event, false);
}
void trusty_app_init(void)
{
struct trusty_app_img *app_img;
finalize_registration();
app_mgr_init();
for (app_img = __trusty_app_list_start;
app_img != __trusty_app_list_end; app_img++) {
if (trusty_app_create(app_img) != NO_ERROR)
panic("Failed to create builtin apps\n");
}
}
trusty_app_t *trusty_app_find_by_uuid(uuid_t *uuid)
{
trusty_app_t *ta;
/* find app for this uuid */
list_for_every_entry(&trusty_app_list, ta, trusty_app_t, node)
if (!memcmp(&ta->props.uuid, uuid, sizeof(uuid_t)))
return ta;
return NULL;
}
/* rather export trusty_app_list? */
void trusty_app_forall(void (*fn)(trusty_app_t *ta, void *data), void *data)
{
trusty_app_t *ta;
if (fn == NULL)
return;
list_for_every_entry(&trusty_app_list, ta, trusty_app_t, node)
fn(ta, data);
}
static void start_apps(uint level)
{
trusty_app_t *trusty_app;
int ret;
list_for_every_entry(&trusty_app_list, trusty_app, trusty_app_t, node) {
ret = trusty_app_request_start(trusty_app);
if (ret != NO_ERROR)
panic("Cannot start(%d) Trusty app %u!\n", ret, trusty_app->app_id);
}
}
LK_INIT_HOOK(libtrusty_apps, start_apps, LK_INIT_LEVEL_APPS + 1);