blob: 72f575c39e50c83dd67c2b800decef1cc9c49080 [file] [log] [blame]
/*
* Copyright (c) 2013, Google, Inc. All rights reserved
* Copyright (c) 2013, 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 <assert.h>
#include <debug.h>
#include <err.h>
#include <kernel/thread.h>
#include <kernel/usercopy.h>
#include <kernel/mutex.h>
#include <uapi/mm.h>
#include <stdlib.h>
#include <string.h>
#include <trace.h>
#include <platform.h>
#include <lib/trusty/sys_fd.h>
#include <lib/trusty/trusty_app.h>
#define LOCAL_TRACE 0
static int32_t sys_std_write(uint32_t fd, user_addr_t user_ptr, uint32_t size);
static mutex_t fd_lock = MUTEX_INITIAL_VALUE(fd_lock);
static const struct sys_fd_ops sys_std_fd_op = {
.write = sys_std_write,
};
static struct sys_fd_ops const *sys_fds[MAX_SYS_FD_HADLERS] = {
[1] = &sys_std_fd_op, /* stdout */
[2] = &sys_std_fd_op, /* stderr */
};
status_t install_sys_fd_handler(uint32_t fd, const struct sys_fd_ops *ops)
{
status_t ret;
if (fd >= countof(sys_fds))
return ERR_INVALID_ARGS;
mutex_acquire(&fd_lock);
if (!sys_fds[fd]) {
sys_fds[fd] = ops;
ret = NO_ERROR;
} else {
ret = ERR_ALREADY_EXISTS;
}
mutex_release(&fd_lock);
return ret;
}
static const struct sys_fd_ops *get_sys_fd_handler(uint32_t fd)
{
if (fd >= countof(sys_fds))
return NULL;
return sys_fds[fd];
}
static bool valid_address(vaddr_t addr, u_int size)
{
size = ROUNDUP(size + (addr & (PAGE_SIZE - 1)), PAGE_SIZE);
addr = ROUNDDOWN(addr, PAGE_SIZE);
while (size) {
if (!is_user_address(addr) || !vaddr_to_paddr((void*)addr)) {
return false;
}
addr += PAGE_SIZE;
size -= PAGE_SIZE;
}
return true;
}
/* handle stdout/stderr */
static int32_t sys_std_write(uint32_t fd, user_addr_t user_ptr, uint32_t size)
{
size_t ret;
/* check buffer is in task's address space */
if (!valid_address((vaddr_t)user_ptr, size))
return ERR_INVALID_ARGS;
if (((fd == 2) ? INFO : SPEW) > LK_DEBUGLEVEL) {
ret = size;
} else {
ret = fwrite((const void *)(uintptr_t)user_ptr, 1, size,
(fd == 2) ? stderr : stdout);
}
return ret;
}
long sys_write(uint32_t fd, user_addr_t user_ptr, uint32_t size)
{
const struct sys_fd_ops *ops = get_sys_fd_handler(fd);
if (ops && ops->write)
return ops->write(fd, user_ptr, size);
return ERR_NOT_SUPPORTED;
}
long sys_brk(u_int brk)
{
trusty_app_t *trusty_app = current_trusty_app();
/* update brk, if within range */
if ((brk >= trusty_app->start_brk) && (brk <= trusty_app->end_brk)) {
trusty_app->cur_brk = brk;
}
return (long) trusty_app->cur_brk;
}
long sys_exit_etc(int32_t status, uint32_t flags)
{
thread_t *current = get_current_thread();
LTRACEF("exit called, thread %p, name %s\n", current, current->name);
trusty_app_exit(status);
return 0L;
}
long sys_read(uint32_t fd, user_addr_t user_ptr, uint32_t size)
{
const struct sys_fd_ops *ops = get_sys_fd_handler(fd);
if (ops && ops->read)
return ops->read(fd, user_ptr, size);
return ERR_NOT_SUPPORTED;
}
long sys_ioctl(uint32_t fd, uint32_t req, user_addr_t user_ptr)
{
const struct sys_fd_ops *ops = get_sys_fd_handler(fd);
if (ops && ops->ioctl)
return ops->ioctl(fd, req, user_ptr);
return ERR_NOT_SUPPORTED;
}
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
long sys_nanosleep(uint32_t clock_id, uint32_t flags,
uint32_t sleep_time_l, uint32_t sleep_time_h)
{
uint64_t sleep_time = sleep_time_l + ((uint64_t)sleep_time_h << 32);
thread_sleep((lk_time_t)(DIV_ROUND_UP(sleep_time, 1000 * 1000)));
return NO_ERROR;
}
long sys_gettime(uint32_t clock_id, uint32_t flags, user_addr_t time)
{
// return time in nanoseconds
lk_bigtime_t t = current_time_hires() * 1000;
return copy_to_user(time, &t, sizeof(int64_t));
}
long sys_mmap(user_addr_t uaddr, uint32_t size, uint32_t flags, uint32_t handle)
{
trusty_app_t *trusty_app = current_trusty_app();
vaddr_t vaddr;
long ret;
/*
* Only allows mapping on IO region specified by handle (id) and uaddr
* must be 0 for now.
* TBD: Add support in to use uaddr as a hint.
*/
if (flags != MMAP_FLAG_IO_HANDLE || uaddr != 0)
return ERR_INVALID_ARGS;
ret = trusty_app_setup_mmio(trusty_app, handle, &vaddr, size);
if (ret != NO_ERROR)
return ret;
return vaddr;
}
long sys_munmap(user_addr_t uaddr, uint32_t size)
{
trusty_app_t *trusty_app = current_trusty_app();
/*
* vmm_free_region always unmaps whole region.
* TBD: Add support to unmap partial region when there's use case.
*/
return vmm_free_region_etc(trusty_app->aspace, uaddr, size, 0);
}
long sys_prepare_dma(user_addr_t uaddr, uint32_t size, uint32_t flags,
user_addr_t pmem)
{
struct dma_pmem kpmem;
uint32_t mapped_size = 0;
uint32_t entries = 0;
long ret;
vaddr_t vaddr = uaddr;
LTRACEF("uaddr 0x%x, size 0x%x, flags 0x%x, pmem 0x%x\n",
uaddr, size, flags, pmem);
if (size == 0 || !valid_address(vaddr, size))
return ERR_INVALID_ARGS;
do {
kpmem.paddr = vaddr_to_paddr((void *)(vaddr + mapped_size));
if (!kpmem.paddr)
return ERR_INVALID_ARGS;
kpmem.size = MIN(size - mapped_size,
PAGE_SIZE - (kpmem.paddr & (PAGE_SIZE - 1)));
ret = copy_to_user(pmem, &kpmem, sizeof(struct dma_pmem));
if (ret != NO_ERROR)
return ret;
pmem += sizeof(struct dma_pmem);
mapped_size += kpmem.size;
entries++;
} while (mapped_size < size && (flags & DMA_FLAG_MULTI_PMEM));
if (flags & DMA_FLAG_FROM_DEVICE)
arch_clean_invalidate_cache_range(vaddr, mapped_size);
else
arch_clean_cache_range(vaddr, mapped_size);
if (!(flags & DMA_FLAG_ALLOW_PARTIAL) && mapped_size != size)
return ERR_BAD_LEN;
return entries;
}
long sys_finish_dma(user_addr_t uaddr, uint32_t size, uint32_t flags)
{
LTRACEF("uaddr 0x%x, size 0x%x, flags 0x%x\n", uaddr, size, flags);
/* check buffer is in task's address space */
if (!valid_address((vaddr_t)uaddr, size))
return ERR_INVALID_ARGS;
if (flags & DMA_FLAG_FROM_DEVICE)
arch_clean_invalidate_cache_range(uaddr, size);
return NO_ERROR;
}