summaryrefslogtreecommitdiffstats
path: root/sys/boot/common
diff options
context:
space:
mode:
authorroyger <royger@FreeBSD.org>2016-01-20 13:23:02 +0000
committerroyger <royger@FreeBSD.org>2016-01-20 13:23:02 +0000
commit0ad0bd3893133b318d166ac9e4a4a468247c2107 (patch)
tree0f6c21d198dbf57fc7d64f7e183bc129440fc75a /sys/boot/common
parent2f8109724febc7f9ea2083413f50d5eceb064058 (diff)
downloadFreeBSD-src-0ad0bd3893133b318d166ac9e4a4a468247c2107.zip
FreeBSD-src-0ad0bd3893133b318d166ac9e4a4a468247c2107.tar.gz
MFC r277215, r277291, r277418, r280953 and r280954:
loader: implement multiboot support for Xen Dom0 Note that only the subset of the multiboot specification needed in order to boot a Xen Dom0 is implemented. Sponsored by: Citrix Systems R&D
Diffstat (limited to 'sys/boot/common')
-rw-r--r--sys/boot/common/bootstrap.h7
-rw-r--r--sys/boot/common/load_elf.c265
-rw-r--r--sys/boot/common/load_elf_obj.c9
-rw-r--r--sys/boot/common/module.c11
4 files changed, 226 insertions, 66 deletions
diff --git a/sys/boot/common/bootstrap.h b/sys/boot/common/bootstrap.h
index c9bcc52..de77c7b 100644
--- a/sys/boot/common/bootstrap.h
+++ b/sys/boot/common/bootstrap.h
@@ -231,9 +231,9 @@ int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]);
int mod_loadkld(const char *name, int argc, char *argv[]);
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,
@@ -257,6 +257,9 @@ int __elfN(obj_loadfile)(char *filename, u_int64_t dest,
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
/*
diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c
index edbad05..200a29c 100644
--- a/sys/boot/common/load_elf.c
+++ b/sys/boot/common/load_elf.c
@@ -76,7 +76,8 @@ static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t
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,
+ Elf_Addr p_start, Elf_Addr p_end);
static symaddr_fn __elfN(symaddr);
static char *fake_modname(const char *name);
@@ -85,6 +86,61 @@ const char *__elfN(moduletype) = "elf module";
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,54 +149,32 @@ 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));
#ifdef __powerpc__
/*
* Kernels can be ET_DYN, so just assume the first loaded object is the
@@ -177,6 +211,11 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result)
} else 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;
@@ -209,10 +248,14 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result)
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)
@@ -240,7 +283,8 @@ __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result)
out:
if (ef.firstpage)
free(ef.firstpage);
- close(ef.fd);
+ if (ef.fd != -1)
+ close(ef.fd);
return(err);
}
@@ -269,6 +313,8 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
int symtabindex;
Elf_Size size;
u_int fpcopy;
+ Elf_Sym sym;
+ Elf_Addr p_start, p_end;
dp = NULL;
shdr = NULL;
@@ -571,7 +617,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 */
@@ -634,7 +688,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;
+ Elf_Addr 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,
+ Elf_Addr p_start, Elf_Addr p_end)
{
struct mod_metadata md;
#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64
@@ -644,20 +814,13 @@ __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef)
#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)
diff --git a/sys/boot/common/load_elf_obj.c b/sys/boot/common/load_elf_obj.c
index 2c8e184..3d8b02a 100644
--- a/sys/boot/common/load_elf_obj.c
+++ b/sys/boot/common/load_elf_obj.c
@@ -129,20 +129,13 @@ __elfN(obj_loadfile)(char *filename, u_int64_t dest,
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);
diff --git a/sys/boot/common/module.c b/sys/boot/common/module.c
index c7d19d1..2af9c20 100644
--- a/sys/boot/common/module.c
+++ b/sys/boot/common/module.c
@@ -138,7 +138,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 ?
@@ -193,7 +193,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);
}
COMMAND_SET(unload, "unload", "unload all modules", command_unload);
@@ -362,7 +362,7 @@ file_load_dependencies(struct preloaded_file *base_file)
* 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;
@@ -421,7 +421,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);
}
@@ -524,7 +525,7 @@ mod_loadkld(const char *kldname, int argc, char *argv[])
* 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;
OpenPOWER on IntegriCloud