1 /*
2 * Copyright (c) 2011 Google, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/types.h>
28 #include <sys/disk.h>
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/filio.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 <limits.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <termios.h>
44 #include <unistd.h>
45
46 #include <userboot.h>
47
48 int *zfs_debug;
49
50 static int script;
51 static char *host_base;
52 static struct termios term, oldterm;
53 static char *image;
54 static size_t image_size;
55
56 uint64_t regs[16];
57 uint64_t pc;
58 int *disk_fd;
59 int disk_index = -1;
60
61 void test_exit(void *arg, int v);
62
63 const char *
_umem_debug_init(void)64 _umem_debug_init(void)
65 {
66 return ("default,verbose"); /* $UMEM_DEBUG setting */
67 }
68
69 const char *
_umem_logging_init(void)70 _umem_logging_init(void)
71 {
72 return ("fail,contents"); /* $UMEM_LOGGING setting */
73 }
74
75 /*
76 * Console i/o
77 */
78
79 void
test_putc(void * arg,int ch)80 test_putc(void *arg, int ch)
81 {
82 char c = ch;
83
84 (void) write(1, &c, 1);
85 }
86
87 int
test_getc(void * arg)88 test_getc(void *arg)
89 {
90 char c;
91
92 if (read(script, &c, 1) == 1) {
93 if (c == '\n')
94 c = '\r';
95 return (c);
96 }
97 return (-1);
98 }
99
100 int
test_poll(void * arg)101 test_poll(void *arg)
102 {
103 int n;
104
105 if (ioctl(script, FIONREAD, &n) >= 0)
106 return (n > 0);
107 return (0);
108 }
109
110 /*
111 * Host filesystem i/o
112 */
113
114 struct test_file {
115 char *tf_name;
116 int tf_isdir;
117 size_t tf_size;
118 struct stat tf_stat;
119 union {
120 int fd;
121 DIR *dir;
122 } tf_u;
123 };
124
125 int
test_open(void * arg,const char * filename,void ** h_return)126 test_open(void *arg, const char *filename, void **h_return)
127 {
128 struct test_file *tf;
129 char path[PATH_MAX];
130
131 if (!host_base)
132 return (ENOENT);
133
134 (void) strlcpy(path, host_base, PATH_MAX);
135 if (path[strlen(path) - 1] == '/')
136 path[strlen(path) - 1] = 0;
137 (void) strlcat(path, filename, PATH_MAX);
138 tf = malloc(sizeof (struct test_file));
139 if (stat(path, &tf->tf_stat) < 0) {
140 free(tf);
141 return (errno);
142 }
143
144 tf->tf_name = strdup(path);
145 tf->tf_size = tf->tf_stat.st_size;
146 if (S_ISDIR(tf->tf_stat.st_mode)) {
147 tf->tf_isdir = 1;
148 tf->tf_u.dir = opendir(path);
149 if (!tf->tf_u.dir)
150 goto out;
151 *h_return = tf;
152 return (0);
153 }
154 if (S_ISREG(tf->tf_stat.st_mode)) {
155 tf->tf_isdir = 0;
156 tf->tf_u.fd = open(path, O_RDONLY);
157 if (tf->tf_u.fd < 0)
158 goto out;
159 *h_return = tf;
160 return (0);
161 }
162
163 out:
164 free(tf);
165 return (EINVAL);
166 }
167
168 int
test_close(void * arg,void * h)169 test_close(void *arg, void *h)
170 {
171 struct test_file *tf = h;
172
173 if (tf->tf_isdir)
174 (void) closedir(tf->tf_u.dir);
175 else
176 (void) close(tf->tf_u.fd);
177 free(tf->tf_name);
178 free(tf);
179
180 return (0);
181 }
182
183 int
test_isdir(void * arg,void * h)184 test_isdir(void *arg, void *h)
185 {
186 struct test_file *tf = h;
187
188 return (tf->tf_isdir);
189 }
190
191 int
test_read(void * arg,void * h,void * dst,size_t size,size_t * resid_return)192 test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return)
193 {
194 struct test_file *tf = h;
195 ssize_t sz;
196
197 if (tf->tf_isdir)
198 return (EINVAL);
199 sz = read(tf->tf_u.fd, dst, size);
200 if (sz < 0)
201 return (EINVAL);
202 *resid_return = size - sz;
203 return (0);
204 }
205
206 int
test_readdir(void * arg,void * h,uint32_t * fileno_return,uint8_t * type_return,size_t * namelen_return,char * name)207 test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
208 size_t *namelen_return, char *name)
209 {
210 struct test_file *tf = h;
211 struct dirent *dp;
212 struct stat st;
213 char path[PATH_MAX];
214
215 if (!tf->tf_isdir)
216 return (EINVAL);
217
218 dp = readdir(tf->tf_u.dir);
219 if (!dp)
220 return (ENOENT);
221 (void) snprintf(path, sizeof (path), "%s/%s/%s", host_base, tf->tf_name,
222 dp->d_name);
223 if (lstat(path, &st) < 0) {
224 return (errno);
225 } else {
226 *type_return = (st.st_mode & S_IFMT) >> 12;
227 }
228
229 /*
230 * Note: d_namlen is in the range 0..255 and therefore less
231 * than PATH_MAX so we don't need to test before copying.
232 */
233 *fileno_return = dp->d_ino;
234 *namelen_return = strlen(dp->d_name);
235 (void) strcpy(name, dp->d_name);
236
237 return (0);
238 }
239
240 int
test_seek(void * arg,void * h,uint64_t offset,int whence)241 test_seek(void *arg, void *h, uint64_t offset, int whence)
242 {
243 struct test_file *tf = h;
244
245 if (tf->tf_isdir)
246 return (EINVAL);
247 if (lseek(tf->tf_u.fd, offset, whence) < 0)
248 return (errno);
249 return (0);
250 }
251
252 int
test_stat(void * arg,void * h,int * mode_return,int * uid_return,int * gid_return,uint64_t * size_return)253 test_stat(void *arg, void *h, int *mode_return, int *uid_return,
254 int *gid_return, uint64_t *size_return)
255 {
256 struct test_file *tf = h;
257
258 *mode_return = tf->tf_stat.st_mode;
259 *uid_return = tf->tf_stat.st_uid;
260 *gid_return = tf->tf_stat.st_gid;
261 *size_return = tf->tf_stat.st_size;
262 return (0);
263 }
264
265 /*
266 * Disk image i/o
267 */
268
269 int
test_diskwrite(void * arg,int unit,uint64_t offset,void * src,size_t size,size_t * resid_return)270 test_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size,
271 size_t *resid_return)
272 {
273 ssize_t n;
274
275 if (unit > disk_index || disk_fd[unit] == -1)
276 return (EIO);
277 n = pwrite(disk_fd[unit], src, size, offset);
278 if (n < 0)
279 return (errno);
280 *resid_return = size - n;
281 return (0);
282 }
283
284 int
test_diskread(void * arg,int unit,uint64_t offset,void * dst,size_t size,size_t * resid_return)285 test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size,
286 size_t *resid_return)
287 {
288 ssize_t n;
289
290 if (unit > disk_index || disk_fd[unit] == -1)
291 return (EIO);
292 n = pread(disk_fd[unit], dst, size, offset);
293 if (n == 0) {
294 printf("%s: end of disk (%ju)\n", __func__, (intmax_t)offset);
295 return (EIO);
296 }
297
298 if (n < 0)
299 return (errno);
300 *resid_return = size - n;
301 return (0);
302 }
303
304 int
test_diskioctl(void * arg,int unit,ulong_t cmd,void * data)305 test_diskioctl(void *arg, int unit, ulong_t cmd, void *data)
306 {
307 struct stat sb;
308
309 if (unit > disk_index || disk_fd[unit] == -1)
310 return (EBADF);
311 switch (cmd) {
312 case DIOCGSECTORSIZE:
313 *(uint_t *)data = 512;
314 break;
315 case DIOCGMEDIASIZE:
316 if (fstat(disk_fd[unit], &sb) == 0)
317 *(off_t *)data = sb.st_size;
318 else
319 return (ENOTTY);
320 break;
321 default:
322 return (ENOTTY);
323 }
324 return (0);
325 }
326
327 /*
328 * Guest virtual machine i/o
329 *
330 * Note: guest addresses are kernel virtual
331 */
332
333 int
test_copyin(void * arg,const void * from,uint64_t to,size_t size)334 test_copyin(void *arg, const void *from, uint64_t to, size_t size)
335 {
336
337 to &= 0x7fffffff;
338 if (to > image_size)
339 return (EFAULT);
340 if (to + size > image_size)
341 size = image_size - to;
342 memcpy(&image[to], from, size);
343 return (0);
344 }
345
346 int
test_copyout(void * arg,uint64_t from,void * to,size_t size)347 test_copyout(void *arg, uint64_t from, void *to, size_t size)
348 {
349
350 from &= 0x7fffffff;
351 if (from > image_size)
352 return (EFAULT);
353 if (from + size > image_size)
354 size = image_size - from;
355 memcpy(to, &image[from], size);
356 return (0);
357 }
358
359 void
test_setreg(void * arg,int r,uint64_t v)360 test_setreg(void *arg, int r, uint64_t v)
361 {
362
363 if (r < 0 || r >= 16)
364 return;
365 regs[r] = v;
366 }
367
368 void
test_setmsr(void * arg,int r,uint64_t v)369 test_setmsr(void *arg, int r, uint64_t v)
370 {
371 }
372
373 void
test_setcr(void * arg,int r,uint64_t v)374 test_setcr(void *arg, int r, uint64_t v)
375 {
376 }
377
378 void
test_setgdt(void * arg,uint64_t v,size_t sz)379 test_setgdt(void *arg, uint64_t v, size_t sz)
380 {
381 }
382
383 void
test_exec(void * arg,uint64_t pc)384 test_exec(void *arg, uint64_t pc)
385 {
386 printf("Execute at 0x%"PRIx64"\n\r", pc);
387 test_exit(arg, 0);
388 }
389
390 /*
391 * Misc
392 */
393
394 void
test_delay(void * arg,int usec)395 test_delay(void *arg, int usec)
396 {
397
398 (void) usleep(usec);
399 }
400
401 void
test_exit(void * arg,int v)402 test_exit(void *arg, int v)
403 {
404
405 (void) tcsetattr(0, TCSAFLUSH, &oldterm);
406 exit(v);
407 }
408
409 void
test_getmem(void * arg,uint64_t * lowmem,uint64_t * highmem)410 test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem)
411 {
412
413 *lowmem = 128*1024*1024;
414 *highmem = 0;
415 }
416
417 char *
test_getenv(void * arg,int idx)418 test_getenv(void *arg, int idx)
419 {
420 extern char **environ;
421
422 return (environ[idx]);
423 }
424
425 struct loader_callbacks cb = {
426 .putc = test_putc,
427 .getc = test_getc,
428 .poll = test_poll,
429
430 .open = test_open,
431 .close = test_close,
432 .isdir = test_isdir,
433 .read = test_read,
434 .readdir = test_readdir,
435 .seek = test_seek,
436 .stat = test_stat,
437
438 .diskread = test_diskread,
439 .diskwrite = test_diskwrite,
440 .diskioctl = test_diskioctl,
441
442 .copyin = test_copyin,
443 .copyout = test_copyout,
444 .setreg = test_setreg,
445 .setmsr = test_setmsr,
446 .setcr = test_setcr,
447 .setgdt = test_setgdt,
448 .exec = test_exec,
449
450 .delay = test_delay,
451 .exit = test_exit,
452 .getmem = test_getmem,
453
454 .getenv = test_getenv,
455 };
456
457 void
usage(void)458 usage(void)
459 {
460
461 printf("usage: [-b <userboot shared object>] [-d <disk image path>] "
462 "[-h <host filesystem path>\n");
463 exit(1);
464 }
465
466 int
main(int argc,char ** argv)467 main(int argc, char **argv)
468 {
469 void *h;
470 void (*func)(struct loader_callbacks *, void *, int, int) __NORETURN;
471 int opt;
472 const char *userboot_obj = "/boot/userboot.so";
473 struct winsize ws;
474 int cols = 80, rows = 24, z = 0;
475 int oflag = O_RDONLY;
476 char *buffer = NULL;
477
478 if (ioctl(1, TIOCGWINSZ, &ws) != -1) {
479 if (ws.ws_col)
480 cols = ws.ws_col;
481 if (ws.ws_row)
482 rows = ws.ws_row;
483 }
484
485 (void) clearenv();
486 if (asprintf(&buffer, "%d", cols) > 0)
487 (void) setenv("screen-#cols", buffer, 1);
488 free(buffer);
489 if (asprintf(&buffer, "%d", rows) > 0)
490 (void) setenv("screen-#rows", buffer, 1);
491 free(buffer);
492 (void) setenv("ISADIR", "amd64", 1);
493
494 while ((opt = getopt(argc, argv, "b:d:h:s:wz")) != -1) {
495 switch (opt) {
496 case 'b':
497 userboot_obj = optarg;
498 break;
499
500 case 'd':
501 disk_index++;
502 disk_fd = reallocarray(disk_fd, disk_index + 1,
503 sizeof (int));
504 disk_fd[disk_index] = open(optarg, oflag);
505 if (disk_fd[disk_index] < 0)
506 err(1, "Can't open disk image '%s'", optarg);
507 break;
508
509 case 'h':
510 host_base = optarg;
511 break;
512
513 case 's':
514 if (strcmp(optarg, "-") == 0)
515 script = 0;
516 else
517 script = open(optarg, O_RDONLY);
518
519 if (script < 0)
520 err(1, "Can't open script file '%s'", optarg);
521 break;
522
523 case 'w':
524 oflag = O_RDWR;
525 break;
526
527 case 'z':
528 z = 1;
529 break;
530
531 case '?':
532 usage();
533 }
534 }
535
536 h = dlopen(userboot_obj, RTLD_LAZY | RTLD_LOCAL);
537 if (!h) {
538 printf("%s\n", dlerror());
539 return (1);
540 }
541 func = dlsym(h, "loader_main");
542 if (!func) {
543 printf("%s\n", dlerror());
544 return (1);
545 }
546 zfs_debug = dlsym(h, "zfs_debug");
547 if (zfs_debug != NULL)
548 *zfs_debug = z;
549
550 image_size = 128*1024*1024;
551 image = malloc(image_size);
552
553 (void) tcgetattr(0, &term);
554 oldterm = term;
555 term.c_iflag &= ~(ICRNL | INPCK | ISTRIP);
556 term.c_lflag &= ~(ICANON | ECHO | IEXTEN);
557 term.c_cflag &= ~(CSIZE | PARENB);
558 term.c_cflag |= CS8;
559 term.c_oflag &= ~(OPOST);
560 term.c_cc[VMIN] = 1;
561 term.c_cc[VTIME] = 0;
562 (void) tcsetattr(0, TCSADRAIN, &term);
563
564 func(&cb, NULL, USERBOOT_VERSION_3, disk_index + 1);
565 }
566