From owner-svn-src-all@FreeBSD.ORG Thu Jan 15 16:27:25 2015 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id F1879B65; Thu, 15 Jan 2015 16:27:24 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id DA395A63; Thu, 15 Jan 2015 16:27:24 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t0FGROE0062460; Thu, 15 Jan 2015 16:27:24 GMT (envelope-from royger@FreeBSD.org) Received: (from royger@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t0FGRLY2062439; Thu, 15 Jan 2015 16:27:21 GMT (envelope-from royger@FreeBSD.org) Message-Id: <201501151627.t0FGRLY2062439@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: royger set sender to royger@FreeBSD.org using -f From: Roger Pau Monné Date: Thu, 15 Jan 2015 16:27:21 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r277215 - in head/sys: amd64/include boot/common boot/fdt boot/forth boot/i386/libi386 boot/i386/loader i386/include x86/xen X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 15 Jan 2015 16:27:25 -0000 Author: royger Date: Thu Jan 15 16:27:20 2015 New Revision: 277215 URL: https://svnweb.freebsd.org/changeset/base/277215 Log: loader: implement multiboot support for Xen Dom0 Implement a subset of the multiboot specification in order to boot Xen and a FreeBSD Dom0 from the FreeBSD bootloader. This multiboot implementation is tailored to boot Xen and FreeBSD Dom0, and it will most surely fail to boot any other multiboot compilant kernel. In order to detect and boot the Xen microkernel, two new file formats are added to the bootloader, multiboot and multiboot_obj. Multiboot support must be tested before regular ELF support, since Xen is a multiboot kernel that also uses ELF. After a multiboot kernel is detected, all the other loaded kernels/modules are parsed by the multiboot_obj format. The layout of the loaded objects in memory is the following; first the Xen kernel is loaded as a 32bit ELF into memory (Xen will switch to long mode by itself), after that the FreeBSD kernel is loaded as a RAW file (Xen will parse and load it using it's internal ELF loader), and finally the metadata and the modules are loaded using the native FreeBSD way. After everything is loaded we jump into Xen's entry point using a small trampoline. The order of the multiboot modules passed to Xen is the following, the first module is the RAW FreeBSD kernel, and the second module is the metadata and the FreeBSD modules. Since Xen will relocate the memory position of the second multiboot module (the one that contains the metadata and native FreeBSD modules), we need to stash the original modulep address inside of the metadata itself in order to recalculate its position once booted. This also means the metadata must come before the loaded modules, so after loading the FreeBSD kernel a portion of memory is reserved in order to place the metadata before booting. In order to tell the loader to boot Xen and then the FreeBSD kernel the following has to be added to the /boot/loader.conf file: xen_cmdline="dom0_mem=1024M dom0_max_vcpus=2 dom0pvh=1 console=com1,vga" xen_kernel="/boot/xen" The first argument contains the command line that will be passed to the Xen kernel, while the second argument is the path to the Xen kernel itself. This can also be done manually from the loader command line, by for example typing the following set of commands: OK unload OK load /boot/xen dom0_mem=1024M dom0_max_vcpus=2 dom0pvh=1 console=com1,vga OK load kernel OK load zfs OK load if_tap OK load ... OK boot Sponsored by: Citrix Systems R&D Reviewed by: jhb Differential Revision: https://reviews.freebsd.org/D517 For the Forth bits: Submitted by: Julien Grall Added: head/sys/boot/i386/libi386/multiboot.c (contents, props changed) head/sys/boot/i386/libi386/multiboot.h (contents, props changed) head/sys/boot/i386/libi386/multiboot_tramp.S (contents, props changed) Modified: head/sys/amd64/include/metadata.h head/sys/boot/common/bootstrap.h head/sys/boot/common/load_elf.c head/sys/boot/common/load_elf_obj.c head/sys/boot/common/module.c head/sys/boot/fdt/fdt_loader_cmd.c head/sys/boot/forth/beastie.4th head/sys/boot/forth/loader.4th head/sys/boot/forth/support.4th head/sys/boot/i386/libi386/Makefile head/sys/boot/i386/libi386/bootinfo64.c head/sys/boot/i386/libi386/elf64_freebsd.c head/sys/boot/i386/libi386/libi386.h head/sys/boot/i386/loader/conf.c head/sys/i386/include/metadata.h head/sys/x86/xen/pv.c Modified: head/sys/amd64/include/metadata.h ============================================================================== --- head/sys/amd64/include/metadata.h Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/amd64/include/metadata.h Thu Jan 15 16:27:20 2015 (r277215) @@ -34,6 +34,7 @@ #define MODINFOMD_DTBP 0x1003 #define MODINFOMD_EFI_MAP 0x1004 #define MODINFOMD_EFI_FB 0x1005 +#define MODINFOMD_MODULEP 0x1006 struct efi_map_header { size_t memory_size; Modified: head/sys/boot/common/bootstrap.h ============================================================================== --- head/sys/boot/common/bootstrap.h Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/common/bootstrap.h Thu Jan 15 16:27:20 2015 (r277215) @@ -232,9 +232,9 @@ int mod_loadkld(const char *name, int void unload(void); struct preloaded_file *file_alloc(void); -struct preloaded_file *file_findfile(char *name, char *type); +struct preloaded_file *file_findfile(const char *name, const char *type); struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); -struct preloaded_file *file_loadraw(char *name, char *type); +struct preloaded_file *file_loadraw(char *name, char *type, int insert); void file_discard(struct preloaded_file *fp); void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p); int file_addmodule(struct preloaded_file *fp, char *modname, int version, @@ -258,6 +258,9 @@ int __elfN(obj_loadfile)(char *filename, int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len); +int __elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot); +int __elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest); #endif /* Modified: head/sys/boot/common/load_elf.c ============================================================================== --- head/sys/boot/common/load_elf.c Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/common/load_elf.c Thu Jan 15 16:27:20 2015 (r277215) @@ -76,7 +76,8 @@ static int __elfN(loadimage)(struct prel static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); -static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); +static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, + u_int64_t p_start, u_int64_t p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); @@ -85,6 +86,61 @@ const char *__elfN(moduletype) = "elf mo u_int64_t __elfN(relocation_offset) = 0; +static int +__elfN(load_elf_header)(char *filename, elf_file_t ef) +{ + ssize_t bytes_read; + Elf_Ehdr *ehdr; + int err; + + /* + * Open the image, read and validate the ELF header + */ + if (filename == NULL) /* can't handle nameless */ + return (EFTYPE); + if ((ef->fd = open(filename, O_RDONLY)) == -1) + return (errno); + ef->firstpage = malloc(PAGE_SIZE); + if (ef->firstpage == NULL) { + close(ef->fd); + return (ENOMEM); + } + bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE); + ef->firstlen = (size_t)bytes_read; + if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { + err = EFTYPE; /* could be EIO, but may be small file */ + goto error; + } + ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; + + /* Is it ELF? */ + if (!IS_ELF(*ehdr)) { + err = EFTYPE; + goto error; + } + if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ + ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ + ehdr->e_version != EV_CURRENT || + ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ + err = EFTYPE; + goto error; + } + + return (0); + +error: + if (ef->firstpage != NULL) { + free(ef->firstpage); + ef->firstpage = NULL; + } + if (ef->fd != -1) { + close(ef->fd); + ef->fd = -1; + } + return (err); +} + /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object @@ -93,56 +149,39 @@ u_int64_t __elfN(relocation_offset) = 0; int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) { + return (__elfN(loadfile_raw)(filename, dest, result, 0)); +} + +int +__elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot) +{ struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; - ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; - /* - * Open the image, read and validate the ELF header - */ - if (filename == NULL) /* can't handle nameless */ - return(EFTYPE); - if ((ef.fd = open(filename, O_RDONLY)) == -1) - return(errno); - ef.firstpage = malloc(PAGE_SIZE); - if (ef.firstpage == NULL) { - close(ef.fd); - return(ENOMEM); - } - bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); - ef.firstlen = (size_t)bytes_read; - if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { - err = EFTYPE; /* could be EIO, but may be small file */ - goto oerr; - } - ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; - - /* Is it ELF? */ - if (!IS_ELF(*ehdr)) { - err = EFTYPE; - goto oerr; - } - if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ - ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || - ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ - ehdr->e_version != EV_CURRENT || - ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ - err = EFTYPE; - goto oerr; - } + err = __elfN(load_elf_header)(filename, &ef); + if (err != 0) + return (err); + ehdr = ef.ehdr; /* * Check to see what sort of module we are. */ - kfp = file_findfile(NULL, NULL); + kfp = file_findfile(NULL, __elfN(kerneltype)); if (ehdr->e_type == ET_DYN) { /* Looks like a kld module */ + if (multiboot != 0) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module as multiboot\n"); + err = EPERM; + goto oerr; + } if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); err = EPERM; @@ -193,10 +232,14 @@ __elfN(loadfile)(char *filename, u_int64 err = EPERM; goto out; } - if (ef.kernel) + if (ef.kernel == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); - fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); + if (multiboot == 0) + fp->f_type = strdup(ef.kernel ? + __elfN(kerneltype) : __elfN(moduletype)); + else + fp->f_type = strdup("elf multiboot kernel"); #ifdef ELF_VERBOSE if (ef.kernel) @@ -224,7 +267,8 @@ __elfN(loadfile)(char *filename, u_int64 out: if (ef.firstpage) free(ef.firstpage); - close(ef.fd); + if (ef.fd != -1) + close(ef.fd); return(err); } @@ -255,6 +299,8 @@ __elfN(loadimage)(struct preloaded_file int symtabindex; Elf_Size size; u_int fpcopy; + Elf_Sym sym; + u_int64_t p_start, p_end; dp = NULL; shdr = NULL; @@ -587,7 +633,15 @@ nosyms: COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); ef->buckets = ef->hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; - if (__elfN(parse_modmetadata)(fp, ef) == 0) + + if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) + return 0; + p_start = sym.st_value + ef->off; + if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) + return ENOENT; + p_end = sym.st_value + ef->off; + + if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ @@ -650,7 +704,123 @@ struct mod_metadata32 { #endif int -__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) +__elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest) +{ + struct elf_file ef; + int err, i, j; + Elf_Shdr *sh_meta, *shdr = NULL; + Elf_Shdr *sh_data[2]; + char *shstrtab = NULL; + size_t size; + u_int64_t p_start, p_end; + + bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; + + err = __elfN(load_elf_header)(fp->f_name, &ef); + if (err != 0) + goto out; + + if (ef.ehdr->e_type == ET_EXEC) { + ef.kernel = 1; + } else if (ef.ehdr->e_type != ET_DYN) { + err = EFTYPE; + goto out; + } + + size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize; + shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size); + if (shdr == NULL) { + err = ENOMEM; + goto out; + } + + /* Load shstrtab. */ + shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset, + shdr[ef.ehdr->e_shstrndx].sh_size); + if (shstrtab == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load shstrtab\n"); + err = EFTYPE; + goto out; + } + + /* Find set_modmetadata_set and data sections. */ + sh_data[0] = sh_data[1] = sh_meta = NULL; + for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { + if (strcmp(&shstrtab[shdr[i].sh_name], + "set_modmetadata_set") == 0) { + sh_meta = &shdr[i]; + } + if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || + (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { + sh_data[j++] = &shdr[i]; + } + } + if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); + err = EFTYPE; + goto out; + } + + /* Load set_modmetadata_set into memory */ + err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); + goto out; + } + p_start = dest; + p_end = dest + sh_meta->sh_size; + dest += sh_meta->sh_size; + + /* Load data sections into memory. */ + err = kern_pread(ef.fd, dest, sh_data[0]->sh_size, + sh_data[0]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + /* + * We have to increment the dest, so that the offset is the same into + * both the .rodata and .data sections. + */ + ef.off = -(sh_data[0]->sh_addr - dest); + dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); + + err = kern_pread(ef.fd, dest, sh_data[1]->sh_size, + sh_data[1]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to parse metadata: %d\n", err); + goto out; + } + +out: + if (shstrtab != NULL) + free(shstrtab); + if (shdr != NULL) + free(shdr); + if (ef.firstpage != NULL) + free(ef.firstpage); + if (ef.fd != -1) + close(ef.fd); + return (err); +} + +int +__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, + u_int64_t p_start, u_int64_t p_end) { struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 @@ -660,20 +830,13 @@ __elfN(parse_modmetadata)(struct preload #endif struct mod_depend *mdepend; struct mod_version mver; - Elf_Sym sym; char *s; int error, modcnt, minfolen; - Elf_Addr v, p, p_stop; - - if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) - return 0; - p = sym.st_value + ef->off; - if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) - return ENOENT; - p_stop = sym.st_value + ef->off; + Elf_Addr v, p; modcnt = 0; - while (p < p_stop) { + p = p_start; + while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) Modified: head/sys/boot/common/load_elf_obj.c ============================================================================== --- head/sys/boot/common/load_elf_obj.c Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/common/load_elf_obj.c Thu Jan 15 16:27:20 2015 (r277215) @@ -129,20 +129,13 @@ __elfN(obj_loadfile)(char *filename, u_i goto oerr; } - kfp = file_findfile(NULL, NULL); + kfp = file_findfile(NULL, __elfN(obj_kerneltype)); if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } - if (strcmp(__elfN(obj_kerneltype), kfp->f_type)) { - printf("elf" __XSTRING(__ELF_WORD_SIZE) - "_obj_loadfile: can't load module with kernel type '%s'\n", - kfp->f_type); - err = EPERM; - goto oerr; - } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); Modified: head/sys/boot/common/module.c ============================================================================== --- head/sys/boot/common/module.c Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/common/module.c Thu Jan 15 16:27:20 2015 (r277215) @@ -139,7 +139,7 @@ command_load(int argc, char *argv[]) command_errmsg = "invalid load type"; return(CMD_ERROR); } - return(file_loadraw(argv[1], typestr) ? CMD_OK : CMD_ERROR); + return (file_loadraw(argv[1], typestr, 1) ? CMD_OK : CMD_ERROR); } /* * Do we have explicit KLD load ? @@ -194,7 +194,7 @@ command_load_geli(int argc, char *argv[] argv += (optind - 1); argc -= (optind - 1); sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); - return(file_loadraw(argv[2], typestr) ? CMD_OK : CMD_ERROR); + return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); } void @@ -371,7 +371,7 @@ file_load_dependencies(struct preloaded_ * no arguments or anything. */ struct preloaded_file * -file_loadraw(char *name, char *type) +file_loadraw(char *name, char *type, int insert) { struct preloaded_file *fp; char *cp; @@ -434,7 +434,8 @@ file_loadraw(char *name, char *type) loadaddr = laddr; /* Add to the list of loaded files */ - file_insert_tail(fp); + if (insert != 0) + file_insert_tail(fp); close(fd); return(fp); } @@ -537,7 +538,7 @@ mod_loadkld(const char *kldname, int arg * NULL may be passed as a wildcard to either. */ struct preloaded_file * -file_findfile(char *name, char *type) +file_findfile(const char *name, const char *type) { struct preloaded_file *fp; Modified: head/sys/boot/fdt/fdt_loader_cmd.c ============================================================================== --- head/sys/boot/fdt/fdt_loader_cmd.c Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/fdt/fdt_loader_cmd.c Thu Jan 15 16:27:20 2015 (r277215) @@ -261,7 +261,7 @@ fdt_load_dtb_file(const char * filename) oldbfp = file_findfile(NULL, "dtb"); /* Attempt to load and validate a new dtb from a file. */ - if ((bfp = file_loadraw(filename, "dtb")) == NULL) { + if ((bfp = file_loadraw(filename, "dtb", 1)) == NULL) { sprintf(command_errbuf, "failed to load file '%s'", filename); return (1); } Modified: head/sys/boot/forth/beastie.4th ============================================================================== --- head/sys/boot/forth/beastie.4th Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/forth/beastie.4th Thu Jan 15 16:27:20 2015 (r277215) @@ -251,6 +251,7 @@ variable logoY dup -1 <> if s" YES" compare-insensitive 0= if any_conf_read? if + load_xen_throw load_kernel load_modules then Modified: head/sys/boot/forth/loader.4th ============================================================================== --- head/sys/boot/forth/loader.4th Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/forth/loader.4th Thu Jan 15 16:27:20 2015 (r277215) @@ -143,13 +143,14 @@ include /boot/check-password.4th \ was succesfully loaded! any_conf_read? if s" loader_delay" getenv -1 = if + load_xen_throw load_kernel load_modules else drop ." Loading Kernel and Modules (Ctrl-C to Abort)" cr s" also support-functions" evaluate - s" set delay_command='load_kernel load_modules'" evaluate + s" set delay_command='load_xen_throw load_kernel load_modules'" evaluate s" set delay_showdots" evaluate delay_execute then Modified: head/sys/boot/forth/support.4th ============================================================================== --- head/sys/boot/forth/support.4th Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/forth/support.4th Thu Jan 15 16:27:20 2015 (r277215) @@ -1437,6 +1437,20 @@ also builtins abort" Unable to load a kernel!" ; +: load_xen ( -- ) + s" xen_kernel" getenv dup -1 <> if + 1 1 load + else + drop + 0 + then +; + +: load_xen_throw ( -- ) ( throws: abort ) + load_xen + abort" Unable to load Xen!" +; + : set_defaultoptions ( -- ) s" kernel_options" getenv dup -1 = if drop @@ -1555,12 +1569,15 @@ also builtins else drop then - r> if ( a path was passed ) - load_directory_or_file - else - standard_kernel_search + load_xen + ?dup 0= if ( success ) + r> if ( a path was passed ) + load_directory_or_file + else + standard_kernel_search + then + ?dup 0= if ['] load_modules catch then then - ?dup 0= if ['] load_modules catch then ; \ Go back to straight forth vocabulary Modified: head/sys/boot/i386/libi386/Makefile ============================================================================== --- head/sys/boot/i386/libi386/Makefile Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/i386/libi386/Makefile Thu Jan 15 16:27:20 2015 (r277215) @@ -6,7 +6,7 @@ INTERNALLIB= SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \ comconsole.c devicename.c elf32_freebsd.c \ - elf64_freebsd.c \ + elf64_freebsd.c multiboot.c multiboot_tramp.S \ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c .PATH: ${.CURDIR}/../../zfs @@ -68,6 +68,7 @@ machine: # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS} +CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS} CFLAGS+= ${CFLAGS.${.IMPSRC:T}} .if ${MACHINE_CPUARCH} == "amd64" Modified: head/sys/boot/i386/libi386/bootinfo64.c ============================================================================== --- head/sys/boot/i386/libi386/bootinfo64.c Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/i386/libi386/bootinfo64.c Thu Jan 15 16:27:20 2015 (r277215) @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include "bootstrap.h" @@ -176,12 +177,12 @@ bi_checkcpu(void) * - Module metadata are formatted and placed in kernel space. */ int -bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) +bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, + vm_offset_t *kernendp, int add_smap) { struct preloaded_file *xp, *kfp; struct i386_devdesc *rootdev; struct file_metadata *md; - vm_offset_t addr; u_int64_t kernend; u_int64_t envp; vm_offset_t size; @@ -210,21 +211,18 @@ bi_load64(char *args, vm_offset_t *modul /* Try reading the /etc/fstab file to select the root device */ getrootmount(i386_fmtdev((void *)rootdev)); - /* find the last module in the chain */ - addr = 0; - for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { - if (addr < (xp->f_addr + xp->f_size)) - addr = xp->f_addr + xp->f_size; + if (addr == 0) { + /* find the last module in the chain */ + for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { + if (addr < (xp->f_addr + xp->f_size)) + addr = xp->f_addr + xp->f_size; + } } /* pad to a page boundary */ addr = roundup(addr, PAGE_SIZE); - /* copy our environment */ - envp = addr; - addr = bi_copyenv(addr); - - /* pad to a page boundary */ - addr = roundup(addr, PAGE_SIZE); + /* place the metadata before anything */ + *modulep = addr; kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) @@ -235,20 +233,30 @@ bi_load64(char *args, vm_offset_t *modul file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); - bios_addsmapdata(kfp); + file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof modulep, modulep); + if (add_smap != 0) + bios_addsmapdata(kfp); - /* Figure out the size and location of the metadata */ - *modulep = addr; size = bi_copymodules64(0); - kernend = roundup(addr + size, PAGE_SIZE); + + /* copy our environment */ + envp = roundup(addr + size, PAGE_SIZE); + addr = bi_copyenv(envp); + + /* set kernend */ + kernend = roundup(addr, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); + /* patch MODINFOMD_ENVP */ + md = file_findmetadata(kfp, MODINFOMD_ENVP); + bcopy(&envp, md->md_data, sizeof envp); + /* copy module list and metadata */ - (void)bi_copymodules64(addr); + (void)bi_copymodules64(*modulep); return(0); } Modified: head/sys/boot/i386/libi386/elf64_freebsd.c ============================================================================== --- head/sys/boot/i386/libi386/elf64_freebsd.c Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/i386/libi386/elf64_freebsd.c Thu Jan 15 16:27:20 2015 (r277215) @@ -81,7 +81,7 @@ elf64_exec(struct preloaded_file *fp) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); - err = bi_load64(fp->f_args, &modulep, &kernend); + err = bi_load64(fp->f_args, 0, &modulep, &kernend, 1); if (err != 0) return(err); Modified: head/sys/boot/i386/libi386/libi386.h ============================================================================== --- head/sys/boot/i386/libi386/libi386.h Thu Jan 15 16:09:35 2015 (r277214) +++ head/sys/boot/i386/libi386/libi386.h Thu Jan 15 16:27:20 2015 (r277215) @@ -122,7 +122,8 @@ void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); -int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend); +int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, + vm_offset_t *kernend, int add_smap); char *pxe_default_rc(void); void pxe_enable(void *pxeinfo); Added: head/sys/boot/i386/libi386/multiboot.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/boot/i386/libi386/multiboot.c Thu Jan 15 16:27:20 2015 (r277215) @@ -0,0 +1,418 @@ +/*- + * Copyright (c) 2014 Roger Pau Monné + * 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. + */ + +/* + * This multiboot implementation only implements a subset of the full + * multiboot specification in order to be able to boot Xen and a + * FreeBSD Dom0. Trying to use it to boot other multiboot compliant + * kernels will most surely fail. + * + * The full multiboot specification can be found here: + * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#define _MACHINE_ELF_WANT_32BIT +#include +#include +#include + +#include "bootstrap.h" +#include "multiboot.h" +#include "../i386/libi386/libi386.h" +#include "../i386/btx/lib/btxv86.h" + +#define MULTIBOOT_SUPPORTED_FLAGS \ + (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) +#define NUM_MODULES 2 +#define METADATA_FIXED_SIZE (PAGE_SIZE*4) +#define METADATA_MODULE_SIZE PAGE_SIZE + +#define METADATA_RESV_SIZE(mod_num) \ + roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE) + +extern int elf32_loadfile_raw(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot); +extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest); +extern int elf64_obj_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result); + +static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **); +static int multiboot_exec(struct preloaded_file *); + +static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **); +static int multiboot_obj_exec(struct preloaded_file *fp); + +struct file_format multiboot = { multiboot_loadfile, multiboot_exec }; +struct file_format multiboot_obj = + { multiboot_obj_loadfile, multiboot_obj_exec }; + +extern void multiboot_tramp(); + +static const char mbl_name[] = "FreeBSD Loader"; + +static int +num_modules(struct preloaded_file *kfp) +{ + struct kernel_module *kmp; + int mod_num = 0; + + for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next) + mod_num++; + + return (mod_num); +} + +static vm_offset_t +max_addr(void) +{ + struct preloaded_file *fp; + vm_offset_t addr = 0; + + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + if (addr < (fp->f_addr + fp->f_size)) + addr = fp->f_addr + fp->f_size; + } + + return (addr); +} + +static int +multiboot_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result) +{ + uint32_t *magic; + int i, error; + caddr_t header_search; + ssize_t search_size; + int fd; + struct multiboot_header *header; + char *cmdline; + + /* + * Read MULTIBOOT_SEARCH size in order to search for the + * multiboot magic header. + */ + if (filename == NULL) + return (EFTYPE); + if ((fd = open(filename, O_RDONLY)) == -1) + return (errno); + header_search = malloc(MULTIBOOT_SEARCH); + if (header_search == NULL) { + close(fd); + return (ENOMEM); + } + search_size = read(fd, header_search, MULTIBOOT_SEARCH); + magic = (uint32_t *)header_search; + + header = NULL; + for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { + if (magic[i] == MULTIBOOT_HEADER_MAGIC) { + header = (struct multiboot_header *)&magic[i]; + break; + } + } + + if (header == NULL) { + error = EFTYPE; + goto out; + } + + /* Valid multiboot header has been found, validate checksum */ + if (header->magic + header->flags + header->checksum != 0) { + printf( + "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", + header->magic, header->flags, header->checksum); + error = EFTYPE; + goto out; + } + + if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { + printf("Unsupported multiboot flags found: 0x%x\n", + header->flags); + error = EFTYPE; + goto out; + } + + error = elf32_loadfile_raw(filename, dest, result, 1); + if (error != 0) { + printf( + "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", + error); + goto out; + } + + /* + * f_addr is already aligned to PAGE_SIZE, make sure + * f_size it's also aligned so when the modules are loaded + * they are aligned to PAGE_SIZE. + */ + (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); + +out: + free(header_search); + close(fd); + return (error); +} + +static int +multiboot_exec(struct preloaded_file *fp) +{ + vm_offset_t module_start, last_addr, metadata_size; + vm_offset_t modulep, kernend, entry; + struct file_metadata *md; + Elf_Ehdr *ehdr; + struct multiboot_info *mb_info = NULL; + struct multiboot_mod_list *mb_mod = NULL; + char *cmdline = NULL; + size_t len; + int error, mod_num; + + /* + * Don't pass the memory size found by the bootloader, the memory + * available to Dom0 will be lower than that. + */ + unsetenv("smbios.memory.enabled"); + + /* Allocate the multiboot struct and fill the basic details. */ + mb_info = malloc(sizeof(struct multiboot_info)); + if (mb_info == NULL) { + error = ENOMEM; + goto error; + } + bzero(mb_info, sizeof(struct multiboot_info)); + mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; + mb_info->mem_lower = bios_basemem / 1024; + mb_info->mem_upper = bios_extmem / 1024; + mb_info->boot_loader_name = VTOP(mbl_name); + + /* Set the Xen command line. */ + if (fp->f_args == NULL) { + /* Add the Xen command line if it is set. */ + cmdline = getenv("xen_cmdline"); + if (cmdline != NULL) { + fp->f_args = strdup(cmdline); + if (fp->f_args == NULL) { + error = ENOMEM; + goto error; + } + } + } + if (fp->f_args != NULL) { + len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; + cmdline = malloc(len); + if (cmdline == NULL) { + error = ENOMEM; + goto error; + } + snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); + mb_info->cmdline = VTOP(cmdline); + mb_info->flags |= MULTIBOOT_INFO_CMDLINE; + } + + /* Find the entry point of the Xen kernel and save it for later */ + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { + printf("Unable to find %s entry point\n", fp->f_name); + error = EFTYPE; + goto error; + } + ehdr = (Elf_Ehdr *)&(md->md_data); + entry = ehdr->e_entry & 0xffffff; + + /* + * Prepare the multiboot module list, Xen assumes the first + * module is the Dom0 kernel, and the second one is the initramfs. + * This is not optimal for FreeBSD, that doesn't have a initramfs + * but instead loads modules dynamically and creates the metadata + * info on-the-fly. + * + * As expected, the first multiboot module is going to be the + * FreeBSD kernel loaded as a raw file. The second module is going + * to contain the metadata info and the loaded modules. + * + * On native FreeBSD loads all the modules and then places the + * metadata info at the end, but this is painful when running on Xen, + * because it relocates the second multiboot module wherever it + * likes. In order to workaround this limitation the metadata + * information is placed at the start of the second module and + * the original modulep value is saved together with the other + * metadata, so we can relocate everything. + */ + fp = file_findfile(NULL, "elf kernel"); + if (fp == NULL) { + printf("No FreeBSD kernel provided, aborting\n"); + error = EFTYPE; + goto error; + } + mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); + + /* + * Calculate how much memory is needed for the metatdata. We did + * an approximation of the maximum size when loading the kernel, + * but now we know the exact size, so we can release some of this + * preallocated memory if not needed. + */ + last_addr = roundup(max_addr(), PAGE_SIZE); + mod_num = num_modules(fp); + + /* + * Place the metadata after the last used address in order to + * calculate it's size, this will not be used. + */ + error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0); + if (error != 0) { + printf("bi_load64 failed: %d\n", error); + goto error; + } + metadata_size = roundup(kernend - last_addr, PAGE_SIZE); + + /* Check that the size is not greater than what we have reserved */ + if (metadata_size > METADATA_RESV_SIZE(mod_num)) { + printf("Required memory for metadata is greater than reserved " + "space, please increase METADATA_FIXED_SIZE and " + "METADATA_MODULE_SIZE and rebuild the loader\n"); + error = ENOMEM; + goto error; + } + + /* + * This is the position where the second multiboot module + * will be placed. + */ + module_start = fp->f_addr + fp->f_size - metadata_size; + + error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0); + if (error != 0) { + printf("bi_load64 failed: %d\n", error); + goto error; + } + + mb_mod[0].mod_start = fp->f_addr; + mb_mod[0].mod_end = fp->f_addr + fp->f_size; + mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num); + + mb_mod[1].mod_start = module_start; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***