| /* |
| * Copyright (c) 2013-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 <errno.h> |
| #include <err.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <arch/arm.h> |
| #include <arch/outercache.h> |
| #include <kernel/thread.h> |
| #include <stdlib.h> |
| #include <platform/memmap.h> |
| #include <lib/ote/ote_protocol.h> |
| #include <ote_intf.h> |
| #include <platform/platform_p.h> |
| #include <kernel/task_load.h> |
| |
| #include "../lib/external/libc/include/syscall.h" |
| |
| struct timespec { |
| long tv_sec; /* seconds */ |
| long tv_nsec; /* nanoseconds */ |
| }; |
| |
| #define MAP_SHARED 0x01 /* Share changes. */ |
| #define MAP_PRIVATE 0x02 /* Changes are private. */ |
| #define MAP_ANONYMOUS 0x20 /* Don't use a file. */ |
| |
| static int platform_ta_to_ta_request_handler(te_ta_to_ta_request_args_t *args) |
| { |
| te_error_t result; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| dprintf(SPEW, "%s: %d\n", __func__, args->request.type); |
| |
| switch (args->request.type) { |
| case OPEN_SESSION: |
| result = te_handle_open_session(&args->request, true); |
| break; |
| case CLOSE_SESSION: |
| result = te_handle_close_session(&args->request, true); |
| break; |
| case LAUNCH_OPERATION: |
| result = te_handle_launch_op(&args->request, true); |
| break; |
| default: |
| dprintf(SPEW, "%s: unknown cmd=0x%x\n", |
| __func__, args->request.type); |
| return -EINVAL; |
| } |
| |
| args->request.result = result; |
| |
| /* consider any failure here to have occured in common TEE code */ |
| if (args->request.result != OTE_SUCCESS) { |
| args->request.result_origin = OTE_RESULT_ORIGIN_KERNEL; |
| } |
| |
| te_get_completed_cmd(&args->request, true); |
| |
| return 0; |
| } |
| |
| static int platform_get_property_handler(te_get_property_args_t *args) |
| { |
| int retval = 0; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| dprintf(SPEW, "%s: propset 0x%x\n", __func__, args->prop); |
| |
| switch (args->prop) { |
| case TE_PROPERTY_CURRENT_TA: |
| retval = te_get_current_ta_property(args); |
| break; |
| case TE_PROPERTY_CURRENT_CLIENT: |
| retval = te_get_current_client_property(args); |
| break; |
| case TE_PROPERTY_TE_IMPLEMENTATION: |
| retval = te_get_implementation_property(args); |
| break; |
| default: |
| args->result = OTE_ERROR_BAD_PARAMETERS; |
| retval = -EINVAL; |
| break; |
| } |
| |
| return retval; |
| } |
| |
| static int platform_ioctl_handler(u_int cmd, void *cmdbuf) |
| { |
| task_t *taskp = current_thread->arch.task; |
| |
| dprintf(SPEW, "%s: cmd 0x%x cmdbuf 0x%p\n", |
| __func__, cmd, cmdbuf); |
| |
| switch (cmd) { |
| case OTE_IOCTL_GET_MAP_MEM_ADDR: |
| { |
| te_map_mem_addr_args_t *args = cmdbuf; |
| task_map_t *mptr; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| /* only non-zero IDs are unique */ |
| if (!args->id) { |
| return -EINVAL; |
| } |
| |
| mptr = task_find_mapping_by_id(taskp, args->id); |
| if (mptr == NULL) { |
| return -EINVAL; |
| } |
| |
| args->addr = (void *)((uint32_t)mptr->vaddr + mptr->offset); |
| |
| break; |
| } |
| case OTE_IOCTL_V_TO_P: |
| { |
| te_v_to_p_args_t *args = cmdbuf; |
| paddr_t paddr; |
| status_t retval; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| retval = task_get_physaddr(taskp, (vaddr_t)args->vaddr, &paddr); |
| if (retval != NO_ERROR) |
| return -EINVAL; |
| |
| args->paddr = paddr; |
| break; |
| } |
| case OTE_IOCTL_CACHE_MAINT: |
| { |
| te_cache_maint_args_t *args = cmdbuf; |
| #if ARM_WITH_OUTER_CACHE |
| vaddr_t vaddr; |
| uint32_t nbytes; |
| #endif |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| /* operate on L1 (virtual addrs) */ |
| switch (args->op) { |
| case OTE_EXT_NV_CM_OP_CLEAN: |
| arch_clean_cache_range((addr_t)args->vaddr, args->length); |
| break; |
| case OTE_EXT_NV_CM_OP_INVALIDATE: |
| arch_invalidate_cache_range((addr_t)args->vaddr, args->length); |
| break; |
| case OTE_EXT_NV_CM_OP_FLUSH: |
| arch_clean_invalidate_cache_range((addr_t)args->vaddr, args->length); |
| break; |
| default: |
| return -EINVAL; |
| } |
| #if ARM_WITH_OUTER_CACHE |
| /* operate on L2 (physical addrs) */ |
| nbytes = args->length; |
| vaddr = (vaddr_t)args->vaddr; |
| |
| while (nbytes) { |
| uint32_t len; |
| paddr_t paddr; |
| status_t retval; |
| |
| retval = task_get_physaddr(taskp, vaddr, &paddr); |
| if (retval != NO_ERROR) |
| return -EFAULT; |
| |
| /* careful in crossing page boundaries */ |
| len = PAGE_SIZE - (paddr & PAGE_MASK); |
| len = MIN(nbytes, len); |
| |
| switch (args->op) { |
| case OTE_EXT_NV_CM_OP_CLEAN: |
| outer_clean_range(paddr, len); |
| break; |
| case OTE_EXT_NV_CM_OP_INVALIDATE: |
| outer_inv_range(paddr, len); |
| break; |
| case OTE_EXT_NV_CM_OP_FLUSH: |
| outer_flush_range(paddr, len); |
| break; |
| } |
| vaddr += len; |
| nbytes -= len; |
| } |
| #endif |
| break; |
| } |
| case OTE_IOCTL_TA_TO_TA_REQUEST: |
| { |
| te_ta_to_ta_request_args_t *args = cmdbuf; |
| |
| return platform_ta_to_ta_request_handler(args); |
| } |
| case OTE_IOCTL_GET_PROPERTY: |
| { |
| te_get_property_args_t *args = cmdbuf; |
| |
| return platform_get_property_handler(args); |
| } |
| case OTE_IOCTL_SS_REQUEST: |
| { |
| return platform_ss_request_handler(); |
| } |
| case OTE_IOCTL_SS_GET_CONFIG: |
| { |
| uint32_t *ss_config = cmdbuf; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)ss_config, |
| sizeof(uint32_t))) |
| return -EFAULT; |
| |
| platform_ss_get_config(ss_config); |
| break; |
| } |
| case OTE_IOCTL_GET_DEVICE_ID: |
| { |
| te_device_id_args_t *args = cmdbuf; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| platform_get_device_id(args); |
| break; |
| } |
| case OTE_IOCTL_GET_TIME_US: |
| { |
| uint32_t *args = cmdbuf; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)cmdbuf, |
| sizeof(uint32_t))) |
| return -EFAULT; |
| |
| *args = platform_get_time_us(); |
| break; |
| } |
| case OTE_IOCTL_GET_RAND32: |
| { |
| uint32_t *args = cmdbuf; |
| |
| /* validate command buffer */ |
| if (!task_valid_address((vaddr_t)cmdbuf, |
| sizeof(uint32_t))) |
| return -EFAULT; |
| |
| *args = platform_get_rand32(); |
| break; |
| } |
| case OTE_IOCTL_TASK_REQUEST: |
| { |
| te_task_request_args_t *args = cmdbuf; |
| |
| if (!task_valid_address((vaddr_t)args, sizeof(*args))) { |
| return -EFAULT; |
| } |
| |
| return task_request_handler(args); |
| } |
| case OTE_IOCTL_TASK_PANIC: |
| { |
| te_panic_args_t *args = cmdbuf; |
| task_t *task = current_thread->arch.task; |
| |
| /* use NULL msg if arg isn't valid for some reason */ |
| if (task_valid_address((vaddr_t)args, sizeof(*args))) |
| task_panic(task->task_name, args->msg); |
| else |
| task_panic(task->task_name, NULL); |
| break; |
| } |
| |
| case OTE_IOCTL_REGISTER_TA_EVENT: |
| { |
| status_t ret; |
| |
| ret = task_register_ta_events(taskp, (uint32_t)cmdbuf); |
| switch (ret) { |
| case NO_ERROR: |
| return NO_ERROR; |
| |
| case ERR_INVALID_ARGS: |
| return -EINVAL; |
| |
| default: |
| /* Should never hit this case. */ |
| dprintf(CRITICAL, "%s: Unknown error " |
| "from task_set_power_callbacks %d\n", |
| __func__, ret); |
| return ret; |
| } |
| } |
| |
| default: |
| { |
| dprintf(SPEW, "%s: invalid ioctl: cmd=0x%x\n", |
| __func__, cmd); |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| bool platform_syscall_handler(void *arg) |
| { |
| task_t *task = current_thread->arch.task; |
| uint32_t *r = arg; |
| |
| if (r[12] == __NR_read) { /* read */ |
| /* only expect reads of the TA pipe fd */ |
| r[0] = te_handle_ta_message(r[0], |
| (void *)(r[1]), |
| r[2], |
| true); |
| return true; |
| } |
| if (r[12] == __NR_write) { /* write */ |
| /* check buffer is in task's address space */ |
| if (task_valid_address(r[1], r[2]) == false) { |
| r[0] = -EFAULT; |
| return true; |
| } |
| if ((r[0] == 1) || (r[0] == 2)) { |
| u_int i; |
| /* handle stdout/stderr */ |
| for (i = 0; i < r[2]; i++) { |
| dprintf(CRITICAL, "%c", ((char *)r[1])[i]); |
| } |
| r[0] = r[2]; |
| } else { |
| /* handle writes of one of the TA pipe fd's */ |
| r[0] = te_handle_ta_message(r[0], |
| (void *)(r[1]), |
| r[2], |
| false); |
| } |
| return true; |
| } |
| if (r[12] == __NR_brk) { /* brk */ |
| u_int brk = r[0]; |
| |
| /* update brk, if within range */ |
| if ((brk >= task->start_brk) && (brk < task->end_brk)) { |
| task->curr_brk = brk; |
| } else { |
| if (brk >= task->end_brk) { |
| dprintf(CRITICAL, |
| "%s: task %d: " |
| "no more heap (size 0x%lx bytes)\n", |
| __func__, task->task_index, |
| task->end_brk - task->start_brk); |
| } else if ((brk < task->start_brk) && (brk != 0)) { |
| dprintf(CRITICAL, |
| "%s: task %d: " |
| "brk 0x%x < heap start 0x%lx\n", |
| __func__, task->task_index, |
| brk, task->start_brk); |
| } |
| } |
| |
| r[0] = task->curr_brk; |
| return true; |
| } |
| if (r[12] == __NR_nanosleep) { /* nanosleep */ |
| struct timespec *ts; |
| |
| /* check buffer is in task's address space */ |
| if (task_valid_address(r[0], sizeof(struct timespec)) == false) { |
| r[0] = -EFAULT; |
| return true; |
| } |
| |
| ts = (struct timespec *)r[0]; |
| if (ts->tv_sec) { |
| dprintf(SPEW, "ignoring seconds arg to usleep\n"); |
| ts->tv_sec = 0; |
| } |
| |
| spin(ts->tv_nsec / 1000); |
| |
| r[0] = 0; |
| return true; |
| } |
| if (r[12] == __NR_gettid) { /* gettid */ |
| r[0] = (u_int)current_thread; |
| return true; |
| } |
| if (r[12] == __NR_exit_group) { /* exit_group */ |
| /* terminate thread (and all task threads) */ |
| /* thread_exit(r[0]); */ |
| |
| /* for now, just suspend the thread */ |
| thread_suspend(); |
| return true; |
| } |
| if (r[12] == __NR_ioctl) { /* ioctl */ |
| r[0] = platform_ioctl_handler(r[1], (void *)r[2]); |
| |
| return true; |
| } |
| |
| dprintf(CRITICAL, "%s: unhandled syscall: 0x%x\n", __func__, r[12]); |
| |
| return true; |
| } |