diff options
Diffstat (limited to 'lib/debugfs/debugfs_smc.c')
-rw-r--r-- | lib/debugfs/debugfs_smc.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/lib/debugfs/debugfs_smc.c b/lib/debugfs/debugfs_smc.c new file mode 100644 index 0000000..400c166 --- /dev/null +++ b/lib/debugfs/debugfs_smc.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include <lib/debugfs.h> +#include <lib/smccc.h> +#include <lib/spinlock.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <smccc_helpers.h> + +#define MAX_PATH_LEN 256 + +#define MOUNT 0 +#define CREATE 1 +#define OPEN 2 +#define CLOSE 3 +#define READ 4 +#define WRITE 5 +#define SEEK 6 +#define BIND 7 +#define STAT 8 +#define INIT 10 +#define VERSION 11 + +/* This is the virtual address to which we map the NS shared buffer */ +#define DEBUGFS_SHARED_BUF_VIRT ((void *)0x81000000U) + +static union debugfs_parms { + struct { + char fname[MAX_PATH_LEN]; + } open; + + struct { + char srv[MAX_PATH_LEN]; + char where[MAX_PATH_LEN]; + char spec[MAX_PATH_LEN]; + } mount; + + struct { + char path[MAX_PATH_LEN]; + dir_t dir; + } stat; + + struct { + char oldpath[MAX_PATH_LEN]; + char newpath[MAX_PATH_LEN]; + } bind; +} parms; + +/* debugfs_access_lock protects shared buffer and internal */ +/* FS functions from concurrent acccesses. */ +static spinlock_t debugfs_access_lock; + +static bool debugfs_initialized; + +uintptr_t debugfs_smc_handler(unsigned int smc_fid, + u_register_t cmd, + u_register_t arg2, + u_register_t arg3, + u_register_t arg4, + void *cookie, + void *handle, + u_register_t flags) +{ + int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0; + int ret; + + /* Allow calls from non-secure only */ + if (is_caller_secure(flags)) { + SMC_RET1(handle, DEBUGFS_E_DENIED); + } + + /* Expect a SiP service fast call */ + if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) || + (GET_SMC_OEN(smc_fid) != OEN_SIP_START)) { + SMC_RET1(handle, SMC_UNK); + } + + /* Truncate parameters if 32b SMC convention call */ + if (GET_SMC_CC(smc_fid) == SMC_32) { + arg2 &= 0xffffffff; + arg3 &= 0xffffffff; + arg4 &= 0xffffffff; + } + + spin_lock(&debugfs_access_lock); + + if (debugfs_initialized == true) { + /* Copy NS shared buffer to internal secure location */ + memcpy(&parms, (void *)DEBUGFS_SHARED_BUF_VIRT, + sizeof(union debugfs_parms)); + } + + switch (cmd) { + case INIT: + if (debugfs_initialized == false) { + /* TODO: check PA validity e.g. whether */ + /* it is an NS region. */ + ret = mmap_add_dynamic_region(arg2, + (uintptr_t)DEBUGFS_SHARED_BUF_VIRT, + PAGE_SIZE_4KB, + MT_MEMORY | MT_RW | MT_NS); + if (ret == 0) { + debugfs_initialized = true; + smc_ret = SMC_OK; + smc_resp = 0; + } + } + break; + + case VERSION: + smc_ret = SMC_OK; + smc_resp = DEBUGFS_VERSION; + break; + + case MOUNT: + ret = mount(parms.mount.srv, + parms.mount.where, + parms.mount.spec); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case OPEN: + ret = open(parms.open.fname, arg2); + if (ret >= 0) { + smc_ret = SMC_OK; + smc_resp = ret; + } + break; + + case CLOSE: + ret = close(arg2); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case READ: + ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3); + if (ret >= 0) { + smc_ret = SMC_OK; + smc_resp = ret; + } + break; + + case SEEK: + ret = seek(arg2, arg3, arg4); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case BIND: + ret = bind(parms.bind.oldpath, parms.bind.newpath); + if (ret == 0) { + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + case STAT: + ret = stat(parms.stat.path, &parms.stat.dir); + if (ret == 0) { + memcpy((void *)DEBUGFS_SHARED_BUF_VIRT, &parms, + sizeof(union debugfs_parms)); + smc_ret = SMC_OK; + smc_resp = 0; + } + break; + + /* Not implemented */ + case CREATE: + /* Intentional fall-through */ + + /* Not implemented */ + case WRITE: + /* Intentional fall-through */ + + default: + smc_ret = SMC_UNK; + smc_resp = 0; + } + + spin_unlock(&debugfs_access_lock); + + SMC_RET2(handle, smc_ret, smc_resp); + + /* Not reached */ + return smc_ret; +} + +int debugfs_smc_setup(void) +{ + debugfs_initialized = false; + debugfs_access_lock.lock = 0; + + return 0; +} |