summaryrefslogtreecommitdiffstats
path: root/lib/debugfs/debugfs_smc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/debugfs/debugfs_smc.c')
-rw-r--r--lib/debugfs/debugfs_smc.c209
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;
+}