1 /* 2 * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <stdint.h> 8 #include <stdbool.h> 9 #include <string.h> 10 11 #include <lib/debugfs.h> 12 #include <lib/smccc.h> 13 #include <lib/spinlock.h> 14 #include <lib/xlat_tables/xlat_tables_v2.h> 15 #include <smccc_helpers.h> 16 17 #define MAX_PATH_LEN 256 18 19 #define MOUNT 0 20 #define CREATE 1 21 #define OPEN 2 22 #define CLOSE 3 23 #define READ 4 24 #define WRITE 5 25 #define SEEK 6 26 #define BIND 7 27 #define STAT 8 28 #define INIT 10 29 #define VERSION 11 30 31 /* This is the virtual address to which we map the NS shared buffer */ 32 #define DEBUGFS_SHARED_BUF_VIRT ((void *)0x81000000U) 33 34 static union debugfs_parms { 35 struct { 36 char fname[MAX_PATH_LEN]; 37 } open; 38 39 struct { 40 char srv[MAX_PATH_LEN]; 41 char where[MAX_PATH_LEN]; 42 char spec[MAX_PATH_LEN]; 43 } mount; 44 45 struct { 46 char path[MAX_PATH_LEN]; 47 dir_t dir; 48 } stat; 49 50 struct { 51 char oldpath[MAX_PATH_LEN]; 52 char newpath[MAX_PATH_LEN]; 53 } bind; 54 } parms; 55 56 /* debugfs_access_lock protects shared buffer and internal */ 57 /* FS functions from concurrent acccesses. */ 58 static spinlock_t debugfs_access_lock; 59 60 static bool debugfs_initialized; 61 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)62 uintptr_t debugfs_smc_handler(unsigned int smc_fid, 63 u_register_t cmd, 64 u_register_t arg2, 65 u_register_t arg3, 66 u_register_t arg4, 67 void *cookie, 68 void *handle, 69 u_register_t flags) 70 { 71 int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0; 72 int ret; 73 74 /* Allow calls from non-secure only */ 75 if (is_caller_secure(flags)) { 76 SMC_RET1(handle, DEBUGFS_E_DENIED); 77 } 78 79 /* Expect a SiP service fast call */ 80 if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) || 81 (GET_SMC_OEN(smc_fid) != OEN_SIP_START)) { 82 SMC_RET1(handle, SMC_UNK); 83 } 84 85 /* Truncate parameters if 32b SMC convention call */ 86 if (GET_SMC_CC(smc_fid) == SMC_32) { 87 arg2 &= 0xffffffff; 88 arg3 &= 0xffffffff; 89 arg4 &= 0xffffffff; 90 } 91 92 spin_lock(&debugfs_access_lock); 93 94 if (debugfs_initialized == true) { 95 /* Copy NS shared buffer to internal secure location */ 96 memcpy(&parms, (void *)DEBUGFS_SHARED_BUF_VIRT, 97 sizeof(union debugfs_parms)); 98 } 99 100 switch (cmd) { 101 case INIT: 102 if (debugfs_initialized == false) { 103 /* TODO: check PA validity e.g. whether */ 104 /* it is an NS region. */ 105 ret = mmap_add_dynamic_region(arg2, 106 (uintptr_t)DEBUGFS_SHARED_BUF_VIRT, 107 PAGE_SIZE_4KB, 108 MT_MEMORY | MT_RW | MT_NS); 109 if (ret == 0) { 110 debugfs_initialized = true; 111 smc_ret = SMC_OK; 112 smc_resp = 0; 113 } 114 } 115 break; 116 117 case VERSION: 118 smc_ret = SMC_OK; 119 smc_resp = DEBUGFS_VERSION; 120 break; 121 122 case MOUNT: 123 ret = mount(parms.mount.srv, 124 parms.mount.where, 125 parms.mount.spec); 126 if (ret == 0) { 127 smc_ret = SMC_OK; 128 smc_resp = 0; 129 } 130 break; 131 132 case OPEN: 133 ret = open(parms.open.fname, arg2); 134 if (ret >= 0) { 135 smc_ret = SMC_OK; 136 smc_resp = ret; 137 } 138 break; 139 140 case CLOSE: 141 ret = close(arg2); 142 if (ret == 0) { 143 smc_ret = SMC_OK; 144 smc_resp = 0; 145 } 146 break; 147 148 case READ: 149 ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3); 150 if (ret >= 0) { 151 smc_ret = SMC_OK; 152 smc_resp = ret; 153 } 154 break; 155 156 case SEEK: 157 ret = seek(arg2, arg3, arg4); 158 if (ret == 0) { 159 smc_ret = SMC_OK; 160 smc_resp = 0; 161 } 162 break; 163 164 case BIND: 165 ret = bind(parms.bind.oldpath, parms.bind.newpath); 166 if (ret == 0) { 167 smc_ret = SMC_OK; 168 smc_resp = 0; 169 } 170 break; 171 172 case STAT: 173 ret = stat(parms.stat.path, &parms.stat.dir); 174 if (ret == 0) { 175 memcpy((void *)DEBUGFS_SHARED_BUF_VIRT, &parms, 176 sizeof(union debugfs_parms)); 177 smc_ret = SMC_OK; 178 smc_resp = 0; 179 } 180 break; 181 182 /* Not implemented */ 183 case CREATE: 184 /* Intentional fall-through */ 185 186 /* Not implemented */ 187 case WRITE: 188 /* Intentional fall-through */ 189 190 default: 191 smc_ret = SMC_UNK; 192 smc_resp = 0; 193 } 194 195 spin_unlock(&debugfs_access_lock); 196 197 SMC_RET2(handle, smc_ret, smc_resp); 198 199 /* Not reached */ 200 return smc_ret; 201 } 202 debugfs_smc_setup(void)203 int debugfs_smc_setup(void) 204 { 205 debugfs_initialized = false; 206 debugfs_access_lock.lock = 0; 207 208 return 0; 209 } 210