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