diff options
Diffstat (limited to 'sys/boot/userboot/test/test.c')
-rw-r--r-- | sys/boot/userboot/test/test.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/sys/boot/userboot/test/test.c b/sys/boot/userboot/test/test.c new file mode 100644 index 0000000..a752a80 --- /dev/null +++ b/sys/boot/userboot/test/test.c @@ -0,0 +1,428 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <dirent.h> +#include <dlfcn.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include <boot/userboot/userboot.h> + +char *host_base = NULL; +struct termios term, oldterm; +char *image; +size_t image_size; +int disk_fd = -1; + +uint64_t regs[16]; +uint64_t pc; + +void test_exit(void *arg, int v); + +/* + * Console i/o + */ + +void +test_putc(void *arg, int ch) +{ + char c = ch; + + write(1, &c, 1); +} + +int +test_getc(void *arg) +{ + char c; + + if (read(0, &c, 1) == 1) + return c; + return -1; +} + +int +test_poll(void *arg) +{ + int n; + + if (ioctl(0, FIONREAD, &n) >= 0) + return (n > 0); + return (0); +} + +/* + * Host filesystem i/o + */ + +struct test_file { + int tf_isdir; + size_t tf_size; + struct stat tf_stat; + union { + int fd; + DIR *dir; + } tf_u; +}; + +int +test_open(void *arg, const char *filename, void **h_return) +{ + struct stat st; + struct test_file *tf; + char path[PATH_MAX]; + + if (!host_base) + return (ENOENT); + + strlcpy(path, host_base, PATH_MAX); + if (path[strlen(path) - 1] == '/') + path[strlen(path) - 1] = 0; + strlcat(path, filename, PATH_MAX); + tf = malloc(sizeof(struct test_file)); + if (stat(path, &tf->tf_stat) < 0) { + free(tf); + return (errno); + } + + tf->tf_size = st.st_size; + if (S_ISDIR(tf->tf_stat.st_mode)) { + tf->tf_isdir = 1; + tf->tf_u.dir = opendir(path); + if (!tf->tf_u.dir) + goto out; + *h_return = tf; + return (0); + } + if (S_ISREG(tf->tf_stat.st_mode)) { + tf->tf_isdir = 0; + tf->tf_u.fd = open(path, O_RDONLY); + if (tf->tf_u.fd < 0) + goto out; + *h_return = tf; + return (0); + } + +out: + free(tf); + return (EINVAL); +} + +int +test_close(void *arg, void *h) +{ + struct test_file *tf = h; + + if (tf->tf_isdir) + closedir(tf->tf_u.dir); + else + close(tf->tf_u.fd); + free(tf); + + return (0); +} + +int +test_isdir(void *arg, void *h) +{ + struct test_file *tf = h; + + return (tf->tf_isdir); +} + +int +test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return) +{ + struct test_file *tf = h; + ssize_t sz; + + if (tf->tf_isdir) + return (EINVAL); + sz = read(tf->tf_u.fd, dst, size); + if (sz < 0) + return (EINVAL); + *resid_return = size - sz; + return (0); +} + +int +test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, + size_t *namelen_return, char *name) +{ + struct test_file *tf = h; + struct dirent *dp; + + if (!tf->tf_isdir) + return (EINVAL); + + dp = readdir(tf->tf_u.dir); + if (!dp) + return (ENOENT); + + /* + * Note: d_namlen is in the range 0..255 and therefore less + * than PATH_MAX so we don't need to test before copying. + */ + *fileno_return = dp->d_fileno; + *type_return = dp->d_type; + *namelen_return = dp->d_namlen; + memcpy(name, dp->d_name, dp->d_namlen); + name[dp->d_namlen] = 0; + + return (0); +} + +int +test_seek(void *arg, void *h, uint64_t offset, int whence) +{ + struct test_file *tf = h; + + if (tf->tf_isdir) + return (EINVAL); + if (lseek(tf->tf_u.fd, offset, whence) < 0) + return (errno); + return (0); +} + +int +test_stat(void *arg, void *h, int *mode_return, int *uid_return, int *gid_return, + uint64_t *size_return) +{ + struct test_file *tf = h; + + *mode_return = tf->tf_stat.st_mode; + *uid_return = tf->tf_stat.st_uid; + *gid_return = tf->tf_stat.st_gid; + *size_return = tf->tf_stat.st_size; + return (0); +} + +/* + * Disk image i/o + */ + +int +test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size, + size_t *resid_return) +{ + ssize_t n; + + if (unit != 0 || disk_fd == -1) + return (EIO); + n = pread(disk_fd, dst, size, offset); + if (n < 0) + return (errno); + *resid_return = size - n; + return (0); +} + +/* + * Guest virtual machine i/o + * + * Note: guest addresses are kernel virtual + */ + +int +test_copyin(void *arg, const void *from, uint64_t to, size_t size) +{ + + to &= 0x7fffffff; + if (to > image_size) + return (EFAULT); + if (to + size > image_size) + size = image_size - to; + memcpy(&image[to], from, size); +} + +int +test_copyout(void *arg, uint64_t from, void *to, size_t size) +{ + + from &= 0x7fffffff; + if (from > image_size) + return (EFAULT); + if (from + size > image_size) + size = image_size - from; + memcpy(to, &image[from], size); +} + +void +test_setreg(void *arg, int r, uint64_t v) +{ + + if (r < 0 || r >= 16) + return; + regs[r] = v; +} + +void +test_setmsr(void *arg, int r, uint64_t v) +{ +} + +void +test_setcr(void *arg, int r, uint64_t v) +{ +} + +void +test_setgdt(void *arg, uint64_t v, size_t sz) +{ +} + +void +test_exec(void *arg, uint64_t pc) +{ + printf("Execute at 0x%llx\n", pc); + test_exit(arg, 0); +} + +/* + * Misc + */ + +void +test_delay(void *arg, int usec) +{ + + usleep(usec); +} + +void +test_exit(void *arg, int v) +{ + + tcsetattr(0, TCSAFLUSH, &oldterm); + exit(v); +} + +void +test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem) +{ + + *lowmem = 128*1024*1024; + *highmem = 0; +} + +struct loader_callbacks_v1 cb = { + .putc = test_putc, + .getc = test_getc, + .poll = test_poll, + + .open = test_open, + .close = test_close, + .isdir = test_isdir, + .read = test_read, + .readdir = test_readdir, + .seek = test_seek, + .stat = test_stat, + + .diskread = test_diskread, + + .copyin = test_copyin, + .copyout = test_copyout, + .setreg = test_setreg, + .setmsr = test_setmsr, + .setcr = test_setcr, + .setgdt = test_setgdt, + .exec = test_exec, + + .delay = test_delay, + .exit = test_exit, + .getmem = test_getmem, +}; + +void +usage() +{ + + printf("usage: %s [-d <disk image path>] [-h <host filesystem path>\n"); + exit(1); +} + +int +main(int argc, char** argv) +{ + void *h; + void (*func)(struct loader_callbacks_v1 *, void *, int, int); + int opt; + char *disk_image = NULL; + + while ((opt = getopt(argc, argv, "d:h:")) != -1) { + switch (opt) { + case 'd': + disk_image = optarg; + break; + + case 'h': + host_base = optarg; + break; + + case '?': + usage(); + } + } + + h = dlopen("/boot/userboot.so", + RTLD_LOCAL); + if (!h) { + printf("%s\n", dlerror()); + return (1); + } + func = dlsym(h, "loader_main"); + if (!func) { + printf("%s\n", dlerror()); + return (1); + } + + image_size = 128*1024*1024; + image = malloc(image_size); + if (disk_image) { + disk_fd = open(disk_image, O_RDONLY); + if (disk_fd < 0) + err(1, "Can't open disk image '%s'", disk_image); + } + + tcgetattr(0, &term); + oldterm = term; + term.c_iflag &= ~(ICRNL); + term.c_lflag &= ~(ICANON|ECHO); + tcsetattr(0, TCSAFLUSH, &term); + + func(&cb, NULL, USERBOOT_VERSION_1, disk_fd >= 0); +} |