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 * $FreeBSD$ 27 */ 28 29 #include <sys/types.h> 30 #include <sys/disk.h> 31 #include <sys/ioctl.h> 32 #include <sys/stat.h> 33 #include <dirent.h> 34 #include <dlfcn.h> 35 #include <err.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <getopt.h> 39 #include <inttypes.h> 40 #include <limits.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <termios.h> 45 #include <unistd.h> 46 47 #include <userboot.h> 48 49 char *host_base = NULL; 50 struct termios term, oldterm; 51 char *image; 52 size_t image_size; 53 54 uint64_t regs[16]; 55 uint64_t pc; 56 int *disk_fd; 57 int disk_index = -1; 58 59 void test_exit(void *arg, int v); 60 61 /* 62 * Console i/o 63 */ 64 65 void 66 test_putc(void *arg, int ch) 67 { 68 char c = ch; 69 70 write(1, &c, 1); 71 } 72 73 int 74 test_getc(void *arg) 75 { 76 char c; 77 78 if (read(0, &c, 1) == 1) 79 return c; 80 return -1; 81 } 82 83 int 84 test_poll(void *arg) 85 { 86 int n; 87 88 if (ioctl(0, FIONREAD, &n) >= 0) 89 return (n > 0); 90 return (0); 91 } 92 93 /* 94 * Host filesystem i/o 95 */ 96 97 struct test_file { 98 int tf_isdir; 99 size_t tf_size; 100 struct stat tf_stat; 101 union { 102 int fd; 103 DIR *dir; 104 } tf_u; 105 }; 106 107 int 108 test_open(void *arg, const char *filename, void **h_return) 109 { 110 struct stat st; 111 struct test_file *tf; 112 char path[PATH_MAX]; 113 114 if (!host_base) 115 return (ENOENT); 116 117 strlcpy(path, host_base, PATH_MAX); 118 if (path[strlen(path) - 1] == '/') 119 path[strlen(path) - 1] = 0; 120 strlcat(path, filename, PATH_MAX); 121 tf = malloc(sizeof(struct test_file)); 122 if (stat(path, &tf->tf_stat) < 0) { 123 free(tf); 124 return (errno); 125 } 126 127 tf->tf_size = st.st_size; 128 if (S_ISDIR(tf->tf_stat.st_mode)) { 129 tf->tf_isdir = 1; 130 tf->tf_u.dir = opendir(path); 131 if (!tf->tf_u.dir) 132 goto out; 133 *h_return = tf; 134 return (0); 135 } 136 if (S_ISREG(tf->tf_stat.st_mode)) { 137 tf->tf_isdir = 0; 138 tf->tf_u.fd = open(path, O_RDONLY); 139 if (tf->tf_u.fd < 0) 140 goto out; 141 *h_return = tf; 142 return (0); 143 } 144 145 out: 146 free(tf); 147 return (EINVAL); 148 } 149 150 int 151 test_close(void *arg, void *h) 152 { 153 struct test_file *tf = h; 154 155 if (tf->tf_isdir) 156 closedir(tf->tf_u.dir); 157 else 158 close(tf->tf_u.fd); 159 free(tf); 160 161 return (0); 162 } 163 164 int 165 test_isdir(void *arg, void *h) 166 { 167 struct test_file *tf = h; 168 169 return (tf->tf_isdir); 170 } 171 172 int 173 test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return) 174 { 175 struct test_file *tf = h; 176 ssize_t sz; 177 178 if (tf->tf_isdir) 179 return (EINVAL); 180 sz = read(tf->tf_u.fd, dst, size); 181 if (sz < 0) 182 return (EINVAL); 183 *resid_return = size - sz; 184 return (0); 185 } 186 187 int 188 test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, 189 size_t *namelen_return, char *name) 190 { 191 struct test_file *tf = h; 192 struct dirent *dp; 193 194 if (!tf->tf_isdir) 195 return (EINVAL); 196 197 dp = readdir(tf->tf_u.dir); 198 if (!dp) 199 return (ENOENT); 200 201 /* 202 * Note: d_namlen is in the range 0..255 and therefore less 203 * than PATH_MAX so we don't need to test before copying. 204 */ 205 *fileno_return = dp->d_fileno; 206 *type_return = dp->d_type; 207 *namelen_return = dp->d_namlen; 208 memcpy(name, dp->d_name, dp->d_namlen); 209 name[dp->d_namlen] = 0; 210 211 return (0); 212 } 213 214 int 215 test_seek(void *arg, void *h, uint64_t offset, int whence) 216 { 217 struct test_file *tf = h; 218 219 if (tf->tf_isdir) 220 return (EINVAL); 221 if (lseek(tf->tf_u.fd, offset, whence) < 0) 222 return (errno); 223 return (0); 224 } 225 226 int 227 test_stat(void *arg, void *h, struct stat *stp) 228 { 229 struct test_file *tf = h; 230 231 if (!stp) 232 return (-1); 233 memset(stp, 0, sizeof(struct stat)); 234 stp->st_mode = tf->tf_stat.st_mode; 235 stp->st_uid = tf->tf_stat.st_uid; 236 stp->st_gid = tf->tf_stat.st_gid; 237 stp->st_size = tf->tf_stat.st_size; 238 stp->st_ino = tf->tf_stat.st_ino; 239 stp->st_dev = tf->tf_stat.st_dev; 240 stp->st_mtime = tf->tf_stat.st_mtime; 241 return (0); 242 } 243 244 /* 245 * Disk image i/o 246 */ 247 248 int 249 test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size, 250 size_t *resid_return) 251 { 252 ssize_t n; 253 254 if (unit > disk_index || disk_fd[unit] == -1) 255 return (EIO); 256 n = pread(disk_fd[unit], dst, size, offset); 257 if (n == 0) { 258 printf("%s: end of disk (%ju)\n", __func__, (intmax_t)offset); 259 return (EIO); 260 } 261 262 if (n < 0) 263 return (errno); 264 *resid_return = size - n; 265 return (0); 266 } 267 268 int 269 test_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size, 270 size_t *resid_return) 271 { 272 ssize_t n; 273 274 if (unit > disk_index || disk_fd[unit] == -1) 275 return (EIO); 276 n = pwrite(disk_fd[unit], src, size, offset); 277 if (n < 0) 278 return (errno); 279 *resid_return = size - n; 280 return (0); 281 } 282 283 int 284 test_diskioctl(void *arg, int unit, u_long cmd, void *data) 285 { 286 struct stat sb; 287 288 if (unit > disk_index || disk_fd[unit] == -1) 289 return (EBADF); 290 switch (cmd) { 291 case DIOCGSECTORSIZE: 292 *(u_int *)data = 512; 293 break; 294 case DIOCGMEDIASIZE: 295 if (fstat(disk_fd[unit], &sb) == 0) 296 *(off_t *)data = sb.st_size; 297 else 298 return (ENOTTY); 299 break; 300 default: 301 return (ENOTTY); 302 } 303 return (0); 304 } 305 306 /* 307 * Guest virtual machine i/o 308 * 309 * Note: guest addresses are kernel virtual 310 */ 311 312 int 313 test_copyin(void *arg, const void *from, uint64_t to, size_t size) 314 { 315 316 to &= 0x7fffffff; 317 if (to > image_size) 318 return (EFAULT); 319 if (to + size > image_size) 320 size = image_size - to; 321 memcpy(&image[to], from, size); 322 return(0); 323 } 324 325 int 326 test_copyout(void *arg, uint64_t from, void *to, size_t size) 327 { 328 329 from &= 0x7fffffff; 330 if (from > image_size) 331 return (EFAULT); 332 if (from + size > image_size) 333 size = image_size - from; 334 memcpy(to, &image[from], size); 335 return(0); 336 } 337 338 void 339 test_setreg(void *arg, int r, uint64_t v) 340 { 341 342 if (r < 0 || r >= 16) 343 return; 344 regs[r] = v; 345 } 346 347 void 348 test_setmsr(void *arg, int r, uint64_t v) 349 { 350 } 351 352 void 353 test_setcr(void *arg, int r, uint64_t v) 354 { 355 } 356 357 void 358 test_setgdt(void *arg, uint64_t v, size_t sz) 359 { 360 } 361 362 void 363 test_exec(void *arg, uint64_t pc) 364 { 365 printf("Execute at 0x%"PRIx64"\n", pc); 366 test_exit(arg, 0); 367 } 368 369 /* 370 * Misc 371 */ 372 373 void 374 test_delay(void *arg, int usec) 375 { 376 377 usleep(usec); 378 } 379 380 void 381 test_exit(void *arg, int v) 382 { 383 384 tcsetattr(0, TCSAFLUSH, &oldterm); 385 exit(v); 386 } 387 388 void 389 test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem) 390 { 391 392 *lowmem = 128*1024*1024; 393 *highmem = 0; 394 } 395 396 char * 397 test_getenv(void *arg, int idx) 398 { 399 static char *vars[] = { 400 "foo=bar", 401 "bar=barbar", 402 NULL 403 }; 404 405 return (vars[idx]); 406 } 407 408 struct loader_callbacks cb = { 409 .putc = test_putc, 410 .getc = test_getc, 411 .poll = test_poll, 412 413 .open = test_open, 414 .close = test_close, 415 .isdir = test_isdir, 416 .read = test_read, 417 .readdir = test_readdir, 418 .seek = test_seek, 419 .stat = test_stat, 420 421 .diskread = test_diskread, 422 .diskwrite = test_diskwrite, 423 .diskioctl = test_diskioctl, 424 425 .copyin = test_copyin, 426 .copyout = test_copyout, 427 .setreg = test_setreg, 428 .setmsr = test_setmsr, 429 .setcr = test_setcr, 430 .setgdt = test_setgdt, 431 .exec = test_exec, 432 433 .delay = test_delay, 434 .exit = test_exit, 435 .getmem = test_getmem, 436 437 .getenv = test_getenv, 438 }; 439 440 void 441 usage() 442 { 443 444 printf("usage: [-b <userboot shared object>] [-d <disk image path>] [-h <host filesystem path>\n"); 445 exit(1); 446 } 447 448 int 449 main(int argc, char** argv) 450 { 451 void *h; 452 void (*func)(struct loader_callbacks *, void *, int, int) __dead2; 453 int opt; 454 const char *userboot_obj = "/boot/userboot.so"; 455 int oflag = O_RDONLY; 456 457 while ((opt = getopt(argc, argv, "wb:d:h:")) != -1) { 458 switch (opt) { 459 case 'b': 460 userboot_obj = optarg; 461 break; 462 463 case 'd': 464 disk_index++; 465 disk_fd = reallocarray(disk_fd, disk_index + 1, 466 sizeof (int)); 467 disk_fd[disk_index] = open(optarg, oflag); 468 if (disk_fd[disk_index] < 0) 469 err(1, "Can't open disk image '%s'", optarg); 470 break; 471 472 case 'h': 473 host_base = optarg; 474 break; 475 476 case 'w': 477 oflag = O_RDWR; 478 break; 479 480 case '?': 481 usage(); 482 } 483 } 484 485 h = dlopen(userboot_obj, RTLD_LOCAL); 486 if (!h) { 487 printf("%s\n", dlerror()); 488 return (1); 489 } 490 func = dlsym(h, "loader_main"); 491 if (!func) { 492 printf("%s\n", dlerror()); 493 return (1); 494 } 495 496 image_size = 128*1024*1024; 497 image = malloc(image_size); 498 499 tcgetattr(0, &term); 500 oldterm = term; 501 term.c_iflag &= ~(ICRNL); 502 term.c_lflag &= ~(ICANON|ECHO); 503 tcsetattr(0, TCSAFLUSH, &term); 504 505 func(&cb, NULL, USERBOOT_VERSION_3, disk_index + 1); 506 } 507