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 return (errno); 259 *resid_return = size - n; 260 return (0); 261 } 262 263 int 264 test_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size, 265 size_t *resid_return) 266 { 267 ssize_t n; 268 269 if (unit > disk_index || disk_fd[unit] == -1) 270 return (EIO); 271 n = pwrite(disk_fd[unit], src, size, offset); 272 if (n < 0) 273 return (errno); 274 *resid_return = size - n; 275 return (0); 276 } 277 278 int 279 test_diskioctl(void *arg, int unit, u_long cmd, void *data) 280 { 281 struct stat sb; 282 283 if (unit > disk_index || disk_fd[unit] == -1) 284 return (EBADF); 285 switch (cmd) { 286 case DIOCGSECTORSIZE: 287 *(u_int *)data = 512; 288 break; 289 case DIOCGMEDIASIZE: 290 if (fstat(disk_fd[unit], &sb) == 0) 291 *(off_t *)data = sb.st_size; 292 else 293 return (ENOTTY); 294 break; 295 default: 296 return (ENOTTY); 297 } 298 return (0); 299 } 300 301 /* 302 * Guest virtual machine i/o 303 * 304 * Note: guest addresses are kernel virtual 305 */ 306 307 int 308 test_copyin(void *arg, const void *from, uint64_t to, size_t size) 309 { 310 311 to &= 0x7fffffff; 312 if (to > image_size) 313 return (EFAULT); 314 if (to + size > image_size) 315 size = image_size - to; 316 memcpy(&image[to], from, size); 317 return(0); 318 } 319 320 int 321 test_copyout(void *arg, uint64_t from, void *to, size_t size) 322 { 323 324 from &= 0x7fffffff; 325 if (from > image_size) 326 return (EFAULT); 327 if (from + size > image_size) 328 size = image_size - from; 329 memcpy(to, &image[from], size); 330 return(0); 331 } 332 333 void 334 test_setreg(void *arg, int r, uint64_t v) 335 { 336 337 if (r < 0 || r >= 16) 338 return; 339 regs[r] = v; 340 } 341 342 void 343 test_setmsr(void *arg, int r, uint64_t v) 344 { 345 } 346 347 void 348 test_setcr(void *arg, int r, uint64_t v) 349 { 350 } 351 352 void 353 test_setgdt(void *arg, uint64_t v, size_t sz) 354 { 355 } 356 357 void 358 test_exec(void *arg, uint64_t pc) 359 { 360 printf("Execute at 0x%"PRIx64"\n", pc); 361 test_exit(arg, 0); 362 } 363 364 /* 365 * Misc 366 */ 367 368 void 369 test_delay(void *arg, int usec) 370 { 371 372 usleep(usec); 373 } 374 375 void 376 test_exit(void *arg, int v) 377 { 378 379 tcsetattr(0, TCSAFLUSH, &oldterm); 380 exit(v); 381 } 382 383 void 384 test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem) 385 { 386 387 *lowmem = 128*1024*1024; 388 *highmem = 0; 389 } 390 391 char * 392 test_getenv(void *arg, int idx) 393 { 394 static char *vars[] = { 395 "foo=bar", 396 "bar=barbar", 397 NULL 398 }; 399 400 return (vars[idx]); 401 } 402 403 struct loader_callbacks cb = { 404 .putc = test_putc, 405 .getc = test_getc, 406 .poll = test_poll, 407 408 .open = test_open, 409 .close = test_close, 410 .isdir = test_isdir, 411 .read = test_read, 412 .readdir = test_readdir, 413 .seek = test_seek, 414 .stat = test_stat, 415 416 .diskread = test_diskread, 417 .diskwrite = test_diskwrite, 418 .diskioctl = test_diskioctl, 419 420 .copyin = test_copyin, 421 .copyout = test_copyout, 422 .setreg = test_setreg, 423 .setmsr = test_setmsr, 424 .setcr = test_setcr, 425 .setgdt = test_setgdt, 426 .exec = test_exec, 427 428 .delay = test_delay, 429 .exit = test_exit, 430 .getmem = test_getmem, 431 432 .getenv = test_getenv, 433 }; 434 435 void 436 usage() 437 { 438 439 printf("usage: [-b <userboot shared object>] [-d <disk image path>] [-h <host filesystem path>\n"); 440 exit(1); 441 } 442 443 int 444 main(int argc, char** argv) 445 { 446 void *h; 447 void (*func)(struct loader_callbacks *, void *, int, int) __dead2; 448 int opt; 449 const char *userboot_obj = "/boot/userboot.so"; 450 int oflag = O_RDONLY; 451 452 while ((opt = getopt(argc, argv, "wb:d:h:")) != -1) { 453 switch (opt) { 454 case 'b': 455 userboot_obj = optarg; 456 break; 457 458 case 'd': 459 disk_index++; 460 disk_fd = reallocarray(disk_fd, disk_index + 1, 461 sizeof (int)); 462 disk_fd[disk_index] = open(optarg, oflag); 463 if (disk_fd[disk_index] < 0) 464 err(1, "Can't open disk image '%s'", optarg); 465 break; 466 467 case 'h': 468 host_base = optarg; 469 break; 470 471 case 'w': 472 oflag = O_RDWR; 473 break; 474 475 case '?': 476 usage(); 477 } 478 } 479 480 h = dlopen(userboot_obj, RTLD_LOCAL); 481 if (!h) { 482 printf("%s\n", dlerror()); 483 return (1); 484 } 485 func = dlsym(h, "loader_main"); 486 if (!func) { 487 printf("%s\n", dlerror()); 488 return (1); 489 } 490 491 image_size = 128*1024*1024; 492 image = malloc(image_size); 493 494 tcgetattr(0, &term); 495 oldterm = term; 496 term.c_iflag &= ~(ICRNL); 497 term.c_lflag &= ~(ICANON|ECHO); 498 tcsetattr(0, TCSAFLUSH, &term); 499 500 func(&cb, NULL, USERBOOT_VERSION_3, disk_index + 1); 501 } 502