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