svn commit: r277215 - in head/sys: amd64/include boot/common boot/fdt boot/forth boot/i386/libi386 boot/i386/loader i386/include x86/xen

Roger Pau Monné royger at FreeBSD.org
Thu Jan 15 16:27:25 UTC 2015


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 <julien.grall AT citrix.com>

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 <sys/linker.h>
 #include <machine/bootinfo.h>
 #include <machine/cpufunc.h>
+#include <machine/metadata.h>
 #include <machine/psl.h>
 #include <machine/specialreg.h>
 #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é <royger at FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/stdint.h>
+#define _MACHINE_ELF_WANT_32BIT
+#include <machine/elf.h>
+#include <string.h>
+#include <stand.h>
+
+#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 ***


More information about the svn-src-all mailing list