blob: 89589df1581e7e37d1c89f69dc209aebe38ea726 [file] [log] [blame]
/*
* Copyright (c) 2012-2015, NVIDIA CORPORATION. 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 <debug.h>
#include <sys/types.h>
#include <compiler.h>
#include <assert.h>
#include <string.h>
#include <malloc.h>
#include <err.h>
#include <stdlib.h>
#include <arch.h>
#include <arch/arm.h>
#include <arch/arm/mmu.h>
#include <kernel/task.h>
#include <kernel/thread.h>
#include <kernel/elf.h>
#include <platform.h>
#include <platform/platform_p.h>
#include <kernel/task_load.h>
#include <lib/ote/ote_protocol.h>
/*! page aligned area for storing static task headers before heap is initialized */
#define TASK_LIST_CARVEOUT_PAGES 1
/*! max number of tasks embedded in the TLK task image */
#define MAX_STATIC_TASK_COUNT ((TASK_LIST_CARVEOUT_PAGES * PAGE_SIZE) / sizeof(task_t))
/* task list and used index */
static u_int task_next_index; /* next task index [ 0..N ] */
static task_t *task_blist; /* boot time fixed size task list */
/* task_blist is converted to task_list after heap is initialized */
static struct list_node task_list;
static char *task_image_start;
static char *task_image_end;
static u_int task_image_size;
extern u_int __tasks_start;
extern u_int __tasks_end;
extern int _end; /* end of binary &_end (heap starts after this) */
extern int _heap_end; /* heap ends here, adjusted by carve-outs below */
/* memory carved off from the top (before heap_init) */
#define carveout_taskmem _heap_end
void task_print_uuid(uint32_t level, const te_service_id_t *uuid)
{
if (uuid) {
dprintf(level, "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
uuid->time_low,
uuid->time_mid,
uuid->time_hi_and_version,
uuid->clock_seq_and_node[0], /* clock_seq_hi_and_reserved */
uuid->clock_seq_and_node[1], /* clock_seq_low */
uuid->clock_seq_and_node[2],
uuid->clock_seq_and_node[3],
uuid->clock_seq_and_node[4],
uuid->clock_seq_and_node[5],
uuid->clock_seq_and_node[6],
uuid->clock_seq_and_node[7]);
}
}
static status_t task_load_config_options(u_int task_image_addr, task_t *taskp, Elf32_Shdr *shdr)
{
status_t err = NO_ERROR;
OTE_MANIFEST *manifest;
u_int *config_blob, config_blob_size;
u_int i;
te_service_id_t null_uuid = NULL_UUID;
if (shdr->sh_size < offsetof(OTE_MANIFEST, config_options)) {
err = ERR_NOT_VALID;
goto exit;
}
/* init default config options before parsing manifest */
taskp->props.min_heap_size = 5 * PAGE_SIZE;
taskp->props.min_stack_size = DEFAULT_STACK_SIZE;
manifest = (OTE_MANIFEST *)(task_image_addr + shdr->sh_offset);
/*
* Informative name field may be zero filled and only non-zero names are used.
* Task loading may also override this field value.
*/
memcpy(&taskp->task_name[0], &manifest->name[0], sizeof(taskp->task_name));
taskp->task_name[sizeof(taskp->task_name) - 1] = '\000';
/*
* Copy TA specific config data (optional field, define semantics per task).
* E.g. could hold SHA1 digest of something you wish to load to the task
* at runtime.
*/
memcpy(&taskp->task_private_data[0], &manifest->private_data[0],
sizeof(taskp->task_private_data));
/* reject all tasks with NULL UUID */
if (!memcmp(&manifest->uuid, &null_uuid, sizeof(te_service_id_t))) {
err = ERR_NOT_VALID;
goto exit;
}
memcpy(&taskp->props.uuid, &manifest->uuid, sizeof(te_service_id_t));
task_print_id(SPEW, "task load uuid = ", taskp);
config_blob = (u_int *)((char *)manifest + offsetof(OTE_MANIFEST, config_options));
config_blob_size = (shdr->sh_size - offsetof(OTE_MANIFEST, config_options));
taskp->props.config_entry_cnt = config_blob_size / sizeof(u_int);
/* if no config options we're done */
if (taskp->props.config_entry_cnt != 0) {
/* save off configuration blob start so it can be accessed later */
taskp->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 < taskp->props.config_entry_cnt; i++) {
switch (config_blob[i]) {
case OTE_CONFIG_KEY_MIN_STACK_SIZE:
/* MIN_STACK_SIZE takes 1 data value */
if ((taskp->props.config_entry_cnt - i) <= 1) {
err = ERR_NOT_VALID;
goto exit;
}
taskp->props.min_stack_size =
ROUNDUP(config_blob[++i], 4096);
if (taskp->props.min_stack_size <= 0) {
err = ERR_NOT_VALID;
goto exit;
}
break;
case OTE_CONFIG_KEY_MIN_HEAP_SIZE:
/* MIN_HEAP_SIZE takes 1 data value */
if ((taskp->props.config_entry_cnt - i) <= 1) {
err = ERR_NOT_VALID;
goto exit;
}
taskp->props.min_heap_size =
ROUNDUP(config_blob[++i], 4096);
if (taskp->props.min_heap_size <= 0) {
err = ERR_NOT_VALID;
goto exit;
}
break;
case OTE_CONFIG_KEY_MAP_MEM:
/* MAP_MEM takes 3 data values */
if ((taskp->props.config_entry_cnt - i) <= 3) {
err = ERR_NOT_VALID;
goto exit;
}
taskp->props.map_io_mem_cnt++;
i += 3;
break;
case OTE_CONFIG_KEY_RESTRICT_ACCESS:
/* Set clients who are restricted access. */
if ((taskp->props.config_entry_cnt - i) <= 1) {
err = ERR_NOT_VALID;
goto exit;
}
taskp->props.restrict_access = config_blob[++i];
break;
case OTE_CONFIG_KEY_AUTHORIZE:
/* tasks which are authorized to perform restricted operations */
if ((taskp->props.config_entry_cnt - i) <= 1) {
err = ERR_NOT_VALID;
goto exit;
}
taskp->props.authorizations = config_blob[++i];
break;
case OTE_CONFIG_KEY_TASK_ISTATE:
/* task initial state attributes */
if ((taskp->props.config_entry_cnt - i) <= 1) {
err = ERR_NOT_VALID;
goto exit;
}
taskp->props.initial_state = config_blob[++i];
break;
default:
dprintf(CRITICAL,
"%s: unknown OTE_CONFIG_KEY_VALUE: %d\n",
__func__, config_blob[i]);
err = ERR_NOT_VALID;
goto exit;
}
}
}
if (0) {
exit:
if (err == NO_ERROR) {
err = ERR_NOT_VALID;
}
}
return err;
}
static void task_setup_mmio(task_t *taskp)
{
u_int i;
u_int id, offset, size;
/* step thru configuration blob looking for I/O mapping requests */
for (i = 0; i < taskp->props.config_entry_cnt; i++) {
if (taskp->props.config_blob[i] == OTE_CONFIG_KEY_MAP_MEM) {
/* found one; setup mapping to io range */
id = taskp->props.config_blob[++i];
offset = taskp->props.config_blob[++i];
size = taskp->props.config_blob[++i];
/* check mapping ID is non-zero and unique */
ASSERT(id);
ASSERT(!task_find_mapping_by_id(taskp, id));
arch_task_setup_mmio(taskp, id, offset, size);
} else {
/* all other config options take 1 data value */
i++;
}
}
}
static void task_set_valloc_start(task_t *taskp)
{
struct list_node *node;
task_map_t *mptr;
/*
* Dynamic allocs start after the static alloc preceding the
* stack (expected to be called before dynamic allocs begin).
*/
node = &taskp->stack_map->node;
taskp->valloc_list = list_prev(node, node);
mptr = containerof(taskp->valloc_list, task_map_t, node);
taskp->valloc_start = mptr->vaddr + mptr->size;
taskp->valloc_end = taskp->stack_map->vaddr;
}
addr_t task_find_address_space(task_t *taskp, u_int size, u_int align)
{
addr_t astart, aend;
task_map_t *mptr;
astart = ROUNDUP(taskp->valloc_start, align);
aend = astart + size;
/* find first fit */
list_for_every_entry(taskp->valloc_list, mptr, task_map_t, node) {
if (aend < mptr->vaddr)
break; /* fits before mptr alloc */
if (mptr->vaddr == taskp->valloc_end) {
/* hit end without finding space */
dprintf(CRITICAL, "failed to find task address space\n");
return 0;
}
astart = ROUNDUP((mptr->vaddr + mptr->size), align);
aend = astart + size;
}
ASSERT(!(astart & (align - 1)));
return astart;
}
void task_add_mapping(task_t *taskp, task_map_t *new_mptr)
{
task_map_t *mptr;
ASSERT(taskp);
ASSERT(new_mptr);
ASSERT(new_mptr->vaddr && new_mptr->size);
ASSERT(!new_mptr->id || (new_mptr->flags & TM_IO));
list_for_every_entry(&taskp->map_list, mptr, task_map_t, node) {
if (mptr->vaddr > new_mptr->vaddr) {
ASSERT((new_mptr->vaddr + new_mptr->size) <= mptr->vaddr);
list_add_before(&mptr->node, &new_mptr->node);
return;
}
}
list_add_tail(&taskp->map_list, &new_mptr->node);
}
void task_delete_mapping(task_t *taskp, task_map_t *mptr)
{
list_delete(&mptr->node);
}
task_map_t *task_find_mapping(task_t *taskp, addr_t vaddr, u_int size)
{
task_map_t *mptr;
list_for_every_entry(&taskp->map_list, mptr, task_map_t, node) {
if ((mptr->vaddr <= vaddr) &&
((mptr->vaddr + mptr->size) >= (vaddr + size))) {
return mptr;
}
}
dprintf(CRITICAL,
"task %d: vaddr 0x%08x for 0x%08x bytes not mapped\n",
taskp->task_index, (u_int)vaddr, size);
return NULL;
}
task_map_t *task_find_mapping_by_id(task_t *taskp, u_int id)
{
task_map_t *mptr;
list_for_every_entry(&taskp->map_list, mptr, task_map_t, node) {
if (mptr->id == id)
return mptr;
}
return NULL;
}
status_t task_get_physaddr(task_t *taskp, addr_t vaddr, paddr_t *paddr)
{
task_map_t *mptr;
mptr = task_find_mapping(taskp, vaddr, 0);
if (mptr == NULL)
return ERR_INVALID_ARGS;
if (mptr->flags & TM_PHYS_CONTIG) {
*paddr = mptr->u_phys.contig + (vaddr - mptr->vaddr);
} else {
uint32_t pageno = (vaddr - mptr->vaddr) / PAGE_SIZE;
*paddr = mptr->u_phys.pagelist[pageno] + (vaddr & PAGE_MASK);
}
return NO_ERROR;
}
bool task_valid_address(vaddr_t addr, u_int size)
{
task_t *taskp;
task_map_t *mptr;
taskp = current_thread->arch.task;
mptr = task_find_mapping(taskp, addr, size);
return !!mptr;
}
static status_t task_init_stack(task_t *taskp)
{
task_map_t *mptr;
mptr = calloc(1, sizeof(task_map_t));
if (mptr == NULL)
return ERR_NO_MEMORY;
mptr->size = taskp->props.min_stack_size;
mptr->u_phys.contig = (addr_t) memalign(PAGE_SIZE, mptr->size);
if (mptr->u_phys.contig == NULL) {
free(mptr);
return ERR_NO_MEMORY;
}
mptr->u_phys.contig = virtual_to_physical(mptr->u_phys.contig);
mptr->vaddr = TASK_STACK_ADDR - mptr->size;
mptr->flags = (TM_UW | TM_UR | TM_PHYS_CONTIG | TM_PHYS_ALLOCATED);
mptr->offset = 0;
mptr->map_attrs = NULL;
taskp->stack_map = mptr;
task_add_mapping(taskp, mptr);
dprintf(SPEW,
"task %d: stack vaddr 0x%08lx, paddr 0x%08llx, msize 0x%08x\n",
taskp->task_index, mptr->vaddr, (uint64_t)mptr->u_phys.contig, mptr->size);
return NO_ERROR;
}
static status_t task_init_brk(u_int task_image_addr, task_t *taskp, Elf32_Ehdr *ehdr)
{
task_map_t *mptr;
vaddr_t vbrk;
uint32_t brklen;
/* find mapping in which brk resides */
mptr = task_find_mapping(taskp, taskp->start_brk, 0);
if (mptr == NULL) {
dprintf(CRITICAL, "task failed to find brk in mappings\n");
halt();
}
/* what's leftover in the mapping goes to brk */
taskp->curr_brk = taskp->start_brk;
taskp->end_brk = taskp->start_brk +
((mptr->vaddr + mptr->size) - taskp->start_brk);
/* mmap expects MAP_ANONYMOUS to be zeros */
vbrk = physical_to_virtual(mptr->u_phys.contig) +
(taskp->start_brk - mptr->vaddr);
brklen = taskp->end_brk - taskp->curr_brk;
memset((void *)vbrk, 0, brklen);
platform_clean_invalidate_cache_range(vbrk, brklen);
/* increase user mode heap (if not enough remains) */
if ((taskp->end_brk - taskp->curr_brk) < taskp->props.min_heap_size) {
mptr = calloc(1, sizeof(task_map_t));
if (mptr == NULL)
return ERR_NO_MEMORY;
mptr->size = taskp->props.min_heap_size;
mptr->u_phys.contig = (addr_t) memalign(PAGE_SIZE, mptr->size);
if (mptr->u_phys.contig == NULL) {
free(mptr);
return ERR_NO_MEMORY;
}
/* mmap expects MAP_ANONYMOUS to be zeros */
memset((void *)(addr_t)mptr->u_phys.contig, 0, mptr->size);
/* mptr->paddr still virtual at this point */
platform_clean_invalidate_cache_range(mptr->u_phys.contig, mptr->size);
mptr->u_phys.contig = virtual_to_physical(mptr->u_phys.contig);
mptr->vaddr = taskp->end_brk;
mptr->flags = (TM_UW | TM_UR | TM_PHYS_CONTIG | TM_PHYS_ALLOCATED);
mptr->offset = 0;
mptr->map_attrs = NULL;
task_add_mapping(taskp, mptr);
taskp->end_brk += mptr->size;
}
dprintf(SPEW,
"task %d: brk vaddr 0x%08lx, msize 0x%08x\n",
taskp->task_index, taskp->start_brk,
(u_int)(taskp->end_brk - taskp->start_brk));
return NO_ERROR;
}
static status_t task_alloc_address_map(task_t *taskp)
{
Elf32_Ehdr *elf_hdr;
Elf32_Phdr *prg_hdr;
u_int i;
u_int task_image_addr;
task_map_t *mptr;
status_t ret;
elf_hdr = taskp->elf_hdr;
task_image_addr = (u_int)elf_hdr;
taskp->start_code = ~0;
/* alloc user stack */
ret = task_init_stack(taskp);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed to load task: stack creation error\n");
return ret;
}
/* create mappings for PT_LOAD sections */
for (i = 0; i < elf_hdr->e_phnum; i++) {
addr_t first, last, last_mem;
prg_hdr = (Elf32_Phdr *)((u_int)elf_hdr + elf_hdr->e_phoff +
(i * sizeof(Elf32_Phdr)));
if (prg_hdr->p_type != PT_LOAD)
continue;
/* skip PT_LOAD if it's below task start or above .bss */
if ((prg_hdr->p_vaddr < TASK_START_ADDR) ||
(prg_hdr->p_vaddr >= taskp->end_bss))
continue;
/*
* We're expecting to be able to execute the task in-place,
* meaning its PT_LOAD segments, should be page-aligned.
*/
if((prg_hdr->p_vaddr & PAGE_MASK) || (prg_hdr->p_offset & PAGE_MASK))
return ERR_TASK_GENERIC;
mptr = calloc(1, sizeof(task_map_t));
if (mptr == NULL)
return ERR_NO_MEMORY;
mptr->size = (prg_hdr->p_memsz + PAGE_MASK) & ~PAGE_MASK;
mptr->u_phys.contig = virtual_to_physical(task_image_addr) + prg_hdr->p_offset;
mptr->vaddr = prg_hdr->p_vaddr;
mptr->flags = (prg_hdr->p_flags & PF_FLAG_MASK) | TM_PHYS_CONTIG;
mptr->offset = 0;
mptr->map_attrs = NULL;
task_add_mapping(taskp, mptr);
/* check for overlap into user stack range */
if ((TASK_STACK_ADDR - taskp->stack_map->size) < (mptr->vaddr + mptr->size)) {
dprintf(CRITICAL,
"failed to load task: (overlaps user stack 0x%lx)\n",
TASK_STACK_ADDR - taskp->stack_map->size);
return ERR_TOO_BIG;
}
dprintf(SPEW,
"task %d: load vaddr 0x%08lx, paddr 0x%08llx"
" rsize 0x%08x, msize 0x%08x, flags 0x%08x\n",
taskp->task_index, mptr->vaddr, (uint64_t)mptr->u_phys.contig,
mptr->size, prg_hdr->p_memsz, mptr->flags);
/* start of code/data */
first = prg_hdr->p_vaddr;
if (first < taskp->start_code)
taskp->start_code = first;
if (taskp->start_data < first)
taskp->start_data = first;
/* end of code/data */
last = prg_hdr->p_vaddr + prg_hdr->p_filesz;
if ((prg_hdr->p_flags & PF_X) && taskp->end_code < last)
taskp->end_code = last;
if (taskp->end_data < last)
taskp->end_data = last;
/* end of brk */
last_mem = prg_hdr->p_vaddr + prg_hdr->p_memsz;
if (last_mem > taskp->start_brk)
taskp->start_brk = last_mem;
}
ret = task_init_brk(task_image_addr, taskp, elf_hdr);
if (ret != NO_ERROR) {
dprintf(CRITICAL, "failed to load task: task heap creation error\n");
return ret;
}
dprintf(SPEW, "task %d: code: start 0x%08lx end 0x%08lx\n",
taskp->task_index, taskp->start_code, taskp->end_code);
dprintf(SPEW, "task %d: data: start 0x%08lx end 0x%08lx\n",
taskp->task_index, taskp->start_data, taskp->end_data);
dprintf(SPEW, "task %d: bss: end 0x%08lx\n",
taskp->task_index, taskp->end_bss);
dprintf(SPEW, "task %d: brk: start 0x%08lx end 0x%08lx\n",
taskp->task_index, taskp->start_brk, taskp->end_brk);
taskp->entry = elf_hdr->e_entry;
dprintf(SPEW, "task %d: entry 0x%08lx\n", taskp->task_index, taskp->entry);
return NO_ERROR;
}
/*
* Align the next task to a page boundary, by copying what remains
* in the task image to the aligned next task start. This should be
* called after we're done with the section headers as the previous
* tasks .shstrtab section will be clobbered.
*
* Note: task_image_size remains the carved out part in LK to exit
* the bootloader loop, so still increment by max_extent. Because of
* the copy down to an aligned next task addr, task_image_size is
* more than what we're actually using.
*/
static char *task_align_next_task(Elf32_Ehdr *elf_hdr, Elf32_Shdr *pad_hdr)
{
char *next_task_align_start;
char *next_task_fsize_start;
char *task_image_addr;
u_int copy_size;
u_int max_extent;
ASSERT(pad_hdr);
ASSERT(elf_hdr);
task_image_addr = (char *)elf_hdr;
max_extent = (elf_hdr->e_shoff + (elf_hdr->e_shnum * elf_hdr->e_shentsize)) - 1;
ASSERT((task_image_addr + max_extent + 1) <= task_image_end);
next_task_align_start = task_image_addr + pad_hdr->sh_offset + pad_hdr->sh_size;
next_task_fsize_start = task_image_addr + max_extent + 1;
ASSERT(next_task_align_start <= next_task_fsize_start);
copy_size = task_image_end - next_task_fsize_start;
if (copy_size) {
/*
* Copy remaining image bytes to aligned start for the next
* (and subsequent) tasks. Also decrement task_image_end, so
* we copy less each time we realign for the next task.
*/
memcpy(next_task_align_start, next_task_fsize_start, copy_size);
platform_clean_invalidate_cache_range((addr_t)next_task_align_start,
copy_size);
task_image_end -= (next_task_fsize_start - next_task_align_start);
}
task_image_size -= (max_extent + 1);
return next_task_align_start;
}
status_t task_prepare(char *task_addr, u_int task_size, task_t *taskp,
Elf32_Shdr **bss_pad_shdr_p, task_type_t task_type)
{
status_t err = NO_ERROR;
Elf32_Ehdr *ehdr = NULL;
Elf32_Shdr *shdr = NULL;
Elf32_Shdr *bss_shdr = NULL;
Elf32_Shdr *bss_pad_shdr = NULL;
Elf32_Shdr *manifest_shdr = NULL;
char *shstbl = NULL;
vaddr_t bss_addr = 0;
u_int i = 0;
u_int task_max_extent = 0;
if (!task_addr || !taskp || task_size == 0) {
err = ERR_INVALID_ARGS;
goto exit;
}
/* For the preloaded tasks: the size includes this task and all
* other tasks that follow in the same image.
*/
dprintf(SPEW, "%s task: start %p size %d (0x%08x)\n",
__func__, task_addr, task_size, task_size);
ehdr = (Elf32_Ehdr *) task_addr;
if (strncmp((char *)ehdr->e_ident, ELFMAG, SELFMAG)) {
dprintf(CRITICAL, "%s: ELF header not found\n",
__func__);
err = ERR_NOT_VALID;
goto exit;
}
if (bss_pad_shdr_p)
*bss_pad_shdr_p = NULL;
shdr = (Elf32_Shdr *)((u_int)ehdr + ehdr->e_shoff);
shstbl = (char *)((u_int)ehdr + shdr[ehdr->e_shstrndx].sh_offset);
bss_shdr = bss_pad_shdr = manifest_shdr = NULL;
/* calculate task end */
for (i = 0; i < ehdr->e_shnum; i++) {
u_int extent;
if (shdr[i].sh_type == SHT_NULL)
continue;
#if 0
dprintf(CRITICAL, "task: sect %d, off 0x%08x, size 0x%08x, name %s\n",
i, shdr[i].sh_offset, shdr[i].sh_size, shstbl + shdr[i].sh_name);
#endif
/* track bss and manifest sections */
if (!strcmp((shstbl + shdr[i].sh_name), ".bss")) {
bss_shdr = shdr + i;
taskp->end_bss = bss_shdr->sh_addr + bss_shdr->sh_size;
}
else if (!strcmp((shstbl + shdr[i].sh_name), ".bss-pad")) {
bss_pad_shdr = shdr + i;
}
else if (!strcmp((shstbl + shdr[i].sh_name),
".ote.manifest")) {
manifest_shdr = shdr + i;
}
if (shdr[i].sh_type != SHT_NOBITS) {
extent = shdr[i].sh_offset + shdr[i].sh_size;
if (task_max_extent < extent)
task_max_extent = extent;
}
}
/*
* We need these sections.
* Manifest is handled later.
*/
if (!bss_shdr || !bss_pad_shdr) {
dprintf(CRITICAL, "%s: Invalid task image\n", __func__);
err = ERR_NOT_VALID;
goto exit;
}
if (bss_pad_shdr_p)
*bss_pad_shdr_p = bss_pad_shdr;
if ((bss_shdr->sh_offset + bss_shdr->sh_size) > task_max_extent) {
dprintf(CRITICAL, "%s: Invalid task image\n", __func__);
err = ERR_NOT_VALID;
goto exit;
}
if (ROUNDUP(task_max_extent, 4) != ehdr->e_shoff) {
dprintf(CRITICAL, "%s: Invalid task image\n", __func__);
err = ERR_NOT_VALID;
goto exit;
}
/* clear .bss */
bss_addr = (vaddr_t)(task_addr + bss_shdr->sh_offset);
memset((uint8_t *)bss_addr, 0, bss_shdr->sh_size);
platform_clean_invalidate_cache_range(bss_addr, bss_shdr->sh_size);
/* let the caller decide if it can handle binaries without manifest */
if (manifest_shdr == NULL) {
taskp->props.manifest_exists = 0;
} else {
taskp->props.manifest_exists = 1;
err = task_load_config_options((u_int)task_addr, taskp, manifest_shdr);
if (err != NO_ERROR) {
dprintf(CRITICAL, "Invalid task manifest: 0x%x\n", err);
goto exit;
}
}
taskp->elf_hdr = ehdr;
taskp->task_size = task_size;
taskp->task_type = task_type;
taskp->task_state = TASK_STATE_INIT;
if (0) {
exit:
if (err == NO_ERROR)
err = ERR_GENERIC;
}
return err;
}
/*
* Carveout memory for task headers.
* Called before heap_init.
*
* The task headers are converted to a list after the heap is initialized.
*/
static void task_mem_init()
{
if (task_image_size != 0) {
carveout_taskmem &= ~PAGE_MASK;
/* list of tasks (static and loaded) */
carveout_taskmem -= (TASK_LIST_CARVEOUT_PAGES * PAGE_SIZE);
task_blist = (task_t *)carveout_taskmem;
task_load_config((vaddr_t)&_end,
(vaddr_t *)&carveout_taskmem);
}
ASSERT(!(carveout_taskmem & PAGE_MASK));
}
/*
* Look in the kernel's ELF header for task sections and
* carveout memory for their LOAD-able sections. This is
* called before heap_init.
*
* This sets up the built-in tasks, they are started later with task_init.
*/
static void task_bootloader()
{
char *task_image_addr = NULL;
task_t *taskp = NULL;
status_t err = NO_ERROR;
dprintf(SPEW, "static tasks: start %p size 0x%08x end %p\n",
task_image_start, task_image_size, task_image_end);
task_image_addr = task_image_start;
task_mem_init();
memset(task_blist, 0, TASK_LIST_CARVEOUT_PAGES * PAGE_SIZE);
taskp = task_blist;
while (task_image_size > 0) {
u_int i = 0;
Elf32_Shdr *bss_pad_shdr = NULL;
if ((task_next_index + 1) > MAX_STATIC_TASK_COUNT) {
dprintf(CRITICAL, "%s: Too many (%d) tasks in image\n",
__func__, task_next_index+1);
halt();
}
err = task_prepare(task_image_addr, task_image_size,
taskp, &bss_pad_shdr, TASK_TYPE_STATIC);
/* statically loaded tasks must run or the system halts */
if (err != NO_ERROR) {
dprintf(CRITICAL, "%s: task#%u preparation failed (%d)\n",
__func__, task_next_index, err);
halt();
}
/* Because the size passed to task_prepare above can be larger than the
* actual task size in memory (it is larger unless this is the
* last task of the image) => fixup the task size here.
*
* BSS-PAD section is the last accepted PT_LOAD elf section of the secure task,
* so task actual memory size can be calculated as below.
*/
taskp->task_size = bss_pad_shdr->sh_offset + bss_pad_shdr->sh_size;
/* static tasks must contain a manifest section */
if (!taskp->props.manifest_exists) {
dprintf(CRITICAL, "%s: Invalid task#%u in image, no manifest\n",
__func__, task_next_index);
halt();
}
/*
* Make sure UUID doesn't already exist. Note that
* this search won't include the task we are processing
* here because task_next_index hasn't been incremented yet.
*
* task_find_task_by_uuid() can not yet be used.
*/
for (i = 0; i < task_next_index; i++) {
task_t *ts = &task_blist[i];
if (!memcmp(&ts->props.uuid, &taskp->props.uuid, sizeof(te_service_id_t))) {
dprintf(CRITICAL, "%s: task#%u duplicate UUID found!\n",
__func__, task_next_index);
halt();
}
}
/*
* The next tasks in the image are moved down to the next free
* page aligned address after the current task.
*/
task_image_addr = task_align_next_task(taskp->elf_hdr, bss_pad_shdr);
taskp->task_index = task_next_index++;
taskp++;
}
}
void task_early_init()
{
task_image_start = (char *)&__tasks_start;
task_image_end = (char *)&__tasks_end;
task_image_size = (task_image_end - task_image_start);
ASSERT(!((uint32_t)task_image_start & PAGE_MASK));
/*
* If there is no TAs that are loaded, we will then skip the
* task initialization phase
*/
if (task_image_size != 0)
task_bootloader();
}
status_t task_init_one_task(task_t *task)
{
status_t err = NO_ERROR;
char name[32];
thread_t *thread;
const char *state_str = "(unknown)";
if (!task || task->task_index >= task_next_index) {
err = ERR_INVALID_ARGS;
goto exit;
}
if (task->task_state != TASK_STATE_INIT) {
dprintf(CRITICAL, "%s: Task not startable in state %d\n",
__func__, task->task_state);
err = ERR_TASK_GENERIC;
goto exit;
}
list_initialize(&task->map_list);
list_initialize(&task->thread_node);
err = task_alloc_address_map(task);
if (err != NO_ERROR) {
dprintf(CRITICAL, "%s: failed to load address map\n",
__func__);
goto exit;
}
/* setup dynamic alloc range */
task_set_valloc_start(task);
/* reserve mmio va ranges here */
if (task->props.map_io_mem_cnt > 0) {
task_setup_mmio(task);
/* reset valloc start */
task_set_valloc_start(task);
}
/* force zero terminated task context derived thread names */
if (task->task_name[0] != '\000') {
snprintf(name, sizeof(name) - 1, "%s_%u_T0", task->task_name,
task->task_index);
} else {
snprintf(name, sizeof(name) - 1, "task_%u_T0", task->task_index);
}
name[sizeof(name) - 1] = '\000';
thread = thread_create(name, (thread_start_routine)(task->entry), 0, LOW_PRIORITY, 4096);
if (thread == NULL) {
dprintf(CRITICAL, "%s: allocate user thread failed\n",
__func__);
err = ERR_GENERIC;
goto exit;
}
task->task_state = TASK_STATE_STARTING;
if (arch_task_init(thread, task) == false) {
dprintf(CRITICAL, "%s: arch thread/task init failed\n",
__func__);
err = ERR_GENERIC;
goto exit;
}
if (task->props.initial_state & OTE_MANIFEST_TASK_ISTATE_BLOCKED) {
task->task_state = TASK_STATE_BLOCKED;
state_str = "blocked";
} else {
task->task_state = TASK_STATE_ACTIVE;
state_str = "active";
}
/* start it */
if (task->entry) {
dprintf(INFO, "starting %s task#%u%s\n", state_str, task->task_index,
task_get_name_str(task, " (", ")", name, sizeof(name)));
thread_resume(thread);
}
if (0) {
exit:
if (err == NO_ERROR)
err = ERR_GENERIC;
}
return err;
}
/*
* Start static tasks initialized by task_early_init()
*
* Convert the boot time task_blist into run-time task_list.
*/
void task_init()
{
status_t err = NO_ERROR;
task_t *task;
u_int i;
task_image_start = NULL;
task_image_end = NULL;
task_image_size = 0;
list_initialize(&task_list);
/*
* If there is no TAs that are loaded, we will then skip the
* task initialization phase
*/
if (task_next_index == 0)
return;
/* convert the boot time task_blist into a run-time task_list */
for (i = 0, task = task_blist; i < task_next_index; i++, task++) {
task_t *taskp = malloc(sizeof(task_t));
if (!taskp) {
dprintf(CRITICAL, "%s: out of memory -- halting\n", __func__);
halt();
}
memcpy(taskp, task, sizeof(task_t));
err = task_init_one_task(taskp);
if (err != NO_ERROR) {
dprintf(CRITICAL, "%s: static task start failed %d -- halting\n",
__func__, err);
halt();
}
list_add_tail(&task_list, &taskp->node);
}
/* boot time task header pages are no longer used */
memset(task_blist, 0, TASK_LIST_CARVEOUT_PAGES * PAGE_SIZE);
task_blist = NULL;
task_unload_init();
task_load_init();
}
task_t *task_find_task_by_uuid(te_service_id_t *uuid)
{
task_t *task = NULL;
/* find task for this uuid */
if (uuid) {
te_service_id_t null_uuid = NULL_UUID;
if (!memcmp(&null_uuid, uuid, sizeof(te_service_id_t)))
return NULL;
list_for_every_entry(&task_list, task, task_t, node) {
if (task->task_state != TASK_STATE_UNKNOWN) {
if (!memcmp(&task->props.uuid, uuid, sizeof(te_service_id_t))) {
return task;
}
}
}
}
return NULL;
}
task_t *task_find_task_by_index(uint32_t index)
{
task_t *task = NULL;
if (index >= task_next_index)
return NULL;
list_for_every_entry(&task_list, task, task_t, node) {
if (task->task_state != TASK_STATE_UNKNOWN) {
if (task->task_index == index) {
return task;
}
}
}
return NULL;
}
const char *task_get_name_str(const task_t *task, const char *prefix, const char *suffix,
char *buf, uint32_t buflen)
{
uint32_t pslen = 0;
if (prefix)
pslen += strlen(prefix);
else
prefix = "";
if (suffix)
pslen += strlen(suffix);
else
suffix = "";
/* OTE_TASK_NAME_MAX_LENGTH includes the NUL character at end of task name */
if (!task || !buf || buflen < (OTE_TASK_NAME_MAX_LENGTH + pslen) ||
!task->task_name[0])
return "";
snprintf(buf, buflen, "%s%s%s", prefix, task->task_name, suffix);
return buf;
}
void task_print_id(uint32_t level, const char *prefix, const task_t *taskp)
{
te_service_id_t null_uuid = NULL_UUID;
if (taskp) {
char tp_name[OTE_TASK_NAME_MAX_LENGTH+3];
dprintf(level, "%s", (prefix ? prefix : ""));
if (memcmp(&taskp->props.uuid, &null_uuid, sizeof(te_service_id_t)))
task_print_uuid(level, &taskp->props.uuid);
dprintf(level, "%s\n", task_get_name_str(taskp, " (", ")", tp_name, sizeof(tp_name)));
}
}
/*
* This is only used by task loading code but placed here because it modifies
* task_next_index and the task_list when a new task is loaded.
*/
status_t task_register(task_t **task_p)
{
status_t err = NO_ERROR;
task_t *dtask = NULL;
if (!task_p || !*task_p) {
err = ERR_INVALID_ARGS;
goto exit;
}
dtask = malloc(sizeof(task_t));
if (!dtask) {
err = ERR_NO_MEMORY;
dprintf(CRITICAL, "error allocating task header: 0x%x\n", err);
goto exit;
}
memcpy(dtask, *task_p, sizeof(task_t));
enter_critical_section();
do {
/*
* Make sure UUID doesn't already exist.
*/
if (task_find_task_by_uuid(&dtask->props.uuid) != NULL) {
err = ERR_ALREADY_EXISTS;
break;
}
/* Current task index now reserved for this task */
dtask->task_index = task_next_index++;
list_add_tail(&task_list, &dtask->node);
} while (0);
exit_critical_section();
if (err != NO_ERROR)
goto exit;
memset(*task_p, 0, sizeof(task_t));
/* swap *task_p to point to the registered object */
*task_p = dtask;
if (0) {
exit:
if (dtask)
free(dtask);
}
return err;
}
u_int task_get_count()
{
return task_next_index;
}
u_int task_get_active_count()
{
task_t *task = NULL;
int count = 0;
list_for_every_entry(&task_list, task, task_t, node) {
if ((task->task_state == TASK_STATE_ACTIVE) ||
(task->task_state == TASK_STATE_BLOCKED)) {
count++;
}
}
return count;
}
const struct list_node *task_get_task_list()
{
return &task_list;
}
status_t task_register_ta_events(task_t *task, uint32_t events_mask)
{
if (task == NULL)
return ERR_INVALID_ARGS;
if ((events_mask & TA_EVENT_MASK) != events_mask) {
dprintf(INFO, "%s: bad events_mask: %d\n", __func__,
events_mask);
return ERR_INVALID_ARGS;
}
task->ta_events_mask = events_mask;
return NO_ERROR;
}
/*
* Signal all tasks that have requested the supplied event.
* void *arg is supplied by the caller and is the struct te_command
* message to send to the task when it is scheduled.
*/
status_t task_signal_ta_event(enum ta_event_id event, void *arg)
{
task_t *tmp;
task_t *task;
thread_t *thread;
list_for_every_entry_safe(&task_list, task, tmp, task_t, node) {
/* Only schedule tasks that have requested callbacks. */
if ((task->ta_events_mask & (1 << event)) == 0)
continue;
thread = list_peek_head_type(&task->thread_node,
thread_t, task_node);
if (thread->state != THREAD_BLOCKED) {
dprintf(CRITICAL,
"%s:%d thread in bad state (%d) for event\n",
__func__, __LINE__, thread->state);
continue;
}
enter_critical_section();
thread->arg = arg;
/* kickoff thread */
thread_unblock_from_wait_queue(thread, false, NO_ERROR);
thread_yield();
exit_critical_section();
}
return NO_ERROR;
}