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