blob: 3af52f65aed1c5b153ba7db372271286c5e6c0db [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2016, Linaro Limited
*/
#include <assert.h>
#include <kernel/tee_misc.h>
#include <kernel/thread.h>
#include <mm/core_memprot.h>
#include <optee_rpc_cmd.h>
#include <stdlib.h>
#include <string_ext.h>
#include <string.h>
#include <tee/tee_fs.h>
#include <tee/tee_fs_rpc.h>
#include <tee/tee_pobj.h>
#include <tee/tee_svc_storage.h>
#include <trace.h>
#include <util.h>
struct tee_fs_dir {
int nw_dir;
struct tee_fs_dirent d;
};
static TEE_Result operation_commit(struct tee_fs_rpc_operation *op)
{
return thread_rpc_cmd(op->id, op->num_params, op->params);
}
static TEE_Result operation_open(uint32_t id, unsigned int cmd,
struct tee_pobj *po, int *fd)
{
struct mobj *mobj;
TEE_Result res;
void *va;
va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
res = tee_svc_storage_create_filename(va, TEE_FS_NAME_MAX,
po, po->temporary);
if (res != TEE_SUCCESS)
return res;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 3, .params = {
[0] = THREAD_PARAM_VALUE(IN, cmd, 0, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, TEE_FS_NAME_MAX),
[2] = THREAD_PARAM_VALUE(OUT, 0, 0, 0),
} };
res = operation_commit(&op);
if (res == TEE_SUCCESS)
*fd = op.params[2].u.value.a;
return res;
}
TEE_Result tee_fs_rpc_open(uint32_t id, struct tee_pobj *po, int *fd)
{
return operation_open(id, OPTEE_RPC_FS_OPEN, po, fd);
}
TEE_Result tee_fs_rpc_create(uint32_t id, struct tee_pobj *po, int *fd)
{
return operation_open(id, OPTEE_RPC_FS_CREATE, po, fd);
}
static TEE_Result operation_open_dfh(uint32_t id, unsigned int cmd,
const struct tee_fs_dirfile_fileh *dfh,
int *fd)
{
struct mobj *mobj;
TEE_Result res;
void *va;
va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
res = tee_svc_storage_create_filename_dfh(va, TEE_FS_NAME_MAX, dfh);
if (res != TEE_SUCCESS)
return res;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 3, .params = {
[0] = THREAD_PARAM_VALUE(IN, cmd, 0, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, TEE_FS_NAME_MAX),
[2] = THREAD_PARAM_VALUE(OUT, 0, 0, 0),
} };
res = operation_commit(&op);
if (res == TEE_SUCCESS)
*fd = op.params[2].u.value.a;
return res;
}
TEE_Result tee_fs_rpc_open_dfh(uint32_t id,
const struct tee_fs_dirfile_fileh *dfh, int *fd)
{
return operation_open_dfh(id, OPTEE_RPC_FS_OPEN, dfh, fd);
}
TEE_Result tee_fs_rpc_create_dfh(uint32_t id,
const struct tee_fs_dirfile_fileh *dfh,
int *fd)
{
return operation_open_dfh(id, OPTEE_RPC_FS_CREATE, dfh, fd);
}
TEE_Result tee_fs_rpc_close(uint32_t id, int fd)
{
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 1, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_CLOSE, fd, 0),
},
};
return operation_commit(&op);
}
TEE_Result tee_fs_rpc_read_init(struct tee_fs_rpc_operation *op,
uint32_t id, int fd, tee_fs_off_t offset,
size_t data_len, void **out_data)
{
struct mobj *mobj;
uint8_t *va;
if (offset < 0)
return TEE_ERROR_BAD_PARAMETERS;
va = tee_fs_rpc_cache_alloc(data_len, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
*op = (struct tee_fs_rpc_operation){
.id = id, .num_params = 2, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_READ, fd,
offset),
[1] = THREAD_PARAM_MEMREF(OUT, mobj, 0, data_len),
},
};
*out_data = va;
return TEE_SUCCESS;
}
TEE_Result tee_fs_rpc_read_final(struct tee_fs_rpc_operation *op,
size_t *data_len)
{
TEE_Result res = operation_commit(op);
if (res == TEE_SUCCESS)
*data_len = op->params[1].u.memref.size;
return res;
}
TEE_Result tee_fs_rpc_write_init(struct tee_fs_rpc_operation *op,
uint32_t id, int fd, tee_fs_off_t offset,
size_t data_len, void **data)
{
struct mobj *mobj;
uint8_t *va;
if (offset < 0)
return TEE_ERROR_BAD_PARAMETERS;
va = tee_fs_rpc_cache_alloc(data_len, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
*op = (struct tee_fs_rpc_operation){
.id = id, .num_params = 2, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_WRITE, fd,
offset),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, data_len),
},
};
*data = va;
return TEE_SUCCESS;
}
TEE_Result tee_fs_rpc_write_final(struct tee_fs_rpc_operation *op)
{
return operation_commit(op);
}
TEE_Result tee_fs_rpc_truncate(uint32_t id, int fd, size_t len)
{
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 1, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_TRUNCATE, fd,
len),
}
};
return operation_commit(&op);
}
TEE_Result tee_fs_rpc_remove(uint32_t id, struct tee_pobj *po)
{
TEE_Result res;
struct mobj *mobj;
void *va;
va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
res = tee_svc_storage_create_filename(va, TEE_FS_NAME_MAX,
po, po->temporary);
if (res != TEE_SUCCESS)
return res;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 2, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_REMOVE, 0, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, TEE_FS_NAME_MAX),
},
};
return operation_commit(&op);
}
TEE_Result tee_fs_rpc_remove_dfh(uint32_t id,
const struct tee_fs_dirfile_fileh *dfh)
{
TEE_Result res;
struct mobj *mobj;
void *va;
va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
res = tee_svc_storage_create_filename_dfh(va, TEE_FS_NAME_MAX, dfh);
if (res != TEE_SUCCESS)
return res;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 2, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_REMOVE, 0, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, TEE_FS_NAME_MAX),
}
};
return operation_commit(&op);
}
TEE_Result tee_fs_rpc_rename(uint32_t id, struct tee_pobj *old,
struct tee_pobj *new, bool overwrite)
{
TEE_Result res;
struct mobj *mobj;
char *va;
bool temp;
va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX * 2, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
if (new)
temp = old->temporary;
else
temp = true;
res = tee_svc_storage_create_filename(va, TEE_FS_NAME_MAX,
old, temp);
if (res != TEE_SUCCESS)
return res;
if (new) {
res = tee_svc_storage_create_filename(va + TEE_FS_NAME_MAX,
TEE_FS_NAME_MAX,
new, new->temporary);
} else {
res = tee_svc_storage_create_filename(va + TEE_FS_NAME_MAX,
TEE_FS_NAME_MAX,
old, false);
}
if (res != TEE_SUCCESS)
return res;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 3, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_RENAME,
overwrite, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, TEE_FS_NAME_MAX),
[2] = THREAD_PARAM_MEMREF(IN, mobj, TEE_FS_NAME_MAX,
TEE_FS_NAME_MAX),
}
};
return operation_commit(&op);
}
TEE_Result tee_fs_rpc_opendir(uint32_t id, const TEE_UUID *uuid,
struct tee_fs_dir **d)
{
TEE_Result res;
struct mobj *mobj;
void *va;
struct tee_fs_dir *dir = calloc(1, sizeof(*dir));
if (!dir)
return TEE_ERROR_OUT_OF_MEMORY;
va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj);
if (!va) {
res = TEE_ERROR_OUT_OF_MEMORY;
goto err_exit;
}
res = tee_svc_storage_create_dirname(va, TEE_FS_NAME_MAX, uuid);
if (res != TEE_SUCCESS)
goto err_exit;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 3, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_OPENDIR,
0, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, TEE_FS_NAME_MAX),
[2] = THREAD_PARAM_VALUE(OUT, 0, 0, 0),
}
};
res = operation_commit(&op);
if (res != TEE_SUCCESS)
goto err_exit;
dir->nw_dir = op.params[2].u.value.a;
*d = dir;
return TEE_SUCCESS;
err_exit:
free(dir);
return res;
}
TEE_Result tee_fs_rpc_closedir(uint32_t id, struct tee_fs_dir *d)
{
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 1, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_CLOSEDIR,
d->nw_dir, 0),
}
};
free(d);
return operation_commit(&op);
}
TEE_Result tee_fs_rpc_readdir(uint32_t id, struct tee_fs_dir *d,
struct tee_fs_dirent **ent)
{
TEE_Result res;
struct mobj *mobj;
void *va;
const size_t max_name_len = TEE_FS_NAME_MAX + 1;
if (!d)
return TEE_ERROR_ITEM_NOT_FOUND;
va = tee_fs_rpc_cache_alloc(max_name_len, &mobj);
if (!va)
return TEE_ERROR_OUT_OF_MEMORY;
struct tee_fs_rpc_operation op = {
.id = id, .num_params = 2, .params = {
[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_READDIR,
d->nw_dir, 0),
[1] = THREAD_PARAM_MEMREF(IN, mobj, 0, max_name_len),
}
};
res = operation_commit(&op);
if (res != TEE_SUCCESS)
return res;
d->d.oidlen = tee_hs2b(va, d->d.oid, strnlen(va, max_name_len),
sizeof(d->d.oid));
if (!d->d.oidlen)
return TEE_ERROR_OUT_OF_MEMORY;
*ent = &d->d;
return TEE_SUCCESS;
}