diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h index 88ee6de2e520..dca0cf6bb2d5 100644 --- a/stand/common/bootstrap.h +++ b/stand/common/bootstrap.h @@ -1,388 +1,380 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #ifndef _BOOTSTRAP_H_ #define _BOOTSTRAP_H_ #include #include #include #include #include #include "readin.h" /* Commands and return values; nonzero return sets command_errmsg != NULL */ typedef int (bootblk_cmd_t)(int argc, char *argv[]); #define COMMAND_ERRBUFSZ (256) extern const char *command_errmsg; extern char command_errbuf[COMMAND_ERRBUFSZ]; #define CMD_OK 0 #define CMD_WARN 1 #define CMD_ERROR 2 #define CMD_CRIT 3 #define CMD_FATAL 4 /* interp.c */ void interact(void); void interp_emit_prompt(void); int interp_builtin_cmd(int argc, char *argv[]); bool interp_has_builtin_cmd(const char *cmd); /* Called by interp.c for interp_*.c embedded interpreters */ int interp_include(const char *); /* Execute commands from filename */ void interp_preinit(void); /* Initialize interpreater execution engine */ void interp_init(void); /* Initialize interpreater and run main script */ int interp_run(const char *); /* Run a single command */ /* interp_backslash.c */ char *backslash(const char *str); /* interp_parse.c */ int parse(int *argc, char ***argv, const char *str); /* boot.c */ void autoboot_maybe(void); int getrootmount(char *rootdev); /* misc.c */ char *unargv(int argc, char *argv[]); size_t strlenout(vm_offset_t str); char *strdupout(vm_offset_t str); void kern_bzero(vm_offset_t dest, size_t len); int kern_pread(readin_handle_t fd, vm_offset_t dest, size_t len, off_t off); void *alloc_pread(readin_handle_t fd, off_t off, size_t len); /* bcache.c */ void bcache_init(size_t nblks, size_t bsize); void bcache_add_dev(int); void *bcache_allocate(void); void bcache_free(void *); int bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); /* * Disk block cache */ struct bcache_devdata { int (*dv_strategy)(void *, int, daddr_t, size_t, char *, size_t *); void *dv_devdata; void *dv_cache; }; /* * Modular console support. */ struct console { const char *c_name; const char *c_desc; int c_flags; #define C_PRESENTIN (1<<0) /* console can provide input */ #define C_PRESENTOUT (1<<1) /* console can provide output */ #define C_ACTIVEIN (1<<2) /* user wants input from console */ #define C_ACTIVEOUT (1<<3) /* user wants output to console */ #define C_WIDEOUT (1<<4) /* c_out routine groks wide chars */ /* set c_flags to match hardware */ void (* c_probe)(struct console *cp); /* reinit XXX may need more args */ int (* c_init)(int arg); /* emit c */ void (* c_out)(int c); /* wait for and return input */ int (* c_in)(void); /* return nonzero if input waiting */ int (* c_ready)(void); }; extern struct console *consoles[]; void cons_probe(void); bool cons_update_mode(bool); void autoload_font(bool); extern int module_verbose; enum { MODULE_VERBOSE_SILENT, /* say nothing */ MODULE_VERBOSE_SIZE, /* print name and size */ MODULE_VERBOSE_TWIDDLE, /* show progress */ MODULE_VERBOSE_FULL, /* all we have */ }; /* * Plug-and-play enumerator/configurator interface. */ struct pnphandler { const char *pp_name; /* handler/bus name */ void (*pp_enumerate)(void); /* enumerate PnP devices, add to chain */ }; struct pnpident { /* ASCII identifier, actual format varies with bus/handler */ char *id_ident; STAILQ_ENTRY(pnpident) id_link; }; struct pnpinfo { char *pi_desc; /* ASCII description, optional */ int pi_revision; /* optional revision (or -1) if not supported */ char *pi_module; /* module/args nominated to handle device */ int pi_argc; /* module arguments */ char **pi_argv; struct pnphandler *pi_handler; /* handler which detected this device */ STAILQ_HEAD(, pnpident) pi_ident; /* list of identifiers */ STAILQ_ENTRY(pnpinfo) pi_link; }; STAILQ_HEAD(pnpinfo_stql, pnpinfo); extern struct pnphandler *pnphandlers[]; /* provided by MD code */ void pnp_addident(struct pnpinfo *pi, char *ident); struct pnpinfo *pnp_allocinfo(void); void pnp_freeinfo(struct pnpinfo *pi); void pnp_addinfo(struct pnpinfo *pi); char *pnp_eisaformat(uint8_t *data); /* * < 0 - No ISA in system * == 0 - Maybe ISA, search for read data port * > 0 - ISA in system, value is read data port address */ extern int isapnp_readport; /* * Version information */ extern char bootprog_info[]; extern unsigned bootprog_rev; /* * Interpreter information */ extern const char bootprog_interp[]; #define INTERP_DEFINE(interpstr) \ const char bootprog_interp[] = "$Interpreter:" interpstr /* * Preloaded file metadata header. * * Metadata are allocated on our heap, and copied into kernel space * before executing the kernel. */ struct file_metadata { size_t md_size; uint16_t md_type; vm_offset_t md_addr; /* Valid after copied to kernel space */ struct file_metadata *md_next; char md_data[1]; /* data are immediately appended */ }; struct preloaded_file; struct mod_depend; struct kernel_module { char *m_name; /* module name */ int m_version; /* module version */ /* char *m_args; */ /* arguments for the module */ struct preloaded_file *m_fp; struct kernel_module *m_next; }; /* * Preloaded file information. Depending on type, file can contain * additional units called 'modules'. * * At least one file (the kernel) must be loaded in order to boot. * The kernel is always loaded first. * * String fields (m_name, m_type) should be dynamically allocated. */ struct preloaded_file { char *f_name; /* file name */ char *f_type; /* verbose file type, eg 'elf kernel', 'pnptable', etc. */ char *f_args; /* arguments for the file */ /* metadata that will be placed in the module directory */ struct file_metadata *f_metadata; int f_loader; /* index of the loader that read the file */ vm_offset_t f_addr; /* load address */ size_t f_size; /* file size */ struct kernel_module *f_modules; /* list of modules if any */ struct preloaded_file *f_next; /* next file */ #if defined(__amd64__) || (defined(__i386__) && defined(EFI)) bool f_kernphys_relocatable; #endif #if defined(__i386__) && !defined(EFI) bool f_tg_kernel_support; #endif }; struct file_format { /* * Load function must return EFTYPE if it can't handle * the module supplied */ int (*l_load)(char *, uint64_t, struct preloaded_file **); /* * Only a loader that will load a kernel (first module) * should have an exec handler */ int (*l_exec)(struct preloaded_file *); }; extern struct file_format *file_formats[]; /* supplied by consumer */ extern struct preloaded_file *preloaded_files; int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]); int mod_loadkld(const char *name, int argc, char *argv[]); void unload(void); struct preloaded_file *file_alloc(void); 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(const char *name, const char *type, int insert); void file_discard(struct preloaded_file *fp); void file_addmetadata(struct preloaded_file *, int, size_t, void *); int file_addmodule(struct preloaded_file *, char *, int, struct kernel_module **); void file_removemetadata(struct preloaded_file *fp); int file_addbuf(const char *name, const char *type, size_t len, void *buf); int tslog_init(void); int tslog_publish(void); vm_offset_t build_font_module(vm_offset_t); vm_offset_t build_splash_module(vm_offset_t); /* MI module loaders */ #ifdef __elfN /* Relocation types. */ #define ELF_RELOC_REL 1 #define ELF_RELOC_RELA 2 /* Relocation offset for some architectures */ extern uint64_t __elfN(relocation_offset); struct elf_file; typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); int __elfN(loadfile)(char *, uint64_t, struct preloaded_file **); int __elfN(obj_loadfile)(char *, uint64_t, struct preloaded_file **); 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 *, uint64_t, struct preloaded_file **, int); int __elfN(load_modmetadata)(struct preloaded_file *, uint64_t); #endif /* * Support for commands */ struct bootblk_command { const char *c_name; const char *c_desc; bootblk_cmd_t *c_fn; }; #define COMMAND_SET(tag, key, desc, func) \ static bootblk_cmd_t func; \ static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \ DATA_SET(Xcommand_set, _cmd_ ## tag) SET_DECLARE(Xcommand_set, struct bootblk_command); /* * The intention of the architecture switch is to provide a convenient * encapsulation of the interface between the bootstrap MI and MD code. * MD code may selectively populate the switch at runtime based on the * actual configuration of the target system, though some routines are * mandatory. */ struct arch_switch { /* Automatically load modules as required by detected hardware */ int (*arch_autoload)(void); /* Locate the device for (name), return pointer to tail in (*path) */ int (*arch_getdev)(void **dev, const char *name, const char **path); /* * Copy from local address space to module address space, * similar to bcopy() */ ssize_t (*arch_copyin)(const void *, vm_offset_t, const size_t); /* * Copy to local address space from module address space, * similar to bcopy() */ ssize_t (*arch_copyout)(const vm_offset_t, void *, const size_t); /* Read from file to module address space, same semantics as read() */ ssize_t (*arch_readin)(readin_handle_t, vm_offset_t, const size_t); /* Perform ISA byte port I/O (only for systems with ISA) */ int (*arch_isainb)(int port); void (*arch_isaoutb)(int port, int value); - /* - * Interface to adjust the load address according to the "object" - * being loaded. - */ - uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr); -#define LOAD_ELF 1 /* data points to the ELF header. */ -#define LOAD_RAW 2 /* data points to the file name. */ - /* * Interface to inform MD code about a loaded (ELF) segment. This * can be used to flush caches and/or set up translations. */ #ifdef __elfN void (*arch_loadseg)(Elf_Ehdr *eh, Elf_Phdr *ph, uint64_t delta); #else void (*arch_loadseg)(void *eh, void *ph, uint64_t delta); #endif /* Probe ZFS pool(s), if needed. */ void (*arch_zfs_probe)(void); /* Return the hypervisor name/type or NULL if not virtualized. */ const char *(*arch_hypervisor)(void); }; extern struct arch_switch archsw; /* This must be provided by the MD code, but should it be in the archsw? */ void delay(int delay); /* common code to set currdev variable. */ int gen_setcurrdev(struct env_var *ev, int flags, const void *value); int mount_currdev(struct env_var *, int, const void *); void set_currdev(const char *devname); #endif /* !_BOOTSTRAP_H_ */ diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c index 537692e7d745..b9f55a21e403 100644 --- a/stand/common/load_elf.c +++ b/stand/common/load_elf.c @@ -1,1318 +1,1316 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. */ #include #include #include #include #include #include #include #include "bootstrap.h" #include "modinfo.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Phdr *ph; Elf_Ehdr *ehdr; Elf_Sym *symtab; Elf_Hashelt *hashtab; Elf_Hashelt nbuckets; Elf_Hashelt nchains; Elf_Hashelt *buckets; Elf_Hashelt *chains; Elf_Rel *rel; size_t relsz; Elf_Rela *rela; size_t relasz; char *strtab; size_t strsz; int fd; caddr_t firstpage; size_t firstlen; int kernel; uint64_t off; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *sym, unsigned char type); 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, Elf_Addr p_start, Elf_Addr p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); uint64_t __elfN(relocation_offset) = 0; #ifdef __powerpc__ extern void elf_wrong_field_size(void); #define CONVERT_FIELD(b, f, e) \ switch (sizeof((b)->f)) { \ case 2: \ (b)->f = e ## 16toh((b)->f); \ break; \ case 4: \ (b)->f = e ## 32toh((b)->f); \ break; \ case 8: \ (b)->f = e ## 64toh((b)->f); \ break; \ default: \ /* Force a link time error. */ \ elf_wrong_field_size(); \ break; \ } #define CONVERT_SWITCH(h, d, f) \ switch ((h)->e_ident[EI_DATA]) { \ case ELFDATA2MSB: \ f(d, be); \ break; \ case ELFDATA2LSB: \ f(d, le); \ break; \ default: \ return (EINVAL); \ } static int elf_header_convert(Elf_Ehdr *ehdr) { /* * Fixup ELF header endianness. * * The Xhdr structure was loaded using block read call to optimize file * accesses. It might happen, that the endianness of the system memory * is different that endianness of the ELF header. Swap fields here to * guarantee that Xhdr always contain valid data regardless of * architecture. */ #define HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, e_type, e); \ CONVERT_FIELD(b, e_machine, e); \ CONVERT_FIELD(b, e_version, e); \ CONVERT_FIELD(b, e_entry, e); \ CONVERT_FIELD(b, e_phoff, e); \ CONVERT_FIELD(b, e_shoff, e); \ CONVERT_FIELD(b, e_flags, e); \ CONVERT_FIELD(b, e_ehsize, e); \ CONVERT_FIELD(b, e_phentsize, e); \ CONVERT_FIELD(b, e_phnum, e); \ CONVERT_FIELD(b, e_shentsize, e); \ CONVERT_FIELD(b, e_shnum, e); \ CONVERT_FIELD(b, e_shstrndx, e) CONVERT_SWITCH(ehdr, ehdr, HEADER_FIELDS); #undef HEADER_FIELDS return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { #define PROGRAM_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, p_type, e); \ CONVERT_FIELD(b, p_flags, e); \ CONVERT_FIELD(b, p_offset, e); \ CONVERT_FIELD(b, p_vaddr, e); \ CONVERT_FIELD(b, p_paddr, e); \ CONVERT_FIELD(b, p_filesz, e); \ CONVERT_FIELD(b, p_memsz, e); \ CONVERT_FIELD(b, p_align, e) CONVERT_SWITCH(ehdr, phdr, PROGRAM_HEADER_FIELDS); #undef PROGRAM_HEADER_FIELDS return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { #define SECTION_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, sh_name, e); \ CONVERT_FIELD(b, sh_type, e); \ CONVERT_FIELD(b, sh_link, e); \ CONVERT_FIELD(b, sh_info, e); \ CONVERT_FIELD(b, sh_flags, e); \ CONVERT_FIELD(b, sh_addr, e); \ CONVERT_FIELD(b, sh_offset, e); \ CONVERT_FIELD(b, sh_size, e); \ CONVERT_FIELD(b, sh_addralign, e); \ CONVERT_FIELD(b, sh_entsize, e) CONVERT_SWITCH(ehdr, shdr, SECTION_HEADER_FIELDS); #undef SECTION_HEADER_FIELDS return (0); } #undef CONVERT_SWITCH #undef CONVERT_FIELD #else static int elf_header_convert(Elf_Ehdr *ehdr) { return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { return (0); } #endif #if defined(__amd64__) || (defined(__i386__) && defined(EFI)) static bool is_kernphys_relocatable(elf_file_t ef) { Elf_Sym sym; return (__elfN(lookup_symbol)(ef, "kernphys", &sym, STT_OBJECT) == 0); } #endif #ifdef __i386__ static bool is_tg_kernel_support(struct preloaded_file *fp, elf_file_t ef) { Elf_Sym sym; Elf_Addr p_start, p_end, v, p; char vd_name[16]; int error; if (__elfN(lookup_symbol)(ef, "__start_set_vt_drv_set", &sym, STT_NOTYPE) != 0) return (false); p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_vt_drv_set", &sym, STT_NOTYPE) != 0) return (false); p_end = sym.st_value + ef->off; /* * Walk through vt_drv_set, each vt driver structure starts with * static 16 chars for driver name. If we have "vbefb", return true. */ for (p = p_start; p < p_end; p += sizeof(Elf_Addr)) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) v += ef->off; else if (error != 0) return (false); COPYOUT(v, &vd_name, sizeof(vd_name)); if (strncmp(vd_name, "vbefb", sizeof(vd_name)) == 0) return (true); } return (false); } #endif 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); } preload(ef->fd); #ifdef LOADER_VERIEXEC_VECTX { int verror; ef->vctx = vectx_open(ef->fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef->fd); free(ef->vctx); return (EAUTH); } } #endif bytes_read = VECTX_READ(VECTX_HANDLE(ef), 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 ? */ { err = EFTYPE; goto error; } err = elf_header_convert(ehdr); if (err) goto error; if (ehdr->e_version != EV_CURRENT || ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ err = EFTYPE; goto error; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef->fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto error; } #endif return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { #ifdef LOADER_VERIEXEC_VECTX free(ef->vctx); #endif 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 * will be saved in (result). */ int __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { return (__elfN(loadfile_raw)(filename, dest, result, 0)); } int __elfN(loadfile_raw)(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; fp = NULL; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; 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, md_kerntype); #ifdef __powerpc__ /* * Kernels can be ET_DYN, so just assume the first loaded object is the * kernel. This assumption will be checked later. */ if (kfp == NULL) ef.kernel = 1; #endif if (ef.kernel || ehdr->e_type == ET_EXEC) { /* Looks like a kernel */ if (kfp != NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); err = EPERM; goto oerr; } /* * Calculate destination address based on kernel entrypoint. * * For ARM, the destination address is independent of any values * in the elf header (an ARM kernel can be loaded at any 2MB * boundary), so we leave dest set to the value calculated by * archsw.arch_loadaddr() and passed in to this function. + * XXX This comment is obsolete, but it still seems to work */ #ifndef __arm__ if (ehdr->e_type == ET_EXEC) dest = (ehdr->e_entry & ~PAGE_MASK); #endif if ((ehdr->e_entry & ~PAGE_MASK) == 0) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); err = EPERM; goto oerr; } ef.kernel = 1; } 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; goto oerr; } if (strcmp(md_kerntype, kfp->f_type)) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); err = EPERM; goto oerr; } /* Looks OK, got ahead */ ef.kernel = 0; } else { err = EFTYPE; goto oerr; } - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest); - else - dest = roundup(dest, PAGE_SIZE); + dest = md_align(dest); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } if (ef.kernel == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); if (multiboot == 0) fp->f_type = strdup(ef.kernel ? md_kerntype : md_modtype); else fp->f_type = strdup(md_kerntype_mb); if (module_verbose >= MODULE_VERBOSE_FULL) { if (ef.kernel) printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); } else if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", filename); fp->f_size = __elfN(loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; #if defined(__amd64__) || (defined(__i386__) && defined(EFI)) fp->f_kernphys_relocatable = multiboot || is_kernphys_relocatable(&ef); #endif #if defined(__i386__) && !defined(EFI) fp->f_tg_kernel_support = is_tg_kernel_support(fp, &ef); #endif goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); if (ef.fd != -1) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); } return (err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { int i; u_int j; Elf_Ehdr *ehdr; Elf_Phdr *phdr, *php; Elf_Shdr *shdr; char *shstr; int ret; vm_offset_t firstaddr; vm_offset_t lastaddr; size_t chunk; ssize_t result; Elf_Addr ssym, esym; Elf_Dyn *dp; Elf_Addr adp; Elf_Addr ctors; int ndp; int symstrindex; int symtabindex; Elf_Size size; u_int fpcopy; Elf_Sym sym; Elf_Addr p_start, p_end; dp = NULL; shdr = NULL; ret = 0; firstaddr = lastaddr = 0; ehdr = ef->ehdr; #ifdef __powerpc__ if (ef->kernel) { #else if (ehdr->e_type == ET_EXEC) { #endif #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 /* x86_64 relocates after locore */ off = - (off & 0xffffffffff000000ull); #else /* i386 relocates after locore */ off = - (off & 0xff000000u); #endif #elif defined(__powerpc__) /* * On the purely virtual memory machines like e500, the kernel * is linked against its final VA range, which is most often * not available at the loader stage, but only after kernel * initializes and completes its VM settings. In such cases we * cannot use p_vaddr field directly to load ELF segments, but * put them at some 'load-time' locations. */ if (off & 0xf0000000u) { off = -(off & 0xf0000000u); /* * XXX the physical load address should not be * hardcoded. Note that the Book-E kernel assumes that * it's loaded at a 16MB boundary for now... */ off += 0x01000000; } ehdr->e_entry += off; if (module_verbose >= MODULE_VERBOSE_FULL) printf("Converted entry 0x%jx\n", (uintmax_t)ehdr->e_entry); #elif defined(__arm__) && !defined(EFI) /* * The elf headers in arm kernels specify virtual addresses in * all header fields, even the ones that should be physical * addresses. We assume the entry point is in the first page, * and masking the page offset will leave us with the virtual * address the kernel was linked at. We subtract that from the * load offset, making 'off' into the value which, when added * to a virtual address in an elf header, translates it to a * physical address. We do the va->pa conversion on the entry * point address in the header now, so that later we can launch * the kernel by just jumping to that address. * * When booting from UEFI the copyin and copyout functions * handle adjusting the location relative to the first virtual * address. Because of this there is no need to adjust the * offset or entry point address as these will both be handled * by the efi code. */ off -= ehdr->e_entry & ~PAGE_MASK; ehdr->e_entry += off; if (module_verbose >= MODULE_VERBOSE_FULL) printf("ehdr->e_entry 0x%jx, va<->pa off %llx\n", (uintmax_t)ehdr->e_entry, off); #else off = 0; /* other archs use direct mapped kernels */ #endif } ef->off = off; if (ef->kernel) __elfN(relocation_offset) = off; if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); goto out; } phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { if (elf_program_header_convert(ehdr, phdr)) continue; /* We want to load PT_LOAD segments only.. */ if (phdr[i].p_type != PT_LOAD) continue; if (module_verbose >= MODULE_VERBOSE_FULL) { printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", (long)phdr[i].p_filesz, (long)phdr[i].p_offset, (long)(phdr[i].p_vaddr + off), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); } else if (module_verbose > MODULE_VERBOSE_SILENT) { if ((phdr[i].p_flags & PF_W) == 0) { printf("text=0x%lx ", (long)phdr[i].p_filesz); } else { printf("data=0x%lx", (long)phdr[i].p_filesz); if (phdr[i].p_filesz < phdr[i].p_memsz) printf("+0x%lx", (long)(phdr[i].p_memsz - phdr[i].p_filesz)); printf(" "); } } fpcopy = 0; if (ef->firstlen > phdr[i].p_offset) { fpcopy = ef->firstlen - phdr[i].p_offset; archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, phdr[i].p_vaddr + off, fpcopy); } if (phdr[i].p_filesz > fpcopy) { if (kern_pread(VECTX_HANDLE(ef), phdr[i].p_vaddr + off + fpcopy, phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read failed\n"); goto out; } } /* clear space from oversized segments; eg: bss */ if (phdr[i].p_filesz < phdr[i].p_memsz) { if (module_verbose >= MODULE_VERBOSE_FULL) { printf(" (bss: 0x%lx-0x%lx)", (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz -1)); } kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, phdr[i].p_memsz - phdr[i].p_filesz); } if (module_verbose >= MODULE_VERBOSE_FULL) printf("\n"); if (archsw.arch_loadseg != NULL) archsw.arch_loadseg(ehdr, phdr + i, off); if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) firstaddr = phdr[i].p_vaddr + off; if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; } lastaddr = roundup(lastaddr, sizeof(long)); /* * Get the section headers. We need this for finding the .ctors * section as well as for loading any symbols. Both may be hard * to do if reading from a .gz file as it involves seeking. I * think the rule is going to have to be that you must strip a * file to remove symbols before gzipping it. */ chunk = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; shdr = alloc_pread(VECTX_HANDLE(ef), ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } for (i = 0; i < ehdr->e_shnum; i++) elf_section_header_convert(ehdr, &shdr[i]); file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr); /* * Read the section string table and look for the .ctors section. * We need to tell the kernel where it is so that it can call the * ctors. */ chunk = shdr[ehdr->e_shstrndx].sh_size; if (chunk) { shstr = alloc_pread(VECTX_HANDLE(ef), shdr[ehdr->e_shstrndx].sh_offset, chunk); if (shstr) { for (i = 0; i < ehdr->e_shnum; i++) { if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0) continue; ctors = shdr[i].sh_addr; file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors), &ctors); size = shdr[i].sh_size; file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size), &size); break; } free(shstr); } } /* * Now load any symbols. */ symtabindex = -1; symstrindex = -1; for (i = 0; i < ehdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_SYMTAB) continue; for (j = 0; j < ehdr->e_phnum; j++) { if (phdr[j].p_type != PT_LOAD) continue; if (shdr[i].sh_offset >= phdr[j].p_offset && (shdr[i].sh_offset + shdr[i].sh_size <= phdr[j].p_offset + phdr[j].p_filesz)) { shdr[i].sh_offset = 0; shdr[i].sh_size = 0; break; } } if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) continue; /* alread loaded in a PT_LOAD above */ /* Save it for loading below */ symtabindex = i; symstrindex = shdr[i].sh_link; } if (symtabindex < 0 || symstrindex < 0) goto nosyms; /* Ok, committed to a load. */ if (module_verbose >= MODULE_VERBOSE_FULL) printf("syms=["); ssym = lastaddr; for (i = symtabindex; i >= 0; i = symstrindex) { char *secname; switch(shdr[i].sh_type) { case SHT_SYMTAB: /* Symbol table */ secname = "symtab"; break; case SHT_STRTAB: /* String table */ secname = "strtab"; break; default: secname = "WHOA!!"; break; } size = shdr[i].sh_size; archsw.arch_copyin(&size, lastaddr, sizeof(size)); lastaddr += sizeof(size); if (module_verbose >= MODULE_VERBOSE_FULL) { printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname, (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset, (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size)); } else if (module_verbose > MODULE_VERBOSE_SILENT) { if (i == symstrindex) printf("+"); printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); } if (VECTX_LSEEK(VECTX_HANDLE(ef), (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); lastaddr = ssym; ssym = 0; goto nosyms; } result = archsw.arch_readin(VECTX_HANDLE(ef), lastaddr, shdr[i].sh_size); if (result < 0 || (size_t)result != shdr[i].sh_size) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! " "(%ju != %ju)", (uintmax_t)result, (uintmax_t)shdr[i].sh_size); lastaddr = ssym; ssym = 0; goto nosyms; } /* Reset offsets relative to ssym */ lastaddr += shdr[i].sh_size; lastaddr = roundup(lastaddr, sizeof(size)); if (i == symtabindex) symtabindex = -1; else if (i == symstrindex) symstrindex = -1; } esym = lastaddr; if (module_verbose >= MODULE_VERBOSE_FULL) printf("]"); file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); nosyms: if (module_verbose > MODULE_VERBOSE_SILENT) printf("\n"); ret = lastaddr - firstaddr; fp->f_addr = firstaddr; php = NULL; for (i = 0; i < ehdr->e_phnum; i++) { if (phdr[i].p_type == PT_DYNAMIC) { php = phdr + i; adp = php->p_vaddr; file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); break; } } if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ goto out; ndp = php->p_filesz / sizeof(Elf_Dyn); if (ndp == 0) goto out; dp = malloc(php->p_filesz); if (dp == NULL) goto out; archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); ef->strsz = 0; for (i = 0; i < ndp; i++) { if (dp[i].d_tag == 0) break; switch (dp[i].d_tag) { case DT_HASH: ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRTAB: ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRSZ: ef->strsz = dp[i].d_un.d_val; break; case DT_SYMTAB: ef->symtab = (Elf_Sym *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_REL: ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELSZ: ef->relsz = dp[i].d_un.d_val; break; case DT_RELA: ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELASZ: ef->relasz = dp[i].d_un.d_val; break; default: break; } } if (ef->hashtab == NULL || ef->symtab == NULL || ef->strtab == NULL || ef->strsz == 0) goto out; COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); ef->buckets = ef->hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; if (__elfN(lookup_symbol)(ef, "__start_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; 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 */ goto out; out: if (dp) free(dp); if (shdr) free(shdr); return ret; } static char invalid_name[] = "bad"; char * fake_modname(const char *name) { const char *sp, *ep; char *fp; size_t len; sp = strrchr(name, '/'); if (sp) sp++; else sp = name; ep = strrchr(sp, '.'); if (ep == NULL) { ep = sp + strlen(sp); } if (ep == sp) { sp = invalid_name; ep = invalid_name + sizeof(invalid_name) - 1; } len = ep - sp; fp = malloc(len + 1); if (fp == NULL) return NULL; memcpy(fp, sp, len); fp[len] = '\0'; return fp; } #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif #if defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint32_t md_data; /* specific data */ uint32_t md_cval; /* common string label */ }; #endif int __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_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.kernel == 1 || ef.ehdr->e_type == ET_EXEC) { ef.kernel = 1; } else if (ef.ehdr->e_type != ET_DYN) { err = EFTYPE; goto out; } size = (size_t)ef.ehdr->e_shnum * (size_t)ef.ehdr->e_shentsize; shdr = alloc_pread(VECTX_HANDLE(&ef), ef.ehdr->e_shoff, size); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ shstrtab = alloc_pread(VECTX_HANDLE(&ef), 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(VECTX_HANDLE(&ef), 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(VECTX_HANDLE(&ef), 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(VECTX_HANDLE(&ef), 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) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif 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 struct mod_metadata64 md64; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 md32; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p; modcnt = 0; 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) v += ef->off; else if (error != 0) return (error); #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error == EOPNOTSUPP) { md64.md_cval += ef->off; md64.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 COPYOUT(v, &md32, sizeof(md32)); error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32)); if (error == EOPNOTSUPP) { md32.md_cval += ef->off; md32.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md32.md_version; md.md_type = md32.md_type; md.md_cval = (const char *)(uintptr_t)md32.md_cval; md.md_data = (void *)(uintptr_t)md32.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error == EOPNOTSUPP) { md.md_cval += ef->off; md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off); } else if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: if (ef->kernel) /* kernel must not depend on anything */ break; s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; } } if (modcnt == 0) { s = fake_modname(fp->f_name); file_addmodule(fp, s, 1, NULL); free(s); } return 0; } static unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *symp, unsigned char type) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; if (ef->nbuckets == 0) { printf(__elfN(bad_symtable)); return ENOENT; } hash = elf_hash(name); COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf(__elfN(bad_symtable)); return ENOENT; } COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); if (sym.st_name == 0) { printf(__elfN(bad_symtable)); return ENOENT; } strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); if (strcmp(name, strp) == 0) { free(strp); if (sym.st_shndx != SHN_UNDEF && sym.st_value != 0 && ELF_ST_TYPE(sym.st_info) == type) { *symp = sym; return 0; } return ENOENT; } free(strp); COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); } return ENOENT; } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. * * Returns EOPNOTSUPP if no relocation method is supplied. */ static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { size_t n; Elf_Rela a; Elf_Rel r; int error; /* * The kernel is already relocated, but we still want to apply * offset adjustments. */ if (ef->kernel) return (EOPNOTSUPP); for (n = 0; n < ef->relsz / sizeof(r); n++) { COPYOUT(ef->rel + n, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, ef->off, p, val, len); if (error != 0) return (error); } for (n = 0; n < ef->relasz / sizeof(a); n++) { COPYOUT(ef->rela + n, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, ef->off, p, val, len); if (error != 0) return (error); } return (0); } static Elf_Addr __elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) { /* Symbol lookup by index not required here. */ return (0); } diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c index c20da80eb602..9e32daa53696 100644 --- a/stand/common/load_elf_obj.c +++ b/stand/common/load_elf_obj.c @@ -1,575 +1,572 @@ /*- * Copyright (c) 2004 Ian Dowse * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. */ #include #include #include #include #include #include #include "bootstrap.h" #include "modinfo.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Ehdr hdr; Elf_Shdr *e_shdr; int symtabindex; /* Index of symbol table */ int shstrindex; /* Index of section name string table */ int fd; vm_offset_t off; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(obj_lookup_set)(struct preloaded_file *mp, elf_file_t ef, const char *name, Elf_Addr *startp, Elf_Addr *stopp, int *countp); static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(obj_parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx); /* * 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 * will be saved in (result). */ int __elfN(obj_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *hdr; int err; ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); /* * 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); #ifdef LOADER_VERIEXEC_VECTX { int verror; ef.vctx = vectx_open(ef.fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef.fd); free(ef.vctx); return (EAUTH); } } #endif hdr = &ef.hdr; bytes_read = VECTX_READ(VECTX_HANDLE(&ef), hdr, sizeof(*hdr)); if (bytes_read != sizeof(*hdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto oerr; } /* Is it ELF? */ if (!IS_ELF(*hdr)) { err = EFTYPE; goto oerr; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH || /* Machine ? */ hdr->e_type != ET_REL) { err = EFTYPE; goto oerr; } if (hdr->e_shnum * hdr->e_shentsize == 0 || hdr->e_shoff == 0 || hdr->e_shentsize != sizeof(Elf_Shdr)) { err = EFTYPE; goto oerr; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef.fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto oerr; } #endif kfp = file_findfile(NULL, md_kerntype); if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); - else - dest = roundup(dest, PAGE_SIZE); + dest = md_align(dest); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } fp->f_name = strdup(filename); fp->f_type = strdup(md_modtype_obj); if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", filename); fp->f_size = __elfN(obj_loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*hdr), hdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); if (ef.e_shdr != NULL) free(ef.e_shdr); return(err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { Elf_Ehdr *hdr; Elf_Shdr *shdr, *cshdr, *lshdr; vm_offset_t firstaddr, lastaddr; int i, nsym, res, ret, shdrbytes, symstrindex; ret = 0; firstaddr = lastaddr = (vm_offset_t)off; hdr = &ef->hdr; ef->off = (vm_offset_t)off; /* Read in the section headers. */ shdrbytes = hdr->e_shnum * hdr->e_shentsize; shdr = alloc_pread(VECTX_HANDLE(ef), (off_t)hdr->e_shoff, shdrbytes); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read section headers failed\n"); goto out; } ef->e_shdr = shdr; /* * Decide where to load everything, but don't read it yet. * We store the load address as a non-zero sh_addr value. * Start with the code/data and bss. */ for (i = 0; i < hdr->e_shnum; i++) shdr[i].sh_addr = 0; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_size == 0) continue; switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: #if defined(__i386__) || defined(__amd64__) case SHT_X86_64_UNWIND: #endif case SHT_INIT_ARRAY: case SHT_FINI_ARRAY: if ((shdr[i].sh_flags & SHF_ALLOC) == 0) break; lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Symbols. */ nsym = 0; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_SYMTAB: nsym++; ef->symtabindex = i; break; } } if (nsym != 1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no valid symbol table\n"); goto out; } lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign); shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->symtabindex].sh_size; symstrindex = shdr[ef->symtabindex].sh_link; if (symstrindex < 0 || symstrindex >= hdr->e_shnum || shdr[symstrindex].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has invalid symbol strings\n"); goto out; } lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign); shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[symstrindex].sh_size; /* Section names. */ if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum || shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no section names\n"); goto out; } ef->shstrindex = hdr->e_shstrndx; lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign); shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->shstrindex].sh_size; /* Relocation tables. */ for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_REL: case SHT_RELA: if ((shdr[shdr[i].sh_info].sh_flags & SHF_ALLOC) == 0) break; lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Clear the whole area, including bss regions. */ kern_bzero(firstaddr, lastaddr - firstaddr); /* Figure section with the lowest file offset we haven't loaded yet. */ for (cshdr = NULL; /* none */; /* none */) { /* * Find next section to load. The complexity of this loop is * O(n^2), but with the number of sections being typically * small, we do not care. */ lshdr = cshdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_addr == 0 || shdr[i].sh_type == SHT_NOBITS) continue; /* Skip sections that were loaded already. */ if (lshdr != NULL && lshdr->sh_offset >= shdr[i].sh_offset) continue; /* Find section with smallest offset. */ if (cshdr == lshdr || cshdr->sh_offset > shdr[i].sh_offset) cshdr = &shdr[i]; } if (cshdr == lshdr) break; if (kern_pread(VECTX_HANDLE(ef), (vm_offset_t)cshdr->sh_addr, cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read failed\n"); goto out; } } file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr); res = __elfN(obj_parse_modmetadata)(fp, ef); if (res != 0) goto out; ret = lastaddr - firstaddr; fp->f_addr = firstaddr; if (module_verbose > MODULE_VERBOSE_SILENT) printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); out: if (module_verbose > MODULE_VERBOSE_SILENT) printf("\n"); return ret; } #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif int __elfN(obj_parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) { struct mod_metadata md; #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p, p_stop; if (__elfN(obj_lookup_set)(fp, ef, "modmetadata_set", &p, &p_stop, &modcnt) != 0) return 0; modcnt = 0; while (p < p_stop) { COPYOUT(p, &v, sizeof(v)); error = __elfN(obj_reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error != 0) return (error); #if defined(__i386__) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; case MDT_MODULE: case MDT_PNP_INFO: break; default: printf("unknown type %d\n", md.md_type); break; } } return 0; } static int __elfN(obj_lookup_set)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Addr *startp, Elf_Addr *stopp, int *countp) { Elf_Ehdr *hdr; Elf_Shdr *shdr; char *p; vm_offset_t shstrtab; int i; hdr = &ef->hdr; shdr = ef->e_shdr; shstrtab = shdr[ef->shstrindex].sh_addr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_PROGBITS) continue; if (shdr[i].sh_name == 0) continue; p = strdupout(shstrtab + shdr[i].sh_name); if (strncmp(p, "set_", 4) == 0 && strcmp(p + 4, name) == 0) { *startp = shdr[i].sh_addr; *stopp = shdr[i].sh_addr + shdr[i].sh_size; *countp = (*stopp - *startp) / sizeof(Elf_Addr); free(p); return (0); } free(p); } return (ESRCH); } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. */ static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { Elf_Ehdr *hdr; Elf_Shdr *shdr; Elf_Addr off = p; Elf_Addr base; Elf_Rela a, *abase; Elf_Rel r, *rbase; int error, i, j, nrel, nrela; hdr = &ef->hdr; shdr = ef->e_shdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_RELA && shdr[i].sh_type != SHT_REL) continue; base = shdr[shdr[i].sh_info].sh_addr; if (base == 0 || shdr[i].sh_addr == 0) continue; if (off < base || off + len > base + shdr[shdr[i].sh_info].sh_size) continue; switch (shdr[i].sh_type) { case SHT_RELA: abase = (Elf_Rela *)(intptr_t)shdr[i].sh_addr; nrela = shdr[i].sh_size / sizeof(Elf_Rela); for (j = 0; j < nrela; j++) { COPYOUT(abase + j, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &a, ELF_RELOC_RELA, base, off, val, len); if (error != 0) return (error); } break; case SHT_REL: rbase = (Elf_Rel *)(intptr_t)shdr[i].sh_addr; nrel = shdr[i].sh_size / sizeof(Elf_Rel); for (j = 0; j < nrel; j++) { COPYOUT(rbase + j, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &r, ELF_RELOC_REL, base, off, val, len); if (error != 0) return (error); } break; } } return (0); } /* Look up the address of a specified symbol. */ static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx) { Elf_Sym sym; Elf_Addr base; if (symidx >= ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym)) return (0); COPYOUT(ef->e_shdr[ef->symtabindex].sh_addr + symidx * sizeof(Elf_Sym), &sym, sizeof(sym)); if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= ef->hdr.e_shnum) return (0); base = ef->e_shdr[sym.st_shndx].sh_addr; if (base == 0) return (0); return (base + sym.st_value); } diff --git a/stand/common/modinfo.c b/stand/common/modinfo.c index 100f2c704d8f..313469d32f35 100644 --- a/stand/common/modinfo.c +++ b/stand/common/modinfo.c @@ -1,218 +1,218 @@ /*- * Copyright (c) 1998 Michael Smith * 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. * * from: FreeBSD: src/sys/boot/sparc64/loader/metadata.c,v 1.6 */ #include #include #include #include #include #if defined(LOADER_FDT_SUPPORT) #include #endif #ifdef __arm__ #include #endif #include #include "bootstrap.h" #include "modinfo.h" /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. * * Module data is presented in a self-describing format. Each datum * is preceded by a 32-bit identifier and a 32-bit size field. * * Currently, the following data are saved: * * MOD_NAME (variable) module name (string) * MOD_TYPE (variable) module type (string) * MOD_ARGS (variable) module parameters (string) * MOD_ADDR sizeof(vm_offset_t) module load address * MOD_SIZE sizeof(size_t) module size * MOD_METADATA (variable) type-specific metadata * * Clients are required to define a MOD_ALIGN(l) macro which rounds the passed * in length to the required alignment for the kernel being booted. */ #define COPY32(v, a, c) { \ uint32_t x = (v); \ if (c) \ archsw.arch_copyin(&x, a, sizeof(x)); \ a += sizeof(x); \ } #define MOD_STR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(strlen(s) + 1, a, c) \ if (c) \ archsw.arch_copyin(s, a, strlen(s) + 1);\ a += MOD_ALIGN(strlen(s) + 1); \ } #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) #define MOD_VAR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(sizeof(s), a, c); \ if (c) \ archsw.arch_copyin(&s, a, sizeof(s)); \ a += MOD_ALIGN(sizeof(s)); \ } #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) #define MOD_METADATA(a, mm, c) { \ COPY32(MODINFO_METADATA | mm->md_type, a, c);\ COPY32(mm->md_size, a, c); \ if (c) { \ archsw.arch_copyin(mm->md_data, a, mm->md_size);\ mm->md_addr = a; \ } \ a += MOD_ALIGN(mm->md_size); \ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } #define MOD_ALIGN(l) roundup(l, align) const char md_modtype[] = MODTYPE; const char md_kerntype[] = KERNTYPE; const char md_modtype_obj[] = MODTYPE_OBJ; const char md_kerntype_mb[] = KERNTYPE_MB; vm_offset_t md_copymodules(vm_offset_t addr, bool kern64) { struct preloaded_file *fp; struct file_metadata *md; uint64_t scratch64; uint32_t scratch32; int c; int align; align = kern64 ? sizeof(uint64_t) : sizeof(uint32_t); c = addr != 0; /* start with the first module on the list, should be the kernel */ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { MOD_NAME(addr, fp->f_name, c); /* this field must come first */ MOD_TYPE(addr, fp->f_type, c); if (fp->f_args) MOD_ARGS(addr, fp->f_args, c); if (kern64) { scratch64 = fp->f_addr; MOD_ADDR(addr, scratch64, c); scratch64 = fp->f_size; MOD_SIZE(addr, scratch64, c); } else { scratch32 = fp->f_addr; #ifdef __arm__ scratch32 -= __elfN(relocation_offset); #endif MOD_ADDR(addr, scratch32, c); MOD_SIZE(addr, fp->f_size, c); } for (md = fp->f_metadata; md != NULL; md = md->md_next) { if (!(md->md_type & MODINFOMD_NOCOPY)) { MOD_METADATA(addr, md, c); } } } MOD_END(addr, c); return(addr); } /* * Copy the environment into the load area starting at (addr). * Each variable is formatted as =, with a single nul * separating each variable, and a double nul terminating the environment. */ vm_offset_t md_copyenv(vm_offset_t start) { struct env_var *ep; vm_offset_t addr, last; size_t len; addr = last = start; /* Traverse the environment. */ for (ep = environ; ep != NULL; ep = ep->ev_next) { len = strlen(ep->ev_name); if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len) break; addr += len; if (archsw.arch_copyin("=", addr, 1) != 1) break; addr++; if (ep->ev_value != NULL) { len = strlen(ep->ev_value); if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len) break; addr += len; } if (archsw.arch_copyin("", addr, 1) != 1) break; last = ++addr; } if (archsw.arch_copyin("", last++, 1) != 1) last = start; return(last); } /* * Take the ending address and round it up to the currently required * alignment. This typically is the page size, but is the larger of the compiled * kernel page size, the loader page size, and the typical page size on the * platform. * * XXX For the moment, it's just PAGE_SIZE to make the refactoring go faster, - * but needs to hook-in arch_loadaddr (or its replacement) functionality. + * but needs to hook-in the replacement of arch_loadaddr. * * Also, we may need other logical things when dealing with different types of * page sizes and/or masking or sizes. This works well for addr and sizes, but * not for masks. * * Also, this is different than the MOD_ALIGN macro above, which is used for * aligning elements in the metadata lists, not for whare modules can begin. */ vm_offset_t md_align(vm_offset_t addr) { return (roundup(addr, PAGE_SIZE)); } diff --git a/stand/common/module.c b/stand/common/module.c index ce6d095d4315..ed57ebf0caf8 100644 --- a/stand/common/module.c +++ b/stand/common/module.c @@ -1,1800 +1,1797 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ /* * file/module function dispatcher, support, etc. */ #include #include #include #include #include #include #include #include #include #if defined(LOADER_FDT_SUPPORT) #include #endif #include "bootstrap.h" +#include "modinfo.h" #define MDIR_REMOVED 0x0001 #define MDIR_NOHINTS 0x0002 struct moduledir { char *d_path; /* path of modules directory */ u_char *d_hints; /* content of linker.hints file */ int d_hintsz; /* size of hints data */ int d_flags; STAILQ_ENTRY(moduledir) d_link; }; static int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); static int file_load_dependencies(struct preloaded_file *base_mod); static char * file_search(const char *name, char **extlist); static struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); static int file_havepath(const char *name); static char *mod_searchmodule(char *name, struct mod_depend *verinfo); static char * mod_searchmodule_pnpinfo(const char *bus, const char *pnpinfo); static void file_insert_tail(struct preloaded_file *mp); static void file_remove(struct preloaded_file *fp); static void file_remove_tail(struct preloaded_file *fp); struct file_metadata* metadata_next(struct file_metadata *base_mp, int type); static void moduledir_readhints(struct moduledir *mdp); static void moduledir_rebuild(void); /* load address should be tweaked by first module loaded (kernel) */ static vm_offset_t loadaddr = 0; #if defined(LOADER_FDT_SUPPORT) static const char *default_searchpath = "/boot/kernel;/boot/modules;/boot/dtb"; #else static const char *default_searchpath = "/boot/kernel;/boot/modules"; #endif static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); struct preloaded_file *preloaded_files = NULL; static char *kld_ext_list[] = { ".ko", "", ".debug", NULL }; /* * load an object, either a disk file or code module. * * To load a file, the syntax is: * * load -t * * code modules are loaded as: * * load */ COMMAND_SET(load, "load", "load a kernel or module", command_load); static int command_load(int argc, char *argv[]) { struct preloaded_file *fp; char *typestr; #ifdef LOADER_VERIEXEC char *prefix, *skip; int dflag = 0; char *args = "dkp:s:t:"; #else char *args = "kt:"; #endif int dofile, dokld, ch, error; dokld = dofile = 0; optind = 1; optreset = 1; typestr = NULL; if (argc == 1) { command_errmsg = "no filename specified"; return (CMD_CRIT); } #ifdef LOADER_VERIEXEC prefix = NULL; skip = NULL; #endif while ((ch = getopt(argc, argv, args)) != -1) { switch(ch) { #ifdef LOADER_VERIEXEC case 'd': dflag++; break; #endif case 'k': dokld = 1; break; #ifdef LOADER_VERIEXEC case 'p': prefix = optarg; break; case 's': skip = optarg; break; #endif case 't': typestr = optarg; dofile = 1; break; case '?': default: /* getopt has already reported an error */ return (CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); /* * Request to load a raw file? */ if (dofile) { if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { command_errmsg = "invalid load type"; return (CMD_CRIT); } #ifdef LOADER_VERIEXEC if (strncmp(typestr, "manifest", 8) == 0) { if (dflag > 0) ve_debug_set(dflag); return (load_manifest(argv[1], prefix, skip, NULL)); } #ifdef LOADER_VERIEXEC_PASS_MANIFEST if (strncmp(typestr, "pass_manifest", 13) == 0) { if (dflag > 0) ve_debug_set(dflag); return (pass_manifest(argv[1], prefix)); } #endif #endif fp = file_findfile(argv[1], typestr); if (fp) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: file '%s' already loaded", argv[1]); return (CMD_WARN); } if (file_loadraw(argv[1], typestr, 1) != NULL) return (CMD_OK); /* Failing to load mfs_root is never going to end well! */ if (strcmp("mfs_root", typestr) == 0) return (CMD_FATAL); return (CMD_ERROR); } /* * Do we have explicit KLD load ? */ if (dokld || file_havepath(argv[1])) { error = mod_loadkld(argv[1], argc - 2, argv + 2); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: KLD '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } /* * Looks like a request for a module. */ error = mod_load(argv[1], NULL, argc - 2, argv + 2); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } #ifdef LOADER_GELI_SUPPORT COMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); static int command_load_geli(int argc, char *argv[]) { char typestr[80]; char *cp; int ch, num; if (argc < 3) { command_errmsg = "usage is [-n key#] "; return(CMD_ERROR); } num = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "n:")) != -1) { switch(ch) { case 'n': num = strtol(optarg, &cp, 0); if (cp == optarg) { snprintf(command_errbuf, sizeof(command_errbuf), "bad key index '%s'", optarg); return(CMD_ERROR); } break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); } #endif void unload(void) { struct preloaded_file *fp; while (preloaded_files != NULL) { fp = preloaded_files; preloaded_files = preloaded_files->f_next; file_discard(fp); } loadaddr = 0; unsetenv("kernelname"); } COMMAND_SET(unload, "unload", "unload all modules", command_unload); static int command_unload(int argc, char *argv[]) { unload(); return(CMD_OK); } COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); static int command_lsmod(int argc, char *argv[]) { struct preloaded_file *fp; struct kernel_module *mp; struct file_metadata *md; char lbuf[80]; int ch, verbose, ret = 0; verbose = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { case 'v': verbose = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } pager_open(); for (fp = preloaded_files; fp; fp = fp->f_next) { snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr); pager_output(lbuf); pager_output(fp->f_name); snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type, (long)fp->f_size); if (pager_output(lbuf)) break; if (fp->f_args != NULL) { pager_output(" args: "); pager_output(fp->f_args); if (pager_output("\n")) break; } if (fp->f_modules) { pager_output(" modules: "); for (mp = fp->f_modules; mp; mp = mp->m_next) { snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name, mp->m_version); pager_output(lbuf); } if (pager_output("\n")) break; } if (verbose) { /* XXX could add some formatting smarts here to display some better */ for (md = fp->f_metadata; md != NULL; md = md->md_next) { snprintf(lbuf, sizeof(lbuf), " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); if (pager_output(lbuf)) break; } } if (ret) break; } pager_close(); return(CMD_OK); } COMMAND_SET(pnpmatch, "pnpmatch", "list matched modules based on pnpinfo", command_pnpmatch); static int pnp_dump_flag = 0; static int pnp_unbound_flag = 0; static int pnp_verbose_flag = 0; static int command_pnpmatch(int argc, char *argv[]) { char *module; int ch; pnp_verbose_flag = 0; pnp_dump_flag = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "vd")) != -1) { switch(ch) { case 'v': pnp_verbose_flag = 1; break; case 'd': pnp_dump_flag = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += optind; argc -= optind; if (argc != 2) { command_errmsg = "Usage: pnpmatch compat="; return (CMD_CRIT); } module = mod_searchmodule_pnpinfo(argv[0], argv[1]); if (module) printf("Matched module: %s\n", module); else printf("No module matches %s on bus %s\n", argv[1], argv[0]); return (CMD_OK); } COMMAND_SET(pnpload, "pnpload", "load matched modules based on pnpinfo", command_pnpload); static int command_pnpload(int argc, char *argv[]) { char *module; int ch, error; pnp_verbose_flag = 0; pnp_dump_flag = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "vd")) != -1) { switch(ch) { case 'v': pnp_verbose_flag = 1; break; case 'd': pnp_dump_flag = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += optind; argc -= optind; if (argc != 2) { command_errmsg = "Usage: pnpload compat="; return (CMD_ERROR); } module = mod_searchmodule_pnpinfo(argv[0], argv[1]); error = mod_load(module, NULL, 0, NULL); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } #if defined(LOADER_FDT_SUPPORT) static void pnpautoload_fdt_bus(const char *busname) { const char *pnpstring; const char *compatstr; char *pnpinfo = NULL; char *module = NULL; int tag = 0, len, pnplen; int error; while (1) { pnpstring = fdt_devmatch_next(&tag, &len); if (pnpstring == NULL) return; compatstr = pnpstring; for (pnplen = 0; pnplen != len; compatstr = pnpstring + pnplen) { pnplen += strlen(compatstr) + 1; asprintf(&pnpinfo, "compat=%s", compatstr); module = mod_searchmodule_pnpinfo(busname, pnpinfo); if (module) { error = mod_loadkld(module, 0, NULL); if (error) printf("Cannot load module %s\n", module); break; } } free(pnpinfo); free(module); } } #endif struct pnp_bus { const char *name; void (*load)(const char *busname); }; struct pnp_bus pnp_buses[] = { #if defined(LOADER_FDT_SUPPORT) {"simplebus", pnpautoload_fdt_bus}, {"ofwbus", pnpautoload_fdt_bus}, {"iicbus", pnpautoload_fdt_bus}, {"spibus", pnpautoload_fdt_bus}, #endif }; COMMAND_SET(pnpautoload, "pnpautoload", "auto load modules based on pnpinfo", command_pnpautoload); static int command_pnpautoload(int argc, char *argv[]) { int i; int verbose; int ch, match; pnp_verbose_flag = 0; pnp_dump_flag = 0; verbose = 0; optind = 1; optreset = 1; match = 0; while ((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { case 'v': verbose = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); if (argc > 2) return (CMD_ERROR); for (i = 0; i < nitems(pnp_buses); i++) { if (argc == 2 && strcmp(argv[1], pnp_buses[i].name) != 0) { if (verbose) printf("Skipping bus %s\n", pnp_buses[i].name); continue; } if (verbose) printf("Autoloading modules for %s\n", pnp_buses[i].name); pnp_buses[i].load(pnp_buses[i].name); match = 1; } if (match == 0) printf("Unsupported bus %s\n", argv[1]); return (CMD_OK); } /* * File level interface, functions file_* */ int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result) { static int last_file_format = 0; struct preloaded_file *fp; int error; int i; TSENTER2(filename); - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); + dest = md_align(dest); error = EFTYPE; for (i = last_file_format, fp = NULL; file_formats[i] && fp == NULL; i++) { error = (file_formats[i]->l_load)(filename, dest, &fp); if (error == 0) { fp->f_loader = last_file_format = i; /* remember the loader */ *result = fp; break; } else if (last_file_format == i && i != 0) { /* Restart from the beginning */ i = -1; last_file_format = 0; fp = NULL; continue; } if (error == EFTYPE) continue; /* Unknown to this handler? */ if (error) { snprintf(command_errbuf, sizeof(command_errbuf), "can't load file '%s': %s", filename, strerror(error)); break; } } TSEXIT(); return (error); } static int file_load_dependencies(struct preloaded_file *base_file) { struct file_metadata *md; struct preloaded_file *fp; struct mod_depend *verinfo; struct kernel_module *mp; char *dmodname; int error; md = file_findmetadata(base_file, MODINFOMD_DEPLIST); if (md == NULL) return (0); error = 0; do { verinfo = (struct mod_depend*)md->md_data; dmodname = (char *)(verinfo + 1); if (file_findmodule(NULL, dmodname, verinfo) == NULL) { if (module_verbose > MODULE_VERBOSE_SILENT) printf("loading required module '%s'\n", dmodname); error = mod_load(dmodname, verinfo, 0, NULL); if (error) break; /* * If module loaded via kld name which isn't listed * in the linker.hints file, we should check if it have * required version. */ mp = file_findmodule(NULL, dmodname, verinfo); if (mp == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "module '%s' exists but with wrong version", dmodname); error = ENOENT; break; } } md = metadata_next(md, MODINFOMD_DEPLIST); } while (md); if (!error) return (0); /* Load failed; discard everything */ while (base_file != NULL) { fp = base_file; base_file = base_file->f_next; file_discard(fp); } return (error); } #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(fd) vctx #else #define VECTX_HANDLE(fd) fd #endif /* * We've been asked to load (fname) as (type), so just suck it in, * no arguments or anything. */ struct preloaded_file * file_loadraw(const char *fname, const char *type, int insert) { struct preloaded_file *fp; char *name; int fd, got; vm_offset_t laddr; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; int verror; #endif TSENTER2(fname); /* We can't load first */ if ((file_findfile(NULL, NULL)) == NULL) { command_errmsg = "can't load file before kernel"; TSEXIT(); return(NULL); } /* locate the file on the load path */ name = file_search(fname, NULL); if (name == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", fname); TSEXIT(); return(NULL); } if ((fd = open(name, O_RDONLY)) < 0) { snprintf(command_errbuf, sizeof(command_errbuf), "can't open '%s': %s", name, strerror(errno)); free(name); TSEXIT(); return(NULL); } #ifdef LOADER_VERIEXEC_VECTX vctx = vectx_open(fd, name, 0L, NULL, &verror, __func__); if (verror) { sprintf(command_errbuf, "can't verify '%s': %s", name, ve_error_get()); free(name); free(vctx); close(fd); TSEXIT(); return(NULL); } #else #ifdef LOADER_VERIEXEC if (verify_file(fd, name, 0, VE_MUST, __func__) < 0) { sprintf(command_errbuf, "can't verify '%s': %s", name, ve_error_get()); free(name); close(fd); TSEXIT(); return(NULL); } #endif #endif - if (archsw.arch_loadaddr != NULL) - loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); + loadaddr = md_align(loadaddr); if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", name); laddr = loadaddr; for (;;) { /* read in 4k chunks; size is not really important */ got = archsw.arch_readin(VECTX_HANDLE(fd), laddr, 4096); if (got == 0) /* end of file */ break; if (got < 0) { /* error */ snprintf(command_errbuf, sizeof(command_errbuf), "error reading '%s': %s", name, strerror(errno)); free(name); close(fd); #ifdef LOADER_VERIEXEC_VECTX free(vctx); #endif TSEXIT(); return(NULL); } laddr += got; } if (module_verbose > MODULE_VERBOSE_SILENT) printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr)); #ifdef LOADER_VERIEXEC_VECTX verror = vectx_close(vctx, VE_MUST, __func__); if (verror) { free(name); close(fd); free(vctx); TSEXIT(); return(NULL); } #endif /* Looks OK so far; create & populate control structure */ fp = file_alloc(); if (fp == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(name); close(fd); TSEXIT(); return (NULL); } fp->f_name = name; fp->f_type = strdup(type); fp->f_args = NULL; fp->f_metadata = NULL; fp->f_loader = -1; fp->f_addr = loadaddr; fp->f_size = laddr - loadaddr; if (fp->f_type == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(name); close(fd); TSEXIT(); return (NULL); } /* recognise space consumption */ loadaddr = laddr; /* Add to the list of loaded files */ if (insert != 0) file_insert_tail(fp); close(fd); TSEXIT(); return(fp); } /* * Load the module (name), pass it (argc),(argv), add container file * to the list of loaded files. * If module is already loaded just assign new argc/argv. */ int mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) { struct kernel_module *mp; int err; char *filename; TSENTER2(modname); if (file_havepath(modname)) { printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); TSEXIT(); return (mod_loadkld(modname, argc, argv)); } /* see if module is already loaded */ mp = file_findmodule(NULL, modname, verinfo); if (mp) { #ifdef moduleargs free(mp->m_args); mp->m_args = unargv(argc, argv); #endif snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", mp->m_name); TSEXIT(); return (0); } /* locate file with the module on the search path */ filename = mod_searchmodule(modname, verinfo); if (filename == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", modname); TSEXIT(); return (ENOENT); } err = mod_loadkld(filename, argc, argv); free(filename); TSEXIT(); return (err); } /* * Load specified KLD. If path is omitted, then try to locate it via * search path. */ int mod_loadkld(const char *kldname, int argc, char *argv[]) { struct preloaded_file *fp; int err; char *filename; vm_offset_t loadaddr_saved; TSENTER2(kldname); /* * Get fully qualified KLD name */ filename = file_search(kldname, kld_ext_list); if (filename == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", kldname); TSEXIT(); return (ENOENT); } /* * Check if KLD already loaded */ fp = file_findfile(filename, NULL); if (fp) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: KLD '%s' already loaded", filename); free(filename); TSEXIT(); return (0); } do { err = file_load(filename, loadaddr, &fp); if (err) break; fp->f_args = unargv(argc, argv); loadaddr_saved = loadaddr; loadaddr = fp->f_addr + fp->f_size; file_insert_tail(fp); /* Add to the list of loaded files */ if (file_load_dependencies(fp) != 0) { err = ENOENT; file_remove_tail(fp); loadaddr = loadaddr_saved; fp = NULL; break; } } while(0); if (err == EFTYPE) { snprintf(command_errbuf, sizeof(command_errbuf), "don't know how to load module '%s'", filename); } if (err) file_discard(fp); free(filename); TSEXIT(); return (err); } /* * Find a file matching (name) and (type). * NULL may be passed as a wildcard to either. */ struct preloaded_file * file_findfile(const char *name, const char *type) { struct preloaded_file *fp; for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { if (((name == NULL) || !strcmp(name, fp->f_name)) && ((type == NULL) || !strcmp(type, fp->f_type))) break; } return (fp); } /* * Find a module matching (name) inside of given file. * NULL may be passed as a wildcard. */ struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo) { struct kernel_module *mp, *best; int bestver, mver; if (fp == NULL) { for (fp = preloaded_files; fp; fp = fp->f_next) { mp = file_findmodule(fp, modname, verinfo); if (mp) return (mp); } return (NULL); } best = NULL; bestver = 0; for (mp = fp->f_modules; mp; mp = mp->m_next) { if (strcmp(modname, mp->m_name) == 0) { if (verinfo == NULL) return (mp); mver = mp->m_version; if (mver == verinfo->md_ver_preferred) return (mp); if (mver >= verinfo->md_ver_minimum && mver <= verinfo->md_ver_maximum && mver > bestver) { best = mp; bestver = mver; } } } return (best); } /* * Make a copy of (size) bytes of data from (p), and associate them as * metadata of (type) to the module (mp). */ void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) { struct file_metadata *md; md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); if (md != NULL) { md->md_size = size; md->md_type = type; bcopy(p, md->md_data, size); md->md_next = fp->f_metadata; } fp->f_metadata = md; } /* * Find a metadata object of (type) associated with the file (fp) */ struct file_metadata * file_findmetadata(struct preloaded_file *fp, int type) { struct file_metadata *md; for (md = fp->f_metadata; md != NULL; md = md->md_next) if (md->md_type == type) break; return(md); } /* * Remove all metadata from the file. */ void file_removemetadata(struct preloaded_file *fp) { struct file_metadata *md, *next; for (md = fp->f_metadata; md != NULL; md = next) { next = md->md_next; free(md); } fp->f_metadata = NULL; } /* * Add a buffer to the list of preloaded "files". */ int file_addbuf(const char *name, const char *type, size_t len, void *buf) { struct preloaded_file *fp; vm_offset_t dest; /* We can't load first */ if ((file_findfile(NULL, NULL)) == NULL) { command_errmsg = "can't load file before kernel"; return (-1); } /* Figure out where to load the data. */ - dest = loadaddr; - if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_RAW, (void *)name, dest); + dest = md_align(loadaddr); /* Create & populate control structure */ fp = file_alloc(); if (fp == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); return (-1); } fp->f_name = strdup(name); fp->f_type = strdup(type); fp->f_args = NULL; fp->f_metadata = NULL; fp->f_loader = -1; fp->f_addr = dest; fp->f_size = len; if ((fp->f_name == NULL) || (fp->f_type == NULL)) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(fp->f_name); free(fp->f_type); return (-1); } /* Copy the data in. */ archsw.arch_copyin(buf, fp->f_addr, len); loadaddr = fp->f_addr + len; /* Add to the list of loaded files */ file_insert_tail(fp); return(0); } struct file_metadata * metadata_next(struct file_metadata *md, int type) { if (md == NULL) return (NULL); while((md = md->md_next) != NULL) if (md->md_type == type) break; return (md); } static char *emptyextlist[] = { "", NULL }; /* * Check if the given file is in place and return full path to it. */ static char * file_lookup(const char *path, const char *name, int namelen, char **extlist) { struct stat st; char *result, *cp, **cpp; int pathlen, extlen, len; pathlen = strlen(path); extlen = 0; if (extlist == NULL) extlist = emptyextlist; for (cpp = extlist; *cpp; cpp++) { len = strlen(*cpp); if (len > extlen) extlen = len; } result = malloc(pathlen + namelen + extlen + 2); if (result == NULL) return (NULL); bcopy(path, result, pathlen); if (pathlen > 0 && result[pathlen - 1] != '/') result[pathlen++] = '/'; cp = result + pathlen; bcopy(name, cp, namelen); cp += namelen; for (cpp = extlist; *cpp; cpp++) { strcpy(cp, *cpp); if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) return result; } free(result); return NULL; } /* * Check if file name have any qualifiers */ static int file_havepath(const char *name) { const char *cp; archsw.arch_getdev(NULL, name, &cp); return (cp != name || strchr(name, '/') != NULL); } /* * Attempt to find the file (name) on the module searchpath. * If (name) is qualified in any way, we simply check it and * return it or NULL. If it is not qualified, then we attempt * to construct a path using entries in the environment variable * module_path. * * The path we return a pointer to need never be freed, as we manage * it internally. */ static char * file_search(const char *name, char **extlist) { struct moduledir *mdp; struct stat sb; char *result; int namelen; /* Don't look for nothing */ if (name == NULL) return(NULL); if (*name == 0) return(strdup(name)); if (file_havepath(name)) { /* Qualified, so just see if it exists */ if (stat(name, &sb) == 0) return(strdup(name)); return(NULL); } moduledir_rebuild(); result = NULL; namelen = strlen(name); STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = file_lookup(mdp->d_path, name, namelen, extlist); if (result) break; } return(result); } #define INT_ALIGN(base, ptr) ptr = \ (base) + roundup2((ptr) - (base), sizeof(int)) static char * mod_search_hints(struct moduledir *mdp, const char *modname, struct mod_depend *verinfo) { u_char *cp, *recptr, *bufend, *best; char *result; int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; moduledir_readhints(mdp); modnamelen = strlen(modname); found = 0; result = NULL; bestver = 0; if (mdp->d_hints == NULL) goto bad; recptr = mdp->d_hints; bufend = recptr + mdp->d_hintsz; clen = blen = 0; best = cp = NULL; while (recptr < bufend && !found) { intp = (int*)recptr; reclen = *intp++; ival = *intp++; cp = (u_char*)intp; switch (ival) { case MDT_VERSION: clen = *cp++; if (clen != modnamelen || bcmp(cp, modname, clen) != 0) break; cp += clen; INT_ALIGN(mdp->d_hints, cp); ival = *(int*)cp; cp += sizeof(int); clen = *cp++; if (verinfo == NULL || ival == verinfo->md_ver_preferred) { found = 1; break; } if (ival >= verinfo->md_ver_minimum && ival <= verinfo->md_ver_maximum && ival > bestver) { bestver = ival; best = cp; blen = clen; } break; default: break; } recptr += reclen + sizeof(int); } /* * Finally check if KLD is in the place */ if (found) result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL); else if (best) result = file_lookup(mdp->d_path, (const char *)best, blen, NULL); bad: /* * If nothing found or hints is absent - fallback to the old way * by using "kldname[.ko]" as module name. */ if (!found && !bestver && result == NULL) result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); return result; } static int getint(void **ptr) { int *p = *ptr; int rv; p = (int *)roundup2((intptr_t)p, sizeof(int)); rv = *p++; *ptr = p; return rv; } static void getstr(void **ptr, char *val) { int *p = *ptr; char *c = (char *)p; int len = *(uint8_t *)c; memcpy(val, c + 1, len); val[len] = 0; c += len + 1; *ptr = (void *)c; } static int pnpval_as_int(const char *val, const char *pnpinfo) { int rv; char key[256]; char *cp; if (pnpinfo == NULL) return -1; cp = strchr(val, ';'); key[0] = ' '; if (cp == NULL) strlcpy(key + 1, val, sizeof(key) - 1); else { memcpy(key + 1, val, cp - val); key[cp - val + 1] = '\0'; } strlcat(key, "=", sizeof(key)); if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) rv = strtol(pnpinfo + strlen(key + 1), NULL, 0); else { cp = strstr(pnpinfo, key); if (cp == NULL) rv = -1; else rv = strtol(cp + strlen(key), NULL, 0); } return rv; } static void quoted_strcpy(char *dst, const char *src) { char q = ' '; if (*src == '\'' || *src == '"') q = *src++; while (*src && *src != q) *dst++ = *src++; // XXX backtick quoting *dst++ = '\0'; // XXX overflow } static char * pnpval_as_str(const char *val, const char *pnpinfo) { static char retval[256]; char key[256]; char *cp; if (pnpinfo == NULL) { *retval = '\0'; return retval; } cp = strchr(val, ';'); key[0] = ' '; if (cp == NULL) strlcpy(key + 1, val, sizeof(key) - 1); else { memcpy(key + 1, val, cp - val); key[cp - val + 1] = '\0'; } strlcat(key, "=", sizeof(key)); if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) quoted_strcpy(retval, pnpinfo + strlen(key + 1)); else { cp = strstr(pnpinfo, key); if (cp == NULL) strcpy(retval, "MISSING"); else quoted_strcpy(retval, cp + strlen(key)); } return retval; } static char * devmatch_search_hints(struct moduledir *mdp, const char *bus, const char *dev, const char *pnpinfo) { char val1[256], val2[256]; int ival, len, ents, i, notme, mask, bit, v, found; void *ptr, *walker, *hints_end; char *lastmod = NULL, *cp, *s; moduledir_readhints(mdp); found = 0; if (mdp->d_hints == NULL) goto bad; walker = mdp->d_hints; hints_end = walker + mdp->d_hintsz; while (walker < hints_end && !found) { len = getint(&walker); ival = getint(&walker); ptr = walker; switch (ival) { case MDT_VERSION: getstr(&ptr, val1); ival = getint(&ptr); getstr(&ptr, val2); if (pnp_dump_flag || pnp_verbose_flag) printf("Version: if %s.%d kmod %s\n", val1, ival, val2); break; case MDT_MODULE: getstr(&ptr, val1); getstr(&ptr, val2); if (lastmod) free(lastmod); lastmod = strdup(val2); if (pnp_dump_flag || pnp_verbose_flag) printf("module %s in %s\n", val1, val1); break; case MDT_PNP_INFO: if (!pnp_dump_flag && !pnp_unbound_flag && lastmod && strcmp(lastmod, "kernel") == 0) break; getstr(&ptr, val1); getstr(&ptr, val2); ents = getint(&ptr); if (pnp_dump_flag || pnp_verbose_flag) printf("PNP info for bus %s format %s %d entries (%s)\n", val1, val2, ents, lastmod); if (strcmp(val1, "usb") == 0) { if (pnp_verbose_flag) printf("Treating usb as uhub -- bug in source table still?\n"); strcpy(val1, "uhub"); } if (bus && strcmp(val1, bus) != 0) { if (pnp_verbose_flag) printf("Skipped because table for bus %s, looking for %s\n", val1, bus); break; } for (i = 0; i < ents; i++) { if (pnp_verbose_flag) printf("---------- Entry %d ----------\n", i); if (pnp_dump_flag) printf(" "); cp = val2; notme = 0; mask = -1; bit = -1; do { switch (*cp) { /* All integer fields */ case 'I': case 'J': case 'G': case 'L': case 'M': ival = getint(&ptr); if (pnp_dump_flag) { printf("%#x:", ival); break; } if (bit >= 0 && ((1 << bit) & mask) == 0) break; v = pnpval_as_int(cp + 2, pnpinfo); if (pnp_verbose_flag) printf("Matching %s (%c) table=%#x tomatch=%#x\n", cp + 2, *cp, v, ival); switch (*cp) { case 'J': if (ival == -1) break; /*FALLTHROUGH*/ case 'I': if (v != ival) notme++; break; case 'G': if (v < ival) notme++; break; case 'L': if (v > ival) notme++; break; case 'M': mask = ival; break; } break; /* String fields */ case 'D': case 'Z': getstr(&ptr, val1); if (pnp_dump_flag) { printf("'%s':", val1); break; } if (*cp == 'D') break; s = pnpval_as_str(cp + 2, pnpinfo); if (strcmp(s, val1) != 0) notme++; break; /* Key override fields, required to be last in the string */ case 'T': /* * This is imperfect and only does one key and will be redone * to be more general for multiple keys. Currently, nothing * does that. */ if (pnp_dump_flag) /* No per-row data stored */ break; if (cp[strlen(cp) - 1] == ';') /* Skip required ; at end */ cp[strlen(cp) - 1] = '\0'; /* in case it's not there */ if ((s = strstr(pnpinfo, cp + 2)) == NULL) notme++; else if (s > pnpinfo && s[-1] != ' ') notme++; break; default: printf("Unknown field type %c\n:", *cp); break; } bit++; cp = strchr(cp, ';'); if (cp) cp++; } while (cp && *cp); if (pnp_dump_flag) printf("\n"); else if (!notme) { if (!pnp_unbound_flag) { if (pnp_verbose_flag) printf("Matches --- %s ---\n", lastmod); } found++; } } break; default: break; } walker = (void *)(len - sizeof(int) + (intptr_t)walker); } if (pnp_unbound_flag && found == 0 && *pnpinfo) { if (pnp_verbose_flag) printf("------------------------- "); printf("%s on %s pnpinfo %s", *dev ? dev : "unattached", bus, pnpinfo); if (pnp_verbose_flag) printf(" -------------------------"); printf("\n"); } if (found != 0) return (lastmod); free(lastmod); bad: return (NULL); } /* * Attempt to locate the file containing the module (name) */ static char * mod_searchmodule(char *name, struct mod_depend *verinfo) { struct moduledir *mdp; char *result; moduledir_rebuild(); /* * Now we ready to lookup module in the given directories */ result = NULL; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = mod_search_hints(mdp, name, verinfo); if (result) break; } return(result); } static char * mod_searchmodule_pnpinfo(const char *bus, const char *pnpinfo) { struct moduledir *mdp; char *result; moduledir_rebuild(); /* * Now we ready to lookup module in the given directories */ result = NULL; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = devmatch_search_hints(mdp, bus, NULL, pnpinfo); if (result) break; } return(result); } int file_addmodule(struct preloaded_file *fp, char *modname, int version, struct kernel_module **newmp) { struct kernel_module *mp; struct mod_depend mdepend; bzero(&mdepend, sizeof(mdepend)); mdepend.md_ver_preferred = version; mp = file_findmodule(fp, modname, &mdepend); if (mp) return (EEXIST); mp = calloc(1, sizeof(struct kernel_module)); if (mp == NULL) return (ENOMEM); mp->m_name = strdup(modname); if (mp->m_name == NULL) { free(mp); return (ENOMEM); } mp->m_version = version; mp->m_fp = fp; mp->m_next = fp->f_modules; fp->f_modules = mp; if (newmp) *newmp = mp; return (0); } /* * Throw a file away */ void file_discard(struct preloaded_file *fp) { struct file_metadata *md, *md1; struct kernel_module *mp, *mp1; if (fp == NULL) return; md = fp->f_metadata; while (md) { md1 = md; md = md->md_next; free(md1); } mp = fp->f_modules; while (mp) { free(mp->m_name); mp1 = mp; mp = mp->m_next; free(mp1); } free(fp->f_name); free(fp->f_type); free(fp->f_args); free(fp); } /* * Allocate a new file; must be used instead of malloc() * to ensure safe initialisation. */ struct preloaded_file * file_alloc(void) { return (calloc(1, sizeof(struct preloaded_file))); } /* * Add a module to the chain */ static void file_insert_tail(struct preloaded_file *fp) { struct preloaded_file *cm; /* Append to list of loaded file */ fp->f_next = NULL; if (preloaded_files == NULL) { preloaded_files = fp; } else { for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) ; cm->f_next = fp; } } /* * Remove module from the chain */ static void file_remove_impl(struct preloaded_file *fp, bool keep_tail) { struct preloaded_file *cm, *next; if (preloaded_files == NULL) return; if (keep_tail) next = fp->f_next; else next = NULL; if (preloaded_files == fp) { preloaded_files = next; return; } for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) { if (cm->f_next == fp) { cm->f_next = next; return; } } } static void file_remove(struct preloaded_file *fp) { file_remove_impl(fp, true); } static void file_remove_tail(struct preloaded_file *fp) { file_remove_impl(fp, false); } static char * moduledir_fullpath(struct moduledir *mdp, const char *fname) { char *cp; cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); if (cp == NULL) return NULL; strcpy(cp, mdp->d_path); strcat(cp, "/"); strcat(cp, fname); return (cp); } /* * Read linker.hints file into memory performing some sanity checks. */ static void moduledir_readhints(struct moduledir *mdp) { struct stat st; char *path; int fd, size, version; if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) return; path = moduledir_fullpath(mdp, "linker.hints"); if (stat(path, &st) != 0 || st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) || st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) { free(path); mdp->d_flags |= MDIR_NOHINTS; return; } free(path); size = read(fd, &version, sizeof(version)); if (size != sizeof(version) || version != LINKER_HINTS_VERSION) goto bad; size = st.st_size - size; mdp->d_hints = malloc(size); if (mdp->d_hints == NULL) goto bad; if (read(fd, mdp->d_hints, size) != size) goto bad; mdp->d_hintsz = size; close(fd); return; bad: close(fd); free(mdp->d_hints); mdp->d_hints = NULL; mdp->d_flags |= MDIR_NOHINTS; return; } /* * Extract directories from the ';' separated list, remove duplicates. */ static void moduledir_rebuild(void) { struct moduledir *mdp, *mtmp; const char *path, *cp, *ep; size_t cplen; path = getenv("module_path"); if (path == NULL) path = default_searchpath; /* * Rebuild list of module directories if it changed */ STAILQ_FOREACH(mdp, &moduledir_list, d_link) mdp->d_flags |= MDIR_REMOVED; for (ep = path; *ep != 0; ep++) { cp = ep; for (; *ep != 0 && *ep != ';'; ep++) ; /* * Ignore trailing slashes */ for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) ; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) continue; mdp->d_flags &= ~MDIR_REMOVED; break; } if (mdp == NULL) { mdp = malloc(sizeof(*mdp) + cplen + 1); if (mdp == NULL) return; mdp->d_path = (char*)(mdp + 1); bcopy(cp, mdp->d_path, cplen); mdp->d_path[cplen] = 0; mdp->d_hints = NULL; mdp->d_flags = 0; STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); } if (*ep == 0) break; } /* * Delete unused directories if any */ mdp = STAILQ_FIRST(&moduledir_list); while (mdp) { if ((mdp->d_flags & MDIR_REMOVED) == 0) { mdp = STAILQ_NEXT(mdp, d_link); } else { free(mdp->d_hints); mtmp = mdp; mdp = STAILQ_NEXT(mdp, d_link); STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); free(mtmp); } } return; } diff --git a/stand/kboot/kboot/arch/aarch64/exec.c b/stand/kboot/kboot/arch/aarch64/exec.c index 40eb57f371a9..df3e922dfe11 100644 --- a/stand/kboot/kboot/arch/aarch64/exec.c +++ b/stand/kboot/kboot/arch/aarch64/exec.c @@ -1,289 +1,289 @@ /*- * Copyright (c) 2006 Marcel Moolenaar * 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 ``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 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. */ #include #include #include #include #include #ifdef EFI #include #include #include "loader_efi.h" #else #include "host_syscall.h" #endif #include #include "bootstrap.h" #include "efi.h" #include "kboot.h" #include "platform/acfreebsd.h" #include "acconfig.h" #define ACPI_SYSTEM_XFACE #include "actypes.h" #include "actbl.h" #include "cache.h" #ifndef EFI #define LOADER_PAGE_SIZE PAGE_SIZE #endif #ifdef EFI static EFI_GUID acpi_guid = ACPI_TABLE_GUID; static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID; #endif static int elf64_exec(struct preloaded_file *amp); static int elf64_obj_exec(struct preloaded_file *amp); bool do_mem_map = false; /* Usually provided by loader_efi.h -- maybe just delete? */ #ifndef EFI int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, bool exit_bs); #endif static struct file_format arm64_elf = { elf64_loadfile, elf64_exec }; struct file_format *file_formats[] = { &arm64_elf, NULL }; #ifndef EFI extern uintptr_t tramp; extern uint32_t tramp_size; extern uint32_t tramp_data_offset; struct trampoline_data { uint64_t entry; // 0 (PA where kernel loaded) uint64_t modulep; // 8 module metadata uint64_t memmap_src; // 16 Linux-provided memory map PA uint64_t memmap_dst; // 24 Module data copy PA uint64_t memmap_len; // 32 Length to copy }; #endif static int elf64_exec(struct preloaded_file *fp) { vm_offset_t modulep, kernendp; #ifdef EFI vm_offset_t clean_addr; size_t clean_size; void (*entry)(vm_offset_t); #else vm_offset_t trampolinebase; vm_offset_t staging; void *trampcode; uint64_t *trampoline; struct trampoline_data *trampoline_data; int nseg; void *kseg; #endif struct file_metadata *md; Elf_Ehdr *ehdr; int error; #ifdef EFI ACPI_TABLE_RSDP *rsdp; char buf[24]; int revision; /* * Report the RSDP to the kernel. The old code used the 'hints' method * to communite this to the kernel. However, while convenient, the * 'hints' method is fragile and does not work when static hints are * compiled into the kernel. Instead, move to setting different tunables * that start with acpi. The old 'hints' can be removed before we branch * for FreeBSD 15. */ rsdp = efi_get_table(&acpi20_guid); if (rsdp == NULL) { rsdp = efi_get_table(&acpi_guid); } if (rsdp != NULL) { sprintf(buf, "0x%016llx", (unsigned long long)rsdp); setenv("hint.acpi.0.rsdp", buf, 1); setenv("acpi.rsdp", buf, 1); revision = rsdp->Revision; if (revision == 0) revision = 1; sprintf(buf, "%d", revision); setenv("hint.acpi.0.revision", buf, 1); setenv("acpi.revision", buf, 1); strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId)); buf[sizeof(rsdp->OemId)] = '\0'; setenv("hint.acpi.0.oem", buf, 1); setenv("acpi.oem", buf, 1); sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress); setenv("hint.acpi.0.rsdt", buf, 1); setenv("acpi.rsdt", buf, 1); if (revision >= 2) { /* XXX extended checksum? */ sprintf(buf, "0x%016llx", (unsigned long long)rsdp->XsdtPhysicalAddress); setenv("hint.acpi.0.xsdt", buf, 1); setenv("acpi.xsdt", buf, 1); sprintf(buf, "%d", rsdp->Length); setenv("hint.acpi.0.xsdt_length", buf, 1); setenv("acpi.xsdt_length", buf, 1); } } #else vm_offset_t rsdp; rsdp = acpi_rsdp(); if (rsdp != 0) { char buf[24]; printf("Found ACPI 2.0 at %#016lx\n", rsdp); sprintf(buf, "0x%016llx", (unsigned long long)rsdp); setenv("hint.acpi.0.rsdp", buf, 1); /* For 13.1R bootability */ setenv("acpi.rsdp", buf, 1); /* Nobody uses the rest of that stuff */ } // XXX Question: why not just use malloc? trampcode = host_getmem(LOADER_PAGE_SIZE); if (trampcode == NULL) { printf("Unable to allocate trampoline\n"); return (ENOMEM); } bzero((void *)trampcode, LOADER_PAGE_SIZE); bcopy((void *)&tramp, (void *)trampcode, tramp_size); trampoline = (void *)trampcode; /* * Figure out where to put it. * - * Linux does not allow us to kexec_load into any part of memory. Ask - * arch_loadaddr to resolve the first available chunk of physical memory - * where loading is possible (load_addr). + * Linux does not allow us to kexec_load into any part of memory. Find + * the first available chunk of physical memory where loading is + * possible (staging). * * The kernel is loaded at the 'base' address in continguous physical * memory. We use the 2MB in front of the kernel as a place to put our * trampoline, but that's really overkill since we only need ~100 bytes. * The arm64 kernel's entry requirements are only 'load the kernel at a * 2MB alignment' and it figures out the rest, creates the right page * tables, etc. */ staging = kboot_get_phys_load_segment(); printf("Load address at %#jx\n", (uintmax_t)staging); printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset); #endif if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); #ifdef EFI entry = efi_translate(ehdr->e_entry); efi_time_fini(); #endif error = bi_load(fp->f_args, &modulep, &kernendp, true); if (error != 0) { #ifdef EFI efi_time_init(); #endif return (error); } dev_cleanup(); #ifdef EFI /* Clean D-cache under kernel area and invalidate whole I-cache */ clean_addr = (vm_offset_t)efi_translate(fp->f_addr); clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr; cpu_flush_dcache((void *)clean_addr, clean_size); cpu_inval_icache(); (*entry)(modulep); #else /* Linux will flush the caches, just pass this data into our trampoline and go */ trampoline_data = (void *)trampoline + tramp_data_offset; memset(trampoline_data, 0, sizeof(*trampoline_data)); trampoline_data->entry = ehdr->e_entry - fp->f_addr + staging; trampoline_data->modulep = modulep; printf("Modulep = %jx\n", (uintmax_t)modulep); if (efi_map_phys_src != 0) { md = file_findmetadata(fp, MODINFOMD_EFI_MAP); if (md == NULL || md->md_addr == 0) { printf("Need to copy EFI MAP, but EFI MAP not found. %p\n", md); } else { printf("Metadata EFI map loaded at VA %lx\n", md->md_addr); efi_map_phys_dst = md->md_addr + staging + roundup2(sizeof(struct efi_map_header), 16) - fp->f_addr; trampoline_data->memmap_src = efi_map_phys_src; trampoline_data->memmap_dst = efi_map_phys_dst; trampoline_data->memmap_len = efi_map_size - roundup2(sizeof(struct efi_map_header), 16); printf("Copying UEFI Memory Map data from %#lx to %#lx %ld bytes\n", efi_map_phys_src, trampoline_data->memmap_dst, trampoline_data->memmap_len); } } /* * Copy the trampoline to the ksegs. Since we're just bouncing off of * this into the kernel, no need to preserve the pages. On arm64, the * kernel sets up the initial page table, so we don't have to preserve * the memory used for the trampoline past when it calls the kernel. */ printf("kernendp = %#llx\n", (long long)kernendp); trampolinebase = staging + (kernendp - fp->f_addr); printf("trampolinebase = %#llx\n", (long long)trampolinebase); archsw.arch_copyin((void *)trampcode, kernendp, tramp_size); printf("Trampoline bouncing to %#llx\n", (long long)trampoline_data->entry); kboot_kseg_get(&nseg, &kseg); error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_AARCH64); if (error != 0) panic("kexec_load returned error: %d", error); host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC, 0); #endif panic("exec returned"); } static int elf64_obj_exec(struct preloaded_file *fp) { printf("%s called for preloaded file %p (=%s):\n", __func__, fp, fp->f_name); return (ENOSYS); } diff --git a/stand/kboot/kboot/arch/amd64/elf64_freebsd.c b/stand/kboot/kboot/arch/amd64/elf64_freebsd.c index 330646715289..576839e29734 100644 --- a/stand/kboot/kboot/arch/amd64/elf64_freebsd.c +++ b/stand/kboot/kboot/arch/amd64/elf64_freebsd.c @@ -1,481 +1,481 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 2014 The FreeBSD Foundation * 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. */ #define __ELF_WORD_SIZE 64 #include #include #include #include #include #include #include #include #ifdef EFI #include #include #else #include "host_syscall.h" #endif #include "bootstrap.h" #include "kboot.h" #include "efi.h" #include "platform/acfreebsd.h" #include "acconfig.h" #define ACPI_SYSTEM_XFACE #include "actypes.h" #include "actbl.h" #ifdef EFI #include "loader_efi.h" static EFI_GUID acpi_guid = ACPI_TABLE_GUID; static EFI_GUID acpi20_guid = ACPI_20_TABLE_GUID; #else /* Usually provided by loader_efi.h */ extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, bool exit_bs); #endif #ifdef EFI #define LOADER_PAGE_SIZE EFI_PAGE_SIZE #else #define LOADER_PAGE_SIZE PAGE_SIZE #endif static int elf64_exec(struct preloaded_file *amp); static int elf64_obj_exec(struct preloaded_file *amp); static struct file_format amd64_elf = { .l_load = elf64_loadfile, .l_exec = elf64_exec, }; static struct file_format amd64_elf_obj = { .l_load = elf64_obj_loadfile, .l_exec = elf64_obj_exec, }; #ifdef EFI extern struct file_format multiboot2; extern struct file_format multiboot2_obj; #endif struct file_format *file_formats[] = { #ifdef EFI &multiboot2, &multiboot2_obj, #endif &amd64_elf, &amd64_elf_obj, NULL }; #ifndef EFI /* * We create the stack that we want. We store any memory map table that we have * top copy (the metadata has already been filled in). We pop these args off and * copy if neeed be. Then, we have the address of the page tables we make on top * (so we pop that off and set %cr3). We have the entry point to the kernel * (which retq pops off) This leaves the stack that the btext wants: offset 4 is * modulep and offset8 is kernend, with the filler bytes to keep this * aligned. This also makes the trampoline very simple: pop some args, maybe copy * pop the page table and then return into btext as defined in the kernel. */ struct trampoline_data { uint64_t memmap_src; // Linux-provided memory map PA uint64_t memmap_dst; // Module data copy PA uint64_t memmap_len; // Length to copy uint64_t pt4; // Page table address to pop uint64_t entry; // return address to jump to kernel uint32_t fill1; // 0 uint32_t modulep; // 4 module metadata uint32_t kernend; // 8 kernel end uint32_t fill2; // 12 }; _Static_assert(sizeof(struct trampoline_data) == 56, "Bad size for trampoline data"); #endif static pml4_entry_t *PT4; static pdp_entry_t *PT3_l, *PT3_u; static pd_entry_t *PT2_l0, *PT2_l1, *PT2_l2, *PT2_l3, *PT2_u0, *PT2_u1; #ifdef EFI static pdp_entry_t *PT3; static pd_entry_t *PT2; static void (*trampoline)(uint64_t stack, void *copy_finish, uint64_t kernend, uint64_t modulep, pml4_entry_t *pagetable, uint64_t entry); #endif extern uintptr_t tramp; extern uint32_t tramp_size; #ifndef EFI extern uint32_t tramp_data_offset; #endif /* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf64_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t modulep, kernend; int err, i; char buf[24]; #ifdef EFI ACPI_TABLE_RSDP *rsdp = NULL; int revision; int copy_auto; vm_offset_t trampstack, trampcode; #else vm_offset_t rsdp = 0; void *trampcode; int nseg; void *kseg; vm_offset_t trampolinebase; uint64_t *trampoline; struct trampoline_data *trampoline_data; vm_offset_t staging; int error; #endif #ifdef EFI copy_auto = copy_staging == COPY_STAGING_AUTO; if (copy_auto) copy_staging = fp->f_kernphys_relocatable ? COPY_STAGING_DISABLE : COPY_STAGING_ENABLE; #else /* * Figure out where to put it. * - * Linux does not allow to do kexec_load into any part of memory. Ask - * arch_loadaddr to resolve the first available chunk of physical memory - * where loading is possible (load_addr). + * Linux does not allow us to do kexec_load into any part of memory. Ask + * kboot_get_phys_load_segment to resolve the first available chunk of + * physical memory where loading is possible (staging). * * The kernel is loaded at the 'base' address in continguous physical * pages (using 2MB super pages). The first such page is unused by the * kernel and serves as a good place to put not only the trampoline, but * the page table pages that the trampoline needs to setup the proper * kernel starting environment. */ staging = trampolinebase = kboot_get_phys_load_segment(); trampolinebase += 1ULL << 20; /* Copy trampoline to base + 1MB, kernel will wind up at 2MB */ printf("Load address at %#jx\n", (uintmax_t)trampolinebase); printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset); #endif /* * Report the RSDP to the kernel. While this can be found with * a BIOS boot, the RSDP may be elsewhere when booted from UEFI. */ #ifdef EFI rsdp = efi_get_table(&acpi20_guid); if (rsdp == NULL) { rsdp = efi_get_table(&acpi_guid); } #else rsdp = acpi_rsdp(); #endif if (rsdp != 0) { sprintf(buf, "0x%016llx", (unsigned long long)rsdp); setenv("acpi.rsdp", buf, 1); } if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return (EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); #ifdef EFI trampcode = copy_staging == COPY_STAGING_ENABLE ? (vm_offset_t)G(1) : (vm_offset_t)G(4); err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1, (EFI_PHYSICAL_ADDRESS *)&trampcode); if (EFI_ERROR(err)) { printf("Unable to allocate trampoline\n"); if (copy_auto) copy_staging = COPY_STAGING_AUTO; return (ENOMEM); } trampstack = trampcode + LOADER_PAGE_SIZE - 8; #else // XXX Question: why not just use malloc? trampcode = host_getmem(LOADER_PAGE_SIZE); if (trampcode == NULL) { printf("Unable to allocate trampoline\n"); return (ENOMEM); } #endif bzero((void *)trampcode, LOADER_PAGE_SIZE); bcopy((void *)&tramp, (void *)trampcode, tramp_size); trampoline = (void *)trampcode; #ifdef EFI if (copy_staging == COPY_STAGING_ENABLE) { PT4 = (pml4_entry_t *)G(1); err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3, (EFI_PHYSICAL_ADDRESS *)&PT4); if (EFI_ERROR(err)) { printf("Unable to allocate trampoline page table\n"); BS->FreePages(trampcode, 1); if (copy_auto) copy_staging = COPY_STAGING_AUTO; return (ENOMEM); } bzero(PT4, 3 * LOADER_PAGE_SIZE); PT3 = &PT4[512]; PT2 = &PT3[512]; /* * This is kinda brutal, but every single 1GB VM * memory segment points to the same first 1GB of * physical memory. But it is more than adequate. */ for (i = 0; i < NPTEPG; i++) { /* * Each slot of the L4 pages points to the * same L3 page. */ PT4[i] = (pml4_entry_t)PT3; PT4[i] |= PG_V | PG_RW; /* * Each slot of the L3 pages points to the * same L2 page. */ PT3[i] = (pdp_entry_t)PT2; PT3[i] |= PG_V | PG_RW; /* * The L2 page slots are mapped with 2MB pages for 1GB. */ PT2[i] = (pd_entry_t)i * M(2); PT2[i] |= PG_V | PG_RW | PG_PS; } } else { PT4 = (pml4_entry_t *)G(4); err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 9, (EFI_PHYSICAL_ADDRESS *)&PT4); if (EFI_ERROR(err)) { printf("Unable to allocate trampoline page table\n"); BS->FreePages(trampcode, 9); if (copy_auto) copy_staging = COPY_STAGING_AUTO; return (ENOMEM); } bzero(PT4, 9 * LOADER_PAGE_SIZE); PT3_l = &PT4[NPML4EPG * 1]; PT3_u = &PT4[NPML4EPG * 2]; PT2_l0 = &PT4[NPML4EPG * 3]; PT2_l1 = &PT4[NPML4EPG * 4]; PT2_l2 = &PT4[NPML4EPG * 5]; PT2_l3 = &PT4[NPML4EPG * 6]; PT2_u0 = &PT4[NPML4EPG * 7]; PT2_u1 = &PT4[NPML4EPG * 8]; /* 1:1 mapping of lower 4G */ PT4[0] = (pml4_entry_t)PT3_l | PG_V | PG_RW; PT3_l[0] = (pdp_entry_t)PT2_l0 | PG_V | PG_RW; PT3_l[1] = (pdp_entry_t)PT2_l1 | PG_V | PG_RW; PT3_l[2] = (pdp_entry_t)PT2_l2 | PG_V | PG_RW; PT3_l[3] = (pdp_entry_t)PT2_l3 | PG_V | PG_RW; for (i = 0; i < 4 * NPDEPG; i++) { PT2_l0[i] = ((pd_entry_t)i << PDRSHIFT) | PG_V | PG_RW | PG_PS; } /* mapping of kernel 2G below top */ PT4[NPML4EPG - 1] = (pml4_entry_t)PT3_u | PG_V | PG_RW; PT3_u[NPDPEPG - 2] = (pdp_entry_t)PT2_u0 | PG_V | PG_RW; PT3_u[NPDPEPG - 1] = (pdp_entry_t)PT2_u1 | PG_V | PG_RW; /* compat mapping of phys @0 */ PT2_u0[0] = PG_PS | PG_V | PG_RW; /* this maps past staging area */ for (i = 1; i < 2 * NPDEPG; i++) { PT2_u0[i] = ((pd_entry_t)staging + ((pd_entry_t)i - 1) * NBPDR) | PG_V | PG_RW | PG_PS; } } #else { vm_offset_t pabase, pa_pt3_l, pa_pt3_u, pa_pt2_l0, pa_pt2_l1, pa_pt2_l2, pa_pt2_l3, pa_pt2_u0, pa_pt2_u1; /* We'll find a place for these later */ PT4 = (pml4_entry_t *)host_getmem(9 * LOADER_PAGE_SIZE); bzero(PT4, 9 * LOADER_PAGE_SIZE); PT3_l = &PT4[NPML4EPG * 1]; PT3_u = &PT4[NPML4EPG * 2]; PT2_l0 = &PT4[NPML4EPG * 3]; PT2_l1 = &PT4[NPML4EPG * 4]; PT2_l2 = &PT4[NPML4EPG * 5]; PT2_l3 = &PT4[NPML4EPG * 6]; PT2_u0 = &PT4[NPML4EPG * 7]; PT2_u1 = &PT4[NPML4EPG * 8]; pabase = trampolinebase + LOADER_PAGE_SIZE; pa_pt3_l = pabase + LOADER_PAGE_SIZE * 1; pa_pt3_u = pabase + LOADER_PAGE_SIZE * 2; pa_pt2_l0 = pabase + LOADER_PAGE_SIZE * 3; pa_pt2_l1 = pabase + LOADER_PAGE_SIZE * 4; pa_pt2_l2 = pabase + LOADER_PAGE_SIZE * 5; pa_pt2_l3 = pabase + LOADER_PAGE_SIZE * 6; pa_pt2_u0 = pabase + LOADER_PAGE_SIZE * 7; pa_pt2_u1 = pabase + LOADER_PAGE_SIZE * 8; /* 1:1 mapping of lower 4G */ PT4[0] = (pml4_entry_t)pa_pt3_l | PG_V | PG_RW; PT3_l[0] = (pdp_entry_t)pa_pt2_l0 | PG_V | PG_RW; PT3_l[1] = (pdp_entry_t)pa_pt2_l1 | PG_V | PG_RW; PT3_l[2] = (pdp_entry_t)pa_pt2_l2 | PG_V | PG_RW; PT3_l[3] = (pdp_entry_t)pa_pt2_l3 | PG_V | PG_RW; for (i = 0; i < 4 * NPDEPG; i++) { /* we overflow PT2_l0 into _l1, etc */ PT2_l0[i] = ((pd_entry_t)i << PDRSHIFT) | PG_V | PG_RW | PG_PS; } /* mapping of kernel 2G below top */ PT4[NPML4EPG - 1] = (pml4_entry_t)pa_pt3_u | PG_V | PG_RW; PT3_u[NPDPEPG - 2] = (pdp_entry_t)pa_pt2_u0 | PG_V | PG_RW; PT3_u[NPDPEPG - 1] = (pdp_entry_t)pa_pt2_u1 | PG_V | PG_RW; /* compat mapping of phys @0 */ PT2_u0[0] = PG_PS | PG_V | PG_RW; /* this maps past staging area */ /* * Kernel uses the KERNSTART (== KERNBASE + 2MB) entry to figure * out where we loaded the kernel. This is PT2_u0[1] (since * these map 2MB pages. So the PA that this maps has to be * kboot's staging + 2MB. For UEFI we do 'i - 1' since we load * the kernel right at staging (and assume the first address we * load is 2MB in efi_copyin). However for kboot, staging + 1 * * NBPDR == staging + 2MB which is where the kernel starts. Our * trampoline need not be mapped into the kernel space since we * execute PA==VA for that, and the trampoline can just go away * once the kernel is called. * * Staging should likely be as low as possible, though, because * all the 'early' allocations are at kernend (which the kernel * calls physfree). */ for (i = 1; i < 2 * NPDEPG; i++) { /* we overflow PT2_u0 into _u1 */ PT2_u0[i] = ((pd_entry_t)staging + ((pd_entry_t)i) * NBPDR) | PG_V | PG_RW | PG_PS; if (i < 10) printf("Mapping %d to %#lx staging %#lx\n", i, PT2_u0[i], staging); } } #endif #ifdef EFI printf("staging %#lx (%scopying) tramp %p PT4 %p\n", staging, copy_staging == COPY_STAGING_ENABLE ? "" : "not ", trampoline, PT4); #else printf("staging %#lx tramp %p PT4 %p\n", staging, (void *)trampolinebase, (void *)trampolinebase + LOADER_PAGE_SIZE); #endif printf("Start @ 0x%lx ...\n", ehdr->e_entry); #ifdef EFI efi_time_fini(); #endif err = bi_load(fp->f_args, &modulep, &kernend, true); if (err != 0) { #ifdef EFI efi_time_init(); if (copy_auto) copy_staging = COPY_STAGING_AUTO; #endif return (err); } dev_cleanup(); #ifdef EFI trampoline(trampstack, copy_staging == COPY_STAGING_ENABLE ? efi_copy_finish : efi_copy_finish_nop, kernend, modulep, PT4, ehdr->e_entry); #else trampoline_data = (void *)trampoline + tramp_data_offset; trampoline_data->entry = ehdr->e_entry; /* VA since we start MMU with KERNBASE, etc */ if (efi_map_phys_src != 0) { md = file_findmetadata(fp, MODINFOMD_EFI_MAP); if (md == NULL || md->md_addr == 0) { printf("Need to copy EFI MAP, but EFI MAP not found. %p\n", md); } else { printf("Metadata EFI map loaded at VA %lx\n", md->md_addr); efi_map_phys_dst = md->md_addr + staging + /* md_addr is taging relative */ roundup2(sizeof(struct efi_map_header), 16); /* Skip header */ trampoline_data->memmap_src = efi_map_phys_src; trampoline_data->memmap_dst = efi_map_phys_dst; trampoline_data->memmap_len = efi_map_size - roundup2(sizeof(struct efi_map_header), 16); printf("Copying UEFI Memory Map data from %#lx to %#lx %ld bytes\n", trampoline_data->memmap_src, trampoline_data->memmap_dst, trampoline_data->memmap_len); } } /* * So we compute the VA of the module data by modulep + KERNBASE.... * need to make sure that that address is mapped right. We calculate * the start of available memory to allocate via kernend (which is * calculated with a phyaddr of "kernend + PA(PT_u0[1])"), so we better * make sure we're not overwriting the last 2MB of the kernel :). */ trampoline_data->pt4 = trampolinebase + LOADER_PAGE_SIZE; trampoline_data->modulep = modulep; /* Offset from KERNBASE */ trampoline_data->kernend = kernend; /* Offset from the load address */ trampoline_data->fill1 = trampoline_data->fill2 = 0; printf("Modulep = %lx kernend %lx\n", modulep, kernend); /* NOTE: when copyting in, it's relative to the start of our 'area' not an abs addr */ /* Copy the trampoline to the ksegs */ archsw.arch_copyin((void *)trampcode, trampolinebase - staging, tramp_size); /* Copy the page table to the ksegs */ archsw.arch_copyin(PT4, trampoline_data->pt4 - staging, 9 * LOADER_PAGE_SIZE); kboot_kseg_get(&nseg, &kseg); error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_X86_64); if (error != 0) panic("kexec_load returned error: %d", error); host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC, 0); #endif panic("exec returned"); } static int elf64_obj_exec(struct preloaded_file *fp) { return (EFTYPE); } diff --git a/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c b/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c index ffa00e151661..ae398e357df2 100644 --- a/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c +++ b/stand/kboot/kboot/arch/powerpc64/ppc64_elf_freebsd.c @@ -1,177 +1,174 @@ /*- * Copyright (c) 2001 Benno Rice * 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. */ #define __ELF_WORD_SIZE 64 #include #include #include #include #include #include #include "bootstrap.h" #include "syscall_nr.h" #include "host_syscall.h" #include "modinfo.h" #include "kboot.h" extern char end[]; extern void *kerneltramp; extern size_t szkerneltramp; struct trampoline_data { uint32_t kernel_entry; uint32_t dtb; uint32_t phys_mem_offset; uint32_t of_entry; uint32_t mdp; uint32_t mdp_size; }; int ppc64_elf_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { int r; r = __elfN(loadfile)(filename, dest, result); if (r != 0) return (r); return (0); } int ppc64_elf_exec(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp, dtb; Elf_Ehdr *e; int error; uint32_t *trampoline; uint64_t entry; uint64_t trampolinebase; struct trampoline_data *trampoline_data; int nseg; void *kseg; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; /* * Figure out where to put it. * - * Linux does not allow to do kexec_load into - * any part of memory. Ask arch_loadaddr to - * resolve the first available chunk of physical - * memory where loading is possible (load_addr). + * Linux does not allow us to do kexec_load into any part of memory. Ask + * kboot_get_phys_load_segment to resolve the first available chunk of + * physical memory where loading is possible (load_addr). * - * Memory organization is shown below. - * It is assumed, that text segment offset of - * kernel ELF (KERNPHYSADDR) is non-zero, - * which is true for PPC/PPC64 architectures, - * where default is 0x100000. + * Memory organization is shown below. It is assumed, that text segment + * offset of kernel ELF (KERNPHYSADDR) is non-zero, which is true for + * PPC/PPC64 architectures, where default is 0x100000. * * load_addr: trampoline code * load_addr + KERNPHYSADDR: kernel text segment */ - trampolinebase = archsw.arch_loadaddr(LOAD_RAW, NULL, 0); + trampolinebase = kboot_get_phys_load_segment(); printf("Load address at %#jx\n", (uintmax_t)trampolinebase); printf("Relocation offset is %#jx\n", (uintmax_t)elf64_relocation_offset); /* Set up loader trampoline */ trampoline = malloc(szkerneltramp); memcpy(trampoline, &kerneltramp, szkerneltramp); /* Parse function descriptor for ELFv1 kernels */ if ((e->e_flags & 3) == 2) entry = e->e_entry; else { archsw.arch_copyout(e->e_entry + elf64_relocation_offset, &entry, 8); entry = be64toh(entry); } /* * Placeholder for trampoline data is at trampolinebase + 0x08 * CAUTION: all data must be Big Endian */ trampoline_data = (void*)&trampoline[2]; trampoline_data->kernel_entry = htobe32(entry + elf64_relocation_offset); trampoline_data->phys_mem_offset = htobe32(0); trampoline_data->of_entry = htobe32(0); if ((error = md_load64(fp->f_args, &mdp, &dtb)) != 0) return (error); trampoline_data->dtb = htobe32(dtb); trampoline_data->mdp = htobe32(mdp); trampoline_data->mdp_size = htobe32(0xfb5d104d); printf("Kernel entry at %#jx (%#x) ...\n", entry, be32toh(trampoline_data->kernel_entry)); printf("DTB at %#x, mdp at %#x\n", be32toh(trampoline_data->dtb), be32toh(trampoline_data->mdp)); dev_cleanup(); archsw.arch_copyin(trampoline, trampolinebase, szkerneltramp); free(trampoline); kboot_kseg_get(&nseg, &kseg); error = host_kexec_load(trampolinebase, nseg, kseg, HOST_KEXEC_ARCH_PPC64); if (error != 0) panic("kexec_load returned error: %d", error); error = host_reboot(HOST_REBOOT_MAGIC1, HOST_REBOOT_MAGIC2, HOST_REBOOT_CMD_KEXEC, (uintptr_t)NULL); if (error != 0) panic("reboot returned error: %d", error); while (1) {} } struct file_format ppc_elf64 = { ppc64_elf_loadfile, ppc64_elf_exec }; /* * Sort formats so that those that can detect based on arguments rather than * reading the file first. */ struct file_format *file_formats[] = { &ppc_elf64, NULL }; diff --git a/stand/uboot/copy.c b/stand/uboot/copy.c index e5e985c85b27..7758e754666b 100644 --- a/stand/uboot/copy.c +++ b/stand/uboot/copy.c @@ -1,164 +1,163 @@ /*- * Copyright (c) 1998 Michael Smith - * Copyright (c) 2007 Semihalf, Rafal Jaworowski + * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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. */ #include #include #include #include "api_public.h" #include "glue.h" #include "libuboot.h" /* - * MD primitives supporting placement of module data + * MD primitives supporting placement of module data */ #ifdef __arm__ #define KERN_ALIGN (2 * 1024 * 1024) #else #define KERN_ALIGN PAGE_SIZE #endif /* * Avoid low memory, u-boot puts things like args and dtb blobs there. */ #define KERN_MINADDR max(KERN_ALIGN, (1024 * 1024)) extern void _start(void); /* ubldr entry point address. */ +uint64_t loadbase; +bool loadbase_set = false; + /* * This is called for every object loaded (kernel, module, dtb file, etc). The * expected return value is the next address at or after the given addr which is * appropriate for loading the given object described by type and data. On each * call the addr is the next address following the previously loaded object. - * - * The first call is for loading the kernel, and the addr argument will be zero, - * and we search for a big block of ram to load the kernel and modules. - * - * On subsequent calls the addr will be non-zero, and we just round it up so - * that each object begins on a page boundary. */ -uint64_t -uboot_loadaddr(u_int type, void *data, uint64_t addr) +static uint64_t +uboot_loadaddr(void) { struct sys_info *si; uint64_t sblock, eblock, subldr, eubldr; uint64_t biggest_block, this_block; uint64_t biggest_size, this_size; int i; char *envstr; - if (addr == 0) { - /* - * If the loader_kernaddr environment variable is set, blindly - * honor it. It had better be right. We force interpretation - * of the value in base-16 regardless of any leading 0x prefix, - * because that's the U-Boot convention. - */ - envstr = ub_env_get("loader_kernaddr"); - if (envstr != NULL) - return (strtoul(envstr, NULL, 16)); - - /* - * Find addr/size of largest DRAM block. Carve our own address - * range out of the block, because loading the kernel over the - * top ourself is a poor memory-conservation strategy. Avoid - * memory at beginning of the first block of physical ram, - * since u-boot likes to pass args and data there. Assume that - * u-boot has moved itself to the very top of ram and - * optimistically assume that we won't run into it up there. - */ - if ((si = ub_get_sys_info()) == NULL) - panic("could not retrieve system info"); - - biggest_block = 0; - biggest_size = 0; - subldr = rounddown2((uintptr_t)_start, KERN_ALIGN); - eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN); - for (i = 0; i < si->mr_no; i++) { - if (si->mr[i].flags != MR_ATTR_DRAM) - continue; - sblock = roundup2((uint64_t)si->mr[i].start, - KERN_ALIGN); - eblock = rounddown2((uint64_t)si->mr[i].start + - si->mr[i].size, KERN_ALIGN); - if (biggest_size == 0) - sblock += KERN_MINADDR; - if (subldr >= sblock && subldr < eblock) { - if (subldr - sblock > eblock - eubldr) { - this_block = sblock; - this_size = subldr - sblock; - } else { - this_block = eubldr; - this_size = eblock - eubldr; - } - } else if (subldr < sblock && eubldr < eblock) { - /* Loader is below or engulfs the sblock */ - this_block = (eubldr < sblock) ? sblock : eubldr; - this_size = eblock - this_block; + /* + * If the loader_kernaddr environment variable is set, blindly + * honor it. It had better be right. We force interpretation + * of the value in base-16 regardless of any leading 0x prefix, + * because that's the U-Boot convention. + */ + envstr = ub_env_get("loader_kernaddr"); + if (envstr != NULL) + return (strtoul(envstr, NULL, 16)); + + /* + * Find addr/size of largest DRAM block. Carve our own address + * range out of the block, because loading the kernel over the + * top ourself is a poor memory-conservation strategy. Avoid + * memory at beginning of the first block of physical ram, + * since u-boot likes to pass args and data there. Assume that + * u-boot has moved itself to the very top of ram and + * optimistically assume that we won't run into it up there. + */ + if ((si = ub_get_sys_info()) == NULL) + panic("could not retrieve system info"); + + biggest_block = 0; + biggest_size = 0; + subldr = rounddown2((uintptr_t)_start, KERN_ALIGN); + eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN); + for (i = 0; i < si->mr_no; i++) { + if (si->mr[i].flags != MR_ATTR_DRAM) + continue; + sblock = roundup2((uint64_t)si->mr[i].start, + KERN_ALIGN); + eblock = rounddown2((uint64_t)si->mr[i].start + + si->mr[i].size, KERN_ALIGN); + if (biggest_size == 0) + sblock += KERN_MINADDR; + if (subldr >= sblock && subldr < eblock) { + if (subldr - sblock > eblock - eubldr) { + this_block = sblock; + this_size = subldr - sblock; } else { - this_block = 0; - this_size = 0; - } - if (biggest_size < this_size) { - biggest_block = this_block; - biggest_size = this_size; + this_block = eubldr; + this_size = eblock - eubldr; } + } else if (subldr < sblock && eubldr < eblock) { + /* Loader is below or engulfs the sblock */ + this_block = (eubldr < sblock) ? sblock : eubldr; + this_size = eblock - this_block; + } else { + this_block = 0; + this_size = 0; } - if (biggest_size == 0) - panic("Not enough DRAM to load kernel"); + if (biggest_size < this_size) { + biggest_block = this_block; + biggest_size = this_size; + } + } + if (biggest_size == 0) + panic("Not enough DRAM to load kernel"); #if 0 - printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n", - (uintmax_t)biggest_block, - (uintmax_t)biggest_block + biggest_size - 1, - (uintmax_t)biggest_size / 1024 / 1024); + printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n", + (uintmax_t)biggest_block, + (uintmax_t)biggest_block + biggest_size - 1, + (uintmax_t)biggest_size / 1024 / 1024); #endif - return (biggest_block); - } - return roundup2(addr, PAGE_SIZE); + return (biggest_block); } ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len) { - bcopy(src, (void *)dest, len); + if (!loadbase_set) { + loadbase = uboot_loadaddr(); + loadbase_set = true; + } + + bcopy(src, (void *)(dest + loadbase), len); return (len); } ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len) { - bcopy((void *)src, dest, len); + bcopy((void *)(src + loadbase), dest, len); return (len); } ssize_t uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { return (VECTX_READ(fd, (void *)dest, len)); } diff --git a/stand/uboot/libuboot.h b/stand/uboot/libuboot.h index 4723600b0cee..49bfff3ada6f 100644 --- a/stand/uboot/libuboot.h +++ b/stand/uboot/libuboot.h @@ -1,74 +1,73 @@ /*- * Copyright (C) 2000 Benno Rice. * Copyright (C) 2007 Semihalf, Rafal Jaworowski * 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. */ #include #include struct uboot_devdesc { union { struct devdesc dd; struct disk_devdesc d_disk; }; }; /* * Default network packet alignment in memory. On arm arches packets must be * aligned to cacheline boundaries. */ #if defined(__aarch64__) #define PKTALIGN 128 #elif defined(__arm__) #define PKTALIGN 64 #else #define PKTALIGN 32 #endif int uboot_getdev(void **vdev, const char *devspec, const char **path); int uboot_setcurrdev(struct env_var *ev, int flags, const void *value); extern int devs_no; extern struct netif_driver uboot_net; extern struct devsw uboot_storage; extern uintptr_t uboot_heap_start; extern uintptr_t uboot_heap_end; -uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr); ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len); ssize_t uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); extern int uboot_autoload(void); struct preloaded_file; struct file_format; extern struct file_format uboot_elf; void reboot(void); int uboot_diskgetunit(int type, int type_unit); diff --git a/stand/uboot/main.c b/stand/uboot/main.c index 61b691f7edfa..6f53c83543ba 100644 --- a/stand/uboot/main.c +++ b/stand/uboot/main.c @@ -1,726 +1,725 @@ /*- * Copyright (c) 2000 Benno Rice * Copyright (c) 2000 Stephane Potvin * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski * 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 AUTHORS 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. */ #include #include #include "api_public.h" #include "bootstrap.h" #include "glue.h" #include "libuboot.h" #ifndef HEAP_SIZE #define HEAP_SIZE (2 * 1024 * 1024) #endif struct uboot_devdesc currdev; struct arch_switch archsw = { /* MI/MD interface boundary */ - .arch_loadaddr = uboot_loadaddr, .arch_getdev = uboot_getdev, .arch_copyin = uboot_copyin, .arch_copyout = uboot_copyout, .arch_readin = uboot_readin, .arch_autoload = uboot_autoload, }; int devs_no; uintptr_t uboot_heap_start; uintptr_t uboot_heap_end; struct device_type { const char *name; int type; } device_types[] = { { "disk", DEV_TYP_STOR }, { "ide", DEV_TYP_STOR | DT_STOR_IDE }, { "mmc", DEV_TYP_STOR | DT_STOR_MMC }, { "sata", DEV_TYP_STOR | DT_STOR_SATA }, { "scsi", DEV_TYP_STOR | DT_STOR_SCSI }, { "usb", DEV_TYP_STOR | DT_STOR_USB }, { "net", DEV_TYP_NET } }; extern char end[]; extern unsigned char __bss_start[]; extern unsigned char __sbss_start[]; extern unsigned char __sbss_end[]; extern unsigned char _end[]; #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); #endif static void dump_sig(struct api_signature *sig) { #ifdef DEBUG printf("signature:\n"); printf(" version\t= %d\n", sig->version); printf(" checksum\t= 0x%08x\n", sig->checksum); printf(" sc entry\t= 0x%08x\n", sig->syscall); #endif } static void dump_addr_info(void) { #ifdef DEBUG printf("\naddresses info:\n"); printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext); printf(" _edata = 0x%08x\n", (uint32_t)_edata); printf(" __sbss_start = 0x%08x\n", (uint32_t)__sbss_start); printf(" __sbss_end = 0x%08x\n", (uint32_t)__sbss_end); printf(" __sbss_start = 0x%08x\n", (uint32_t)__bss_start); printf(" _end = 0x%08x\n", (uint32_t)_end); printf(" syscall entry = 0x%08x\n", (uint32_t)syscall_ptr); #endif } static uint64_t memsize(struct sys_info *si, int flags) { uint64_t size; int i; size = 0; for (i = 0; i < si->mr_no; i++) if (si->mr[i].flags == flags && si->mr[i].size) size += (si->mr[i].size); return (size); } static void meminfo(void) { uint64_t size; struct sys_info *si; int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM }; int i; if ((si = ub_get_sys_info()) == NULL) panic("could not retrieve system info"); for (i = 0; i < 3; i++) { size = memsize(si, t[i]); if (size > 0) printf("%s: %juMB\n", ub_mem_type(t[i]), (uintmax_t)(size / 1024 / 1024)); } } static const char * get_device_type(const char *devstr, int *devtype) { int i; int namelen; struct device_type *dt; if (devstr) { for (i = 0; i < nitems(device_types); i++) { dt = &device_types[i]; namelen = strlen(dt->name); if (strncmp(dt->name, devstr, namelen) == 0) { *devtype = dt->type; return (devstr + namelen); } } printf("Unknown device type '%s'\n", devstr); } *devtype = DEV_TYP_NONE; return (NULL); } static const char * device_typename(int type) { int i; for (i = 0; i < nitems(device_types); i++) if (device_types[i].type == type) return (device_types[i].name); return (""); } /* * Parse a device string into type, unit, slice and partition numbers. A * returned value of -1 for type indicates a search should be done for the * first loadable device, otherwise a returned value of -1 for unit * indicates a search should be done for the first loadable device of the * given type. * * The returned values for slice and partition are interpreted by * disk_open(). * * The device string can be a standard loader(8) disk specifier: * * disks disk0s1 * disks disk1s2a * diskp disk0p4 * * or one of the following formats: * * Valid device strings: For device types: * * DEV_TYP_STOR, DEV_TYP_NET * DEV_TYP_STOR, DEV_TYP_NET * : DEV_TYP_STOR, DEV_TYP_NET * : DEV_TYP_STOR * :. DEV_TYP_STOR * :. DEV_TYP_STOR * * For valid type names, see the device_types array, above. * * Slice numbers are 1-based. 0 is a wildcard. */ static void get_load_device(int *type, int *unit, int *slice, int *partition) { struct disk_devdesc *dev; char *devstr; const char *p; char *endp; *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; *partition = D_PARTWILD; devstr = ub_env_get("loaderdev"); if (devstr == NULL) { printf("U-Boot env: loaderdev not set, will probe all devices.\n"); return; } printf("U-Boot env: loaderdev='%s'\n", devstr); p = get_device_type(devstr, type); /* * If type is DEV_TYP_STOR we have a disk-like device. If the remainder * of the string contains spaces, dots, or a colon in any location other * than the last char, it's legacy format. Otherwise it might be * standard loader(8) format (e.g., disk0s2a or mmc1p12), so try to * parse the remainder of the string as such, and if it works, return * those results. Otherwise we'll fall through to the code that parses * the legacy format. * * disk_parsedev now assumes that it points to the start of the device * name, but since it doesn't know about uboot's usage, just subtract 4 * since it always adds 4. This is the least-bad solution since it makes * all the other loader code easier (might be better to create a fake * 'disk...' string, but that's more work than uboot is worth). */ if (*type & DEV_TYP_STOR) { size_t len = strlen(p); if (strcspn(p, " .") == len && strcspn(p, ":") >= len - 1 && disk_parsedev((struct devdesc **)&dev, p - 4, NULL) == 0) { /* Hack */ *unit = dev->dd.d_unit; *slice = dev->d_slice; *partition = dev->d_partition; free(dev); return; } } /* Ignore optional spaces after the device name. */ while (*p == ' ') p++; /* Unknown device name, or a known name without unit number. */ if ((*type == DEV_TYP_NONE) || (*p == '\0')) { return; } /* Malformed unit number. */ if (!isdigit(*p)) { *type = DEV_TYP_NONE; return; } /* Guaranteed to extract a number from the string, as *p is a digit. */ *unit = strtol(p, &endp, 10); p = endp; /* Known device name with unit number and nothing else. */ if (*p == '\0') { return; } /* Device string is malformed beyond unit number. */ if (*p != ':') { *type = DEV_TYP_NONE; *unit = -1; return; } p++; /* No slice and partition specification. */ if ('\0' == *p ) return; /* Only DEV_TYP_STOR devices can have a slice specification. */ if (!(*type & DEV_TYP_STOR)) { *type = DEV_TYP_NONE; *unit = -1; return; } *slice = strtoul(p, &endp, 10); /* Malformed slice number. */ if (p == endp) { *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; return; } p = endp; /* No partition specification. */ if (*p == '\0') return; /* Device string is malformed beyond slice number. */ if (*p != '.') { *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; return; } p++; /* No partition specification. */ if (*p == '\0') return; *partition = strtol(p, &endp, 10); p = endp; /* Full, valid device string. */ if (*endp == '\0') return; /* Junk beyond partition number. */ *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; *partition = D_PARTWILD; } static void print_disk_probe_info(void) { char slice[32]; char partition[32]; if (currdev.d_disk.d_slice == D_SLICENONE) strlcpy(slice, "", sizeof(slice)); else if (currdev.d_disk.d_slice == D_SLICEWILD) strlcpy(slice, "", sizeof(slice)); else snprintf(slice, sizeof(slice), "%d", currdev.d_disk.d_slice); if (currdev.d_disk.d_partition == D_PARTNONE) strlcpy(partition, "", sizeof(partition)); else if (currdev.d_disk.d_partition == D_PARTWILD) strlcpy(partition, "", sizeof(partition)); else snprintf(partition, sizeof(partition), "%d", currdev.d_disk.d_partition); printf(" Checking unit=%d slice=%s partition=%s...", currdev.dd.d_unit, slice, partition); } static int probe_disks(int devidx, int load_type, int load_unit, int load_slice, int load_partition) { int open_result, unit; struct open_file f; currdev.d_disk.d_slice = load_slice; currdev.d_disk.d_partition = load_partition; f.f_devdata = &currdev; open_result = -1; if (load_type == -1) { printf(" Probing all disk devices...\n"); /* Try each disk in succession until one works. */ for (currdev.dd.d_unit = 0; currdev.dd.d_unit < UB_MAX_DEV; currdev.dd.d_unit++) { print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f, &currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } return (-1); } if (load_unit == -1) { printf(" Probing all %s devices...\n", device_typename(load_type)); /* Try each disk of given type in succession until one works. */ for (unit = 0; unit < UB_MAX_DEV; unit++) { currdev.dd.d_unit = uboot_diskgetunit(load_type, unit); if (currdev.dd.d_unit == -1) break; print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f, &currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } return (-1); } if ((currdev.dd.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) { print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f,&currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } printf(" Requested disk type/unit/slice/partition not found\n"); return (-1); } int main(int argc, char **argv) { struct api_signature *sig = NULL; int load_type, load_unit, load_slice, load_partition; int i; const char *ldev; /* * We first check if a command line argument was passed to us containing * API's signature address. If it wasn't then we try to search for the * API signature via the usual hinted address. * If we can't find the magic signature and related info, exit with a * unique error code that U-Boot reports as "## Application terminated, * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to * provide a clue. It's better than 0xffffffff anyway. */ if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig)) return (0x01badab1); syscall_ptr = sig->syscall; if (syscall_ptr == NULL) return (0x02badab1); if (sig->version > API_SIG_VERSION) return (0x03badab1); /* Clear BSS sections */ bzero(__sbss_start, __sbss_end - __sbss_start); bzero(__bss_start, _end - __bss_start); /* * Initialise the heap as early as possible. Once this is done, * alloc() is usable. We are using the stack u-boot set up near the top * of physical ram; hopefully there is sufficient space between the end * of our bss and the bottom of the u-boot stack to avoid overlap. */ uboot_heap_start = round_page((uintptr_t)end); uboot_heap_end = uboot_heap_start + HEAP_SIZE; setheap((void *)uboot_heap_start, (void *)uboot_heap_end); /* * Set up console. */ cons_probe(); printf("Compatible U-Boot API signature found @%p\n", sig); printf("\n%s", bootprog_info); printf("\n"); dump_sig(sig); dump_addr_info(); meminfo(); /* Set up currdev variable to have hooks in place. */ env_setenv("currdev", EV_VOLATILE, "", uboot_setcurrdev, env_nounset); /* * Enumerate U-Boot devices */ if ((devs_no = ub_dev_enum()) == 0) { printf("no U-Boot devices found"); goto do_interact; } printf("Number of U-Boot devices: %d\n", devs_no); get_load_device(&load_type, &load_unit, &load_slice, &load_partition); /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) { if (devsw[i]->dv_init == NULL) continue; if ((devsw[i]->dv_init)() != 0) continue; printf("Found U-Boot device: %s\n", devsw[i]->dv_name); currdev.dd.d_dev = devsw[i]; currdev.dd.d_unit = 0; if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_STOR)) && strcmp(devsw[i]->dv_name, "disk") == 0) { if (probe_disks(i, load_type, load_unit, load_slice, load_partition) == 0) break; } if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_NET)) && strcmp(devsw[i]->dv_name, "net") == 0) break; } /* * If we couldn't find a boot device, return an error to u-boot. * U-boot may be running a boot script that can try something different * so returning an error is better than forcing a reboot. */ if (devsw[i] == NULL) { printf("No boot device found!\n"); return (0xbadef1ce); } ldev = devformat(&currdev.dd); env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset); printf("Booting from %s\n", ldev); do_interact: setenv("LINES", "24", 1); /* optional */ setenv("prompt", "loader>", 1); #ifdef __powerpc__ setenv("usefdt", "1", 1); #endif interact(); /* doesn't return */ return (0); } COMMAND_SET(heap, "heap", "show heap usage", command_heap); static int command_heap(int argc, char *argv[]) { printf("heap base at %p, top at %p, used %td\n", end, sbrk(0), sbrk(0) - end); return (CMD_OK); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { printf("Resetting...\n"); ub_reset(); printf("Reset failed!\n"); while (1); __unreachable(); } COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo); static int command_devinfo(int argc, char *argv[]) { int i; if ((devs_no = ub_dev_enum()) == 0) { command_errmsg = "no U-Boot devices found!?"; return (CMD_ERROR); } printf("U-Boot devices:\n"); for (i = 0; i < devs_no; i++) { ub_dump_di(i); printf("\n"); } return (CMD_OK); } COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo); static int command_sysinfo(int argc, char *argv[]) { struct sys_info *si; if ((si = ub_get_sys_info()) == NULL) { command_errmsg = "could not retrieve U-Boot sys info!?"; return (CMD_ERROR); } printf("U-Boot system info:\n"); ub_dump_si(si); return (CMD_OK); } enum ubenv_action { UBENV_UNKNOWN, UBENV_SHOW, UBENV_IMPORT }; static void handle_uboot_env_var(enum ubenv_action action, const char * var) { char ldvar[128]; const char *val; char *wrk; int len; /* * On an import with the variable name formatted as ldname=ubname, * import the uboot variable ubname into the loader variable ldname, * otherwise the historical behavior is to import to uboot.ubname. */ if (action == UBENV_IMPORT) { len = strcspn(var, "="); if (len == 0) { printf("name cannot start with '=': '%s'\n", var); return; } if (var[len] == 0) { strcpy(ldvar, "uboot."); strncat(ldvar, var, sizeof(ldvar) - 7); } else { len = MIN(len, sizeof(ldvar) - 1); strncpy(ldvar, var, len); ldvar[len] = 0; var = &var[len + 1]; } } /* * If the user prepended "uboot." (which is how they usually see these * names) strip it off as a convenience. */ if (strncmp(var, "uboot.", 6) == 0) { var = &var[6]; } /* If there is no variable name left, punt. */ if (var[0] == 0) { printf("empty variable name\n"); return; } val = ub_env_get(var); if (action == UBENV_SHOW) { if (val == NULL) printf("uboot.%s is not set\n", var); else printf("uboot.%s=%s\n", var, val); } else if (action == UBENV_IMPORT) { if (val != NULL) { setenv(ldvar, val, 1); } } } static int command_ubenv(int argc, char *argv[]) { enum ubenv_action action; const char *var; int i; action = UBENV_UNKNOWN; if (argc > 1) { if (strcasecmp(argv[1], "import") == 0) action = UBENV_IMPORT; else if (strcasecmp(argv[1], "show") == 0) action = UBENV_SHOW; } if (action == UBENV_UNKNOWN) { command_errmsg = "usage: 'ubenv [var ...]"; return (CMD_ERROR); } if (argc > 2) { for (i = 2; i < argc; i++) handle_uboot_env_var(action, argv[i]); } else { var = NULL; for (;;) { if ((var = ub_env_enum(var)) == NULL) break; handle_uboot_env_var(action, var); } } return (CMD_OK); } COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv); #ifdef LOADER_FDT_SUPPORT /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif