Index: stand/common/bootstrap.h =================================================================== --- stand/common/bootstrap.h +++ stand/common/bootstrap.h @@ -257,6 +257,7 @@ typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); int __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result); +size_t __elfN(obj_imagesize)(void *); int __elfN(obj_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result); int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, Index: stand/common/load_elf_obj.c =================================================================== --- stand/common/load_elf_obj.c +++ stand/common/load_elf_obj.c @@ -144,8 +144,18 @@ goto oerr; } + /* Read in the section headers. */ + ef.e_shdr = alloc_pread(ef.fd, (off_t)hdr->e_shoff, + hdr->e_shnum * hdr->e_shentsize); + if (ef.e_shdr == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadfile: read section headers failed\n"); + err = errno; + goto oerr; + } + if (archsw.arch_loadaddr != NULL) - dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); + dest = archsw.arch_loadaddr(LOAD_ELF, &ef, dest); else dest = roundup(dest, PAGE_SIZE); @@ -188,6 +198,91 @@ return(err); } +size_t +__elfN(obj_imagesize)(void *ptr) +{ + elf_file_t ef = ptr; + Elf_Ehdr *hdr; + Elf_Shdr *shdr; + size_t size; + int i, nsym, symstrindex; + + hdr = &ef->hdr; + shdr = ef->e_shdr; + size = 0; + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_size == 0) + continue; + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + case SHT_NOBITS: +#if defined(__i386__) || defined(__amd64__) + case SHT_X86_64_UNWIND: +#endif + if ((shdr[i].sh_flags & SHF_ALLOC) == 0) + break; + size = roundup(size, shdr[i].sh_addralign); + size += 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; + size += shdr[i].sh_size; + break; + } + } + if (nsym != 1) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_imagesize: file has no valid symbol table\n"); + goto out; + } + size = roundup(size, shdr[ef->symtabindex].sh_addralign); + size += 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_imagesize: file has invalid symbol strings\n"); + goto out; + } + size = roundup(size, shdr[symstrindex].sh_addralign); + size += 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_imagesize: file has no section names\n"); + goto out; + } + ef->shstrindex = hdr->e_shstrndx; + size = roundup(size, shdr[ef->shstrindex].sh_addralign); + size += shdr[ef->shstrindex].sh_size; + + /* Relocation tables. */ + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_REL: + case SHT_RELA: + if ((shdr[shdr[i].sh_info].sh_flags & SHF_ALLOC) == 0) + break; + size = roundup(size, shdr[i].sh_addralign); + size += shdr[i].sh_size; + break; + } + } +out: + return (size); +} + /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) @@ -198,22 +293,14 @@ Elf_Ehdr *hdr; Elf_Shdr *shdr, *cshdr, *lshdr; vm_offset_t firstaddr, lastaddr; - int i, nsym, res, ret, shdrbytes, symstrindex; + int i, nsym, res, ret, shdrbytes, symstrindex; ret = 0; firstaddr = lastaddr = (vm_offset_t)off; hdr = &ef->hdr; + shdr = ef->e_shdr; 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. Index: stand/common/misc.c =================================================================== --- stand/common/misc.c +++ stand/common/misc.c @@ -149,6 +149,7 @@ #ifdef DEBUG printf("\nmalloc(%d) failed\n", (int)len); #endif + errno = ENOMEM; return (NULL); } if (lseek(fd, off, SEEK_SET) == -1) { Index: stand/i386/libi386/biossmap.c =================================================================== --- stand/i386/libi386/biossmap.c +++ stand/i386/libi386/biossmap.c @@ -119,6 +119,13 @@ } void +bios_getsmapdata(u_int *smaplenp, struct bios_smap **smapbasep) +{ + *smaplenp = smaplen; + *smapbasep = smapbase; +} + +void bios_addsmapdata(struct preloaded_file *kfp) { size_t size; Index: stand/i386/libi386/i386_copy.c =================================================================== --- stand/i386/libi386/i386_copy.c +++ stand/i386/libi386/i386_copy.c @@ -33,9 +33,133 @@ * XXX should check load address/size against memory top. */ #include - +#include +#include +#include "bootstrap.h" #include "libi386.h" #include "btxv86.h" + +extern size_t elf64_obj_imagesize(void *); +/* + * Verify the address is not in use by existing modules. + */ +static vm_offset_t +addr_verify(struct preloaded_file *fp, vm_offset_t addr, size_t size) +{ + vm_offset_t f_addr; + + while (fp != NULL) { + f_addr = fp->f_addr; + + if ((f_addr <= addr) && + (f_addr + fp->f_size >= addr)) { + return (0); + } + if ((f_addr >= addr) && (f_addr <= addr + size)) { + return (0); + } + fp = fp->f_next; + } + return (addr); +} + +/* + * Find smap entry above 1MB, able to contain size bytes from addr. + */ +static vm_offset_t +smap_find(struct bios_smap *smap, int smaplen, vm_offset_t addr, size_t size) +{ + int i; + + for (i = 0; i < smaplen; i++) { + if (smap[i].type != SMAP_TYPE_MEMORY) + continue; + + /* We do not want address below 1MB. */ + if (smap[i].base < 0x100000) + continue; + + /* Do we fit into current entry? */ + if ((smap[i].base <= addr) && + (smap[i].base + smap[i].length >= addr + size)) { + return (addr); + } + + /* Do we fit into new entry? */ + if ((smap[i].base > addr) && (smap[i].length >= size)) { + return (smap[i].base); + } + } + return (0); +} + +/* + * Find usable address for loading. The address for the kernel is fixed, as + * it is determined by kernel linker map. + * For modules, we need to consult smap, the module address has to be + * aligned to page boundary and we have to fit into smap entry. + */ +uint64_t +i386_loadaddr(u_int type, void *data, uint64_t addr) +{ + struct stat st; + size_t size; + struct preloaded_file *fp, *mfp; + struct bios_smap *smap; + u_int smaplen; + vm_offset_t off; + + /* + * For now, assume we have memory for the kernel, the + * required map is [1MB..) This assumption should be safe with x86 BIOS. + */ + fp = file_findfile(NULL, NULL); + if (fp == NULL) { + return (roundup(addr, PAGE_SIZE)); + } + + size = 0; + if (type == LOAD_RAW) { + stat(data, &st); + size = st.st_size; + } + + if (type == LOAD_ELF) { + Elf_Ehdr *hdr = data; + + if (hdr->e_ident[EI_CLASS] == ELFCLASS64) + size = elf64_obj_imagesize(data); + else if (hdr->e_ident[EI_CLASS] == ELFCLASS32) + size = elf32_obj_imagesize(data); + else + return (roundup(addr, PAGE_SIZE)); + } + if (size == 0) + return (roundup(addr, PAGE_SIZE)); + + bios_getsmapdata(&smaplen, &smap); + + /* Start from the end of the kernel. */ + mfp = fp; + do { + if (mfp == NULL) { + off = roundup2(addr + 1, PAGE_SIZE); + } else { + off = roundup2(mfp->f_addr + mfp->f_size + 1, + PAGE_SIZE); + } + off = smap_find(smap, smaplen, off, size); + off = addr_verify(fp, off, size); + if (off != 0) + break; + + if (mfp == NULL) + break; + mfp = mfp->f_next; + } while (off == 0); + + return (off); +} ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len) Index: stand/i386/libi386/libi386.h =================================================================== --- stand/i386/libi386/libi386.h +++ stand/i386/libi386/libi386.h @@ -26,6 +26,8 @@ * $FreeBSD$ */ +#include +#include /* * i386 fully-qualified device descriptor. @@ -108,6 +110,7 @@ struct preloaded_file; void bios_addsmapdata(struct preloaded_file *); +void bios_getsmapdata(u_int *, struct bios_smap **); void bios_getsmap(void); void bios_getmem(void); @@ -142,6 +145,7 @@ void biosacpi_detect(void); int i386_autoload(void); +uint64_t i386_loadaddr(u_int type, void *data, uint64_t addr); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); Index: stand/i386/loader/main.c =================================================================== --- stand/i386/loader/main.c +++ stand/i386/loader/main.c @@ -167,6 +167,7 @@ archsw.arch_readin = i386_readin; archsw.arch_isainb = isa_inb; archsw.arch_isaoutb = isa_outb; + archsw.arch_loadaddr = i386_loadaddr; archsw.arch_hypervisor = x86_hypervisor; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe;