xref: /illumos-gate/usr/src/boot/userboot/test/test.c (revision 10a869258e300c530ae56b29aa3bf43461ca98ff)
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 *
64 _umem_debug_init(void)
65 {
66 	return ("default,verbose"); /* $UMEM_DEBUG setting */
67 }
68 
69 const char *
70 _umem_logging_init(void)
71 {
72 	return ("fail,contents"); /* $UMEM_LOGGING setting */
73 }
74 
75 /*
76  * Console i/o
77  */
78 
79 void
80 test_putc(void *arg, int ch)
81 {
82 	char c = ch;
83 
84 	(void) write(1, &c, 1);
85 }
86 
87 int
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
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
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
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
184 test_isdir(void *arg, void *h)
185 {
186 	struct test_file *tf = h;
187 
188 	return (tf->tf_isdir);
189 }
190 
191 int
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
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
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
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
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
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
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
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
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
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
369 test_setmsr(void *arg, int r, uint64_t v)
370 {
371 }
372 
373 void
374 test_setcr(void *arg, int r, uint64_t v)
375 {
376 }
377 
378 void
379 test_setgdt(void *arg, uint64_t v, size_t sz)
380 {
381 }
382 
383 void
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
395 test_delay(void *arg, int usec)
396 {
397 
398 	(void) usleep(usec);
399 }
400 
401 void
402 test_exit(void *arg, int v)
403 {
404 
405 	(void) tcsetattr(0, TCSAFLUSH, &oldterm);
406 	exit(v);
407 }
408 
409 void
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 *
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
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
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