Index: stable/10/sys/boot/common/bootstrap.h =================================================================== --- stable/10/sys/boot/common/bootstrap.h (revision 294416) +++ stable/10/sys/boot/common/bootstrap.h (revision 294417) @@ -1,340 +1,343 @@ /*- * 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. * * $FreeBSD$ */ #ifndef _BOOTSTRAP_H_ #define _BOOTSTRAP_H_ #include #include #include /* * Generic device specifier; architecture-dependant * versions may be larger, but should be allowed to * overlap. */ struct devdesc { struct devsw *d_dev; int d_type; #define DEVT_NONE 0 #define DEVT_DISK 1 #define DEVT_NET 2 #define DEVT_CD 3 #define DEVT_ZFS 4 int d_unit; void *d_opendata; }; /* Commands and return values; nonzero return sets command_errmsg != NULL */ typedef int (bootblk_cmd_t)(int argc, char *argv[]); extern char *command_errmsg; extern char command_errbuf[]; /* XXX blah, length */ #define CMD_OK 0 #define CMD_ERROR 1 /* interp.c */ void interact(void); int include(const char *filename); /* interp_backslash.c */ char *backslash(char *str); /* interp_parse.c */ int parse(int *argc, char ***argv, char *str); /* interp_forth.c */ void bf_init(void); int bf_run(char *line); /* boot.c */ int autoboot(int timeout, char *prompt); void autoboot_maybe(void); int getrootmount(char *rootdev); /* misc.c */ char *unargv(int argc, char *argv[]); void hexdump(caddr_t region, size_t len); 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(int fd, vm_offset_t dest, size_t len, off_t off); void *alloc_pread(int fd, off_t off, size_t len); /* bcache.c */ int bcache_init(u_int nblks, size_t bsize); void bcache_flush(void); int bcache_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); /* * Disk block cache */ struct bcache_devdata { int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); void *dv_devdata; }; /* * 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 */ void (* c_probe)(struct console *cp); /* set c_flags to match hardware */ int (* c_init)(int arg); /* reinit XXX may need more args */ void (* c_out)(int c); /* emit c */ int (* c_in)(void); /* wait for and return input */ int (* c_ready)(void); /* return nonzer if input waiting */ }; extern struct console *consoles[]; void cons_probe(void); /* * 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 { char *id_ident; /* ASCII identifier, actual format varies with bus/handler */ 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 pnpinfo_stql pnp_devices; 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(u_int8_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; /* * 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; u_int16_t md_type; 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 */ struct file_metadata *f_metadata; /* metadata that will be placed in the module directory */ 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 */ }; struct file_format { /* Load function must return EFTYPE if it can't handle the module supplied */ int (* l_load)(char *filename, u_int64_t dest, struct preloaded_file **result); /* Only a loader that will load a kernel (first module) should have an exec handler */ int (* l_exec)(struct preloaded_file *mp); }; 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[]); struct preloaded_file *file_alloc(void); -struct preloaded_file *file_findfile(char *name, char *type); +struct preloaded_file *file_findfile(const char *name, const char *type); struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); -struct preloaded_file *file_loadraw(char *name, char *type); +struct preloaded_file *file_loadraw(char *name, char *type, int insert); void file_discard(struct preloaded_file *fp); void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p); int file_addmodule(struct preloaded_file *fp, char *modname, int version, struct kernel_module **newmp); /* MI module loaders */ #ifdef __elfN /* Relocation types. */ #define ELF_RELOC_REL 1 #define ELF_RELOC_RELA 2 /* Relocation offset for some architectures */ extern u_int64_t __elfN(relocation_offset); struct elf_file; typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result); int __elfN(obj_loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result); int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len); +int __elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot); +int __elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest); #endif /* * 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. */ 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 *src, vm_offset_t dest, const size_t len); /* Copy to local address space from module address space, similar to bcopy() */ ssize_t (*arch_copyout)(const vm_offset_t src, void *dest, const size_t len); /* Read from file to module address space, same semantics as read() */ ssize_t (*arch_readin)(const int fd, vm_offset_t dest, const size_t len); /* 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); }; extern struct arch_switch archsw; /* This must be provided by the MD code, but should it be in the archsw? */ void delay(int delay); void dev_cleanup(void); time_t time(time_t *tloc); #ifndef CTASSERT /* Allow lint to override */ #define CTASSERT(x) _CTASSERT(x, __LINE__) #define _CTASSERT(x, y) __CTASSERT(x, y) #define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1] #endif #endif /* !_BOOTSTRAP_H_ */ Index: stable/10/sys/boot/common/load_elf.c =================================================================== --- stable/10/sys/boot/common/load_elf.c (revision 294416) +++ stable/10/sys/boot/common/load_elf.c (revision 294417) @@ -1,840 +1,1003 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #define FREEBSD_ELF #include #include "bootstrap.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; u_int64_t off; } *elf_file_t; static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr); static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); -static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); +static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, + Elf_Addr p_start, Elf_Addr p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); const char *__elfN(kerneltype) = "elf kernel"; const char *__elfN(moduletype) = "elf module"; u_int64_t __elfN(relocation_offset) = 0; +static int +__elfN(load_elf_header)(char *filename, elf_file_t ef) +{ + ssize_t bytes_read; + Elf_Ehdr *ehdr; + int err; + + /* + * Open the image, read and validate the ELF header + */ + if (filename == NULL) /* can't handle nameless */ + return (EFTYPE); + if ((ef->fd = open(filename, O_RDONLY)) == -1) + return (errno); + ef->firstpage = malloc(PAGE_SIZE); + if (ef->firstpage == NULL) { + close(ef->fd); + return (ENOMEM); + } + bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE); + ef->firstlen = (size_t)bytes_read; + if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { + err = EFTYPE; /* could be EIO, but may be small file */ + goto error; + } + ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; + + /* Is it ELF? */ + if (!IS_ELF(*ehdr)) { + err = EFTYPE; + goto error; + } + if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ + ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ + ehdr->e_version != EV_CURRENT || + ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ + err = EFTYPE; + goto error; + } + + return (0); + +error: + if (ef->firstpage != NULL) { + free(ef->firstpage); + ef->firstpage = NULL; + } + if (ef->fd != -1) { + close(ef->fd); + ef->fd = -1; + } + return (err); +} + /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) { + return (__elfN(loadfile_raw)(filename, dest, result, 0)); +} + +int +__elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot) +{ struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; - ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; - /* - * Open the image, read and validate the ELF header - */ - if (filename == NULL) /* can't handle nameless */ - return(EFTYPE); - if ((ef.fd = open(filename, O_RDONLY)) == -1) - return(errno); - ef.firstpage = malloc(PAGE_SIZE); - if (ef.firstpage == NULL) { - close(ef.fd); - return(ENOMEM); - } - bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); - ef.firstlen = (size_t)bytes_read; - if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { - err = EFTYPE; /* could be EIO, but may be small file */ - goto oerr; - } - ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; + err = __elfN(load_elf_header)(filename, &ef); + if (err != 0) + return (err); - /* Is it ELF? */ - if (!IS_ELF(*ehdr)) { - err = EFTYPE; - goto oerr; - } - if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ - ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || - ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ - ehdr->e_version != EV_CURRENT || - ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ - err = EFTYPE; - goto oerr; - } + ehdr = ef.ehdr; - /* * Check to see what sort of module we are. */ - kfp = file_findfile(NULL, NULL); + kfp = file_findfile(NULL, __elfN(kerneltype)); #ifdef __powerpc__ /* * Kernels can be ET_DYN, so just assume the first loaded object is the * 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. */ #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(__elfN(kerneltype), 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); /* * 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) + if (ef.kernel == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); - fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); + if (multiboot == 0) + fp->f_type = strdup(ef.kernel ? + __elfN(kerneltype) : __elfN(moduletype)); + else + fp->f_type = strdup("elf multiboot kernel"); #ifdef ELF_VERBOSE if (ef.kernel) printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); #else printf("%s ", filename); #endif 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; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); - close(ef.fd); + if (ef.fd != -1) + 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, u_int64_t off) { int i; u_int j; Elf_Ehdr *ehdr; Elf_Phdr *phdr, *php; Elf_Shdr *shdr; 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; 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; if (ehdr->e_type == ET_EXEC) { #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */ #else off = - (off & 0xff000000u); /* i386 relocates after locore */ #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; #ifdef ELF_VERBOSE printf("Converted entry 0x%08x\n", ehdr->e_entry); #endif } else off = 0; #elif defined(__arm__) /* * 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. */ off -= ehdr->e_entry & ~PAGE_MASK; ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off); #endif #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++) { /* We want to load PT_LOAD segments only.. */ if (phdr[i].p_type != PT_LOAD) continue; #ifdef ELF_VERBOSE 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 ((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(" "); } #endif 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(ef->fd, 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) { #ifdef ELF_VERBOSE 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)); #endif kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, phdr[i].p_memsz - phdr[i].p_filesz); } #ifdef ELF_VERBOSE printf("\n"); #endif 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)); /* * Now grab the symbol tables. This isn't easy if we're reading a * .gz file. I think the rule is going to have to be that you must * strip a file to remove symbols before gzipping it so that we do not * try to lseek() on it. */ chunk = ehdr->e_shnum * ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr); 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. */ #ifndef ELF_VERBOSE printf("syms=["); #endif ssym = lastaddr; for (i = symtabindex; i >= 0; i = symstrindex) { #ifdef ELF_VERBOSE 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; } #endif size = shdr[i].sh_size; archsw.arch_copyin(&size, lastaddr, sizeof(size)); lastaddr += sizeof(size); #ifdef ELF_VERBOSE 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 (i == symstrindex) printf("+"); printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); #endif if (lseek(ef->fd, (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(ef->fd, 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; #ifndef ELF_VERBOSE printf("]"); #endif file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); nosyms: 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(parse_modmetadata)(fp, ef) == 0) + + if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) + return 0; + p_start = sym.st_value + ef->off; + if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) + return ENOENT; + p_end = sym.st_value + ef->off; + + if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ 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(name, '.'); if (ep) { if (ep == name) { sp = invalid_name; ep = invalid_name + sizeof(invalid_name) - 1; } } else ep = name + strlen(name); 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_* */ u_int64_t md_data; /* specific data */ u_int64_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_* */ u_int32_t md_data; /* specific data */ u_int32_t md_cval; /* common string label */ }; #endif int -__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) +__elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest) { + struct elf_file ef; + int err, i, j; + Elf_Shdr *sh_meta, *shdr = NULL; + Elf_Shdr *sh_data[2]; + char *shstrtab = NULL; + size_t size; + Elf_Addr p_start, p_end; + + bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; + + err = __elfN(load_elf_header)(fp->f_name, &ef); + if (err != 0) + goto out; + + if (ef.ehdr->e_type == ET_EXEC) { + ef.kernel = 1; + } else if (ef.ehdr->e_type != ET_DYN) { + err = EFTYPE; + goto out; + } + + size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize; + shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size); + if (shdr == NULL) { + err = ENOMEM; + goto out; + } + + /* Load shstrtab. */ + shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset, + shdr[ef.ehdr->e_shstrndx].sh_size); + if (shstrtab == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load shstrtab\n"); + err = EFTYPE; + goto out; + } + + /* Find set_modmetadata_set and data sections. */ + sh_data[0] = sh_data[1] = sh_meta = NULL; + for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { + if (strcmp(&shstrtab[shdr[i].sh_name], + "set_modmetadata_set") == 0) { + sh_meta = &shdr[i]; + } + if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || + (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { + sh_data[j++] = &shdr[i]; + } + } + if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); + err = EFTYPE; + goto out; + } + + /* Load set_modmetadata_set into memory */ + err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); + goto out; + } + p_start = dest; + p_end = dest + sh_meta->sh_size; + dest += sh_meta->sh_size; + + /* Load data sections into memory. */ + err = kern_pread(ef.fd, dest, sh_data[0]->sh_size, + sh_data[0]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + /* + * We have to increment the dest, so that the offset is the same into + * both the .rodata and .data sections. + */ + ef.off = -(sh_data[0]->sh_addr - dest); + dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); + + err = kern_pread(ef.fd, dest, sh_data[1]->sh_size, + sh_data[1]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to parse metadata: %d\n", err); + goto out; + } + +out: + if (shstrtab != NULL) + free(shstrtab); + if (shdr != NULL) + free(shdr); + if (ef.firstpage != NULL) + free(ef.firstpage); + if (ef.fd != -1) + close(ef.fd); + return (err); +} + +int +__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, + Elf_Addr p_start, Elf_Addr p_end) +{ struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 md32; #endif struct mod_depend *mdepend; struct mod_version mver; - Elf_Sym sym; char *s; int error, modcnt, minfolen; - Elf_Addr v, p, p_stop; + Elf_Addr v, p; - if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) - return 0; - p = sym.st_value + ef->off; - if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) - return ENOENT; - p_stop = sym.st_value + ef->off; - modcnt = 0; - while (p < p_stop) { + p = p_start; + while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) 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 += 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)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Sym *symp) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; 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) == STT_FUNC)) { *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); } Index: stable/10/sys/boot/common/load_elf_obj.c =================================================================== --- stable/10/sys/boot/common/load_elf_obj.c (revision 294416) +++ stable/10/sys/boot/common/load_elf_obj.c (revision 294417) @@ -1,542 +1,535 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #define FREEBSD_ELF #include #include "bootstrap.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; } *elf_file_t; static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_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); const char *__elfN(obj_kerneltype) = "elf kernel"; const char *__elfN(obj_moduletype) = "elf obj module"; /* * 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, u_int64_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); hdr = &ef.hdr; bytes_read = read(ef.fd, 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; } - kfp = file_findfile(NULL, NULL); + kfp = file_findfile(NULL, __elfN(obj_kerneltype)); if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module before kernel\n"); - err = EPERM; - goto oerr; - } - if (strcmp(__elfN(obj_kerneltype), kfp->f_type)) { - printf("elf" __XSTRING(__ELF_WORD_SIZE) - "_obj_loadfile: can't load module with kernel type '%s'\n", - kfp->f_type); err = EPERM; goto oerr; } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); else dest = roundup(dest, PAGE_SIZE); /* * 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(__elfN(obj_moduletype)); 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: 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, u_int64_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(ef->fd, (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: 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; shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; 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: 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(ef->fd, (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; printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); out: 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_* */ u_int64_t md_data; /* specific data */ u_int64_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: 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; int symcnt; symcnt = ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym); if (symidx >= symcnt) 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); } Index: stable/10/sys/boot/common/module.c =================================================================== --- stable/10/sys/boot/common/module.c (revision 294416) +++ stable/10/sys/boot/common/module.c (revision 294417) @@ -1,1021 +1,1022 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); /* * file/module function dispatcher, support, etc. */ #include #include #include #include #include #include #include "bootstrap.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 void file_insert_tail(struct preloaded_file *mp); 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[]) { char *typestr; int dofile, dokld, ch, error; dokld = dofile = 0; optind = 1; optreset = 1; typestr = NULL; if (argc == 1) { command_errmsg = "no filename specified"; return(CMD_ERROR); } while ((ch = getopt(argc, argv, "kt:")) != -1) { switch(ch) { case 'k': dokld = 1; break; 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_ERROR); } - return(file_loadraw(argv[1], typestr) ? CMD_OK : CMD_ERROR); + return (file_loadraw(argv[1], typestr, 1) ? CMD_OK : CMD_ERROR); } /* * Do we have explicit KLD load ? */ if (dokld || file_havepath(argv[1])) { error = mod_loadkld(argv[1], argc - 2, argv + 2); if (error == EEXIST) sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]); return (error == 0 ? CMD_OK : CMD_ERROR); } /* * Looks like a request for a module. */ error = mod_load(argv[1], NULL, argc - 2, argv + 2); if (error == EEXIST) sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]); return (error == 0 ? CMD_OK : CMD_ERROR); } 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) { sprintf(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) ? CMD_OK : CMD_ERROR); + return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); } COMMAND_SET(unload, "unload", "unload all modules", command_unload); static int command_unload(int argc, char *argv[]) { struct preloaded_file *fp; while (preloaded_files != NULL) { fp = preloaded_files; preloaded_files = preloaded_files->f_next; file_discard(fp); } loadaddr = 0; unsetenv("kernelname"); 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; 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) { sprintf(lbuf, " %p: %s (%s, 0x%lx)\n", (void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size); pager_output(lbuf); if (fp->f_args != NULL) { pager_output(" args: "); pager_output(fp->f_args); pager_output("\n"); } if (fp->f_modules) { pager_output(" modules: "); for (mp = fp->f_modules; mp; mp = mp->m_next) { sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version); pager_output(lbuf); } pager_output("\n"); } if (verbose) { /* XXX could add some formatting smarts here to display some better */ for (md = fp->f_metadata; md != NULL; md = md->md_next) { sprintf(lbuf, " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); pager_output(lbuf); } } } pager_close(); 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; if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_RAW, filename, 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) { sprintf(command_errbuf, "can't load file '%s': %s", filename, strerror(error)); break; } } 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) { 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) { sprintf(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); } /* * We've been asked to load (name) as (type), so just suck it in, * no arguments or anything. */ struct preloaded_file * -file_loadraw(char *name, char *type) +file_loadraw(char *name, char *type, int insert) { struct preloaded_file *fp; char *cp; int fd, got; vm_offset_t laddr; /* We can't load first */ if ((file_findfile(NULL, NULL)) == NULL) { command_errmsg = "can't load file before kernel"; return(NULL); } /* locate the file on the load path */ cp = file_search(name, NULL); if (cp == NULL) { sprintf(command_errbuf, "can't find '%s'", name); return(NULL); } name = cp; if ((fd = open(name, O_RDONLY)) < 0) { sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno)); free(name); return(NULL); } if (archsw.arch_loadaddr != NULL) loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); laddr = loadaddr; for (;;) { /* read in 4k chunks; size is not really important */ got = archsw.arch_readin(fd, laddr, 4096); if (got == 0) /* end of file */ break; if (got < 0) { /* error */ sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno)); free(name); close(fd); return(NULL); } laddr += got; } /* Looks OK so far; create & populate control structure */ fp = file_alloc(); 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 = loadaddr; fp->f_size = laddr - loadaddr; /* recognise space consumption */ loadaddr = laddr; /* Add to the list of loaded files */ - file_insert_tail(fp); + if (insert != 0) + file_insert_tail(fp); close(fd); 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; if (file_havepath(modname)) { printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); return (mod_loadkld(modname, argc, argv)); } /* see if module is already loaded */ mp = file_findmodule(NULL, modname, verinfo); if (mp) { #ifdef moduleargs if (mp->m_args) free(mp->m_args); mp->m_args = unargv(argc, argv); #endif sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name); return (0); } /* locate file with the module on the search path */ filename = mod_searchmodule(modname, verinfo); if (filename == NULL) { sprintf(command_errbuf, "can't find '%s'", modname); return (ENOENT); } err = mod_loadkld(filename, argc, argv); 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, *last_file; int err; char *filename; /* * Get fully qualified KLD name */ filename = file_search(kldname, kld_ext_list); if (filename == NULL) { sprintf(command_errbuf, "can't find '%s'", kldname); return (ENOENT); } /* * Check if KLD already loaded */ fp = file_findfile(filename, NULL); if (fp) { sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename); free(filename); return (0); } for (last_file = preloaded_files; last_file != NULL && last_file->f_next != NULL; last_file = last_file->f_next) ; do { err = file_load(filename, loadaddr, &fp); if (err) break; fp->f_args = unargv(argc, argv); 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; last_file->f_next = NULL; loadaddr = last_file->f_addr + last_file->f_size; fp = NULL; break; } } while(0); if (err == EFTYPE) sprintf(command_errbuf, "don't know how to load module '%s'", filename); if (err && fp) file_discard(fp); free(filename); return (err); } /* * Find a file matching (name) and (type). * NULL may be passed as a wildcard to either. */ struct preloaded_file * -file_findfile(char *name, char *type) +file_findfile(const char *name, const char *type) { struct preloaded_file *fp; 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); 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); } 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) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 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 = (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, cp, clen, NULL); else if (best) result = file_lookup(mdp->d_path, 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; } /* * 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); } 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 = malloc(sizeof(struct kernel_module)); if (mp == NULL) return (ENOMEM); bzero(mp, sizeof(struct kernel_module)); mp->m_name = strdup(modname); 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) { if (mp->m_name) free(mp->m_name); mp1 = mp; mp = mp->m_next; free(mp1); } if (fp->f_name != NULL) free(fp->f_name); if (fp->f_type != NULL) free(fp->f_type); if (fp->f_args != NULL) 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) { struct preloaded_file *fp; if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) { bzero(fp, sizeof(struct preloaded_file)); } return (fp); } /* * 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; } } 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 > 100 * 1024 || (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); if (mdp->d_hints) { 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; int 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 { if (mdp->d_hints) free(mdp->d_hints); mtmp = mdp; mdp = STAILQ_NEXT(mdp, d_link); STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); free(mtmp); } } return; } Index: stable/10/sys/boot/fdt/fdt_loader_cmd.c =================================================================== --- stable/10/sys/boot/fdt/fdt_loader_cmd.c (revision 294416) +++ stable/10/sys/boot/fdt/fdt_loader_cmd.c (revision 294417) @@ -1,1576 +1,1576 @@ /*- * Copyright (c) 2009-2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "bootstrap.h" #include "fdt_platform.h" #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif #define FDT_CWD_LEN 256 #define FDT_MAX_DEPTH 6 #define FDT_PROP_SEP " = " #define COPYOUT(s,d,l) archsw.arch_copyout(s, d, l) #define COPYIN(s,d,l) archsw.arch_copyin(s, d, l) #define FDT_STATIC_DTB_SYMBOL "fdt_static_dtb" #define CMD_REQUIRES_BLOB 0x01 /* Location of FDT yet to be loaded. */ /* This may be in read-only memory, so can't be manipulated directly. */ static struct fdt_header *fdt_to_load = NULL; /* Location of FDT on heap. */ /* This is the copy we actually manipulate. */ static struct fdt_header *fdtp = NULL; /* Size of FDT blob */ static size_t fdtp_size = 0; /* Location of FDT in kernel or module. */ /* This won't be set if FDT is loaded from disk or memory. */ /* If it is set, we'll update it when fdt_copy() gets called. */ static vm_offset_t fdtp_va = 0; static int fdt_load_dtb(vm_offset_t va); static int fdt_cmd_nyi(int argc, char *argv[]); static int fdt_cmd_addr(int argc, char *argv[]); static int fdt_cmd_mkprop(int argc, char *argv[]); static int fdt_cmd_cd(int argc, char *argv[]); static int fdt_cmd_hdr(int argc, char *argv[]); static int fdt_cmd_ls(int argc, char *argv[]); static int fdt_cmd_prop(int argc, char *argv[]); static int fdt_cmd_pwd(int argc, char *argv[]); static int fdt_cmd_rm(int argc, char *argv[]); static int fdt_cmd_mknode(int argc, char *argv[]); static int fdt_cmd_mres(int argc, char *argv[]); typedef int cmdf_t(int, char *[]); struct cmdtab { const char *name; cmdf_t *handler; int flags; }; static const struct cmdtab commands[] = { { "addr", &fdt_cmd_addr, 0 }, { "alias", &fdt_cmd_nyi, 0 }, { "cd", &fdt_cmd_cd, CMD_REQUIRES_BLOB }, { "header", &fdt_cmd_hdr, CMD_REQUIRES_BLOB }, { "ls", &fdt_cmd_ls, CMD_REQUIRES_BLOB }, { "mknode", &fdt_cmd_mknode, CMD_REQUIRES_BLOB }, { "mkprop", &fdt_cmd_mkprop, CMD_REQUIRES_BLOB }, { "mres", &fdt_cmd_mres, CMD_REQUIRES_BLOB }, { "prop", &fdt_cmd_prop, CMD_REQUIRES_BLOB }, { "pwd", &fdt_cmd_pwd, CMD_REQUIRES_BLOB }, { "rm", &fdt_cmd_rm, CMD_REQUIRES_BLOB }, { NULL, NULL } }; static char cwd[FDT_CWD_LEN] = "/"; static vm_offset_t fdt_find_static_dtb() { Elf_Ehdr *ehdr; Elf_Shdr *shdr; Elf_Sym sym; vm_offset_t strtab, symtab, fdt_start; uint64_t offs; struct preloaded_file *kfp; struct file_metadata *md; char *strp; int i, sym_count; debugf("fdt_find_static_dtb()\n"); sym_count = symtab = strtab = 0; strp = NULL; offs = __elfN(relocation_offset); kfp = file_findfile(NULL, NULL); if (kfp == NULL) return (0); /* Locate the dynamic symbols and strtab. */ md = file_findmetadata(kfp, MODINFOMD_ELFHDR); if (md == NULL) return (0); ehdr = (Elf_Ehdr *)md->md_data; md = file_findmetadata(kfp, MODINFOMD_SHDR); if (md == NULL) return (0); shdr = (Elf_Shdr *)md->md_data; for (i = 0; i < ehdr->e_shnum; ++i) { if (shdr[i].sh_type == SHT_DYNSYM && symtab == 0) { symtab = shdr[i].sh_addr + offs; sym_count = shdr[i].sh_size / sizeof(Elf_Sym); } else if (shdr[i].sh_type == SHT_STRTAB && strtab == 0) { strtab = shdr[i].sh_addr + offs; } } /* * The most efficent way to find a symbol would be to calculate a * hash, find proper bucket and chain, and thus find a symbol. * However, that would involve code duplication (e.g. for hash * function). So we're using simpler and a bit slower way: we're * iterating through symbols, searching for the one which name is * 'equal' to 'fdt_static_dtb'. To speed up the process a little bit, * we are eliminating symbols type of which is not STT_NOTYPE, or(and) * those which binding attribute is not STB_GLOBAL. */ fdt_start = 0; while (sym_count > 0 && fdt_start == 0) { COPYOUT(symtab, &sym, sizeof(sym)); symtab += sizeof(sym); --sym_count; if (ELF_ST_BIND(sym.st_info) != STB_GLOBAL || ELF_ST_TYPE(sym.st_info) != STT_NOTYPE) continue; strp = strdupout(strtab + sym.st_name); if (strcmp(strp, FDT_STATIC_DTB_SYMBOL) == 0) fdt_start = (vm_offset_t)sym.st_value + offs; free(strp); } return (fdt_start); } static int fdt_load_dtb(vm_offset_t va) { struct fdt_header header; int err; debugf("fdt_load_dtb(0x%08jx)\n", (uintmax_t)va); COPYOUT(va, &header, sizeof(header)); err = fdt_check_header(&header); if (err < 0) { if (err == -FDT_ERR_BADVERSION) sprintf(command_errbuf, "incompatible blob version: %d, should be: %d", fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION); else sprintf(command_errbuf, "error validating blob: %s", fdt_strerror(err)); return (1); } /* * Release previous blob */ if (fdtp) free(fdtp); fdtp_size = fdt_totalsize(&header); fdtp = malloc(fdtp_size); if (fdtp == NULL) { command_errmsg = "can't allocate memory for device tree copy"; return (1); } fdtp_va = va; COPYOUT(va, fdtp, fdtp_size); debugf("DTB blob found at 0x%jx, size: 0x%jx\n", (uintmax_t)va, (uintmax_t)fdtp_size); return (0); } int fdt_load_dtb_addr(struct fdt_header *header) { int err; debugf("fdt_load_dtb_addr(0x%p)\n", header); fdtp_size = fdt_totalsize(header); err = fdt_check_header(header); if (err < 0) { sprintf(command_errbuf, "error validating blob: %s", fdt_strerror(err)); return (err); } free(fdtp); if ((fdtp = malloc(fdtp_size)) == NULL) { command_errmsg = "can't allocate memory for device tree copy"; return (1); } fdtp_va = 0; // Don't write this back into module or kernel. bcopy(header, fdtp, fdtp_size); return (0); } int fdt_load_dtb_file(const char * filename) { struct preloaded_file *bfp, *oldbfp; int err; debugf("fdt_load_dtb_file(%s)\n", filename); oldbfp = file_findfile(NULL, "dtb"); /* Attempt to load and validate a new dtb from a file. */ - if ((bfp = file_loadraw(filename, "dtb")) == NULL) { + if ((bfp = file_loadraw(filename, "dtb", 1)) == NULL) { sprintf(command_errbuf, "failed to load file '%s'", filename); return (1); } if ((err = fdt_load_dtb(bfp->f_addr)) != 0) { file_discard(bfp); return (err); } /* A new dtb was validated, discard any previous file. */ if (oldbfp) file_discard(oldbfp); return (0); } int fdt_setup_fdtp() { struct preloaded_file *bfp; vm_offset_t va; debugf("fdt_setup_fdtp()\n"); /* If we already loaded a file, use it. */ if ((bfp = file_findfile(NULL, "dtb")) != NULL) { if (fdt_load_dtb(bfp->f_addr) == 0) { printf("Using DTB from loaded file '%s'.\n", bfp->f_name); return (0); } } /* If we were given the address of a valid blob in memory, use it. */ if (fdt_to_load != NULL) { if (fdt_load_dtb_addr(fdt_to_load) == 0) { printf("Using DTB from memory address 0x%08X.\n", (unsigned int)fdt_to_load); return (0); } } if (fdt_platform_load_dtb() == 0) return (0); /* If there is a dtb compiled into the kernel, use it. */ if ((va = fdt_find_static_dtb()) != 0) { if (fdt_load_dtb(va) == 0) { printf("Using DTB compiled into kernel.\n"); return (0); } } command_errmsg = "No device tree blob found!\n"; return (1); } #define fdt_strtovect(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \ (cellbuf), (lim), (cellsize), 0); /* Force using base 16 */ #define fdt_strtovectx(str, cellbuf, lim, cellsize) _fdt_strtovect((str), \ (cellbuf), (lim), (cellsize), 16); static int _fdt_strtovect(const char *str, void *cellbuf, int lim, unsigned char cellsize, uint8_t base) { const char *buf = str; const char *end = str + strlen(str) - 2; uint32_t *u32buf = NULL; uint8_t *u8buf = NULL; int cnt = 0; if (cellsize == sizeof(uint32_t)) u32buf = (uint32_t *)cellbuf; else u8buf = (uint8_t *)cellbuf; if (lim == 0) return (0); while (buf < end) { /* Skip white whitespace(s)/separators */ while (!isxdigit(*buf) && buf < end) buf++; if (u32buf != NULL) u32buf[cnt] = cpu_to_fdt32((uint32_t)strtol(buf, NULL, base)); else u8buf[cnt] = (uint8_t)strtol(buf, NULL, base); if (cnt + 1 <= lim - 1) cnt++; else break; buf++; /* Find another number */ while ((isxdigit(*buf) || *buf == 'x') && buf < end) buf++; } return (cnt); } void fdt_fixup_ethernet(const char *str, char *ethstr, int len) { uint8_t tmp_addr[6]; /* Convert macaddr string into a vector of uints */ fdt_strtovectx(str, &tmp_addr, 6, sizeof(uint8_t)); /* Set actual property to a value from vect */ fdt_setprop(fdtp, fdt_path_offset(fdtp, ethstr), "local-mac-address", &tmp_addr, 6 * sizeof(uint8_t)); } void fdt_fixup_cpubusfreqs(unsigned long cpufreq, unsigned long busfreq) { int lo, o = 0, o2, maxo = 0, depth; const uint32_t zero = 0; /* We want to modify every subnode of /cpus */ o = fdt_path_offset(fdtp, "/cpus"); if (o < 0) return; /* maxo should contain offset of node next to /cpus */ depth = 0; maxo = o; while (depth != -1) maxo = fdt_next_node(fdtp, maxo, &depth); /* Find CPU frequency properties */ o = fdt_node_offset_by_prop_value(fdtp, o, "clock-frequency", &zero, sizeof(uint32_t)); o2 = fdt_node_offset_by_prop_value(fdtp, o, "bus-frequency", &zero, sizeof(uint32_t)); lo = MIN(o, o2); while (o != -FDT_ERR_NOTFOUND && o2 != -FDT_ERR_NOTFOUND) { o = fdt_node_offset_by_prop_value(fdtp, lo, "clock-frequency", &zero, sizeof(uint32_t)); o2 = fdt_node_offset_by_prop_value(fdtp, lo, "bus-frequency", &zero, sizeof(uint32_t)); /* We're only interested in /cpus subnode(s) */ if (lo > maxo) break; fdt_setprop_inplace_cell(fdtp, lo, "clock-frequency", (uint32_t)cpufreq); fdt_setprop_inplace_cell(fdtp, lo, "bus-frequency", (uint32_t)busfreq); lo = MIN(o, o2); } } static int fdt_reg_valid(uint32_t *reg, int len, int addr_cells, int size_cells) { int cells_in_tuple, i, tuples, tuple_size; uint32_t cur_start, cur_size; cells_in_tuple = (addr_cells + size_cells); tuple_size = cells_in_tuple * sizeof(uint32_t); tuples = len / tuple_size; if (tuples == 0) return (EINVAL); for (i = 0; i < tuples; i++) { if (addr_cells == 2) cur_start = fdt64_to_cpu(reg[i * cells_in_tuple]); else cur_start = fdt32_to_cpu(reg[i * cells_in_tuple]); if (size_cells == 2) cur_size = fdt64_to_cpu(reg[i * cells_in_tuple + 2]); else cur_size = fdt32_to_cpu(reg[i * cells_in_tuple + 1]); if (cur_size == 0) return (EINVAL); debugf(" reg#%d (start: 0x%0x size: 0x%0x) valid!\n", i, cur_start, cur_size); } return (0); } void fdt_fixup_memory(struct fdt_mem_region *region, size_t num) { struct fdt_mem_region *curmr; uint32_t addr_cells, size_cells; uint32_t *addr_cellsp, *reg, *size_cellsp; int err, i, len, memory, root; size_t realmrno; uint8_t *buf, *sb; uint64_t rstart, rsize; int reserved; root = fdt_path_offset(fdtp, "/"); if (root < 0) { sprintf(command_errbuf, "Could not find root node !"); return; } memory = fdt_path_offset(fdtp, "/memory"); if (memory <= 0) { /* Create proper '/memory' node. */ memory = fdt_add_subnode(fdtp, root, "memory"); if (memory <= 0) { sprintf(command_errbuf, "Could not fixup '/memory' " "node, error code : %d!\n", memory); return; } err = fdt_setprop(fdtp, memory, "device_type", "memory", sizeof("memory")); if (err < 0) return; } addr_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#address-cells", NULL); size_cellsp = (uint32_t *)fdt_getprop(fdtp, root, "#size-cells", NULL); if (addr_cellsp == NULL || size_cellsp == NULL) { sprintf(command_errbuf, "Could not fixup '/memory' node : " "%s %s property not found in root node!\n", (!addr_cellsp) ? "#address-cells" : "", (!size_cellsp) ? "#size-cells" : ""); return; } addr_cells = fdt32_to_cpu(*addr_cellsp); size_cells = fdt32_to_cpu(*size_cellsp); /* * Convert memreserve data to memreserve property * Check if property already exists */ reserved = fdt_num_mem_rsv(fdtp); if (reserved && (fdt_getprop(fdtp, root, "memreserve", NULL) == NULL)) { len = (addr_cells + size_cells) * reserved * sizeof(uint32_t); sb = buf = (uint8_t *)malloc(len); if (!buf) return; bzero(buf, len); for (i = 0; i < reserved; i++) { if (fdt_get_mem_rsv(fdtp, i, &rstart, &rsize)) break; if (rsize) { /* Ensure endianess, and put cells into a buffer */ if (addr_cells == 2) *(uint64_t *)buf = cpu_to_fdt64(rstart); else *(uint32_t *)buf = cpu_to_fdt32(rstart); buf += sizeof(uint32_t) * addr_cells; if (size_cells == 2) *(uint64_t *)buf = cpu_to_fdt64(rsize); else *(uint32_t *)buf = cpu_to_fdt32(rsize); buf += sizeof(uint32_t) * size_cells; } } /* Set property */ if ((err = fdt_setprop(fdtp, root, "memreserve", sb, len)) < 0) printf("Could not fixup 'memreserve' property.\n"); free(sb); } /* Count valid memory regions entries in sysinfo. */ realmrno = num; for (i = 0; i < num; i++) if (region[i].start == 0 && region[i].size == 0) realmrno--; if (realmrno == 0) { sprintf(command_errbuf, "Could not fixup '/memory' node : " "sysinfo doesn't contain valid memory regions info!\n"); return; } len = (addr_cells + size_cells) * realmrno * sizeof(uint32_t); sb = buf = (uint8_t *)malloc(len); if (!buf) return; bzero(buf, len); for (i = 0; i < num; i++) { curmr = ®ion[i]; if (curmr->size != 0) { /* Ensure endianess, and put cells into a buffer */ if (addr_cells == 2) *(uint64_t *)buf = cpu_to_fdt64(curmr->start); else *(uint32_t *)buf = cpu_to_fdt32(curmr->start); buf += sizeof(uint32_t) * addr_cells; if (size_cells == 2) *(uint64_t *)buf = cpu_to_fdt64(curmr->size); else *(uint32_t *)buf = cpu_to_fdt32(curmr->size); buf += sizeof(uint32_t) * size_cells; } } /* Set property */ if ((err = fdt_setprop(fdtp, memory, "reg", sb, len)) < 0) sprintf(command_errbuf, "Could not fixup '/memory' node.\n"); free(sb); } void fdt_fixup_stdout(const char *str) { char *ptr; int serialno; int len, no, sero; const struct fdt_property *prop; char *tmp[10]; ptr = (char *)str + strlen(str) - 1; while (ptr > str && isdigit(*(str - 1))) str--; if (ptr == str) return; serialno = (int)strtol(ptr, NULL, 0); no = fdt_path_offset(fdtp, "/chosen"); if (no < 0) return; prop = fdt_get_property(fdtp, no, "stdout", &len); /* If /chosen/stdout does not extist, create it */ if (prop == NULL || (prop != NULL && len == 0)) { bzero(tmp, 10 * sizeof(char)); strcpy((char *)&tmp, "serial"); if (strlen(ptr) > 3) /* Serial number too long */ return; strncpy((char *)tmp + 6, ptr, 3); sero = fdt_path_offset(fdtp, (const char *)tmp); if (sero < 0) /* * If serial device we're trying to assign * stdout to doesn't exist in DT -- return. */ return; fdt_setprop(fdtp, no, "stdout", &tmp, strlen((char *)&tmp) + 1); fdt_setprop(fdtp, no, "stdin", &tmp, strlen((char *)&tmp) + 1); } } /* * Locate the blob, fix it up and return its location. */ static int fdt_fixup(void) { int chosen, len; len = 0; debugf("fdt_fixup()\n"); if (fdtp == NULL && fdt_setup_fdtp() != 0) return (0); /* Create /chosen node (if not exists) */ if ((chosen = fdt_subnode_offset(fdtp, 0, "chosen")) == -FDT_ERR_NOTFOUND) chosen = fdt_add_subnode(fdtp, 0, "chosen"); /* Value assigned to fixup-applied does not matter. */ if (fdt_getprop(fdtp, chosen, "fixup-applied", NULL)) return (1); fdt_platform_fixups(); fdt_setprop(fdtp, chosen, "fixup-applied", NULL, 0); return (1); } /* * Copy DTB blob to specified location and return size */ int fdt_copy(vm_offset_t va) { int err; debugf("fdt_copy va 0x%08x\n", va); if (fdtp == NULL) { err = fdt_setup_fdtp(); if (err) { printf("No valid device tree blob found!\n"); return (0); } } if (fdt_fixup() == 0) return (0); if (fdtp_va != 0) { /* Overwrite the FDT with the fixed version. */ /* XXX Is this really appropriate? */ COPYIN(fdtp, fdtp_va, fdtp_size); } COPYIN(fdtp, va, fdtp_size); return (fdtp_size); } int command_fdt_internal(int argc, char *argv[]) { cmdf_t *cmdh; int flags; char *cmd; int i, err; if (argc < 2) { command_errmsg = "usage is 'fdt []"; return (CMD_ERROR); } /* * Validate fdt . */ cmd = strdup(argv[1]); i = 0; cmdh = NULL; while (!(commands[i].name == NULL)) { if (strcmp(cmd, commands[i].name) == 0) { /* found it */ cmdh = commands[i].handler; flags = commands[i].flags; break; } i++; } if (cmdh == NULL) { command_errmsg = "unknown command"; return (CMD_ERROR); } if (flags & CMD_REQUIRES_BLOB) { /* * Check if uboot env vars were parsed already. If not, do it now. */ if (fdt_fixup() == 0) return (CMD_ERROR); } /* * Call command handler. */ err = (*cmdh)(argc, argv); return (err); } static int fdt_cmd_addr(int argc, char *argv[]) { struct preloaded_file *fp; struct fdt_header *hdr; const char *addr; char *cp; fdt_to_load = NULL; if (argc > 2) addr = argv[2]; else { sprintf(command_errbuf, "no address specified"); return (CMD_ERROR); } hdr = (struct fdt_header *)strtoul(addr, &cp, 16); if (cp == addr) { sprintf(command_errbuf, "Invalid address: %s", addr); return (CMD_ERROR); } while ((fp = file_findfile(NULL, "dtb")) != NULL) { file_discard(fp); } fdt_to_load = hdr; return (CMD_OK); } static int fdt_cmd_cd(int argc, char *argv[]) { char *path; char tmp[FDT_CWD_LEN]; int len, o; path = (argc > 2) ? argv[2] : "/"; if (path[0] == '/') { len = strlen(path); if (len >= FDT_CWD_LEN) goto fail; } else { /* Handle path specification relative to cwd */ len = strlen(cwd) + strlen(path) + 1; if (len >= FDT_CWD_LEN) goto fail; strcpy(tmp, cwd); strcat(tmp, "/"); strcat(tmp, path); path = tmp; } o = fdt_path_offset(fdtp, path); if (o < 0) { sprintf(command_errbuf, "could not find node: '%s'", path); return (CMD_ERROR); } strcpy(cwd, path); return (CMD_OK); fail: sprintf(command_errbuf, "path too long: %d, max allowed: %d", len, FDT_CWD_LEN - 1); return (CMD_ERROR); } static int fdt_cmd_hdr(int argc __unused, char *argv[] __unused) { char line[80]; int ver; if (fdtp == NULL) { command_errmsg = "no device tree blob pointer?!"; return (CMD_ERROR); } ver = fdt_version(fdtp); pager_open(); sprintf(line, "\nFlattened device tree header (%p):\n", fdtp); pager_output(line); sprintf(line, " magic = 0x%08x\n", fdt_magic(fdtp)); pager_output(line); sprintf(line, " size = %d\n", fdt_totalsize(fdtp)); pager_output(line); sprintf(line, " off_dt_struct = 0x%08x\n", fdt_off_dt_struct(fdtp)); pager_output(line); sprintf(line, " off_dt_strings = 0x%08x\n", fdt_off_dt_strings(fdtp)); pager_output(line); sprintf(line, " off_mem_rsvmap = 0x%08x\n", fdt_off_mem_rsvmap(fdtp)); pager_output(line); sprintf(line, " version = %d\n", ver); pager_output(line); sprintf(line, " last compatible version = %d\n", fdt_last_comp_version(fdtp)); pager_output(line); if (ver >= 2) { sprintf(line, " boot_cpuid = %d\n", fdt_boot_cpuid_phys(fdtp)); pager_output(line); } if (ver >= 3) { sprintf(line, " size_dt_strings = %d\n", fdt_size_dt_strings(fdtp)); pager_output(line); } if (ver >= 17) { sprintf(line, " size_dt_struct = %d\n", fdt_size_dt_struct(fdtp)); pager_output(line); } pager_close(); return (CMD_OK); } static int fdt_cmd_ls(int argc, char *argv[]) { const char *prevname[FDT_MAX_DEPTH] = { NULL }; const char *name; char *path; int i, o, depth, len; path = (argc > 2) ? argv[2] : NULL; if (path == NULL) path = cwd; o = fdt_path_offset(fdtp, path); if (o < 0) { sprintf(command_errbuf, "could not find node: '%s'", path); return (CMD_ERROR); } for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) { name = fdt_get_name(fdtp, o, &len); if (depth > FDT_MAX_DEPTH) { printf("max depth exceeded: %d\n", depth); continue; } prevname[depth] = name; /* Skip root (i = 1) when printing devices */ for (i = 1; i <= depth; i++) { if (prevname[i] == NULL) break; if (strcmp(cwd, "/") == 0) printf("/"); printf("%s", prevname[i]); } printf("\n"); } return (CMD_OK); } static __inline int isprint(int c) { return (c >= ' ' && c <= 0x7e); } static int fdt_isprint(const void *data, int len, int *count) { const char *d; char ch; int yesno, i; if (len == 0) return (0); d = (const char *)data; if (d[len - 1] != '\0') return (0); *count = 0; yesno = 1; for (i = 0; i < len; i++) { ch = *(d + i); if (isprint(ch) || (ch == '\0' && i > 0)) { /* Count strings */ if (ch == '\0') (*count)++; continue; } yesno = 0; break; } return (yesno); } static int fdt_data_str(const void *data, int len, int count, char **buf) { char *b, *tmp; const char *d; int buf_len, i, l; /* * Calculate the length for the string and allocate memory. * * Note that 'len' already includes at least one terminator. */ buf_len = len; if (count > 1) { /* * Each token had already a terminator buried in 'len', but we * only need one eventually, don't count space for these. */ buf_len -= count - 1; /* Each consecutive token requires a ", " separator. */ buf_len += count * 2; } /* Add some space for surrounding double quotes. */ buf_len += count * 2; /* Note that string being put in 'tmp' may be as big as 'buf_len'. */ b = (char *)malloc(buf_len); tmp = (char *)malloc(buf_len); if (b == NULL) goto error; if (tmp == NULL) { free(b); goto error; } b[0] = '\0'; /* * Now that we have space, format the string. */ i = 0; do { d = (const char *)data + i; l = strlen(d) + 1; sprintf(tmp, "\"%s\"%s", d, (i + l) < len ? ", " : ""); strcat(b, tmp); i += l; } while (i < len); *buf = b; free(tmp); return (0); error: return (1); } static int fdt_data_cell(const void *data, int len, char **buf) { char *b, *tmp; const uint32_t *c; int count, i, l; /* Number of cells */ count = len / 4; /* * Calculate the length for the string and allocate memory. */ /* Each byte translates to 2 output characters */ l = len * 2; if (count > 1) { /* Each consecutive cell requires a " " separator. */ l += (count - 1) * 1; } /* Each cell will have a "0x" prefix */ l += count * 2; /* Space for surrounding <> and terminator */ l += 3; b = (char *)malloc(l); tmp = (char *)malloc(l); if (b == NULL) goto error; if (tmp == NULL) { free(b); goto error; } b[0] = '\0'; strcat(b, "<"); for (i = 0; i < len; i += 4) { c = (const uint32_t *)((const uint8_t *)data + i); sprintf(tmp, "0x%08x%s", fdt32_to_cpu(*c), i < (len - 4) ? " " : ""); strcat(b, tmp); } strcat(b, ">"); *buf = b; free(tmp); return (0); error: return (1); } static int fdt_data_bytes(const void *data, int len, char **buf) { char *b, *tmp; const char *d; int i, l; /* * Calculate the length for the string and allocate memory. */ /* Each byte translates to 2 output characters */ l = len * 2; if (len > 1) /* Each consecutive byte requires a " " separator. */ l += (len - 1) * 1; /* Each byte will have a "0x" prefix */ l += len * 2; /* Space for surrounding [] and terminator. */ l += 3; b = (char *)malloc(l); tmp = (char *)malloc(l); if (b == NULL) goto error; if (tmp == NULL) { free(b); goto error; } b[0] = '\0'; strcat(b, "["); for (i = 0, d = data; i < len; i++) { sprintf(tmp, "0x%02x%s", d[i], i < len - 1 ? " " : ""); strcat(b, tmp); } strcat(b, "]"); *buf = b; free(tmp); return (0); error: return (1); } static int fdt_data_fmt(const void *data, int len, char **buf) { int count; if (len == 0) { *buf = NULL; return (1); } if (fdt_isprint(data, len, &count)) return (fdt_data_str(data, len, count, buf)); else if ((len % 4) == 0) return (fdt_data_cell(data, len, buf)); else return (fdt_data_bytes(data, len, buf)); } static int fdt_prop(int offset) { char *line, *buf; const struct fdt_property *prop; const char *name; const void *data; int len, rv; line = NULL; prop = fdt_offset_ptr(fdtp, offset, sizeof(*prop)); if (prop == NULL) return (1); name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff)); len = fdt32_to_cpu(prop->len); rv = 0; buf = NULL; if (len == 0) { /* Property without value */ line = (char *)malloc(strlen(name) + 2); if (line == NULL) { rv = 2; goto out2; } sprintf(line, "%s\n", name); goto out1; } /* * Process property with value */ data = prop->data; if (fdt_data_fmt(data, len, &buf) != 0) { rv = 3; goto out2; } line = (char *)malloc(strlen(name) + strlen(FDT_PROP_SEP) + strlen(buf) + 2); if (line == NULL) { sprintf(command_errbuf, "could not allocate space for string"); rv = 4; goto out2; } sprintf(line, "%s" FDT_PROP_SEP "%s\n", name, buf); out1: pager_open(); pager_output(line); pager_close(); out2: if (buf) free(buf); if (line) free(line); return (rv); } static int fdt_modprop(int nodeoff, char *propname, void *value, char mode) { uint32_t cells[100]; const char *buf; int len, rv; const struct fdt_property *p; p = fdt_get_property(fdtp, nodeoff, propname, NULL); if (p != NULL) { if (mode == 1) { /* Adding inexistant value in mode 1 is forbidden */ sprintf(command_errbuf, "property already exists!"); return (CMD_ERROR); } } else if (mode == 0) { sprintf(command_errbuf, "property does not exist!"); return (CMD_ERROR); } len = strlen(value); rv = 0; buf = value; switch (*buf) { case '&': /* phandles */ break; case '<': /* Data cells */ len = fdt_strtovect(buf, (void *)&cells, 100, sizeof(uint32_t)); rv = fdt_setprop(fdtp, nodeoff, propname, &cells, len * sizeof(uint32_t)); break; case '[': /* Data bytes */ len = fdt_strtovect(buf, (void *)&cells, 100, sizeof(uint8_t)); rv = fdt_setprop(fdtp, nodeoff, propname, &cells, len * sizeof(uint8_t)); break; case '"': default: /* Default -- string */ rv = fdt_setprop_string(fdtp, nodeoff, propname, value); break; } if (rv != 0) { if (rv == -FDT_ERR_NOSPACE) sprintf(command_errbuf, "Device tree blob is too small!\n"); else sprintf(command_errbuf, "Could not add/modify property!\n"); } return (rv); } /* Merge strings from argv into a single string */ static int fdt_merge_strings(int argc, char *argv[], int start, char **buffer) { char *buf; int i, idx, sz; *buffer = NULL; sz = 0; for (i = start; i < argc; i++) sz += strlen(argv[i]); /* Additional bytes for whitespaces between args */ sz += argc - start; buf = (char *)malloc(sizeof(char) * sz); if (buf == NULL) { sprintf(command_errbuf, "could not allocate space " "for string"); return (1); } bzero(buf, sizeof(char) * sz); idx = 0; for (i = start, idx = 0; i < argc; i++) { strcpy(buf + idx, argv[i]); idx += strlen(argv[i]); buf[idx] = ' '; idx++; } buf[sz - 1] = '\0'; *buffer = buf; return (0); } /* Extract offset and name of node/property from a given path */ static int fdt_extract_nameloc(char **pathp, char **namep, int *nodeoff) { int o; char *path = *pathp, *name = NULL, *subpath = NULL; subpath = strrchr(path, '/'); if (subpath == NULL) { o = fdt_path_offset(fdtp, cwd); name = path; path = (char *)&cwd; } else { *subpath = '\0'; if (strlen(path) == 0) path = cwd; name = subpath + 1; o = fdt_path_offset(fdtp, path); } if (strlen(name) == 0) { sprintf(command_errbuf, "name not specified"); return (1); } if (o < 0) { sprintf(command_errbuf, "could not find node: '%s'", path); return (1); } *namep = name; *nodeoff = o; *pathp = path; return (0); } static int fdt_cmd_prop(int argc, char *argv[]) { char *path, *propname, *value; int o, next, depth, rv; uint32_t tag; path = (argc > 2) ? argv[2] : NULL; value = NULL; if (argc > 3) { /* Merge property value strings into one */ if (fdt_merge_strings(argc, argv, 3, &value) != 0) return (CMD_ERROR); } else value = NULL; if (path == NULL) path = cwd; rv = CMD_OK; if (value) { /* If value is specified -- try to modify prop. */ if (fdt_extract_nameloc(&path, &propname, &o) != 0) return (CMD_ERROR); rv = fdt_modprop(o, propname, value, 0); if (rv) return (CMD_ERROR); return (CMD_OK); } /* User wants to display properties */ o = fdt_path_offset(fdtp, path); if (o < 0) { sprintf(command_errbuf, "could not find node: '%s'", path); rv = CMD_ERROR; goto out; } depth = 0; while (depth >= 0) { tag = fdt_next_tag(fdtp, o, &next); switch (tag) { case FDT_NOP: break; case FDT_PROP: if (depth > 1) /* Don't process properties of nested nodes */ break; if (fdt_prop(o) != 0) { sprintf(command_errbuf, "could not process " "property"); rv = CMD_ERROR; goto out; } break; case FDT_BEGIN_NODE: depth++; if (depth > FDT_MAX_DEPTH) { printf("warning: nesting too deep: %d\n", depth); goto out; } break; case FDT_END_NODE: depth--; if (depth == 0) /* * This is the end of our starting node, force * the loop finish. */ depth--; break; } o = next; } out: return (rv); } static int fdt_cmd_mkprop(int argc, char *argv[]) { int o; char *path, *propname, *value; path = (argc > 2) ? argv[2] : NULL; value = NULL; if (argc > 3) { /* Merge property value strings into one */ if (fdt_merge_strings(argc, argv, 3, &value) != 0) return (CMD_ERROR); } else value = NULL; if (fdt_extract_nameloc(&path, &propname, &o) != 0) return (CMD_ERROR); if (fdt_modprop(o, propname, value, 1)) return (CMD_ERROR); return (CMD_OK); } static int fdt_cmd_rm(int argc, char *argv[]) { int o, rv; char *path = NULL, *propname; if (argc > 2) path = argv[2]; else { sprintf(command_errbuf, "no node/property name specified"); return (CMD_ERROR); } o = fdt_path_offset(fdtp, path); if (o < 0) { /* If node not found -- try to find & delete property */ if (fdt_extract_nameloc(&path, &propname, &o) != 0) return (CMD_ERROR); if ((rv = fdt_delprop(fdtp, o, propname)) != 0) { sprintf(command_errbuf, "could not delete" "%s\n", (rv == -FDT_ERR_NOTFOUND) ? "(property/node does not exist)" : ""); return (CMD_ERROR); } else return (CMD_OK); } /* If node exists -- remove node */ rv = fdt_del_node(fdtp, o); if (rv) { sprintf(command_errbuf, "could not delete node"); return (CMD_ERROR); } return (CMD_OK); } static int fdt_cmd_mknode(int argc, char *argv[]) { int o, rv; char *path = NULL, *nodename = NULL; if (argc > 2) path = argv[2]; else { sprintf(command_errbuf, "no node name specified"); return (CMD_ERROR); } if (fdt_extract_nameloc(&path, &nodename, &o) != 0) return (CMD_ERROR); rv = fdt_add_subnode(fdtp, o, nodename); if (rv < 0) { if (rv == -FDT_ERR_NOSPACE) sprintf(command_errbuf, "Device tree blob is too small!\n"); else sprintf(command_errbuf, "Could not add node!\n"); return (CMD_ERROR); } return (CMD_OK); } static int fdt_cmd_pwd(int argc, char *argv[]) { char line[FDT_CWD_LEN]; pager_open(); sprintf(line, "%s\n", cwd); pager_output(line); pager_close(); return (CMD_OK); } static int fdt_cmd_mres(int argc, char *argv[]) { uint64_t start, size; int i, total; char line[80]; pager_open(); total = fdt_num_mem_rsv(fdtp); if (total > 0) { pager_output("Reserved memory regions:\n"); for (i = 0; i < total; i++) { fdt_get_mem_rsv(fdtp, i, &start, &size); sprintf(line, "reg#%d: (start: 0x%jx, size: 0x%jx)\n", i, start, size); pager_output(line); } } else pager_output("No reserved memory regions\n"); pager_close(); return (CMD_OK); } static int fdt_cmd_nyi(int argc, char *argv[]) { printf("command not yet implemented\n"); return (CMD_ERROR); } Index: stable/10/sys/boot/forth/beastie.4th =================================================================== --- stable/10/sys/boot/forth/beastie.4th (revision 294416) +++ stable/10/sys/boot/forth/beastie.4th (revision 294417) @@ -1,114 +1,115 @@ \ Copyright (c) 2003 Scott Long \ Copyright (c) 2003 Aleksander Fafula \ Copyright (c) 2006-2015 Devin Teske \ 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. \ \ $FreeBSD$ marker task-beastie.4th only forth definitions variable logoX variable logoY \ Initialize logo placement to defaults 46 logoX ! 4 logoY ! \ This function draws any number of beastie logos at (loader_logo_x, \ loader_logo_y) if defined, else (46,4) (to the right of the menu). To choose \ your beastie, set the variable `loader_logo' to the respective logo name. \ \ NOTE: Each is defined as a logo function in /boot/logo-${loader_logo}.4th \ NOTE: If `/boot/logo-${loader_logo}.4th' does not exist or does not define \ a `logo' function, no beastie is drawn. \ : draw-beastie ( -- ) \ at (loader_logo_x,loader_logo_y), else (46,4) s" loader_logo_x" getenv dup -1 <> if ?number 1 = if logoX ! then else drop then s" loader_logo_y" getenv dup -1 <> if ?number 1 = if logoY ! then else drop then \ If `logo' is defined, execute it s" logo" sfind ( -- xt|0 bool ) if logoX @ logoY @ rot execute else \ Not defined; try-include desired logo file drop ( xt = 0 ) \ cruft s" loader_logo" getenv dup -1 = over 0= or if dup 0= if 2drop else drop then \ getenv result unused loader_color? if s" try-include /boot/logo-orb.4th" else s" try-include /boot/logo-orbbw.4th" then else 2drop ( c-addr/u -- ) \ getenv result unused s" try-include /boot/logo-${loader_logo}.4th" then evaluate 1 spaces \ Execute `logo' if defined now s" logo" sfind if logoX @ logoY @ rot execute else drop then then ; also support-functions : beastie-start ( -- ) \ starts the menu s" console" getenv dup -1 <> if s" efi" 2swap contains? if s" set beastie_disable=YES" evaluate then else drop then s" beastie_disable" getenv dup -1 <> if s" YES" compare-insensitive 0= if any_conf_read? if + load_xen_throw load_kernel load_modules then exit \ to autoboot (default) then else drop then s" loader_delay" getenv -1 = if s" include /boot/menu.rc" evaluate else drop ." Loading Menu (Ctrl-C to Abort)" cr s" set delay_command='include /boot/menu.rc'" evaluate s" set delay_showdots" evaluate delay_execute then ; only forth definitions Index: stable/10/sys/boot/forth/loader.4th =================================================================== --- stable/10/sys/boot/forth/loader.4th (revision 294416) +++ stable/10/sys/boot/forth/loader.4th (revision 294417) @@ -1,251 +1,252 @@ \ Copyright (c) 1999 Daniel C. Sobral \ Copyright (c) 2011-2015 Devin Teske \ 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. \ \ $FreeBSD$ only forth definitions s" arch-i386" environment? [if] [if] s" loader_version" environment? [if] 11 < [if] .( Loader version 1.1+ required) cr abort [then] [else] .( Could not get loader version!) cr abort [then] [then] [then] 256 dictthreshold ! \ 256 cells minimum free space 2048 dictincrease ! \ 2048 additional cells each time include /boot/support.4th include /boot/color.4th include /boot/delay.4th include /boot/check-password.4th only forth definitions : bootmsg ( -- ) loader_color? dup ( -- bool bool ) if 7 fg 4 bg then ." Booting..." if me then cr ; : try-menu-unset \ menu-unset may not be present s" beastie_disable" getenv dup -1 <> if s" YES" compare-insensitive 0= if exit then else drop then s" menu-unset" sfind if execute else drop then s" menusets-unset" sfind if execute else drop then ; only forth also support-functions also builtins definitions : boot 0= if ( interpreted ) get_arguments then \ Unload only if a path was passed dup if >r over r> swap c@ [char] - <> if 0 1 unload drop else s" kernelname" getenv? if ( a kernel has been loaded ) try-menu-unset bootmsg 1 boot exit then load_kernel_and_modules ?dup if exit then try-menu-unset bootmsg 0 1 boot exit then else s" kernelname" getenv? if ( a kernel has been loaded ) try-menu-unset bootmsg 1 boot exit then load_kernel_and_modules ?dup if exit then try-menu-unset bootmsg 0 1 boot exit then load_kernel_and_modules ?dup 0= if bootmsg 0 1 boot then ; \ ***** boot-conf \ \ Prepares to boot as specified by loaded configuration files. : boot-conf 0= if ( interpreted ) get_arguments then 0 1 unload drop load_kernel_and_modules ?dup 0= if 0 1 autoboot then ; also forth definitions previous builtin: boot builtin: boot-conf only forth definitions also support-functions \ ***** start \ \ Initializes support.4th global variables, sets loader_conf_files, \ processes conf files, and, if any one such file was succesfully \ read to the end, loads kernel and modules. : start ( -- ) ( throws: abort & user-defined ) s" /boot/defaults/loader.conf" initialize include_conf_files include_nextboot_file \ Will *NOT* try to load kernel and modules if no configuration file \ was succesfully loaded! any_conf_read? if s" loader_delay" getenv -1 = if + load_xen_throw load_kernel load_modules else drop ." Loading Kernel and Modules (Ctrl-C to Abort)" cr s" also support-functions" evaluate - s" set delay_command='load_kernel load_modules'" evaluate + s" set delay_command='load_xen_throw load_kernel load_modules'" evaluate s" set delay_showdots" evaluate delay_execute then then ; \ ***** initialize \ \ Overrides support.4th initialization word with one that does \ everything start one does, short of loading the kernel and \ modules. Returns a flag : initialize ( -- flag ) s" /boot/defaults/loader.conf" initialize include_conf_files include_nextboot_file any_conf_read? ; \ ***** read-conf \ \ Read a configuration file, whose name was specified on the command \ line, if interpreted, or given on the stack, if compiled in. : (read-conf) ( addr len -- ) conf_files string= include_conf_files \ Will recurse on new loader_conf_files definitions ; : read-conf ( | addr len -- ) ( throws: abort & user-defined ) state @ if \ Compiling postpone (read-conf) else \ Interpreting bl parse (read-conf) then ; immediate \ show, enable, disable, toggle module loading. They all take module from \ the next word : set-module-flag ( module_addr val -- ) \ set and print flag over module.flag ! dup module.name strtype module.flag @ if ." will be loaded" else ." will not be loaded" then cr ; : enable-module find-module ?dup if true set-module-flag then ; : disable-module find-module ?dup if false set-module-flag then ; : toggle-module find-module ?dup if dup module.flag @ 0= set-module-flag then ; \ ***** show-module \ \ Show loading information about a module. : show-module ( -- ) find-module ?dup if show-one-module then ; \ Words to be used inside configuration files : retry false ; \ For use in load error commands : ignore true ; \ For use in load error commands \ Return to strict forth vocabulary : #type over - >r type r> spaces ; : .? 2 spaces 2swap 15 #type 2 spaces type cr ; : ? ['] ? execute s" boot-conf" s" load kernel and modules, then autoboot" .? s" read-conf" s" read a configuration file" .? s" enable-module" s" enable loading of a module" .? s" disable-module" s" disable loading of a module" .? s" toggle-module" s" toggle loading of a module" .? s" show-module" s" show module load data" .? s" try-include" s" try to load/interpret files" .? ; : try-include ( -- ) \ see loader.4th(8) ['] include ( -- xt ) \ get the execution token of `include' catch ( xt -- exception# | 0 ) if \ failed LF parse ( c -- s-addr/u ) 2drop \ advance >in to EOL (drop data) \ ... prevents words unused by `include' from being interpreted then ; immediate \ interpret immediately for access to `source' (aka tib) only forth definitions Index: stable/10/sys/boot/forth/support.4th =================================================================== --- stable/10/sys/boot/forth/support.4th (revision 294416) +++ stable/10/sys/boot/forth/support.4th (revision 294417) @@ -1,1589 +1,1606 @@ \ Copyright (c) 1999 Daniel C. Sobral \ 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. \ \ $FreeBSD$ \ Loader.rc support functions: \ \ initialize ( addr len -- ) as above, plus load_conf_files \ load_conf ( addr len -- ) load conf file given \ include_conf_files ( -- ) load all conf files in load_conf_files \ print_syntax_error ( -- ) print line and marker of where a syntax \ error was detected \ print_line ( -- ) print last line processed \ load_kernel ( -- ) load kernel \ load_modules ( -- ) load modules flagged \ \ Exported structures: \ \ string counted string structure \ cell .addr string address \ cell .len string length \ module module loading information structure \ cell module.flag should we load it? \ string module.name module's name \ string module.loadname name to be used in loading the module \ string module.type module's type \ string module.args flags to be passed during load \ string module.beforeload command to be executed before load \ string module.afterload command to be executed after load \ string module.loaderror command to be executed if load fails \ cell module.next list chain \ \ Exported global variables; \ \ string conf_files configuration files to be loaded \ cell modules_options pointer to first module information \ value verbose? indicates if user wants a verbose loading \ value any_conf_read? indicates if a conf file was succesfully read \ \ Other exported words: \ note, strlen is internal \ strdup ( addr len -- addr' len) similar to strdup(3) \ strcat ( addr len addr' len' -- addr len+len' ) similar to strcat(3) \ s' ( | string' -- addr len | ) similar to s" \ rudimentary structure support \ Exception values 1 constant ESYNTAX 2 constant ENOMEM 3 constant EFREE 4 constant ESETERROR \ error setting environment variable 5 constant EREAD \ error reading 6 constant EOPEN 7 constant EEXEC \ XXX never catched 8 constant EBEFORELOAD 9 constant EAFTERLOAD \ I/O constants 0 constant SEEK_SET 1 constant SEEK_CUR 2 constant SEEK_END 0 constant O_RDONLY 1 constant O_WRONLY 2 constant O_RDWR \ Crude structure support : structure: create here 0 , ['] drop , 0 does> create here swap dup @ allot cell+ @ execute ; : member: create dup , over , + does> cell+ @ + ; : ;structure swap ! ; : constructor! >body cell+ ! ; : constructor: over :noname ; : ;constructor postpone ; swap cell+ ! ; immediate : sizeof ' >body @ state @ if postpone literal then ; immediate : offsetof ' >body cell+ @ state @ if postpone literal then ; immediate : ptr 1 cells member: ; : int 1 cells member: ; \ String structure structure: string ptr .addr int .len constructor: 0 over .addr ! 0 swap .len ! ;constructor ;structure \ Module options linked list structure: module int module.flag sizeof string member: module.name sizeof string member: module.loadname sizeof string member: module.type sizeof string member: module.args sizeof string member: module.beforeload sizeof string member: module.afterload sizeof string member: module.loaderror ptr module.next ;structure \ Internal loader structures (preloaded_file, kernel_module, file_metadata) \ must be in sync with the C struct in sys/boot/common/bootstrap.h structure: preloaded_file ptr pf.name ptr pf.type ptr pf.args ptr pf.metadata \ file_metadata int pf.loader int pf.addr int pf.size ptr pf.modules \ kernel_module ptr pf.next \ preloaded_file ;structure structure: kernel_module ptr km.name \ ptr km.args ptr km.fp \ preloaded_file ptr km.next \ kernel_module ;structure structure: file_metadata int md.size 2 member: md.type \ this is not ANS Forth compatible (XXX) ptr md.next \ file_metadata 0 member: md.data \ variable size ;structure \ end of structures \ Global variables string conf_files string nextboot_conf_file create module_options sizeof module.next allot 0 module_options ! create last_module_option sizeof module.next allot 0 last_module_option ! 0 value verbose? 0 value nextboot? \ Support string functions : strdup { addr len -- addr' len' } len allocate if ENOMEM throw then addr over len move len ; : strcat { addr len addr' len' -- addr len+len' } addr' addr len + len' move addr len len' + ; : strchr { addr len c -- addr' len' } begin len while addr c@ c = if addr len exit then addr 1 + to addr len 1 - to len repeat 0 0 ; : s' \ same as s", allows " in the string [char] ' parse state @ if postpone sliteral then ; immediate : 2>r postpone >r postpone >r ; immediate : 2r> postpone r> postpone r> ; immediate : 2r@ postpone 2r> postpone 2dup postpone 2>r ; immediate : getenv? getenv -1 = if false else drop true then ; \ determine if a word appears in a string, case-insensitive : contains? ( addr1 len1 addr2 len2 -- 0 | -1 ) 2 pick 0= if 2drop 2drop true exit then dup 0= if 2drop 2drop false exit then begin begin swap dup c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop while 1+ swap 1- repeat swap 2 pick 1- over < while 2over 2over drop over compare-insensitive 0= if 2 pick over = if 2drop 2drop true exit then 2 pick tuck - -rot + swap over c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop if 2drop 2drop true exit then then begin swap dup c@ dup 32 = over 9 = or over 10 = or over 13 = or over 44 = or swap drop if false else true then 2 pick 0> and while 1+ swap 1- repeat swap repeat 2drop 2drop false ; : boot_serial? ( -- 0 | -1 ) s" console" getenv dup -1 <> if s" comconsole" 2swap contains? else drop false then s" boot_serial" getenv dup -1 <> if swap drop 0> else drop false then or \ console contains comconsole ( or ) boot_serial s" boot_multicons" getenv dup -1 <> if swap drop 0> else drop false then or \ previous boolean ( or ) boot_multicons ; \ Private definitions vocabulary support-functions only forth also support-functions definitions \ Some control characters constants 7 constant bell 8 constant backspace 9 constant tab 10 constant lf 13 constant \ Read buffer size 80 constant read_buffer_size \ Standard suffixes : load_module_suffix s" _load" ; : module_loadname_suffix s" _name" ; : module_type_suffix s" _type" ; : module_args_suffix s" _flags" ; : module_beforeload_suffix s" _before" ; : module_afterload_suffix s" _after" ; : module_loaderror_suffix s" _error" ; \ Support operators : >= < 0= ; : <= > 0= ; \ Assorted support functions : free-memory free if EFREE throw then ; : strget { var -- addr len } var .addr @ var .len @ ; \ assign addr len to variable. : strset { addr len var -- } addr var .addr ! len var .len ! ; \ free memory and reset fields : strfree { var -- } var .addr @ ?dup if free-memory 0 0 var strset then ; \ free old content, make a copy of the string and assign to variable : string= { addr len var -- } var strfree addr len strdup var strset ; : strtype ( str -- ) strget type ; \ assign a reference to what is on the stack : strref { addr len var -- addr len } addr var .addr ! len var .len ! addr len ; \ unquote a string : unquote ( addr len -- addr len ) over c@ [char] " = if 2 chars - swap char+ swap then ; \ Assignment data temporary storage string name_buffer string value_buffer \ Line by line file reading functions \ \ exported: \ line_buffer \ end_of_file? \ fd \ read_line \ reset_line_reading vocabulary line-reading also line-reading definitions \ File data temporary storage string read_buffer 0 value read_buffer_ptr \ File's line reading function get-current ( -- wid ) previous definitions string line_buffer 0 value end_of_file? variable fd >search ( wid -- ) definitions : skip_newlines begin read_buffer .len @ read_buffer_ptr > while read_buffer .addr @ read_buffer_ptr + c@ lf = if read_buffer_ptr char+ to read_buffer_ptr else exit then repeat ; : scan_buffer ( -- addr len ) read_buffer_ptr >r begin read_buffer .len @ r@ > while read_buffer .addr @ r@ + c@ lf = if read_buffer .addr @ read_buffer_ptr + ( -- addr ) r@ read_buffer_ptr - ( -- len ) r> to read_buffer_ptr exit then r> char+ >r repeat read_buffer .addr @ read_buffer_ptr + ( -- addr ) r@ read_buffer_ptr - ( -- len ) r> to read_buffer_ptr ; : line_buffer_resize ( len -- len ) >r line_buffer .len @ if line_buffer .addr @ line_buffer .len @ r@ + resize if ENOMEM throw then else r@ allocate if ENOMEM throw then then line_buffer .addr ! r> ; : append_to_line_buffer ( addr len -- ) line_buffer strget 2swap strcat line_buffer .len ! drop ; : read_from_buffer scan_buffer ( -- addr len ) line_buffer_resize ( len -- len ) append_to_line_buffer ( addr len -- ) ; : refill_required? read_buffer .len @ read_buffer_ptr = end_of_file? 0= and ; : refill_buffer 0 to read_buffer_ptr read_buffer .addr @ 0= if read_buffer_size allocate if ENOMEM throw then read_buffer .addr ! then fd @ read_buffer .addr @ read_buffer_size fread dup -1 = if EREAD throw then dup 0= if true to end_of_file? then read_buffer .len ! ; get-current ( -- wid ) previous definitions >search ( wid -- ) : reset_line_reading 0 to read_buffer_ptr ; : read_line line_buffer strfree skip_newlines begin read_from_buffer refill_required? while refill_buffer repeat ; only forth also support-functions definitions \ Conf file line parser: \ ::= '='[] | \ [] \ ::= {||'_'} \ ::= '"'{|'\'}'"' | \ ::= ASCII 32 to 126, except '\' and '"' \ ::= '#'{} \ \ exported: \ line_pointer \ process_conf 0 value line_pointer vocabulary file-processing also file-processing definitions \ parser functions \ \ exported: \ get_assignment vocabulary parser also parser definitions 0 value parsing_function 0 value end_of_line : end_of_line? line_pointer end_of_line = ; \ classifiers for various character classes in the input line : letter? line_pointer c@ >r r@ [char] A >= r@ [char] Z <= and r@ [char] a >= r> [char] z <= and or ; : digit? line_pointer c@ >r r@ [char] - = r@ [char] 0 >= r> [char] 9 <= and or ; : quote? line_pointer c@ [char] " = ; : assignment_sign? line_pointer c@ [char] = = ; : comment? line_pointer c@ [char] # = ; : space? line_pointer c@ bl = line_pointer c@ tab = or ; : backslash? line_pointer c@ [char] \ = ; : underscore? line_pointer c@ [char] _ = ; : dot? line_pointer c@ [char] . = ; \ manipulation of input line : skip_character line_pointer char+ to line_pointer ; : skip_to_end_of_line end_of_line to line_pointer ; : eat_space begin end_of_line? if 0 else space? then while skip_character repeat ; : parse_name ( -- addr len ) line_pointer begin end_of_line? if 0 else letter? digit? underscore? dot? or or or then while skip_character repeat line_pointer over - strdup ; : remove_backslashes { addr len | addr' len' -- addr' len' } len allocate if ENOMEM throw then to addr' addr >r begin addr c@ [char] \ <> if addr c@ addr' len' + c! len' char+ to len' then addr char+ to addr r@ len + addr = until r> drop addr' len' ; : parse_quote ( -- addr len ) line_pointer skip_character end_of_line? if ESYNTAX throw then begin quote? 0= while backslash? if skip_character end_of_line? if ESYNTAX throw then then skip_character end_of_line? if ESYNTAX throw then repeat skip_character line_pointer over - remove_backslashes ; : read_name parse_name ( -- addr len ) name_buffer strset ; : read_value quote? if parse_quote ( -- addr len ) else parse_name ( -- addr len ) then value_buffer strset ; : comment skip_to_end_of_line ; : white_space_4 eat_space comment? if ['] comment to parsing_function exit then end_of_line? 0= if ESYNTAX throw then ; : variable_value read_value ['] white_space_4 to parsing_function ; : white_space_3 eat_space letter? digit? quote? or or if ['] variable_value to parsing_function exit then ESYNTAX throw ; : assignment_sign skip_character ['] white_space_3 to parsing_function ; : white_space_2 eat_space assignment_sign? if ['] assignment_sign to parsing_function exit then ESYNTAX throw ; : variable_name read_name ['] white_space_2 to parsing_function ; : white_space_1 eat_space letter? if ['] variable_name to parsing_function exit then comment? if ['] comment to parsing_function exit then end_of_line? 0= if ESYNTAX throw then ; get-current ( -- wid ) previous definitions >search ( wid -- ) : get_assignment line_buffer strget + to end_of_line line_buffer .addr @ to line_pointer ['] white_space_1 to parsing_function begin end_of_line? 0= while parsing_function execute repeat parsing_function ['] comment = parsing_function ['] white_space_1 = parsing_function ['] white_space_4 = or or 0= if ESYNTAX throw then ; only forth also support-functions also file-processing definitions \ Process line : assignment_type? ( addr len -- flag ) name_buffer strget compare 0= ; : suffix_type? ( addr len -- flag ) name_buffer .len @ over <= if 2drop false exit then name_buffer .len @ over - name_buffer .addr @ + over compare 0= ; : loader_conf_files? s" loader_conf_files" assignment_type? ; : nextboot_flag? s" nextboot_enable" assignment_type? ; : nextboot_conf? s" nextboot_conf" assignment_type? ; : verbose_flag? s" verbose_loading" assignment_type? ; : execute? s" exec" assignment_type? ; : module_load? load_module_suffix suffix_type? ; : module_loadname? module_loadname_suffix suffix_type? ; : module_type? module_type_suffix suffix_type? ; : module_args? module_args_suffix suffix_type? ; : module_beforeload? module_beforeload_suffix suffix_type? ; : module_afterload? module_afterload_suffix suffix_type? ; : module_loaderror? module_loaderror_suffix suffix_type? ; \ build a 'set' statement and execute it : set_environment_variable name_buffer .len @ value_buffer .len @ + 5 chars + \ size of result string allocate if ENOMEM throw then dup 0 \ start with an empty string and append the pieces s" set " strcat name_buffer strget strcat s" =" strcat value_buffer strget strcat ['] evaluate catch if 2drop free drop ESETERROR throw else free-memory then ; : set_conf_files set_environment_variable s" loader_conf_files" getenv conf_files string= ; : set_nextboot_conf value_buffer strget unquote nextboot_conf_file string= ; : append_to_module_options_list ( addr -- ) module_options @ 0= if dup module_options ! last_module_option ! else dup last_module_option @ module.next ! last_module_option ! then ; : set_module_name { addr -- } \ check leaks name_buffer strget addr module.name string= ; : yes_value? value_buffer strget \ XXX could use unquote 2dup s' "YES"' compare >r 2dup s' "yes"' compare >r 2dup s" YES" compare >r s" yes" compare r> r> r> and and and 0= ; : find_module_option ( -- addr | 0 ) \ return ptr to entry matching name_buffer module_options @ begin dup while dup module.name strget name_buffer strget compare 0= if exit then module.next @ repeat ; : new_module_option ( -- addr ) sizeof module allocate if ENOMEM throw then dup sizeof module erase dup append_to_module_options_list dup set_module_name ; : get_module_option ( -- addr ) find_module_option ?dup 0= if new_module_option then ; : set_module_flag name_buffer .len @ load_module_suffix nip - name_buffer .len ! yes_value? get_module_option module.flag ! ; : set_module_args name_buffer .len @ module_args_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.args string= ; : set_module_loadname name_buffer .len @ module_loadname_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.loadname string= ; : set_module_type name_buffer .len @ module_type_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.type string= ; : set_module_beforeload name_buffer .len @ module_beforeload_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.beforeload string= ; : set_module_afterload name_buffer .len @ module_afterload_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.afterload string= ; : set_module_loaderror name_buffer .len @ module_loaderror_suffix nip - name_buffer .len ! value_buffer strget unquote get_module_option module.loaderror string= ; : set_nextboot_flag yes_value? to nextboot? ; : set_verbose yes_value? to verbose? ; : execute_command value_buffer strget unquote ['] evaluate catch if EEXEC throw then ; : process_assignment name_buffer .len @ 0= if exit then loader_conf_files? if set_conf_files exit then nextboot_flag? if set_nextboot_flag exit then nextboot_conf? if set_nextboot_conf exit then verbose_flag? if set_verbose exit then execute? if execute_command exit then module_load? if set_module_flag exit then module_loadname? if set_module_loadname exit then module_type? if set_module_type exit then module_args? if set_module_args exit then module_beforeload? if set_module_beforeload exit then module_afterload? if set_module_afterload exit then module_loaderror? if set_module_loaderror exit then set_environment_variable ; \ free_buffer ( -- ) \ \ Free some pointers if needed. The code then tests for errors \ in freeing, and throws an exception if needed. If a pointer is \ not allocated, it's value (0) is used as flag. : free_buffers name_buffer strfree value_buffer strfree ; \ Higher level file processing get-current ( -- wid ) previous definitions >search ( wid -- ) : process_conf begin end_of_file? 0= while free_buffers read_line get_assignment ['] process_assignment catch ['] free_buffers catch swap throw throw repeat ; : peek_file ( addr len -- ) 0 to end_of_file? reset_line_reading O_RDONLY fopen fd ! fd @ -1 = if EOPEN throw then free_buffers read_line get_assignment ['] process_assignment catch ['] free_buffers catch fd @ fclose swap throw throw ; only forth also support-functions definitions \ Interface to loading conf files : load_conf ( addr len -- ) 0 to end_of_file? reset_line_reading O_RDONLY fopen fd ! fd @ -1 = if EOPEN throw then ['] process_conf catch fd @ fclose throw ; : print_line line_buffer strtype cr ; : print_syntax_error line_buffer strtype cr line_buffer .addr @ begin line_pointer over <> while bl emit char+ repeat drop ." ^" cr ; \ Debugging support functions only forth definitions also support-functions : test-file ['] load_conf catch dup . ESYNTAX = if cr print_syntax_error then ; \ find a module name, leave addr on the stack (0 if not found) : find-module ( -- ptr | 0 ) bl parse ( addr len ) module_options @ >r ( store current pointer ) begin r@ while 2dup ( addr len addr len ) r@ module.name strget compare 0= if drop drop r> exit then ( found it ) r> module.next @ >r repeat type ." was not found" cr r> ; : show-nonempty ( addr len mod -- ) strget dup verbose? or if 2swap type type cr else drop drop drop drop then ; : show-one-module { addr -- addr } ." Name: " addr module.name strtype cr s" Path: " addr module.loadname show-nonempty s" Type: " addr module.type show-nonempty s" Flags: " addr module.args show-nonempty s" Before load: " addr module.beforeload show-nonempty s" After load: " addr module.afterload show-nonempty s" Error: " addr module.loaderror show-nonempty ." Status: " addr module.flag @ if ." Load" else ." Don't load" then cr cr addr ; : show-module-options module_options @ begin ?dup while show-one-module module.next @ repeat ; : free-one-module { addr -- addr } addr module.name strfree addr module.loadname strfree addr module.type strfree addr module.args strfree addr module.beforeload strfree addr module.afterload strfree addr module.loaderror strfree addr ; : free-module-options module_options @ begin ?dup while free-one-module dup module.next @ swap free-memory repeat 0 module_options ! 0 last_module_option ! ; only forth also support-functions definitions \ Variables used for processing multiple conf files string current_file_name_ref \ used to print the file name \ Indicates if any conf file was succesfully read 0 value any_conf_read? \ loader_conf_files processing support functions : get_conf_files ( -- addr len ) \ put addr/len on stack, reset var conf_files strget 0 0 conf_files strset ; : skip_leading_spaces { addr len pos -- addr len pos' } begin pos len = if 0 else addr pos + c@ bl = then while pos char+ to pos repeat addr len pos ; \ return the file name at pos, or free the string if nothing left : get_file_name { addr len pos -- addr len pos' addr' len' || 0 } pos len = if addr free abort" Fatal error freeing memory" 0 exit then pos >r begin \ stay in the loop until have chars and they are not blank pos len = if 0 else addr pos + c@ bl <> then while pos char+ to pos repeat addr len pos addr r@ + pos r> - ; : get_next_file ( addr len ptr -- addr len ptr' addr' len' | 0 ) skip_leading_spaces get_file_name ; : print_current_file current_file_name_ref strtype ; : process_conf_errors dup 0= if true to any_conf_read? drop exit then >r 2drop r> dup ESYNTAX = if ." Warning: syntax error on file " print_current_file cr print_syntax_error drop exit then dup ESETERROR = if ." Warning: bad definition on file " print_current_file cr print_line drop exit then dup EREAD = if ." Warning: error reading file " print_current_file cr drop exit then dup EOPEN = if verbose? if ." Warning: unable to open file " print_current_file cr then drop exit then dup EFREE = abort" Fatal error freeing memory" dup ENOMEM = abort" Out of memory" throw \ Unknown error -- pass ahead ; \ Process loader_conf_files recursively \ Interface to loader_conf_files processing : include_conf_files get_conf_files 0 ( addr len offset ) begin get_next_file ?dup ( addr len 1 | 0 ) while current_file_name_ref strref ['] load_conf catch process_conf_errors conf_files .addr @ if recurse then repeat ; : get_nextboot_conf_file ( -- addr len ) nextboot_conf_file strget ; : rewrite_nextboot_file ( -- ) get_nextboot_conf_file O_WRONLY fopen fd ! fd @ -1 = if EOPEN throw then fd @ s' nextboot_enable="NO" ' fwrite ( fd buf len -- nwritten ) drop fd @ fclose ; : include_nextboot_file ( -- ) get_nextboot_conf_file ['] peek_file catch if 2drop then nextboot? if get_nextboot_conf_file current_file_name_ref strref ['] load_conf catch process_conf_errors ['] rewrite_nextboot_file catch if 2drop then then ; \ Module loading functions : load_parameters { addr -- addr addrN lenN ... addr1 len1 N } addr addr module.args strget addr module.loadname .len @ if addr module.loadname strget else addr module.name strget then addr module.type .len @ if addr module.type strget s" -t " 4 ( -t type name flags ) else 2 ( name flags ) then ; : before_load ( addr -- addr ) dup module.beforeload .len @ if dup module.beforeload strget ['] evaluate catch if EBEFORELOAD throw then then ; : after_load ( addr -- addr ) dup module.afterload .len @ if dup module.afterload strget ['] evaluate catch if EAFTERLOAD throw then then ; : load_error ( addr -- addr ) dup module.loaderror .len @ if dup module.loaderror strget evaluate \ This we do not intercept so it can throw errors then ; : pre_load_message ( addr -- addr ) verbose? if dup module.name strtype ." ..." then ; : load_error_message verbose? if ." failed!" cr then ; : load_succesful_message verbose? if ." ok" cr then ; : load_module load_parameters load ; : process_module ( addr -- addr ) pre_load_message before_load begin ['] load_module catch if dup module.loaderror .len @ if load_error \ Command should return a flag! else load_error_message true \ Do not retry then else after_load load_succesful_message true \ Succesful, do not retry then until ; : process_module_errors ( addr ior -- ) dup EBEFORELOAD = if drop ." Module " dup module.name strtype dup module.loadname .len @ if ." (" dup module.loadname strtype ." )" then cr ." Error executing " dup module.beforeload strtype cr \ XXX there was a typo here abort then dup EAFTERLOAD = if drop ." Module " dup module.name .addr @ over module.name .len @ type dup module.loadname .len @ if ." (" dup module.loadname strtype ." )" then cr ." Error executing " dup module.afterload strtype cr abort then throw \ Don't know what it is all about -- pass ahead ; \ Module loading interface \ scan the list of modules, load enabled ones. : load_modules ( -- ) ( throws: abort & user-defined ) module_options @ ( list_head ) begin ?dup while dup module.flag @ if ['] process_module catch process_module_errors then module.next @ repeat ; \ h00h00 magic used to try loading either a kernel with a given name, \ or a kernel with the default name in a directory of a given name \ (the pain!) : bootpath s" /boot/" ; : modulepath s" module_path" ; \ Functions used to save and restore module_path's value. : saveenv ( addr len | -1 -- addr' len | 0 -1 ) dup -1 = if 0 swap exit then strdup ; : freeenv ( addr len | 0 -1 ) -1 = if drop else free abort" Freeing error" then ; : restoreenv ( addr len | 0 -1 -- ) dup -1 = if ( it wasn't set ) 2drop modulepath unsetenv else over >r modulepath setenv r> free abort" Freeing error" then ; : clip_args \ Drop second string if only one argument is passed 1 = if 2swap 2drop 1 else 2 then ; also builtins \ Parse filename from a semicolon-separated list \ replacement, not working yet : newparse-; { addr len | a1 -- a' len-x addr x } addr len [char] ; strchr dup if ( a1 len1 ) swap to a1 ( store address ) 1 - a1 @ 1 + swap ( remove match ) addr a1 addr - else 0 0 addr len then ; : parse-; ( addr len -- addr' len-x addr x ) over 0 2swap ( addr 0 addr len ) begin dup 0 <> ( addr 0 addr len ) while over c@ [char] ; <> ( addr 0 addr len flag ) while 1- swap 1+ swap 2swap 1+ 2swap repeat then dup 0 <> if 1- swap 1+ swap then 2swap ; \ Try loading one of multiple kernels specified : try_multiple_kernels ( addr len addr' len' args -- flag ) >r begin parse-; 2>r 2over 2r> r@ clip_args s" DEBUG" getenv? if s" echo Module_path: ${module_path}" evaluate ." Kernel : " >r 2dup type r> cr dup 2 = if ." Flags : " >r 2over type r> cr then then 1 load while dup 0= until 1 >r \ Failure else 0 >r \ Success then 2drop 2drop r> r> drop ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ \ Flags are passed, if available. If not, dummy values must be given. \ \ The kernel gets loaded from the current module_path. : load_a_kernel ( flags len 1 | x x 0 -- flag ) local args 2local flags 0 0 2local kernel end-locals \ Check if a default kernel name exists at all, exits if not s" bootfile" getenv dup -1 <> if to kernel flags kernel args 1+ try_multiple_kernels dup 0= if exit then then drop s" kernel" getenv dup -1 <> if to kernel else drop 1 exit \ Failure then \ Try all default kernel names flags kernel args 1+ try_multiple_kernels ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ \ Flags are passed, if provided. \ \ The kernel will be loaded from a directory computed from the \ path given. Two directories will be tried in the following order: \ \ 1. /boot/path \ 2. path \ \ The module_path variable is overridden if load is succesful, by \ prepending the successful path. : load_from_directory ( path len 1 | flags len' path len 2 -- flag ) local args 2local path args 1 = if 0 0 then 2local flags 0 0 2local oldmodulepath \ like a string 0 0 2local newmodulepath \ like a string end-locals \ Set the environment variable module_path, and try loading \ the kernel again. modulepath getenv saveenv to oldmodulepath \ Try prepending /boot/ first bootpath nip path nip + \ total length oldmodulepath nip dup -1 = if drop else 1+ + \ add oldpath -- XXX why the 1+ ? then allocate if ( out of memory ) 1 exit then \ XXX throw ? 0 bootpath strcat path strcat 2dup to newmodulepath modulepath setenv \ Try all default kernel names flags args 1- load_a_kernel 0= if ( success ) oldmodulepath nip -1 <> if newmodulepath s" ;" strcat oldmodulepath strcat modulepath setenv newmodulepath drop free-memory oldmodulepath drop free-memory then 0 exit then \ Well, try without the prepended /boot/ path newmodulepath drop swap move newmodulepath drop path nip 2dup to newmodulepath modulepath setenv \ Try all default kernel names flags args 1- load_a_kernel if ( failed once more ) oldmodulepath restoreenv newmodulepath drop free-memory 1 else oldmodulepath nip -1 <> if newmodulepath s" ;" strcat oldmodulepath strcat modulepath setenv newmodulepath drop free-memory oldmodulepath drop free-memory then 0 then ; \ Try to load a kernel; the kernel name is taken from one of \ the following lists, as ordered: \ \ 1. The "bootfile" environment variable \ 2. The "kernel" environment variable \ 3. The "path" argument \ \ Flags are passed, if provided. \ \ The kernel will be loaded from a directory computed from the \ path given. Two directories will be tried in the following order: \ \ 1. /boot/path \ 2. path \ \ Unless "path" is meant to be kernel name itself. In that case, it \ will first be tried as a full path, and, next, search on the \ directories pointed by module_path. \ \ The module_path variable is overridden if load is succesful, by \ prepending the successful path. : load_directory_or_file ( path len 1 | flags len' path len 2 -- flag ) local args 2local path args 1 = if 0 0 then 2local flags end-locals \ First, assume path is an absolute path to a directory flags path args clip_args load_from_directory dup 0= if exit else drop then \ Next, assume path points to the kernel flags path args try_multiple_kernels ; : initialize ( addr len -- ) strdup conf_files strset ; : kernel_options ( -- addr len 1 | 0 ) s" kernel_options" getenv dup -1 = if drop 0 else 1 then ; : standard_kernel_search ( flags 1 | 0 -- flag ) local args args 0= if 0 0 then 2local flags s" kernel" getenv dup -1 = if 0 swap then 2local path end-locals path nip -1 = if ( there isn't a "kernel" environment variable ) flags args load_a_kernel else flags path args 1+ clip_args load_directory_or_file then ; : load_kernel ( -- ) ( throws: abort ) kernel_options standard_kernel_search abort" Unable to load a kernel!" ; +: load_xen ( -- ) + s" xen_kernel" getenv dup -1 <> if + 1 1 load + else + drop + 0 + then +; + +: load_xen_throw ( -- ) ( throws: abort ) + load_xen + abort" Unable to load Xen!" +; + : set_defaultoptions ( -- ) s" kernel_options" getenv dup -1 = if drop else s" temp_options" setenv then ; \ pick the i-th argument, i starts at 0 : argv[] ( aN uN ... a1 u1 N i -- aN uN ... a1 u1 N ai+1 ui+1 ) 2dup = if 0 0 exit then \ out of range dup >r 1+ 2* ( skip N and ui ) pick r> 1+ 2* ( skip N and ai ) pick ; : drop_args ( aN uN ... a1 u1 N -- ) 0 ?do 2drop loop ; : argc dup ; : queue_argv ( aN uN ... a1 u1 N a u -- a u aN uN ... a1 u1 N+1 ) >r over 2* 1+ -roll r> over 2* 1+ -roll 1+ ; : unqueue_argv ( aN uN ... a1 u1 N -- aN uN ... a2 u2 N-1 a1 u1 ) 1- -rot ; \ compute the length of the buffer including the spaces between words : strlen(argv) ( aN uN .. a1 u1 N -- aN uN .. a1 u1 N len ) dup 0= if 0 exit then 0 >r \ Size 0 >r \ Index begin argc r@ <> while r@ argv[] nip r> r> rot + 1+ >r 1+ >r repeat r> drop r> ; : concat_argv ( aN uN ... a1 u1 N -- a u ) strlen(argv) allocate if ENOMEM throw then 0 2>r ( save addr 0 on return stack ) begin dup while unqueue_argv ( ... N a1 u1 ) 2r> 2swap ( old a1 u1 ) strcat s" " strcat ( append one space ) \ XXX this gives a trailing space 2>r ( store string on the result stack ) repeat drop_args 2r> ; : set_tempoptions ( addrN lenN ... addr1 len1 N -- addr len 1 | 0 ) \ Save the first argument, if it exists and is not a flag argc if 0 argv[] drop c@ [char] - <> if unqueue_argv 2>r \ Filename 1 >r \ Filename present else 0 >r \ Filename not present then else 0 >r \ Filename not present then \ If there are other arguments, assume they are flags ?dup if concat_argv 2dup s" temp_options" setenv drop free if EFREE throw then else set_defaultoptions then \ Bring back the filename, if one was provided r> if 2r> 1 else 0 then ; : get_arguments ( -- addrN lenN ... addr1 len1 N ) 0 begin \ Get next word on the command line parse-word ?dup while queue_argv repeat drop ( empty string ) ; : load_kernel_and_modules ( args -- flag ) set_tempoptions argc >r s" temp_options" getenv dup -1 <> if queue_argv else drop then - r> if ( a path was passed ) - load_directory_or_file - else - standard_kernel_search + load_xen + ?dup 0= if ( success ) + r> if ( a path was passed ) + load_directory_or_file + else + standard_kernel_search + then + ?dup 0= if ['] load_modules catch then then - ?dup 0= if ['] load_modules catch then ; only forth definitions Index: stable/10/sys/boot/i386/libi386/Makefile =================================================================== --- stable/10/sys/boot/i386/libi386/Makefile (revision 294416) +++ stable/10/sys/boot/i386/libi386/Makefile (revision 294417) @@ -1,72 +1,73 @@ # $FreeBSD$ # LIB= i386 INTERNALLIB= SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \ comconsole.c devicename.c elf32_freebsd.c \ - elf64_freebsd.c \ + elf64_freebsd.c multiboot.c multiboot_tramp.S \ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c .PATH: ${.CURDIR}/../../zfs SRCS+= devicename_stubs.c # Enable PXE TFTP or NFS support, not both. .if defined(LOADER_TFTP_SUPPORT) CFLAGS+= -DLOADER_TFTP_SUPPORT .else CFLAGS+= -DLOADER_NFS_SUPPORT .endif BOOT_COMCONSOLE_PORT?= 0x3f8 CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT} BOOT_COMCONSOLE_SPEED?= 9600 CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} .ifdef(BOOT_BIOSDISK_DEBUG) # Make the disk code more talkative CFLAGS+= -DDISK_DEBUG .endif .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS .if defined(BOOT_LITTLE_ENDIAN_UUID) # Use little-endian UUID format as defined in SMBIOS 2.6. CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID .endif .endif # Include simple terminal emulation (cons25-compatible) CFLAGS+= -DTERM_EMU # XXX: make alloca() useable CFLAGS+= -Dalloca=__builtin_alloca CFLAGS+= -I${.CURDIR}/../../common -I${.CURDIR}/../common \ -I${.CURDIR}/../btx/lib \ -I${.CURDIR}/../../../contrib/dev/acpica/include \ -I${.CURDIR}/../../.. -I. # the location of libstand CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ # Suppress warning from clang for FreeBSD %b and %D formats CFLAGS+= -fformat-extensions .if ${MACHINE_CPUARCH} == "amd64" CLEANFILES+= machine machine: ln -sf ${.CURDIR}/../../../i386/include machine .endif .include # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS} +CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS} CFLAGS+= ${CFLAGS.${.IMPSRC:T}} .if ${MACHINE_CPUARCH} == "amd64" beforedepend ${OBJS}: machine .endif Index: stable/10/sys/boot/i386/libi386/bootinfo64.c =================================================================== --- stable/10/sys/boot/i386/libi386/bootinfo64.c (revision 294416) +++ stable/10/sys/boot/i386/libi386/bootinfo64.c (revision 294417) @@ -1,254 +1,263 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include +#include #include #include #include "bootstrap.h" #include "libi386.h" #include "btxv86.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 */ #define COPY32(v, a, c) { \ u_int32_t x = (v); \ if (c) \ i386_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) \ i386_copyin(s, a, strlen(s) + 1); \ a += roundup(strlen(s) + 1, sizeof(u_int64_t));\ } #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) \ i386_copyin(&s, a, sizeof(s)); \ a += roundup(sizeof(s), sizeof(u_int64_t)); \ } #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) \ i386_copyin(mm->md_data, a, mm->md_size); \ a += roundup(mm->md_size, sizeof(u_int64_t));\ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } static vm_offset_t bi_copymodules64(vm_offset_t addr) { struct preloaded_file *fp; struct file_metadata *md; int c; u_int64_t v; 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); v = fp->f_addr; MOD_ADDR(addr, v, c); v = fp->f_size; MOD_SIZE(addr, v, 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); } /* * Check to see if this CPU supports long mode. */ static int bi_checkcpu(void) { char *cpu_vendor; int vendor[3]; int eflags; unsigned int regs[4]; /* Check for presence of "cpuid". */ eflags = read_eflags(); write_eflags(eflags ^ PSL_ID); if (!((eflags ^ read_eflags()) & PSL_ID)) return (0); /* Fetch the vendor string. */ do_cpuid(0, regs); vendor[0] = regs[1]; vendor[1] = regs[3]; vendor[2] = regs[2]; cpu_vendor = (char *)vendor; /* Check for vendors that support AMD features. */ if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 && strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 && strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0) return (0); /* Has to support AMD features. */ do_cpuid(0x80000000, regs); if (!(regs[0] >= 0x80000001)) return (0); /* Check for long mode. */ do_cpuid(0x80000001, regs); return (regs[3] & AMDID_LM); } /* * Load the information expected by an amd64 kernel. * * - The 'boothowto' argument is constructed * - The 'bootdev' argument is constructed * - The 'bootinfo' struct is constructed, and copied into the kernel space. * - The kernel environment is copied into kernel space. * - Module metadata are formatted and placed in kernel space. */ int -bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) +bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, + vm_offset_t *kernendp, int add_smap) { struct preloaded_file *xp, *kfp; struct i386_devdesc *rootdev; struct file_metadata *md; - vm_offset_t addr; u_int64_t kernend; u_int64_t envp; + u_int64_t module; vm_offset_t size; char *rootdevname; int howto; if (!bi_checkcpu()) { printf("CPU doesn't support long mode\n"); return (EINVAL); } howto = bi_getboothowto(args); /* * Allow the environment variable 'rootdev' to override the supplied device * This should perhaps go to MI code and/or have $rootdev tested/set by * MI code before launching the kernel. */ rootdevname = getenv("rootdev"); i386_getdev((void **)(&rootdev), rootdevname, NULL); if (rootdev == NULL) { /* bad $rootdev/$currdev */ printf("can't determine root device\n"); return(EINVAL); } /* Try reading the /etc/fstab file to select the root device */ getrootmount(i386_fmtdev((void *)rootdev)); - /* find the last module in the chain */ - addr = 0; - for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { - if (addr < (xp->f_addr + xp->f_size)) - addr = xp->f_addr + xp->f_size; + if (addr == 0) { + /* find the last module in the chain */ + for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { + if (addr < (xp->f_addr + xp->f_size)) + addr = xp->f_addr + xp->f_size; + } } /* pad to a page boundary */ addr = roundup(addr, PAGE_SIZE); - /* copy our environment */ - envp = addr; - addr = bi_copyenv(addr); + /* place the metadata before anything */ + module = *modulep = addr; - /* pad to a page boundary */ - addr = roundup(addr, PAGE_SIZE); - kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf64 kernel"); if (kfp == NULL) panic("can't find kernel file"); kernend = 0; /* fill it in later */ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); - bios_addsmapdata(kfp); + file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module); + if (add_smap != 0) + bios_addsmapdata(kfp); - /* Figure out the size and location of the metadata */ - *modulep = addr; size = bi_copymodules64(0); - kernend = roundup(addr + size, PAGE_SIZE); + + /* copy our environment */ + envp = roundup(addr + size, PAGE_SIZE); + addr = bi_copyenv(envp); + + /* set kernend */ + kernend = roundup(addr, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); + /* patch MODINFOMD_ENVP */ + md = file_findmetadata(kfp, MODINFOMD_ENVP); + bcopy(&envp, md->md_data, sizeof envp); + /* copy module list and metadata */ - (void)bi_copymodules64(addr); + (void)bi_copymodules64(*modulep); return(0); } Index: stable/10/sys/boot/i386/libi386/elf64_freebsd.c =================================================================== --- stable/10/sys/boot/i386/libi386/elf64_freebsd.c (revision 294416) +++ stable/10/sys/boot/i386/libi386/elf64_freebsd.c (revision 294417) @@ -1,126 +1,126 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); #define __ELF_WORD_SIZE 64 #include #include #include #include #include #include #include #include "bootstrap.h" #include "libi386.h" #include "btxv86.h" static int elf64_exec(struct preloaded_file *amp); static int elf64_obj_exec(struct preloaded_file *amp); struct file_format amd64_elf = { elf64_loadfile, elf64_exec }; struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec }; #define PG_V 0x001 #define PG_RW 0x002 #define PG_U 0x004 #define PG_PS 0x080 typedef u_int64_t p4_entry_t; typedef u_int64_t p3_entry_t; typedef u_int64_t p2_entry_t; extern p4_entry_t PT4[]; extern p3_entry_t PT3[]; extern p2_entry_t PT2[]; u_int32_t entry_hi; u_int32_t entry_lo; extern void amd64_tramp(); /* * 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; int i; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); - err = bi_load64(fp->f_args, &modulep, &kernend); + err = bi_load64(fp->f_args, 0, &modulep, &kernend, 1); if (err != 0) return(err); bzero(PT4, PAGE_SIZE); bzero(PT3, PAGE_SIZE); bzero(PT2, PAGE_SIZE); /* * 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 < 512; i++) { /* Each slot of the level 4 pages points to the same level 3 page */ PT4[i] = (p4_entry_t)VTOP((uintptr_t)&PT3[0]); PT4[i] |= PG_V | PG_RW | PG_U; /* Each slot of the level 3 pages points to the same level 2 page */ PT3[i] = (p3_entry_t)VTOP((uintptr_t)&PT2[0]); PT3[i] |= PG_V | PG_RW | PG_U; /* The level 2 page slots are mapped with 2MB pages for 1GB. */ PT2[i] = i * (2 * 1024 * 1024); PT2[i] |= PG_V | PG_RW | PG_PS | PG_U; } entry_lo = ehdr->e_entry & 0xffffffff; entry_hi = (ehdr->e_entry >> 32) & 0xffffffff; #ifdef DEBUG printf("Start @ %#llx ...\n", ehdr->e_entry); #endif dev_cleanup(); __exec((void *)VTOP(amd64_tramp), modulep, kernend); panic("exec returned"); } static int elf64_obj_exec(struct preloaded_file *fp) { return (EFTYPE); } Index: stable/10/sys/boot/i386/libi386/libi386.h =================================================================== --- stable/10/sys/boot/i386/libi386/libi386.h (revision 294416) +++ stable/10/sys/boot/i386/libi386/libi386.h (revision 294417) @@ -1,122 +1,123 @@ /*- * 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. * * $FreeBSD$ */ /* * i386 fully-qualified device descriptor. * Note, this must match the 'struct devdesc' declaration * in bootstrap.h and also with struct zfs_devdesc for zfs * support. */ struct i386_devdesc { struct devsw *d_dev; int d_type; int d_unit; union { struct { void *data; int slice; int partition; off_t offset; } biosdisk; struct { void *data; } bioscd; struct { void *data; uint64_t pool_guid; uint64_t root_guid; } zfs; } d_kind; }; int i386_getdev(void **vdev, const char *devspec, const char **path); char *i386_fmtdev(void *vdev); int i386_setcurrdev(struct env_var *ev, int flags, const void *value); extern struct devdesc currdev; /* our current device */ #define MAXDEV 31 /* maximum number of distinct devices */ #define MAXBDDEV MAXDEV /* exported devices XXX rename? */ extern struct devsw bioscd; extern struct devsw biosdisk; extern struct devsw pxedisk; extern struct fs_ops pxe_fsops; int bc_add(int biosdev); /* Register CD booted from. */ int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ int bc_bios2unit(int biosdev); /* xlate BIOS device -> bioscd unit */ int bc_unit2bios(int unit); /* xlate bioscd unit -> BIOS device */ uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */ int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */ int bd_unit2bios(int unit); /* xlate biosdisk unit -> BIOS device */ int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len); ssize_t i386_readin(const int fd, vm_offset_t dest, const size_t len); struct preloaded_file; void bios_addsmapdata(struct preloaded_file *); void bios_getsmap(void); void bios_getmem(void); extern uint32_t bios_basemem; /* base memory in bytes */ extern uint32_t bios_extmem; /* extended memory in bytes */ extern vm_offset_t memtop; /* last address of physical memory + 1 */ extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */ /* when heap is at the top of */ /* extended memory; for other cases */ /* just the same as memtop */ extern uint32_t high_heap_size; /* extended memory region available */ extern vm_offset_t high_heap_base; /* for use as the heap */ int biospci_find_devclass(uint32_t class, int index, uint32_t *locator); int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val); int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val); uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function); void biosacpi_detect(void); int i386_autoload(void); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); -int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend); +int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, + vm_offset_t *kernend, int add_smap); void pxe_enable(void *pxeinfo); Index: stable/10/sys/boot/i386/libi386/multiboot.c =================================================================== --- stable/10/sys/boot/i386/libi386/multiboot.c (nonexistent) +++ stable/10/sys/boot/i386/libi386/multiboot.c (revision 294417) @@ -0,0 +1,425 @@ +/*- + * Copyright (c) 2014 Roger Pau MonnĂ© + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This multiboot implementation only implements a subset of the full + * multiboot specification in order to be able to boot Xen and a + * FreeBSD Dom0. Trying to use it to boot other multiboot compliant + * kernels will most surely fail. + * + * The full multiboot specification can be found here: + * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#define _MACHINE_ELF_WANT_32BIT +#include +#include +#include + +#include "bootstrap.h" +#include "multiboot.h" +#include "../i386/libi386/libi386.h" +#include "../i386/btx/lib/btxv86.h" + +#define MULTIBOOT_SUPPORTED_FLAGS \ + (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) +#define NUM_MODULES 2 +#define METADATA_FIXED_SIZE (PAGE_SIZE*4) +#define METADATA_MODULE_SIZE PAGE_SIZE + +#define METADATA_RESV_SIZE(mod_num) \ + roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE) + +extern int elf32_loadfile_raw(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot); +extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest); +extern int elf64_obj_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result); + +static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **); +static int multiboot_exec(struct preloaded_file *); + +static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **); +static int multiboot_obj_exec(struct preloaded_file *fp); + +struct file_format multiboot = { multiboot_loadfile, multiboot_exec }; +struct file_format multiboot_obj = + { multiboot_obj_loadfile, multiboot_obj_exec }; + +extern void multiboot_tramp(); + +static const char mbl_name[] = "FreeBSD Loader"; + +static int +num_modules(struct preloaded_file *kfp) +{ + struct kernel_module *kmp; + int mod_num = 0; + + for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next) + mod_num++; + + return (mod_num); +} + +static vm_offset_t +max_addr(void) +{ + struct preloaded_file *fp; + vm_offset_t addr = 0; + + for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { + if (addr < (fp->f_addr + fp->f_size)) + addr = fp->f_addr + fp->f_size; + } + + return (addr); +} + +static int +multiboot_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result) +{ + uint32_t *magic; + int i, error; + caddr_t header_search; + ssize_t search_size; + int fd; + struct multiboot_header *header; + char *cmdline; + + /* + * Read MULTIBOOT_SEARCH size in order to search for the + * multiboot magic header. + */ + if (filename == NULL) + return (EFTYPE); + if ((fd = open(filename, O_RDONLY)) == -1) + return (errno); + header_search = malloc(MULTIBOOT_SEARCH); + if (header_search == NULL) { + close(fd); + return (ENOMEM); + } + search_size = read(fd, header_search, MULTIBOOT_SEARCH); + magic = (uint32_t *)header_search; + + header = NULL; + for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { + if (magic[i] == MULTIBOOT_HEADER_MAGIC) { + header = (struct multiboot_header *)&magic[i]; + break; + } + } + + if (header == NULL) { + error = EFTYPE; + goto out; + } + + /* Valid multiboot header has been found, validate checksum */ + if (header->magic + header->flags + header->checksum != 0) { + printf( + "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", + header->magic, header->flags, header->checksum); + error = EFTYPE; + goto out; + } + + if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { + printf("Unsupported multiboot flags found: 0x%x\n", + header->flags); + error = EFTYPE; + goto out; + } + + error = elf32_loadfile_raw(filename, dest, result, 1); + if (error != 0) { + printf( + "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", + error); + goto out; + } + + /* + * f_addr is already aligned to PAGE_SIZE, make sure + * f_size it's also aligned so when the modules are loaded + * they are aligned to PAGE_SIZE. + */ + (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); + +out: + free(header_search); + close(fd); + return (error); +} + +static int +multiboot_exec(struct preloaded_file *fp) +{ + vm_offset_t module_start, last_addr, metadata_size; + vm_offset_t modulep, kernend, entry; + struct file_metadata *md; + Elf_Ehdr *ehdr; + struct multiboot_info *mb_info = NULL; + struct multiboot_mod_list *mb_mod = NULL; + char *cmdline = NULL; + size_t len; + int error, mod_num; + + /* + * Don't pass the memory size found by the bootloader, the memory + * available to Dom0 will be lower than that. + */ + unsetenv("smbios.memory.enabled"); + + /* Allocate the multiboot struct and fill the basic details. */ + mb_info = malloc(sizeof(struct multiboot_info)); + if (mb_info == NULL) { + error = ENOMEM; + goto error; + } + bzero(mb_info, sizeof(struct multiboot_info)); + mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; + mb_info->mem_lower = bios_basemem / 1024; + mb_info->mem_upper = bios_extmem / 1024; + mb_info->boot_loader_name = VTOP(mbl_name); + + /* Set the Xen command line. */ + if (fp->f_args == NULL) { + /* Add the Xen command line if it is set. */ + cmdline = getenv("xen_cmdline"); + if (cmdline != NULL) { + fp->f_args = strdup(cmdline); + if (fp->f_args == NULL) { + error = ENOMEM; + goto error; + } + } + } + if (fp->f_args != NULL) { + len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; + cmdline = malloc(len); + if (cmdline == NULL) { + error = ENOMEM; + goto error; + } + snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); + mb_info->cmdline = VTOP(cmdline); + mb_info->flags |= MULTIBOOT_INFO_CMDLINE; + } + + /* Find the entry point of the Xen kernel and save it for later */ + if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { + printf("Unable to find %s entry point\n", fp->f_name); + error = EINVAL; + goto error; + } + ehdr = (Elf_Ehdr *)&(md->md_data); + entry = ehdr->e_entry & 0xffffff; + + /* + * Prepare the multiboot module list, Xen assumes the first + * module is the Dom0 kernel, and the second one is the initramfs. + * This is not optimal for FreeBSD, that doesn't have a initramfs + * but instead loads modules dynamically and creates the metadata + * info on-the-fly. + * + * As expected, the first multiboot module is going to be the + * FreeBSD kernel loaded as a raw file. The second module is going + * to contain the metadata info and the loaded modules. + * + * On native FreeBSD loads all the modules and then places the + * metadata info at the end, but this is painful when running on Xen, + * because it relocates the second multiboot module wherever it + * likes. In order to workaround this limitation the metadata + * information is placed at the start of the second module and + * the original modulep value is saved together with the other + * metadata, so we can relocate everything. + */ + fp = file_findfile(NULL, "elf kernel"); + if (fp == NULL) { + printf("No FreeBSD kernel provided, aborting\n"); + error = EINVAL; + goto error; + } + + mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); + if (mb_mod == NULL) { + error = ENOMEM; + goto error; + } + + bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); + + /* + * Calculate how much memory is needed for the metatdata. We did + * an approximation of the maximum size when loading the kernel, + * but now we know the exact size, so we can release some of this + * preallocated memory if not needed. + */ + last_addr = roundup(max_addr(), PAGE_SIZE); + mod_num = num_modules(fp); + + /* + * Place the metadata after the last used address in order to + * calculate it's size, this will not be used. + */ + error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0); + if (error != 0) { + printf("bi_load64 failed: %d\n", error); + goto error; + } + metadata_size = roundup(kernend - last_addr, PAGE_SIZE); + + /* Check that the size is not greater than what we have reserved */ + if (metadata_size > METADATA_RESV_SIZE(mod_num)) { + printf("Required memory for metadata is greater than reserved " + "space, please increase METADATA_FIXED_SIZE and " + "METADATA_MODULE_SIZE and rebuild the loader\n"); + error = ENOMEM; + goto error; + } + + /* + * This is the position where the second multiboot module + * will be placed. + */ + module_start = fp->f_addr + fp->f_size - metadata_size; + + error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0); + if (error != 0) { + printf("bi_load64 failed: %d\n", error); + goto error; + } + + mb_mod[0].mod_start = fp->f_addr; + mb_mod[0].mod_end = fp->f_addr + fp->f_size; + mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num); + + mb_mod[1].mod_start = module_start; + mb_mod[1].mod_end = last_addr; + + mb_info->mods_count = NUM_MODULES; + mb_info->mods_addr = VTOP(mb_mod); + mb_info->flags |= MULTIBOOT_INFO_MODS; + + dev_cleanup(); + __exec((void *)VTOP(multiboot_tramp), (void *)entry, + (void *)VTOP(mb_info)); + + panic("exec returned"); + +error: + if (mb_mod) + free(mb_mod); + if (mb_info) + free(mb_info); + if (cmdline) + free(cmdline); + return (error); +} + +static int +multiboot_obj_loadfile(char *filename, u_int64_t dest, + struct preloaded_file **result) +{ + struct preloaded_file *mfp, *kfp, *rfp; + struct kernel_module *kmp; + int error, mod_num; + + /* See if there's a multiboot kernel loaded */ + mfp = file_findfile(NULL, "elf multiboot kernel"); + if (mfp == NULL) + return (EFTYPE); + + /* + * We have a multiboot kernel loaded, see if there's a FreeBSD + * kernel loaded also. + */ + kfp = file_findfile(NULL, "elf kernel"); + if (kfp == NULL) { + /* + * No kernel loaded, this must be it. The kernel has to + * be loaded as a raw file, it will be processed by + * Xen and correctly loaded as an ELF file. + */ + rfp = file_loadraw(filename, "elf kernel", 0); + if (rfp == NULL) { + printf( + "Unable to load %s as a multiboot payload kernel\n", + filename); + return (EINVAL); + } + + /* Load kernel metadata... */ + setenv("kernelname", filename, 1); + error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); + if (error) { + printf("Unable to load kernel %s metadata error: %d\n", + rfp->f_name, error); + return (EINVAL); + } + + /* + * Save space at the end of the kernel in order to place + * the metadata information. We do an approximation of the + * max metadata size, this is not optimal but it's probably + * the best we can do at this point. Once all modules are + * loaded and the size of the metadata is known this + * space will be recovered if not used. + */ + mod_num = num_modules(rfp); + rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); + rfp->f_size += METADATA_RESV_SIZE(mod_num); + *result = rfp; + } else { + /* The rest should be loaded as regular modules */ + error = elf64_obj_loadfile(filename, dest, result); + if (error != 0) { + printf("Unable to load %s as an object file, error: %d", + filename, error); + return (error); + } + } + + return (0); +} + +static int +multiboot_obj_exec(struct preloaded_file *fp) +{ + + return (EFTYPE); +} Property changes on: stable/10/sys/boot/i386/libi386/multiboot.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/sys/boot/i386/libi386/multiboot.h =================================================================== --- stable/10/sys/boot/i386/libi386/multiboot.h (nonexistent) +++ stable/10/sys/boot/i386/libi386/multiboot.h (revision 294417) @@ -0,0 +1,225 @@ +/* multiboot.h - Multiboot header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY +* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* $FreeBSD$ +*/ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 8192 + +/* The magic field should contain this. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* This should be in %eax. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* The bits in the required part of flags field we don't support. */ +#define MULTIBOOT_UNSUPPORTED 0x0000fffc + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000004 + +/* Flags set in the 'flags' member of the multiboot header. */ + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +/* Flags to be set in the 'flags' member of the multiboot info structure. */ + +/* is there basic lower/upper memory information? */ +#define MULTIBOOT_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MULTIBOOT_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MULTIBOOT_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MULTIBOOT_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 + +/* is there a full memory map? */ +#define MULTIBOOT_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MULTIBOOT_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800 + +#ifndef ASM_FILE + +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ +/* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + +/* Feature flags. */ + multiboot_uint32_t flags; + +/* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; + +/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; + multiboot_uint32_t entry_addr; + +/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + multiboot_uint32_t mode_type; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ +/* Multiboot info version number */ + multiboot_uint32_t flags; + +/* Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + +/* "root" partition */ + multiboot_uint32_t boot_device; + +/* Kernel command line */ + multiboot_uint32_t cmdline; + +/* Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + +/* Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + +/* Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + +/* ROM configuration table */ + multiboot_uint32_t config_table; + +/* Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + +/* APM table */ + multiboot_uint32_t apm_table; + +/* Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; +}; +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ +/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + +/* Module command line */ + multiboot_uint32_t cmdline; + +/* padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ Property changes on: stable/10/sys/boot/i386/libi386/multiboot.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/sys/boot/i386/libi386/multiboot_tramp.S =================================================================== --- stable/10/sys/boot/i386/libi386/multiboot_tramp.S (nonexistent) +++ stable/10/sys/boot/i386/libi386/multiboot_tramp.S (revision 294417) @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2014 Roger Pau MonnĂ© + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define ASM_FILE +#include "multiboot.h" + +/* + * The multiboot specification requires the executable to be launched + * with %cs set to a flat read/execute segment with offset 0 and limit + * 0xFFFFFFFF, and the rest of the segment registers (%ds, %es, %fs, + * %gs, %ss) to flat read/write segments with the same offset and limit. + * This is already done by the BTX code before calling multiboot_tramp, + * so there is no need to do anything here. + */ + + .globl multiboot_tramp +multiboot_tramp: + /* Be sure that interrupts are disabled. */ + cli + + movl $MULTIBOOT_BOOTLOADER_MAGIC, %eax + /* Get the entry point and address of the multiboot_info parameter. */ + movl 8(%esp), %ebx + movl 4(%esp), %ecx + + call *%ecx Property changes on: stable/10/sys/boot/i386/libi386/multiboot_tramp.S ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/sys/boot/i386/loader/conf.c =================================================================== --- stable/10/sys/boot/i386/loader/conf.c (revision 294416) +++ stable/10/sys/boot/i386/loader/conf.c (revision 294417) @@ -1,159 +1,163 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include "libi386/libi386.h" #if defined(LOADER_ZFS_SUPPORT) #include "../zfs/libzfs.h" #endif /* * We could use linker sets for some or all of these, but * then we would have to control what ended up linked into * the bootstrap. So it's easier to conditionalise things * here. * * XXX rename these arrays to be consistent and less namespace-hostile * * XXX as libi386 and biosboot merge, some of these can become linker sets. */ #if defined(LOADER_NFS_SUPPORT) && defined(LOADER_TFTP_SUPPORT) #error "Cannot have both tftp and nfs support yet." #endif #if defined(LOADER_FIREWIRE_SUPPORT) extern struct devsw fwohci; #endif /* Exported for libstand */ struct devsw *devsw[] = { &bioscd, &biosdisk, #if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT) &pxedisk, #endif #if defined(LOADER_FIREWIRE_SUPPORT) &fwohci, #endif #if defined(LOADER_ZFS_SUPPORT) &zfs_dev, #endif NULL }; struct fs_ops *file_system[] = { #if defined(LOADER_ZFS_SUPPORT) &zfs_fsops, #endif &ufs_fsops, &ext2fs_fsops, &dosfs_fsops, &cd9660_fsops, #if defined(LOADER_NANDFS_SUPPORT) &nandfs_fsops, #endif #ifdef LOADER_NFS_SUPPORT &nfs_fsops, #endif #ifdef LOADER_TFTP_SUPPORT &tftp_fsops, #endif #ifdef LOADER_GZIP_SUPPORT &gzipfs_fsops, #endif #ifdef LOADER_BZIP2_SUPPORT &bzipfs_fsops, #endif #ifdef LOADER_SPLIT_SUPPORT &splitfs_fsops, #endif NULL }; /* Exported for i386 only */ /* * Sort formats so that those that can detect based on arguments * rather than reading the file go first. */ extern struct file_format i386_elf; extern struct file_format i386_elf_obj; extern struct file_format amd64_elf; extern struct file_format amd64_elf_obj; +extern struct file_format multiboot; +extern struct file_format multiboot_obj; struct file_format *file_formats[] = { + &multiboot, + &multiboot_obj, #ifdef LOADER_PREFER_AMD64 &amd64_elf, &amd64_elf_obj, #endif &i386_elf, &i386_elf_obj, #ifndef LOADER_PREFER_AMD64 &amd64_elf, &amd64_elf_obj, #endif NULL }; /* * Consoles * * We don't prototype these in libi386.h because they require * data structures from bootstrap.h as well. */ extern struct console vidconsole; extern struct console comconsole; #if defined(LOADER_FIREWIRE_SUPPORT) extern struct console dconsole; #endif extern struct console nullconsole; extern struct console spinconsole; struct console *consoles[] = { &vidconsole, &comconsole, #if defined(LOADER_FIREWIRE_SUPPORT) &dconsole, #endif &nullconsole, &spinconsole, NULL }; extern struct pnphandler isapnphandler; extern struct pnphandler biospnphandler; extern struct pnphandler biospcihandler; struct pnphandler *pnphandlers[] = { &biospnphandler, /* should go first, as it may set isapnp_readport */ &isapnphandler, &biospcihandler, NULL }; Index: stable/10 =================================================================== --- stable/10 (revision 294416) +++ stable/10 (revision 294417) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r277215,277291,277418,280953-280954