1992f091bSAmbroise Vincent /*
2*4db17f4eSSlava Andrianov * Copyright (c) 2019-2025, Arm Limited and Contributors. All rights reserved.
3992f091bSAmbroise Vincent *
4992f091bSAmbroise Vincent * SPDX-License-Identifier: BSD-3-Clause
5992f091bSAmbroise Vincent */
6992f091bSAmbroise Vincent
7992f091bSAmbroise Vincent #include <stdint.h>
8992f091bSAmbroise Vincent #include <stdbool.h>
9992f091bSAmbroise Vincent #include <string.h>
10992f091bSAmbroise Vincent
11992f091bSAmbroise Vincent #include <lib/debugfs.h>
12992f091bSAmbroise Vincent #include <lib/smccc.h>
13992f091bSAmbroise Vincent #include <lib/spinlock.h>
14992f091bSAmbroise Vincent #include <lib/xlat_tables/xlat_tables_v2.h>
15992f091bSAmbroise Vincent #include <smccc_helpers.h>
16992f091bSAmbroise Vincent
17992f091bSAmbroise Vincent #define MAX_PATH_LEN 256
18992f091bSAmbroise Vincent
19992f091bSAmbroise Vincent #define MOUNT 0
20992f091bSAmbroise Vincent #define CREATE 1
21992f091bSAmbroise Vincent #define OPEN 2
22992f091bSAmbroise Vincent #define CLOSE 3
23992f091bSAmbroise Vincent #define READ 4
24992f091bSAmbroise Vincent #define WRITE 5
25992f091bSAmbroise Vincent #define SEEK 6
26992f091bSAmbroise Vincent #define BIND 7
27992f091bSAmbroise Vincent #define STAT 8
28992f091bSAmbroise Vincent #define INIT 10
29992f091bSAmbroise Vincent #define VERSION 11
30992f091bSAmbroise Vincent
31992f091bSAmbroise Vincent /* This is the virtual address to which we map the NS shared buffer */
32992f091bSAmbroise Vincent #define DEBUGFS_SHARED_BUF_VIRT ((void *)0x81000000U)
33992f091bSAmbroise Vincent
34992f091bSAmbroise Vincent static union debugfs_parms {
35992f091bSAmbroise Vincent struct {
36992f091bSAmbroise Vincent char fname[MAX_PATH_LEN];
37992f091bSAmbroise Vincent } open;
38992f091bSAmbroise Vincent
39992f091bSAmbroise Vincent struct {
40992f091bSAmbroise Vincent char srv[MAX_PATH_LEN];
41992f091bSAmbroise Vincent char where[MAX_PATH_LEN];
42992f091bSAmbroise Vincent char spec[MAX_PATH_LEN];
43992f091bSAmbroise Vincent } mount;
44992f091bSAmbroise Vincent
45992f091bSAmbroise Vincent struct {
46992f091bSAmbroise Vincent char path[MAX_PATH_LEN];
47992f091bSAmbroise Vincent dir_t dir;
48992f091bSAmbroise Vincent } stat;
49992f091bSAmbroise Vincent
50992f091bSAmbroise Vincent struct {
51992f091bSAmbroise Vincent char oldpath[MAX_PATH_LEN];
52992f091bSAmbroise Vincent char newpath[MAX_PATH_LEN];
53992f091bSAmbroise Vincent } bind;
54992f091bSAmbroise Vincent } parms;
55992f091bSAmbroise Vincent
56992f091bSAmbroise Vincent /* debugfs_access_lock protects shared buffer and internal */
571b491eeaSElyes Haouas /* FS functions from concurrent accesses. */
58992f091bSAmbroise Vincent static spinlock_t debugfs_access_lock;
59992f091bSAmbroise Vincent
60992f091bSAmbroise Vincent static bool debugfs_initialized;
61992f091bSAmbroise Vincent
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)62992f091bSAmbroise Vincent uintptr_t debugfs_smc_handler(unsigned int smc_fid,
63992f091bSAmbroise Vincent u_register_t cmd,
64992f091bSAmbroise Vincent u_register_t arg2,
65992f091bSAmbroise Vincent u_register_t arg3,
66992f091bSAmbroise Vincent u_register_t arg4,
67992f091bSAmbroise Vincent void *cookie,
68992f091bSAmbroise Vincent void *handle,
69992f091bSAmbroise Vincent u_register_t flags)
70992f091bSAmbroise Vincent {
71992f091bSAmbroise Vincent int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0;
72992f091bSAmbroise Vincent int ret;
73992f091bSAmbroise Vincent
74992f091bSAmbroise Vincent /* Allow calls from non-secure only */
754e4a8c58SArvind Ram Prakash if (!is_caller_non_secure(flags)) {
76992f091bSAmbroise Vincent SMC_RET1(handle, DEBUGFS_E_DENIED);
77992f091bSAmbroise Vincent }
78992f091bSAmbroise Vincent
79*4db17f4eSSlava Andrianov /* Expect a vendor specific EL3 service fast call */
80992f091bSAmbroise Vincent if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) ||
81*4db17f4eSSlava Andrianov (GET_SMC_OEN(smc_fid) != OEN_VEN_EL3_START)) {
82992f091bSAmbroise Vincent SMC_RET1(handle, SMC_UNK);
83992f091bSAmbroise Vincent }
84992f091bSAmbroise Vincent
85992f091bSAmbroise Vincent /* Truncate parameters if 32b SMC convention call */
86992f091bSAmbroise Vincent if (GET_SMC_CC(smc_fid) == SMC_32) {
87992f091bSAmbroise Vincent arg2 &= 0xffffffff;
88992f091bSAmbroise Vincent arg3 &= 0xffffffff;
89992f091bSAmbroise Vincent arg4 &= 0xffffffff;
90992f091bSAmbroise Vincent }
91992f091bSAmbroise Vincent
92992f091bSAmbroise Vincent spin_lock(&debugfs_access_lock);
93992f091bSAmbroise Vincent
94992f091bSAmbroise Vincent if (debugfs_initialized == true) {
95992f091bSAmbroise Vincent /* Copy NS shared buffer to internal secure location */
96992f091bSAmbroise Vincent memcpy(&parms, (void *)DEBUGFS_SHARED_BUF_VIRT,
97992f091bSAmbroise Vincent sizeof(union debugfs_parms));
98992f091bSAmbroise Vincent }
99992f091bSAmbroise Vincent
100992f091bSAmbroise Vincent switch (cmd) {
101992f091bSAmbroise Vincent case INIT:
102992f091bSAmbroise Vincent if (debugfs_initialized == false) {
103992f091bSAmbroise Vincent /* TODO: check PA validity e.g. whether */
104992f091bSAmbroise Vincent /* it is an NS region. */
105992f091bSAmbroise Vincent ret = mmap_add_dynamic_region(arg2,
106992f091bSAmbroise Vincent (uintptr_t)DEBUGFS_SHARED_BUF_VIRT,
107992f091bSAmbroise Vincent PAGE_SIZE_4KB,
108992f091bSAmbroise Vincent MT_MEMORY | MT_RW | MT_NS);
109992f091bSAmbroise Vincent if (ret == 0) {
110992f091bSAmbroise Vincent debugfs_initialized = true;
111992f091bSAmbroise Vincent smc_ret = SMC_OK;
112992f091bSAmbroise Vincent smc_resp = 0;
113992f091bSAmbroise Vincent }
114992f091bSAmbroise Vincent }
115992f091bSAmbroise Vincent break;
116992f091bSAmbroise Vincent
117992f091bSAmbroise Vincent case VERSION:
118992f091bSAmbroise Vincent smc_ret = SMC_OK;
119992f091bSAmbroise Vincent smc_resp = DEBUGFS_VERSION;
120992f091bSAmbroise Vincent break;
121992f091bSAmbroise Vincent
122992f091bSAmbroise Vincent case MOUNT:
123992f091bSAmbroise Vincent ret = mount(parms.mount.srv,
124992f091bSAmbroise Vincent parms.mount.where,
125992f091bSAmbroise Vincent parms.mount.spec);
126992f091bSAmbroise Vincent if (ret == 0) {
127992f091bSAmbroise Vincent smc_ret = SMC_OK;
128992f091bSAmbroise Vincent smc_resp = 0;
129992f091bSAmbroise Vincent }
130992f091bSAmbroise Vincent break;
131992f091bSAmbroise Vincent
132992f091bSAmbroise Vincent case OPEN:
133992f091bSAmbroise Vincent ret = open(parms.open.fname, arg2);
134992f091bSAmbroise Vincent if (ret >= 0) {
135992f091bSAmbroise Vincent smc_ret = SMC_OK;
136992f091bSAmbroise Vincent smc_resp = ret;
137992f091bSAmbroise Vincent }
138992f091bSAmbroise Vincent break;
139992f091bSAmbroise Vincent
140992f091bSAmbroise Vincent case CLOSE:
141992f091bSAmbroise Vincent ret = close(arg2);
142992f091bSAmbroise Vincent if (ret == 0) {
143992f091bSAmbroise Vincent smc_ret = SMC_OK;
144992f091bSAmbroise Vincent smc_resp = 0;
145992f091bSAmbroise Vincent }
146992f091bSAmbroise Vincent break;
147992f091bSAmbroise Vincent
148992f091bSAmbroise Vincent case READ:
149992f091bSAmbroise Vincent ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3);
150992f091bSAmbroise Vincent if (ret >= 0) {
151992f091bSAmbroise Vincent smc_ret = SMC_OK;
152992f091bSAmbroise Vincent smc_resp = ret;
153992f091bSAmbroise Vincent }
154992f091bSAmbroise Vincent break;
155992f091bSAmbroise Vincent
156992f091bSAmbroise Vincent case SEEK:
157992f091bSAmbroise Vincent ret = seek(arg2, arg3, arg4);
158992f091bSAmbroise Vincent if (ret == 0) {
159992f091bSAmbroise Vincent smc_ret = SMC_OK;
160992f091bSAmbroise Vincent smc_resp = 0;
161992f091bSAmbroise Vincent }
162992f091bSAmbroise Vincent break;
163992f091bSAmbroise Vincent
164992f091bSAmbroise Vincent case BIND:
165992f091bSAmbroise Vincent ret = bind(parms.bind.oldpath, parms.bind.newpath);
166992f091bSAmbroise Vincent if (ret == 0) {
167992f091bSAmbroise Vincent smc_ret = SMC_OK;
168992f091bSAmbroise Vincent smc_resp = 0;
169992f091bSAmbroise Vincent }
170992f091bSAmbroise Vincent break;
171992f091bSAmbroise Vincent
172992f091bSAmbroise Vincent case STAT:
173992f091bSAmbroise Vincent ret = stat(parms.stat.path, &parms.stat.dir);
174992f091bSAmbroise Vincent if (ret == 0) {
175992f091bSAmbroise Vincent memcpy((void *)DEBUGFS_SHARED_BUF_VIRT, &parms,
176992f091bSAmbroise Vincent sizeof(union debugfs_parms));
177992f091bSAmbroise Vincent smc_ret = SMC_OK;
178992f091bSAmbroise Vincent smc_resp = 0;
179992f091bSAmbroise Vincent }
180992f091bSAmbroise Vincent break;
181992f091bSAmbroise Vincent
182992f091bSAmbroise Vincent /* Not implemented */
183992f091bSAmbroise Vincent case CREATE:
184992f091bSAmbroise Vincent /* Intentional fall-through */
185992f091bSAmbroise Vincent
186992f091bSAmbroise Vincent /* Not implemented */
187992f091bSAmbroise Vincent case WRITE:
188992f091bSAmbroise Vincent /* Intentional fall-through */
189992f091bSAmbroise Vincent
190992f091bSAmbroise Vincent default:
191992f091bSAmbroise Vincent smc_ret = SMC_UNK;
192992f091bSAmbroise Vincent smc_resp = 0;
193992f091bSAmbroise Vincent }
194992f091bSAmbroise Vincent
195992f091bSAmbroise Vincent spin_unlock(&debugfs_access_lock);
196992f091bSAmbroise Vincent
197992f091bSAmbroise Vincent SMC_RET2(handle, smc_ret, smc_resp);
198992f091bSAmbroise Vincent
199992f091bSAmbroise Vincent /* Not reached */
200992f091bSAmbroise Vincent return smc_ret;
201992f091bSAmbroise Vincent }
202992f091bSAmbroise Vincent
debugfs_smc_setup(void)203992f091bSAmbroise Vincent int debugfs_smc_setup(void)
204992f091bSAmbroise Vincent {
205992f091bSAmbroise Vincent debugfs_initialized = false;
206992f091bSAmbroise Vincent debugfs_access_lock.lock = 0;
207992f091bSAmbroise Vincent
208992f091bSAmbroise Vincent return 0;
209992f091bSAmbroise Vincent }
210