Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/userboot/test/test.c
34878 views
1
/*-
2
* Copyright (c) 2011 Google, Inc.
3
* Copyright (c) 2023-2024 Juniper Networks, Inc.
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/types.h>
29
#include <sys/disk.h>
30
#include <sys/ioctl.h>
31
#include <sys/stat.h>
32
#include <dirent.h>
33
#include <dlfcn.h>
34
#include <err.h>
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <getopt.h>
38
#include <inttypes.h>
39
#include <libgen.h>
40
#include <limits.h>
41
#include <stdbool.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <termios.h>
46
#include <unistd.h>
47
48
#include <userboot.h>
49
50
char **vars;
51
52
char *host_base = NULL;
53
struct termios term, oldterm;
54
char *image;
55
size_t image_size;
56
57
uint64_t regs[16];
58
uint64_t pc;
59
int *disk_fd;
60
int disk_index = -1;
61
62
void test_exit(void *arg, int v);
63
64
/*
65
* Console i/o
66
*/
67
68
void
69
test_putc(void *arg, int ch)
70
{
71
char c = ch;
72
73
write(1, &c, 1);
74
}
75
76
int
77
test_getc(void *arg)
78
{
79
char c;
80
81
if (read(0, &c, 1) == 1)
82
return c;
83
return -1;
84
}
85
86
int
87
test_poll(void *arg)
88
{
89
int n;
90
91
if (ioctl(0, FIONREAD, &n) >= 0)
92
return (n > 0);
93
return (0);
94
}
95
96
/*
97
* Host filesystem i/o
98
*/
99
100
struct test_file {
101
int tf_isdir;
102
size_t tf_size;
103
struct stat tf_stat;
104
union {
105
int fd;
106
DIR *dir;
107
} tf_u;
108
};
109
110
static int
111
test_open_internal(void *arg, const char *filename, void **h_return,
112
struct test_file *tf, int depth)
113
{
114
char path[PATH_MAX];
115
char linkpath[PATH_MAX];
116
char *component, *cp, *linkptr;
117
ssize_t slen;
118
int comp_fd, dir_fd, error;
119
char c;
120
bool openbase;
121
122
if (depth++ >= MAXSYMLINKS)
123
return (ELOOP);
124
125
openbase = false;
126
error = EINVAL;
127
if (tf == NULL) {
128
tf = calloc(1, sizeof(struct test_file));
129
if (tf == NULL)
130
return (error);
131
openbase = true;
132
} else if (tf->tf_isdir) {
133
if (filename[0] == '/') {
134
closedir(tf->tf_u.dir);
135
openbase = true;
136
}
137
} else
138
return (error);
139
140
if (openbase) {
141
dir_fd = open(host_base, O_RDONLY);
142
if (dir_fd < 0)
143
goto out;
144
145
tf->tf_isdir = 1;
146
tf->tf_u.dir = fdopendir(dir_fd);
147
148
if (fstat(dir_fd, &tf->tf_stat) < 0) {
149
error = errno;
150
goto out;
151
}
152
tf->tf_size = tf->tf_stat.st_size;
153
}
154
155
strlcpy(path, filename, sizeof(path));
156
cp = path;
157
while (*cp) {
158
/*
159
* The test file should be a directory at this point.
160
* If it is not, then the caller provided an invalid filename.
161
*/
162
if (!tf->tf_isdir)
163
goto out;
164
165
/* Trim leading slashes */
166
while (*cp == '/')
167
cp++;
168
169
/* If we reached the end, we are done */
170
if (*cp == '\0')
171
break;
172
173
/* Get the file descriptor for the directory */
174
dir_fd = dirfd(tf->tf_u.dir);
175
176
/* Get the next component path */
177
component = cp;
178
while ((c = *cp) != '\0' && c != '/')
179
cp++;
180
if (c == '/')
181
*cp++ = '\0';
182
183
/* Get status of the component */
184
if (fstatat(dir_fd, component, &tf->tf_stat,
185
AT_SYMLINK_NOFOLLOW) < 0) {
186
error = errno;
187
goto out;
188
}
189
tf->tf_size = tf->tf_stat.st_size;
190
191
/*
192
* Check that the path component is a directory, regular file,
193
* or a symlink.
194
*/
195
if (!S_ISDIR(tf->tf_stat.st_mode) &&
196
!S_ISREG(tf->tf_stat.st_mode) &&
197
!S_ISLNK(tf->tf_stat.st_mode))
198
goto out;
199
200
/* For anything that is not a symlink, open it */
201
if (!S_ISLNK(tf->tf_stat.st_mode)) {
202
comp_fd = openat(dir_fd, component, O_RDONLY);
203
if (comp_fd < 0)
204
goto out;
205
}
206
207
if (S_ISDIR(tf->tf_stat.st_mode)) {
208
/* Directory */
209
210
/* close the parent directory */
211
closedir(tf->tf_u.dir);
212
213
/* Open the directory from the component descriptor */
214
tf->tf_isdir = 1;
215
tf->tf_u.dir = fdopendir(comp_fd);
216
if (!tf->tf_u.dir)
217
goto out;
218
} else if (S_ISREG(tf->tf_stat.st_mode)) {
219
/* Regular file */
220
221
/* close the parent directory */
222
closedir(tf->tf_u.dir);
223
224
/* Stash the component descriptor */
225
tf->tf_isdir = 0;
226
tf->tf_u.fd = comp_fd;
227
} else if (S_ISLNK(tf->tf_stat.st_mode)) {
228
/* Symlink */
229
230
/* Read what the symlink points to */
231
slen = readlinkat(dir_fd, component, linkpath,
232
sizeof(linkpath));
233
if (slen < 0)
234
goto out;
235
/* NUL-terminate the string */
236
linkpath[(size_t)slen] = '\0';
237
238
/* Open the thing that the symlink points to */
239
error = test_open_internal(arg, linkpath, NULL,
240
tf, depth);
241
if (error != 0)
242
goto out;
243
}
244
}
245
246
/* Completed the entire path and have a good file/directory */
247
if (h_return != NULL)
248
*h_return = tf;
249
return (0);
250
251
out:
252
/* Failure of some sort, clean up */
253
if (tf->tf_isdir)
254
closedir(tf->tf_u.dir);
255
else
256
close(tf->tf_u.fd);
257
free(tf);
258
return (error);
259
}
260
261
int
262
test_open(void *arg, const char *filename, void **h_return)
263
{
264
if (host_base == NULL)
265
return (ENOENT);
266
267
return (test_open_internal(arg, filename, h_return, NULL, 0));
268
}
269
270
int
271
test_close(void *arg, void *h)
272
{
273
struct test_file *tf = h;
274
275
if (tf->tf_isdir)
276
closedir(tf->tf_u.dir);
277
else
278
close(tf->tf_u.fd);
279
free(tf);
280
281
return (0);
282
}
283
284
int
285
test_isdir(void *arg, void *h)
286
{
287
struct test_file *tf = h;
288
289
return (tf->tf_isdir);
290
}
291
292
int
293
test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return)
294
{
295
struct test_file *tf = h;
296
ssize_t sz;
297
298
if (tf->tf_isdir)
299
return (EINVAL);
300
sz = read(tf->tf_u.fd, dst, size);
301
if (sz < 0)
302
return (EINVAL);
303
*resid_return = size - sz;
304
return (0);
305
}
306
307
int
308
test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
309
size_t *namelen_return, char *name)
310
{
311
struct test_file *tf = h;
312
struct dirent *dp;
313
314
if (!tf->tf_isdir)
315
return (EINVAL);
316
317
dp = readdir(tf->tf_u.dir);
318
if (!dp)
319
return (ENOENT);
320
321
/*
322
* Note: d_namlen is in the range 0..255 and therefore less
323
* than PATH_MAX so we don't need to test before copying.
324
*/
325
*fileno_return = dp->d_fileno;
326
*type_return = dp->d_type;
327
*namelen_return = dp->d_namlen;
328
memcpy(name, dp->d_name, dp->d_namlen);
329
name[dp->d_namlen] = 0;
330
331
return (0);
332
}
333
334
int
335
test_seek(void *arg, void *h, uint64_t offset, int whence)
336
{
337
struct test_file *tf = h;
338
339
if (tf->tf_isdir)
340
return (EINVAL);
341
if (lseek(tf->tf_u.fd, offset, whence) < 0)
342
return (errno);
343
return (0);
344
}
345
346
int
347
test_stat(void *arg, void *h, struct stat *stp)
348
{
349
struct test_file *tf = h;
350
351
if (!stp)
352
return (-1);
353
memset(stp, 0, sizeof(struct stat));
354
stp->st_mode = tf->tf_stat.st_mode;
355
stp->st_uid = tf->tf_stat.st_uid;
356
stp->st_gid = tf->tf_stat.st_gid;
357
stp->st_size = tf->tf_stat.st_size;
358
stp->st_ino = tf->tf_stat.st_ino;
359
stp->st_dev = tf->tf_stat.st_dev;
360
stp->st_mtime = tf->tf_stat.st_mtime;
361
return (0);
362
}
363
364
/*
365
* Disk image i/o
366
*/
367
368
int
369
test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size,
370
size_t *resid_return)
371
{
372
ssize_t n;
373
374
if (unit > disk_index || disk_fd[unit] == -1)
375
return (EIO);
376
n = pread(disk_fd[unit], dst, size, offset);
377
if (n == 0) {
378
printf("%s: end of disk (%ju)\n", __func__, (intmax_t)offset);
379
return (EIO);
380
}
381
382
if (n < 0)
383
return (errno);
384
*resid_return = size - n;
385
return (0);
386
}
387
388
int
389
test_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size,
390
size_t *resid_return)
391
{
392
ssize_t n;
393
394
if (unit > disk_index || disk_fd[unit] == -1)
395
return (EIO);
396
n = pwrite(disk_fd[unit], src, size, offset);
397
if (n < 0)
398
return (errno);
399
*resid_return = size - n;
400
return (0);
401
}
402
403
int
404
test_diskioctl(void *arg, int unit, u_long cmd, void *data)
405
{
406
struct stat sb;
407
408
if (unit > disk_index || disk_fd[unit] == -1)
409
return (EBADF);
410
switch (cmd) {
411
case DIOCGSECTORSIZE:
412
*(u_int *)data = 512;
413
break;
414
case DIOCGMEDIASIZE:
415
if (fstat(disk_fd[unit], &sb) == 0)
416
*(off_t *)data = sb.st_size;
417
else
418
return (ENOTTY);
419
break;
420
default:
421
return (ENOTTY);
422
}
423
return (0);
424
}
425
426
/*
427
* Guest virtual machine i/o
428
*
429
* Note: guest addresses are kernel virtual
430
*/
431
432
int
433
test_copyin(void *arg, const void *from, uint64_t to, size_t size)
434
{
435
436
to &= 0x7fffffff;
437
if (to > image_size)
438
return (EFAULT);
439
if (to + size > image_size)
440
size = image_size - to;
441
memcpy(&image[to], from, size);
442
return(0);
443
}
444
445
int
446
test_copyout(void *arg, uint64_t from, void *to, size_t size)
447
{
448
449
from &= 0x7fffffff;
450
if (from > image_size)
451
return (EFAULT);
452
if (from + size > image_size)
453
size = image_size - from;
454
memcpy(to, &image[from], size);
455
return(0);
456
}
457
458
void
459
test_setreg(void *arg, int r, uint64_t v)
460
{
461
462
if (r < 0 || r >= 16)
463
return;
464
regs[r] = v;
465
}
466
467
void
468
test_setmsr(void *arg, int r, uint64_t v)
469
{
470
}
471
472
void
473
test_setcr(void *arg, int r, uint64_t v)
474
{
475
}
476
477
void
478
test_setgdt(void *arg, uint64_t v, size_t sz)
479
{
480
}
481
482
void
483
test_exec(void *arg, uint64_t pc)
484
{
485
printf("Execute at 0x%"PRIx64"\n", pc);
486
test_exit(arg, 0);
487
}
488
489
/*
490
* Misc
491
*/
492
493
void
494
test_delay(void *arg, int usec)
495
{
496
497
usleep(usec);
498
}
499
500
void
501
test_exit(void *arg, int v)
502
{
503
504
tcsetattr(0, TCSAFLUSH, &oldterm);
505
exit(v);
506
}
507
508
void
509
test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem)
510
{
511
512
*lowmem = 128*1024*1024;
513
*highmem = 0;
514
}
515
516
char *
517
test_getenv(void *arg, int idx)
518
{
519
static char *myvars[] = {
520
"USERBOOT=1"
521
};
522
static const int num_myvars = nitems(myvars);
523
524
if (idx < num_myvars)
525
return (myvars[idx]);
526
else
527
return (vars[idx - num_myvars]);
528
}
529
530
struct loader_callbacks cb = {
531
.putc = test_putc,
532
.getc = test_getc,
533
.poll = test_poll,
534
535
.open = test_open,
536
.close = test_close,
537
.isdir = test_isdir,
538
.read = test_read,
539
.readdir = test_readdir,
540
.seek = test_seek,
541
.stat = test_stat,
542
543
.diskread = test_diskread,
544
.diskwrite = test_diskwrite,
545
.diskioctl = test_diskioctl,
546
547
.copyin = test_copyin,
548
.copyout = test_copyout,
549
.setreg = test_setreg,
550
.setmsr = test_setmsr,
551
.setcr = test_setcr,
552
.setgdt = test_setgdt,
553
.exec = test_exec,
554
555
.delay = test_delay,
556
.exit = test_exit,
557
.getmem = test_getmem,
558
559
.getenv = test_getenv,
560
};
561
562
void
563
usage()
564
{
565
566
printf("usage: [-b <userboot shared object>] [-d <disk image path>] [-h <host filesystem path>\n");
567
exit(1);
568
}
569
570
int
571
main(int argc, char** argv, char ** environment)
572
{
573
void *h;
574
void (*func)(struct loader_callbacks *, void *, int, int) __dead2;
575
int opt;
576
const char *userboot_obj = "/boot/userboot.so";
577
int oflag = O_RDONLY;
578
579
vars = environment;
580
581
while ((opt = getopt(argc, argv, "wb:d:h:")) != -1) {
582
switch (opt) {
583
case 'b':
584
userboot_obj = optarg;
585
break;
586
587
case 'd':
588
disk_index++;
589
disk_fd = reallocarray(disk_fd, disk_index + 1,
590
sizeof (int));
591
disk_fd[disk_index] = open(optarg, oflag);
592
if (disk_fd[disk_index] < 0)
593
err(1, "Can't open disk image '%s'", optarg);
594
break;
595
596
case 'h':
597
host_base = optarg;
598
break;
599
600
case 'w':
601
oflag = O_RDWR;
602
break;
603
604
case '?':
605
usage();
606
}
607
}
608
609
h = dlopen(userboot_obj, RTLD_LOCAL);
610
if (!h) {
611
printf("%s\n", dlerror());
612
return (1);
613
}
614
func = dlsym(h, "loader_main");
615
if (!func) {
616
printf("%s\n", dlerror());
617
return (1);
618
}
619
620
image_size = 128*1024*1024;
621
image = malloc(image_size);
622
623
tcgetattr(0, &term);
624
oldterm = term;
625
term.c_iflag &= ~(ICRNL);
626
term.c_lflag &= ~(ICANON|ECHO);
627
tcsetattr(0, TCSAFLUSH, &term);
628
629
func(&cb, NULL, USERBOOT_VERSION_3, disk_index + 1);
630
}
631
632