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