blob: dcc57b2490c1ffdb5d0e9eecb2290e540dc657bb [file] [log] [blame]
Ambroise Vincent81042e32019-07-11 14:49:47 +01001/*
Govindraj Rajaf33112d2024-02-26 09:44:36 -06002 * Copyright (c) 2019-2024, Arm Limited. All rights reserved.
Ambroise Vincent81042e32019-07-11 14:49:47 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <platform_def.h>
8#include <tftf_lib.h>
9#include <xlat_tables_v2.h>
10
11#include "debugfs.h"
12
13#define SMC_OK (0)
14
15#define DEBUGFS_VERSION (0x00000001)
Ambroise Vincent81042e32019-07-11 14:49:47 +010016#define MAX_PATH_LEN (256)
17
Govindraj Rajaf33112d2024-02-26 09:44:36 -060018#ifndef __aarch64__
19#define DEBUGFS_SMC 0x87000010
20#else
21#define DEBUGFS_SMC 0xC7000010
22#endif
23
24
Ambroise Vincent81042e32019-07-11 14:49:47 +010025/* DebugFS shared buffer area */
26#ifndef PLAT_ARM_DEBUGFS_BASE
27#define PLAT_ARM_DEBUGFS_BASE (0x81000000)
28#define PLAT_ARM_DEBUGFS_SIZE (0x1000)
29#endif /* PLAT_ARM_DEBUGFS_BASE */
30
31union debugfs_parms {
32 struct {
33 char fname[MAX_PATH_LEN];
34 } open;
35
36 struct mount {
37 char srv[MAX_PATH_LEN];
38 char where[MAX_PATH_LEN];
39 char spec[MAX_PATH_LEN];
40 } mount;
41
42 struct {
43 char path[MAX_PATH_LEN];
44 dir_t dir;
45 } stat;
46
47 struct {
48 char oldpath[MAX_PATH_LEN];
49 char newpath[MAX_PATH_LEN];
50 } bind;
51};
52
53typedef struct {
54 char *name;
55 qid_t qid;
56} dir_expected_t;
57
58static const dir_expected_t root_dir_expected[] = {
59 { "dev", 0x8001 },
60 { "blobs", 0x8003 },
61 { "fip", 0x8002 }
62};
63
64static unsigned int read_buffer[4096 / sizeof(unsigned int)];
65
66static void *const payload = (void *) PLAT_ARM_DEBUGFS_BASE;
67
68static int init(unsigned long long phys_addr)
69{
70 smc_ret_values ret;
71 smc_args args;
72
Govindraj Rajaf33112d2024-02-26 09:44:36 -060073 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +010074 args.arg1 = INIT;
75 args.arg2 = phys_addr;
76 ret = tftf_smc(&args);
77
78 return (ret.ret0 == SMC_OK) ? 0 : -1;
79}
80
81static int version(void)
82{
83 smc_ret_values ret;
84 smc_args args;
85
Govindraj Rajaf33112d2024-02-26 09:44:36 -060086 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +010087 args.arg1 = VERSION;
88 ret = tftf_smc(&args);
89
90 return (ret.ret0 == SMC_OK) ? ret.ret1 : -1;
91}
92
93static int open(const char *name, int flags)
94{
95 union debugfs_parms *parms = payload;
96 smc_ret_values ret;
97 smc_args args;
98
99 strlcpy(parms->open.fname, name, MAX_PATH_LEN);
100
Govindraj Rajaf33112d2024-02-26 09:44:36 -0600101 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +0100102 args.arg1 = OPEN;
103 args.arg2 = (u_register_t) flags;
104 ret = tftf_smc(&args);
105
106 return (ret.ret0 == SMC_OK) ? ret.ret1 : -1;
107}
108
109static int read(int fd, void *buf, size_t size)
110{
111 smc_ret_values ret;
112 smc_args args;
113
Govindraj Rajaf33112d2024-02-26 09:44:36 -0600114 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +0100115 args.arg1 = READ;
116 args.arg2 = (u_register_t) fd;
117 args.arg3 = (u_register_t) size;
118
119 ret = tftf_smc(&args);
120
121 if (ret.ret0 == SMC_OK) {
122 memcpy(buf, payload, size);
123 return ret.ret1;
124 }
125
126 return -1;
127}
128
129static int close(int fd)
130{
131 smc_ret_values ret;
132 smc_args args;
133
Govindraj Rajaf33112d2024-02-26 09:44:36 -0600134 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +0100135 args.arg1 = CLOSE;
136 args.arg2 = (u_register_t) fd;
137
138 ret = tftf_smc(&args);
139
140 return (ret.ret0 == SMC_OK) ? 0 : -1;
141}
142
143static int mount(char *srv, char *where, char *spec)
144{
145 union debugfs_parms *parms = payload;
146 smc_ret_values ret;
147 smc_args args;
148
149 strlcpy(parms->mount.srv, srv, MAX_PATH_LEN);
150 strlcpy(parms->mount.where, where, MAX_PATH_LEN);
151 strlcpy(parms->mount.spec, spec, MAX_PATH_LEN);
152
Govindraj Rajaf33112d2024-02-26 09:44:36 -0600153 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +0100154 args.arg1 = MOUNT;
155
156 ret = tftf_smc(&args);
157
158 return (ret.ret0 == SMC_OK) ? 0 : -1;
159}
160
161static int stat(const char *name, dir_t *dir)
162{
163 union debugfs_parms *parms = payload;
164 smc_ret_values ret;
165 smc_args args;
166
167 strlcpy(parms->stat.path, name, MAX_PATH_LEN);
168
Govindraj Rajaf33112d2024-02-26 09:44:36 -0600169 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +0100170 args.arg1 = STAT;
171
172 ret = tftf_smc(&args);
173
174 if (ret.ret0 == SMC_OK) {
175 memcpy(dir, &parms->stat.dir, sizeof(dir_t));
176 return 0;
177 }
178
179 return -1;
180}
181
182static int seek(int fd, long offset, int whence)
183{
184 smc_ret_values ret;
185 smc_args args;
186
Govindraj Rajaf33112d2024-02-26 09:44:36 -0600187 args.fid = DEBUGFS_SMC;
Ambroise Vincent81042e32019-07-11 14:49:47 +0100188 args.arg1 = SEEK;
189 args.arg2 = (u_register_t) fd;
190 args.arg3 = (u_register_t) offset;
191 args.arg4 = (u_register_t) whence;
192
193 ret = tftf_smc(&args);
194
195 return (ret.ret0 == SMC_OK) ? 0 : -1;
196}
197
198static bool compare_dir(const dir_expected_t *dir_expected,
199 unsigned int iteration, dir_t *dir)
200{
201 return ((memcmp(dir->name, dir_expected[iteration].name,
202 strlen(dir_expected[iteration].name)) == 0) &&
203 (dir->qid == dir_expected[iteration].qid));
204}
205
206static void dir_print(dir_t *dir)
207{
208 tftf_testcase_printf("name: %s, length: %ld, mode: %d, type: %d, "
209 "dev: %d, qid: 0x%x\n",
210 dir->name,
211 dir->length,
212 dir->mode,
213 dir->type,
214 dir->dev,
215 dir->qid);
216}
217
218/*
219 * @Test_Aim@ Issue SMCs to TF-A calling debugfs functions in order to test
220 * the exposure of the filesystem.
221 * The result is displayed on the console, something that should look like:
222 * > ls /
223 * dev
224 * fip
225 * blobs
226 */
227test_result_t test_debugfs(void)
228{
229 int fd, ret, iteration;
230 dir_t dir;
231
232 /* Get debugfs interface version (if implemented)*/
233 ret = version();
234 if (ret != DEBUGFS_VERSION) {
235 /* Likely debugfs feature is not implemented */
236 return TEST_RESULT_SKIPPED;
237 }
238
239 /* Initialize debugfs feature, this maps the NS shared buffer in SWd */
240 ret = init(PLAT_ARM_DEBUGFS_BASE);
241 if (ret != 0) {
242 return TEST_RESULT_FAIL;
243 }
244
245 /* Calling init a second time should fail */
246 ret = init(PLAT_ARM_DEBUGFS_BASE);
247 if (ret == 0) {
248 tftf_testcase_printf("init succeeded ret=%d\n", ret);
249 return TEST_RESULT_FAIL;
250 }
251
252 /* open non-existing directory */
253 fd = open("/dummy", O_READ);
254 if (fd >= 0) {
255 tftf_testcase_printf("open succeeded fd=%d\n", fd);
256 return TEST_RESULT_FAIL;
257 }
258
259 /* stat non-existent file from root */
260 ret = stat("/unknown", &dir);
261 if (ret == 0) {
262 tftf_testcase_printf("stat succeeded ret=%d\n", ret);
263 return TEST_RESULT_FAIL;
264 }
265
266 /***************** Root directory listing **************/
267 /* open root directory */
268 fd = open("/", O_READ);
269 if (fd < 0) {
270 tftf_testcase_printf("open failed fd=%d\n", fd);
271 return TEST_RESULT_FAIL;
272 }
273
274 /* read directory entries */
275 iteration = 0;
276 ret = read(fd, &dir, sizeof(dir));
277 while (ret > 0) {
278 if (compare_dir(root_dir_expected, iteration++,
279 &dir) == false) {
280 dir_print(&dir);
281 return TEST_RESULT_FAIL;
282 }
283
284 ret = read(fd, &dir, sizeof(dir));
285 }
286
287 /* close root directory handle */
288 ret = close(fd);
289 if (ret < 0) {
290 tftf_testcase_printf("close failed ret=%d\n", ret);
291 return TEST_RESULT_FAIL;
292 }
293
294 /***************** FIP operations **************/
295 /* mount fip */
296 ret = mount("#F", "/fip", "/blobs/fip.bin");
297 if (ret < 0) {
298 tftf_testcase_printf("mount failed ret=%d\n", ret);
299 return TEST_RESULT_FAIL;
300 }
301
302 /* stat a non-existent file from fip */
303 ret = stat("/fip/unknown", &dir);
304 if (ret == 0) {
305 tftf_testcase_printf("stat succeeded ret=%d\n", ret);
306 return TEST_RESULT_FAIL;
307 }
308
309 /* detect bl2 image presence */
310 ret = stat("/fip/bl2.bin", &dir);
311 if (ret != 0) {
312 tftf_testcase_printf("stat failed ret=%d\n", ret);
313 return TEST_RESULT_FAIL;
314 }
315
316 /* open bl2 */
317 fd = open("/fip/bl2.bin", O_READ);
318 if (fd < 0) {
319 tftf_testcase_printf("open failed fd=%d\n", fd);
320 return TEST_RESULT_FAIL;
321 }
322
323 /* read first 128 bytes */
324 ret = read(fd, read_buffer, 128);
325 if (ret != 128) {
326 tftf_testcase_printf("read failed(%d) ret=%d\n", __LINE__, ret);
327 return TEST_RESULT_FAIL;
328 }
329
330 /* Compare first word of bl2 binary */
331 if (read_buffer[0] != 0xaa0003f4) {
332 tftf_testcase_printf("read ret %d, buf[0]: 0x%x\n",
333 ret, read_buffer[0]);
334 return TEST_RESULT_FAIL;
335 }
336
337 /* rewind to file start */
338 ret = seek(fd, 0, KSEEK_SET);
339 if (ret != 0) {
340 tftf_testcase_printf("seek failed ret=%d\n", ret);
341 return TEST_RESULT_FAIL;
342 }
343
344 size_t read_size = 0;
345 do {
346 ret = read(fd, read_buffer, sizeof(read_buffer));
347 if (ret < 0) {
348 tftf_testcase_printf("read failed(%d) ret=%d\n",
349 __LINE__, ret);
350 return TEST_RESULT_FAIL;
351 }
352 read_size += ret;
353 } while (ret);
354
355 if (read_size != dir.length) {
356 tftf_testcase_printf("read size mismatch read_size=%zu "
357 "dir.length=%ld\n", read_size, dir.length);
358 return TEST_RESULT_FAIL;
359 }
360
361 return TEST_RESULT_SUCCESS;
362}
Harrison Mutai0f04afb2025-09-18 11:07:12 +0000363
364/*
365 * @Test_Aim@ Verify that the I/O layer correctly handles out-of-bounds access.
366 *
367 * This test ensures robustness of seek/read APIs against invalid
368 * offsets and prevents potential memory or file corruption due to invalid
369 * access.
370 */
371test_result_t test_oob_access(void)
372{
373 int ret, fd;
374
375 /* Get debugfs interface version (if implemented)*/
376 ret = version();
377 if (ret != DEBUGFS_VERSION) {
378 /* Likely debugfs feature is not implemented */
379 return TEST_RESULT_SKIPPED;
380 }
381
382 /* Open BL2. */
383 fd = open("/fip/bl2.bin", O_READ);
384 if (fd < 0) {
385 tftf_testcase_printf("open failed fd=%d\n", fd);
386 return TEST_RESULT_FAIL;
387 }
388
389 /* Rewind to negative offset. */
390 ret = seek(fd, -1000, KSEEK_SET);
391
392 /* Reading with out-of-bounds cursor position should fail */
393 ret = read(fd, read_buffer, 128);
394 if (ret != 0) {
395 tftf_testcase_printf("Out-of-bounds read(%d)\n", __LINE__);
396 return TEST_RESULT_FAIL;
397 }
398
399 /* Reset cursor position to valid range. */
400 ret = seek(fd, 0, KSEEK_SET);
401
402 /* Read 128 bytes from start */
403 ret = read(fd, read_buffer, 128);
404 if (ret != 128) {
405 tftf_testcase_printf("read failed(%d) ret=%d\n", __LINE__, ret);
406 return TEST_RESULT_FAIL;
407 }
408
409 return TEST_RESULT_SUCCESS;
410}