summaryrefslogtreecommitdiffstats
path: root/lib/debugfs/debugfs_smc.c
blob: 64c64f80e98b315a0a4b9a8363aa4fc2f378eee2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include <lib/debugfs.h>
#include <lib/smccc.h>
#include <lib/spinlock.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <smccc_helpers.h>

#define MAX_PATH_LEN	256

#define MOUNT		0
#define CREATE		1
#define OPEN		2
#define CLOSE		3
#define READ		4
#define WRITE		5
#define SEEK		6
#define BIND		7
#define STAT		8
#define INIT		10
#define VERSION		11

/* This is the virtual address to which we map the NS shared buffer */
#define DEBUGFS_SHARED_BUF_VIRT		((void *)0x81000000U)

static union debugfs_parms {
	struct {
		char fname[MAX_PATH_LEN];
	} open;

	struct {
		char srv[MAX_PATH_LEN];
		char where[MAX_PATH_LEN];
		char spec[MAX_PATH_LEN];
	} mount;

	struct {
		char path[MAX_PATH_LEN];
		dir_t dir;
	} stat;

	struct {
		char oldpath[MAX_PATH_LEN];
		char newpath[MAX_PATH_LEN];
	} bind;
} parms;

/* debugfs_access_lock protects shared buffer and internal */
/* FS functions from concurrent accesses.                 */
static spinlock_t debugfs_access_lock;

static bool debugfs_initialized;

uintptr_t 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)
{
	int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0;
	int ret;

	/* Allow calls from non-secure only */
	if (is_caller_secure(flags)) {
		SMC_RET1(handle, DEBUGFS_E_DENIED);
	}

	/* Expect a SiP service fast call */
	if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) ||
		(GET_SMC_OEN(smc_fid) != OEN_SIP_START)) {
		SMC_RET1(handle, SMC_UNK);
	}

	/* Truncate parameters if 32b SMC convention call */
	if (GET_SMC_CC(smc_fid) == SMC_32) {
		arg2 &= 0xffffffff;
		arg3 &= 0xffffffff;
		arg4 &= 0xffffffff;
	}

	spin_lock(&debugfs_access_lock);

	if (debugfs_initialized == true) {
		/* Copy NS shared buffer to internal secure location */
		memcpy(&parms, (void *)DEBUGFS_SHARED_BUF_VIRT,
		       sizeof(union debugfs_parms));
	}

	switch (cmd) {
	case INIT:
		if (debugfs_initialized == false) {
			/* TODO: check PA validity e.g. whether */
			/* it is an NS region.                  */
			ret = mmap_add_dynamic_region(arg2,
				(uintptr_t)DEBUGFS_SHARED_BUF_VIRT,
				PAGE_SIZE_4KB,
				MT_MEMORY | MT_RW | MT_NS);
			if (ret == 0) {
				debugfs_initialized = true;
				smc_ret = SMC_OK;
				smc_resp = 0;
			}
		}
		break;

	case VERSION:
		smc_ret = SMC_OK;
		smc_resp = DEBUGFS_VERSION;
		break;

	case MOUNT:
		ret = mount(parms.mount.srv,
			    parms.mount.where,
			    parms.mount.spec);
		if (ret == 0) {
			smc_ret = SMC_OK;
			smc_resp = 0;
		}
		break;

	case OPEN:
		ret = open(parms.open.fname, arg2);
		if (ret >= 0) {
			smc_ret = SMC_OK;
			smc_resp = ret;
		}
		break;

	case CLOSE:
		ret = close(arg2);
		if (ret == 0) {
			smc_ret = SMC_OK;
			smc_resp = 0;
		}
		break;

	case READ:
		ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3);
		if (ret >= 0) {
			smc_ret = SMC_OK;
			smc_resp = ret;
		}
		break;

	case SEEK:
		ret = seek(arg2, arg3, arg4);
		if (ret == 0) {
			smc_ret = SMC_OK;
			smc_resp = 0;
		}
		break;

	case BIND:
		ret = bind(parms.bind.oldpath, parms.bind.newpath);
		if (ret == 0) {
			smc_ret = SMC_OK;
			smc_resp = 0;
		}
		break;

	case STAT:
		ret = stat(parms.stat.path, &parms.stat.dir);
		if (ret == 0) {
			memcpy((void *)DEBUGFS_SHARED_BUF_VIRT, &parms,
			       sizeof(union debugfs_parms));
			smc_ret = SMC_OK;
			smc_resp = 0;
		}
		break;

	/* Not implemented */
	case CREATE:
		/* Intentional fall-through */

	/* Not implemented */
	case WRITE:
		/* Intentional fall-through */

	default:
		smc_ret = SMC_UNK;
		smc_resp = 0;
	}

	spin_unlock(&debugfs_access_lock);

	SMC_RET2(handle, smc_ret, smc_resp);

	/* Not reached */
	return smc_ret;
}

int debugfs_smc_setup(void)
{
	debugfs_initialized = false;
	debugfs_access_lock.lock = 0;

	return 0;
}