1 /*
2 * Copyright (c) 2019-2025, 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 static volatile union debugfs_parms *shared_ns_buf = DEBUGFS_SHARED_BUF_VIRT;
57
58 /* debugfs_access_lock protects shared buffer and internal */
59 /* FS functions from concurrent accesses. */
60 static spinlock_t debugfs_access_lock;
61
62 static bool debugfs_initialized;
63
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)64 uintptr_t debugfs_smc_handler(unsigned int smc_fid,
65 u_register_t cmd,
66 u_register_t arg2,
67 u_register_t arg3,
68 u_register_t arg4,
69 void *cookie,
70 void *handle,
71 u_register_t flags)
72 {
73 int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0;
74 int ret;
75
76 /* Allow calls from non-secure only */
77 if (!is_caller_non_secure(flags)) {
78 SMC_RET1(handle, DEBUGFS_E_DENIED);
79 }
80
81 /* Expect a vendor specific EL3 service fast call */
82 if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) ||
83 (GET_SMC_OEN(smc_fid) != OEN_VEN_EL3_START)) {
84 SMC_RET1(handle, SMC_UNK);
85 }
86
87 /* Truncate parameters if 32b SMC convention call */
88 if (GET_SMC_CC(smc_fid) == SMC_32) {
89 arg2 &= 0xffffffff;
90 arg3 &= 0xffffffff;
91 arg4 &= 0xffffffff;
92 }
93
94 spin_lock(&debugfs_access_lock);
95
96 if (debugfs_initialized == true) {
97 /* Copy NS shared buffer to internal secure location */
98 parms = *shared_ns_buf;
99 }
100
101 switch (cmd) {
102 case INIT:
103 if (debugfs_initialized == false) {
104 /* TODO: check PA validity e.g. whether */
105 /* it is an NS region. */
106 ret = mmap_add_dynamic_region(arg2,
107 (uintptr_t)DEBUGFS_SHARED_BUF_VIRT,
108 PAGE_SIZE_4KB,
109 MT_MEMORY | MT_RW | MT_NS);
110 if (ret == 0) {
111 debugfs_initialized = true;
112 smc_ret = SMC_OK;
113 smc_resp = 0;
114 }
115 }
116 break;
117
118 case VERSION:
119 smc_ret = SMC_OK;
120 smc_resp = DEBUGFS_VERSION;
121 break;
122
123 case MOUNT:
124 ret = mount(parms.mount.srv,
125 parms.mount.where,
126 parms.mount.spec);
127 if (ret == 0) {
128 smc_ret = SMC_OK;
129 smc_resp = 0;
130 }
131 break;
132
133 case OPEN:
134 ret = open(parms.open.fname, arg2);
135 if (ret >= 0) {
136 smc_ret = SMC_OK;
137 smc_resp = ret;
138 }
139 break;
140
141 case CLOSE:
142 ret = close(arg2);
143 if (ret == 0) {
144 smc_ret = SMC_OK;
145 smc_resp = 0;
146 }
147 break;
148
149 case READ:
150 ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3);
151 if (ret >= 0) {
152 smc_ret = SMC_OK;
153 smc_resp = ret;
154 }
155 break;
156
157 case SEEK:
158 ret = seek(arg2, arg3, arg4);
159 if (ret == 0) {
160 smc_ret = SMC_OK;
161 smc_resp = 0;
162 }
163 break;
164
165 case BIND:
166 ret = bind(parms.bind.oldpath, parms.bind.newpath);
167 if (ret == 0) {
168 smc_ret = SMC_OK;
169 smc_resp = 0;
170 }
171 break;
172
173 case STAT:
174 ret = stat(parms.stat.path, &parms.stat.dir);
175 if (ret == 0) {
176 *shared_ns_buf = 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