You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
4.0 KiB
209 lines
4.0 KiB
/*
|
|
* 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 accesses. */
|
|
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;
|
|
}
|
|
|