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