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