diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c index c163b50c9737..a213b34970f0 100644 --- a/stand/common/load_elf.c +++ b/stand/common/load_elf.c @@ -1,1342 +1,1324 @@ /*- * 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 #include #define FREEBSD_ELF #include #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; uint64_t off; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *sym, unsigned char type); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end); static bool __elfN(parse_vt_drv_set)(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"; uint64_t __elfN(relocation_offset) = 0; extern void elf_wrong_field_size(void); #define CONVERT_FIELD(b, f, e) \ switch (sizeof((b)->f)) { \ case 2: \ (b)->f = e ## 16toh((b)->f); \ break; \ case 4: \ (b)->f = e ## 32toh((b)->f); \ break; \ case 8: \ (b)->f = e ## 64toh((b)->f); \ break; \ default: \ /* Force a link time error. */ \ elf_wrong_field_size(); \ break; \ } #define CONVERT_SWITCH(h, d, f) \ switch ((h)->e_ident[EI_DATA]) { \ case ELFDATA2MSB: \ f(d, be); \ break; \ case ELFDATA2LSB: \ f(d, le); \ break; \ default: \ return (EINVAL); \ } static int elf_header_convert(Elf_Ehdr *ehdr) { /* * Fixup ELF header endianness. * * The Xhdr structure was loaded using block read call to optimize file * accesses. It might happen, that the endianness of the system memory * is different that endianness of the ELF header. Swap fields here to * guarantee that Xhdr always contain valid data regardless of * architecture. */ #define HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, e_type, e); \ CONVERT_FIELD(b, e_machine, e); \ CONVERT_FIELD(b, e_version, e); \ CONVERT_FIELD(b, e_entry, e); \ CONVERT_FIELD(b, e_phoff, e); \ CONVERT_FIELD(b, e_shoff, e); \ CONVERT_FIELD(b, e_flags, e); \ CONVERT_FIELD(b, e_ehsize, e); \ CONVERT_FIELD(b, e_phentsize, e); \ CONVERT_FIELD(b, e_phnum, e); \ CONVERT_FIELD(b, e_shentsize, e); \ CONVERT_FIELD(b, e_shnum, e); \ CONVERT_FIELD(b, e_shstrndx, e) CONVERT_SWITCH(ehdr, ehdr, HEADER_FIELDS); #undef HEADER_FIELDS return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { #define PROGRAM_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, p_type, e); \ CONVERT_FIELD(b, p_flags, e); \ CONVERT_FIELD(b, p_offset, e); \ CONVERT_FIELD(b, p_vaddr, e); \ CONVERT_FIELD(b, p_paddr, e); \ CONVERT_FIELD(b, p_filesz, e); \ CONVERT_FIELD(b, p_memsz, e); \ CONVERT_FIELD(b, p_align, e) CONVERT_SWITCH(ehdr, phdr, PROGRAM_HEADER_FIELDS); #undef PROGRAM_HEADER_FIELDS return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { #define SECTION_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, sh_name, e); \ CONVERT_FIELD(b, sh_type, e); \ CONVERT_FIELD(b, sh_link, e); \ CONVERT_FIELD(b, sh_info, e); \ CONVERT_FIELD(b, sh_flags, e); \ CONVERT_FIELD(b, sh_addr, e); \ CONVERT_FIELD(b, sh_offset, e); \ CONVERT_FIELD(b, sh_size, e); \ CONVERT_FIELD(b, sh_addralign, e); \ CONVERT_FIELD(b, sh_entsize, e) CONVERT_SWITCH(ehdr, shdr, SECTION_HEADER_FIELDS); #undef SECTION_HEADER_FIELDS return (0); } #undef CONVERT_SWITCH #undef CONVERT_FIELD #ifdef __amd64__ static bool is_kernphys_relocatable(elf_file_t ef) { Elf_Sym sym; return (__elfN(lookup_symbol)(ef, "kernphys", &sym, STT_OBJECT) == 0 && sym.st_size == 8); } #endif static int __elfN(load_elf_header)(char *filename, elf_file_t ef) { ssize_t bytes_read; Elf_Ehdr *ehdr; int err; /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return (EFTYPE); if ((ef->fd = open(filename, O_RDONLY)) == -1) return (errno); ef->firstpage = malloc(PAGE_SIZE); if (ef->firstpage == NULL) { close(ef->fd); return (ENOMEM); } #ifdef LOADER_VERIEXEC_VECTX { int verror; ef->vctx = vectx_open(ef->fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef->fd); free(ef->vctx); return (EAUTH); } } #endif bytes_read = VECTX_READ(VECTX_HANDLE(ef), ef->firstpage, PAGE_SIZE); ef->firstlen = (size_t)bytes_read; if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto error; } ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; /* Is it ELF? */ if (!IS_ELF(*ehdr)) { err = EFTYPE; goto error; } if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || ehdr->e_ident[EI_VERSION] != EV_CURRENT) /* Version ? */ { err = EFTYPE; goto error; } err = elf_header_convert(ehdr); if (err) goto error; if (ehdr->e_version != EV_CURRENT || ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ err = EFTYPE; goto error; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef->fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto error; } #endif return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { #ifdef LOADER_VERIEXEC_VECTX free(ef->vctx); #endif close(ef->fd); ef->fd = -1; } return (err); } /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { return (__elfN(loadfile_raw)(filename, dest, result, 0)); } int __elfN(loadfile_raw)(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; fp = NULL; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; err = __elfN(load_elf_header)(filename, &ef); if (err != 0) return (err); ehdr = ef.ehdr; /* * Check to see what sort of module we are. */ kfp = file_findfile(NULL, __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 == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); 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; #ifdef __amd64__ fp->f_kernphys_relocatable = multiboot || is_kernphys_relocatable(&ef); #endif goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); if (ef.fd != -1) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); } return (err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { int i; u_int j; Elf_Ehdr *ehdr; Elf_Phdr *phdr, *php; Elf_Shdr *shdr; char *shstr; int ret; vm_offset_t firstaddr; vm_offset_t lastaddr; size_t chunk; ssize_t result; Elf_Addr ssym, esym; Elf_Dyn *dp; Elf_Addr adp; Elf_Addr ctors; int ndp; int symstrindex; int symtabindex; Elf_Size size; u_int fpcopy; Elf_Sym sym; Elf_Addr p_start, p_end; dp = NULL; shdr = NULL; ret = 0; firstaddr = lastaddr = 0; ehdr = ef->ehdr; #ifdef __powerpc__ if (ef->kernel) { #else if (ehdr->e_type == ET_EXEC) { #endif #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 /* x86_64 relocates after locore */ off = - (off & 0xffffffffff000000ull); #else /* i386 relocates after locore */ off = - (off & 0xff000000u); #endif #elif defined(__powerpc__) /* * On the purely virtual memory machines like e500, the kernel * is linked against its final VA range, which is most often * not available at the loader stage, but only after kernel * initializes and completes its VM settings. In such cases we * cannot use p_vaddr field directly to load ELF segments, but * put them at some 'load-time' locations. */ if (off & 0xf0000000u) { off = -(off & 0xf0000000u); /* * XXX the physical load address should not be * hardcoded. Note that the Book-E kernel assumes that * it's loaded at a 16MB boundary for now... */ off += 0x01000000; } ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("Converted entry 0x%jx\n", (uintmax_t)ehdr->e_entry); #endif #elif defined(__arm__) && !defined(EFI) /* * The elf headers in arm kernels specify virtual addresses in * all header fields, even the ones that should be physical * addresses. We assume the entry point is in the first page, * and masking the page offset will leave us with the virtual * address the kernel was linked at. We subtract that from the * load offset, making 'off' into the value which, when added * to a virtual address in an elf header, translates it to a * physical address. We do the va->pa conversion on the entry * point address in the header now, so that later we can launch * the kernel by just jumping to that address. * * When booting from UEFI the copyin and copyout functions * handle adjusting the location relative to the first virtual * address. Because of this there is no need to adjust the * offset or entry point address as these will both be handled * by the efi code. */ off -= ehdr->e_entry & ~PAGE_MASK; ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("ehdr->e_entry 0x%jx, va<->pa off %llx\n", (uintmax_t)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++) { if (elf_program_header_convert(ehdr, phdr)) continue; /* 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(VECTX_HANDLE(ef), phdr[i].p_vaddr + off + fpcopy, phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read failed\n"); goto out; } } /* clear space from oversized segments; eg: bss */ if (phdr[i].p_filesz < phdr[i].p_memsz) { #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)); /* * Get the section headers. We need this for finding the .ctors * section as well as for loading any symbols. Both may be hard * to do if reading from a .gz file as it involves seeking. I * think the rule is going to have to be that you must strip a * file to remove symbols before gzipping it. */ chunk = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; shdr = alloc_pread(VECTX_HANDLE(ef), ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } for (i = 0; i < ehdr->e_shnum; i++) elf_section_header_convert(ehdr, &shdr[i]); file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr); /* * Read the section string table and look for the .ctors section. * We need to tell the kernel where it is so that it can call the * ctors. */ chunk = shdr[ehdr->e_shstrndx].sh_size; if (chunk) { shstr = alloc_pread(VECTX_HANDLE(ef), shdr[ehdr->e_shstrndx].sh_offset, chunk); if (shstr) { for (i = 0; i < ehdr->e_shnum; i++) { if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0) continue; ctors = shdr[i].sh_addr; file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors), &ctors); size = shdr[i].sh_size; file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size), &size); break; } free(shstr); } } /* * Now load any symbols. */ symtabindex = -1; symstrindex = -1; for (i = 0; i < ehdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_SYMTAB) continue; for (j = 0; j < ehdr->e_phnum; j++) { if (phdr[j].p_type != PT_LOAD) continue; if (shdr[i].sh_offset >= phdr[j].p_offset && (shdr[i].sh_offset + shdr[i].sh_size <= phdr[j].p_offset + phdr[j].p_filesz)) { shdr[i].sh_offset = 0; shdr[i].sh_size = 0; break; } } if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) continue; /* alread loaded in a PT_LOAD above */ /* Save it for loading below */ symtabindex = i; symstrindex = shdr[i].sh_link; } if (symtabindex < 0 || symstrindex < 0) goto nosyms; /* Ok, committed to a load. */ #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; -#if defined(__powerpc__) - #if __ELF_WORD_SIZE == 64 - size = htobe64(size); - #else - size = htobe32(size); - #endif -#endif 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 (VECTX_LSEEK(VECTX_HANDLE(ef), (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); lastaddr = ssym; ssym = 0; goto nosyms; } result = archsw.arch_readin(VECTX_HANDLE(ef), lastaddr, shdr[i].sh_size); if (result < 0 || (size_t)result != shdr[i].sh_size) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! " "(%ju != %ju)", (uintmax_t)result, (uintmax_t)shdr[i].sh_size); lastaddr = ssym; ssym = 0; goto nosyms; } /* Reset offsets relative to ssym */ lastaddr += shdr[i].sh_size; lastaddr = roundup(lastaddr, sizeof(size)); if (i == symtabindex) symtabindex = -1; else if (i == symstrindex) symstrindex = -1; } esym = lastaddr; #ifndef ELF_VERBOSE printf("]"); #endif -#if defined(__powerpc__) - /* On PowerPC we always need to provide BE data to the kernel */ - #if __ELF_WORD_SIZE == 64 - ssym = htobe64((uint64_t)ssym); - esym = htobe64((uint64_t)esym); - #else - ssym = htobe32((uint32_t)ssym); - esym = htobe32((uint32_t)esym); - #endif -#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 (!gfx_state.tg_kernel_supported && __elfN(lookup_symbol)(ef, "__start_set_vt_drv_set", &sym, STT_NOTYPE) == 0) { p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_vt_drv_set", &sym, STT_NOTYPE) == 0) { p_end = sym.st_value + ef->off; gfx_state.tg_kernel_supported = __elfN(parse_vt_drv_set)(fp, ef, p_start, p_end); } } if (__elfN(lookup_symbol)(ef, "__start_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; p_end = sym.st_value + ef->off; if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ goto out; out: if (dp) free(dp); if (shdr) free(shdr); return ret; } static char invalid_name[] = "bad"; char * fake_modname(const char *name) { const char *sp, *ep; char *fp; size_t len; sp = strrchr(name, '/'); if (sp) sp++; else sp = name; ep = strrchr(sp, '.'); if (ep == NULL) { ep = sp + strlen(sp); } if (ep == sp) { sp = invalid_name; ep = invalid_name + sizeof(invalid_name) - 1; } len = ep - sp; fp = malloc(len + 1); if (fp == NULL) return NULL; memcpy(fp, sp, len); fp[len] = '\0'; return fp; } #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif #if defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint32_t md_data; /* specific data */ uint32_t md_cval; /* common string label */ }; #endif int __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_t dest) { struct elf_file ef; int err, i, j; Elf_Shdr *sh_meta, *shdr = NULL; Elf_Shdr *sh_data[2]; char *shstrtab = NULL; size_t size; Elf_Addr p_start, p_end; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; err = __elfN(load_elf_header)(fp->f_name, &ef); if (err != 0) goto out; if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) { ef.kernel = 1; } else if (ef.ehdr->e_type != ET_DYN) { err = EFTYPE; goto out; } size = (size_t)ef.ehdr->e_shnum * (size_t)ef.ehdr->e_shentsize; shdr = alloc_pread(VECTX_HANDLE(&ef), ef.ehdr->e_shoff, size); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ shstrtab = alloc_pread(VECTX_HANDLE(&ef), shdr[ef.ehdr->e_shstrndx].sh_offset, shdr[ef.ehdr->e_shstrndx].sh_size); if (shstrtab == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load shstrtab\n"); err = EFTYPE; goto out; } /* Find set_modmetadata_set and data sections. */ sh_data[0] = sh_data[1] = sh_meta = NULL; for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { if (strcmp(&shstrtab[shdr[i].sh_name], "set_modmetadata_set") == 0) { sh_meta = &shdr[i]; } if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { sh_data[j++] = &shdr[i]; } } if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); err = EFTYPE; goto out; } /* Load set_modmetadata_set into memory */ err = kern_pread(VECTX_HANDLE(&ef), dest, sh_meta->sh_size, sh_meta->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); goto out; } p_start = dest; p_end = dest + sh_meta->sh_size; dest += sh_meta->sh_size; /* Load data sections into memory. */ err = kern_pread(VECTX_HANDLE(&ef), dest, sh_data[0]->sh_size, sh_data[0]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } /* * We have to increment the dest, so that the offset is the same into * both the .rodata and .data sections. */ ef.off = -(sh_data[0]->sh_addr - dest); dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); err = kern_pread(VECTX_HANDLE(&ef), dest, sh_data[1]->sh_size, sh_data[1]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to parse metadata: %d\n", err); goto out; } out: if (shstrtab != NULL) free(shstrtab); if (shdr != NULL) free(shdr); if (ef.firstpage != NULL) free(ef.firstpage); if (ef.fd != -1) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif close(ef.fd); } return (err); } /* * Walk through vt_drv_set, each vt driver structure starts with * static 16 chars for driver name. If we have "vbefb", return true. */ static bool __elfN(parse_vt_drv_set)(struct preloaded_file *fp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end) { Elf_Addr v, p; char vd_name[16]; int error; 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 (false); COPYOUT(v, &vd_name, sizeof(vd_name)); if (strncmp(vd_name, "vbefb", sizeof(vd_name)) == 0) return (true); p += sizeof(Elf_Addr); } return (false); } int __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end) { struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 md32; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p; modcnt = 0; p = p_start; while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) v += ef->off; else if (error != 0) return (error); #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error == EOPNOTSUPP) { md64.md_cval += ef->off; md64.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 COPYOUT(v, &md32, sizeof(md32)); error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32)); if (error == EOPNOTSUPP) { md32.md_cval += ef->off; md32.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md32.md_version; md.md_type = md32.md_type; md.md_cval = (const char *)(uintptr_t)md32.md_cval; md.md_data = (void *)(uintptr_t)md32.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error == EOPNOTSUPP) { md.md_cval += ef->off; md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off); } else if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: if (ef->kernel) /* kernel must not depend on anything */ break; s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; } } if (modcnt == 0) { s = fake_modname(fp->f_name); file_addmodule(fp, s, 1, NULL); free(s); } return 0; } static unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *symp, unsigned char type) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; if (ef->nbuckets == 0) { printf(__elfN(bad_symtable)); return ENOENT; } hash = elf_hash(name); COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf(__elfN(bad_symtable)); return ENOENT; } COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); if (sym.st_name == 0) { printf(__elfN(bad_symtable)); return ENOENT; } strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); if (strcmp(name, strp) == 0) { free(strp); if (sym.st_shndx != SHN_UNDEF || (sym.st_value != 0 && ELF_ST_TYPE(sym.st_info) == type)) { *symp = sym; return 0; } return ENOENT; } free(strp); COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); } return ENOENT; } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. * * Returns EOPNOTSUPP if no relocation method is supplied. */ static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { size_t n; Elf_Rela a; Elf_Rel r; int error; /* * The kernel is already relocated, but we still want to apply * offset adjustments. */ if (ef->kernel) return (EOPNOTSUPP); for (n = 0; n < ef->relsz / sizeof(r); n++) { COPYOUT(ef->rel + n, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, ef->off, p, val, len); if (error != 0) return (error); } for (n = 0; n < ef->relasz / sizeof(a); n++) { COPYOUT(ef->rela + n, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, ef->off, p, val, len); if (error != 0) return (error); } return (0); } static Elf_Addr __elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) { /* Symbol lookup by index not required here. */ return (0); } diff --git a/stand/defs.mk b/stand/defs.mk index 2b0201b940b5..bbebf270c7ef 100644 --- a/stand/defs.mk +++ b/stand/defs.mk @@ -1,251 +1,253 @@ # $FreeBSD$ .if !defined(__BOOT_DEFS_MK__) __BOOT_DEFS_MK__=${MFILE} # We need to define all the MK_ options before including src.opts.mk # because it includes bsd.own.mk which needs the right MK_ values, # espeically MK_CTF. MK_CTF= no MK_SSP= no MK_PROFILE= no MK_PIE= no MAN= .if !defined(PIC) NO_PIC= INTERNALLIB= .endif # Should be NO_CPU_FLAGS, but bsd.cpu.mk is included too early in bsd.init.mk # via the early include of bsd.opts.mk. Moving Makefile.inc include earlier in # that file causes weirdness, so this is the next best thing. We need to do this # because the loader needs very specific flags to work right, and things like # CPUTYPE?=native prevent that, and introduce an endless game of whack-a-mole # to disable more and more features. Boot loader performance is never improved # enough to make that hassle worth chasing. _CPUCFLAGS= .include .include WARNS?= 1 BOOTSRC= ${SRCTOP}/stand EFISRC= ${BOOTSRC}/efi EFIINC= ${EFISRC}/include EFIINCMD= ${EFIINC}/${MACHINE} FDTSRC= ${BOOTSRC}/fdt FICLSRC= ${BOOTSRC}/ficl LDRSRC= ${BOOTSRC}/common LIBLUASRC= ${BOOTSRC}/liblua LIBOFWSRC= ${BOOTSRC}/libofw LUASRC= ${SRCTOP}/contrib/lua/src SASRC= ${BOOTSRC}/libsa SYSDIR= ${SRCTOP}/sys UBOOTSRC= ${BOOTSRC}/uboot ZFSSRC= ${SASRC}/zfs OZFS= ${SRCTOP}/sys/contrib/openzfs ZFSOSSRC= ${OZFS}/module/os/freebsd/ ZFSOSINC= ${OZFS}/include/os/freebsd LIBCSRC= ${SRCTOP}/lib/libc BOOTOBJ= ${OBJTOP}/stand # BINDIR is where we install BINDIR?= /boot # LUAPATH is where we search for and install lua scripts. LUAPATH?= /boot/lua FLUASRC?= ${SRCTOP}/libexec/flua LIBSA= ${BOOTOBJ}/libsa/libsa.a .if ${MACHINE} == "i386" LIBSA32= ${LIBSA} .else LIBSA32= ${BOOTOBJ}/libsa32/libsa32.a .endif # Standard options: CFLAGS+= -nostdinc # Allow CFLAGS_EARLY.file/target so that code that needs specific stack # of include paths can set them up before our include paths. Normally # the only thing that should be there are -I directives, and as few of # those as possible. CFLAGS+= ${CFLAGS_EARLY} ${CFLAGS_EARLY.${.IMPSRC:T}} ${CFLAGS_EARLY.${.TARGET:T}} .if ${MACHINE_ARCH} == "amd64" && ${DO32:U0} == 1 CFLAGS+= -I${BOOTOBJ}/libsa32 .else CFLAGS+= -I${BOOTOBJ}/libsa .endif CFLAGS+= -I${SASRC} -D_STANDALONE CFLAGS+= -I${SYSDIR} # Spike the floating point interfaces CFLAGS+= -Ddouble=jagged-little-pill -Dfloat=floaty-mcfloatface .if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" # Slim down the image. This saves about 15% in size with clang 6 on x86 # Our most constrained /boot/loader env is BIOS booting on x86, where # our text + data + BTX have to fit into 640k below the ISA hole. # Experience has shown that problems arise between ~520k to ~530k. CFLAGS.clang+= -Oz CFLAGS.gcc+= -Os CFLAGS+= -ffunction-sections -fdata-sections .endif # GELI Support, with backward compat hooks (mostly) .if defined(LOADER_NO_GELI_SUPPORT) MK_LOADER_GELI=no .warning "Please move from LOADER_NO_GELI_SUPPORT to WITHOUT_LOADER_GELI" .endif .if defined(LOADER_GELI_SUPPORT) MK_LOADER_GELI=yes .warning "Please move from LOADER_GELI_SUPPORT to WITH_LOADER_GELI" .endif .if ${MK_LOADER_GELI} == "yes" CFLAGS+= -DLOADER_GELI_SUPPORT CFLAGS+= -I${SASRC}/geli .endif # MK_LOADER_GELI # These should be confined to loader.mk, but can't because uboot/lib # also uses it. It's part of loader, but isn't a loader so we can't # just include loader.mk .if ${LOADER_DISK_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_DISK_SUPPORT .endif # Machine specific flags for all builds here -# Ensure PowerPC64 and PowerPC64LE boot loaders are compiled as 32 bit -# and in big endian. -.if ${MACHINE_ARCH:Mpowerpc64*} != "" +# Ensure PowerPC64 and PowerPC64LE boot loaders are compiled as 32 bit. +# PowerPC64LE boot loaders are 32-bit little-endian. +.if ${MACHINE_ARCH} == "powerpc64" CFLAGS+= -m32 -mcpu=powerpc -mbig-endian +.elif ${MACHINE_ARCH} == "powerpc64le" +CFLAGS+= -m32 -mcpu=powerpc -mlittle-endian .endif # For amd64, there's a bit of mixed bag. Some of the tree (i386, lib*32) is # build 32-bit and some 64-bit (lib*, efi). Centralize all the 32-bit magic here # and activate it when DO32 is explicitly defined to be 1. .if ${MACHINE_ARCH} == "amd64" && ${DO32:U0} == 1 CFLAGS+= -m32 # LD_FLAGS is passed directly to ${LD}, not via ${CC}: LD_FLAGS+= -m elf_i386_fbsd AFLAGS+= --32 .endif # Add in the no float / no SIMD stuff and announce we're freestanding # aarch64 and riscv don't have -msoft-float, but all others do. CFLAGS+= -ffreestanding ${CFLAGS_NO_SIMD} .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -mgeneral-regs-only -ffixed-x18 -fPIC .elif ${MACHINE_CPUARCH} == "riscv" CFLAGS+= -march=rv64imac -mabi=lp64 -fPIC CFLAGS.clang+= -mcmodel=medium CFLAGS.gcc+= -mcmodel=medany .else CFLAGS+= -msoft-float .endif # -msoft-float seems to be insufficient for powerpcspe .if ${MACHINE_ARCH} == "powerpcspe" CFLAGS+= -mno-spe .endif .if ${MACHINE_CPUARCH} == "i386" || (${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 1) CFLAGS+= -march=i386 CFLAGS.gcc+= -mpreferred-stack-boundary=2 .endif .if ${MACHINE_CPUARCH} == "amd64" && ${DO32:U0} == 0 CFLAGS+= -fPIC -mno-red-zone .endif .if ${MACHINE_CPUARCH} == "arm" # Do not generate movt/movw, because the relocation fixup for them does not # translate to the -Bsymbolic -pie format required by self_reloc() in loader(8). # Also, the fpu is not available in a standalone environment. CFLAGS.clang+= -mno-movt CFLAGS.clang+= -mfpu=none CFLAGS+= -fPIC .endif # Some RISC-V linkers have support for relaxations, while some (lld) do not # yet. If this is the case we inhibit the compiler from emitting relaxations. .if ${LINKER_FEATURES:Mriscv-relaxations} == "" CFLAGS+= -mno-relax .endif # The boot loader build uses dd status=none, where possible, for reproducible # build output (since performance varies from run to run). Trouble is that # option was recently (10.3) added to FreeBSD and is non-standard. Only use it # when this test succeeds rather than require dd to be a bootstrap tool. DD_NOSTATUS!=(dd status=none count=0 2> /dev/null && echo status=none) || true DD=dd ${DD_NOSTATUS} .if ${MACHINE_CPUARCH} == "mips" CFLAGS+= -G0 -fno-pic -mno-abicalls .endif # # Have a sensible default # .if ${MK_LOADER_LUA} == "yes" LOADER_DEFAULT_INTERP?=lua .elif ${MK_FORTH} == "yes" LOADER_DEFAULT_INTERP?=4th .else LOADER_DEFAULT_INTERP?=simp .endif LOADER_INTERP?=${LOADER_DEFAULT_INTERP} # Make sure we use the machine link we're about to create CFLAGS+=-I. all: ${PROG} CLEANFILES+= teken_state.h teken.c: teken_state.h teken_state.h: ${SYSDIR}/teken/sequences awk -f ${SYSDIR}/teken/gensequences \ ${SYSDIR}/teken/sequences > teken_state.h .if !defined(NO_OBJ) _ILINKS=include/machine .if ${MACHINE} != ${MACHINE_CPUARCH} && ${MACHINE} != "arm64" _ILINKS+=include/${MACHINE_CPUARCH} .endif .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" _ILINKS+=include/x86 .endif CFLAGS+= -Iinclude CLEANDIRS+= include beforedepend: ${_ILINKS} beforebuild: ${_ILINKS} # Ensure that the links exist without depending on it when it exists which # causes all the modules to be rebuilt when the directory pointed to changes. .for _link in ${_ILINKS} .if !exists(${.OBJDIR}/${_link}) ${OBJS}: ${_link} .endif # _link exists .endfor .NOPATH: ${_ILINKS} ${_ILINKS}: .NOMETA @case ${.TARGET:T} in \ machine) \ if [ ${DO32:U0} -eq 0 ]; then \ path=${SYSDIR}/${MACHINE}/include ; \ else \ path=${SYSDIR}/${MACHINE:C/amd64/i386/}/include ; \ fi ;; \ *) \ path=${SYSDIR}/${.TARGET:T}/include ;; \ esac ; \ case ${.TARGET} in \ */*) mkdir -p ${.TARGET:H};; \ esac ; \ path=`(cd $$path && /bin/pwd)` ; \ ${ECHO} ${.TARGET} "->" $$path ; \ ln -fns $$path ${.TARGET} .endif # !NO_OBJ .endif # __BOOT_DEFS_MK__ diff --git a/stand/libofw/openfirm.c b/stand/libofw/openfirm.c index 0b4198d281fd..b2b89581ae70 100644 --- a/stand/libofw/openfirm.c +++ b/stand/libofw/openfirm.c @@ -1,831 +1,777 @@ /* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /*- * Copyright (C) 2000 Benno Rice. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Benno Rice ``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 TOOLS GMBH 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 "openfirm.h" int (*openfirmware)(void *); phandle_t chosen; ihandle_t mmu; ihandle_t memory; int real_mode = 0; +#define IN(x) htobe32((cell_t)x) +#define OUT(x) be32toh(x) +#define SETUP(a, b, c, d) \ + a.name = IN( (b) ); \ + a.nargs = IN( (c) ); \ + a.nreturns = IN( (d) ); + /* Initialiser */ void OF_init(int (*openfirm)(void *)) { phandle_t options; char mode[sizeof("true")]; openfirmware = openfirm; if ((chosen = OF_finddevice("/chosen")) == -1) OF_exit(); if (OF_getprop(chosen, "memory", &memory, sizeof(memory)) == -1) { memory = OF_open("/memory"); if (memory == -1) memory = OF_open("/memory@0"); if (memory == -1) OF_exit(); } if (OF_getprop(chosen, "mmu", &mmu, sizeof(mmu)) == -1) OF_exit(); /* * Check if we run in real mode. If so, we do not need to map * memory later on. */ options = OF_finddevice("/options"); if (OF_getprop(options, "real-mode?", mode, sizeof(mode)) > 0 && strcmp(mode, "true") == 0) real_mode = 1; } /* * Generic functions */ /* Test to see if a service exists. */ int OF_test(char *name) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t service; cell_t missing; - } args = { - (cell_t)"test", - 1, - 1, - }; + } args = {}; + SETUP(args, "test", 1, 1); - args.service = (cell_t)name; + args.service = IN(name); if (openfirmware(&args) == -1) return (-1); - return (args.missing); + return (OUT(args.missing)); } /* Return firmware millisecond count. */ int OF_milliseconds() { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t ms; - } args = { - (cell_t)"milliseconds", - 0, - 1, - }; + } args = {}; + SETUP(args, "milliseconds", 0, 1); openfirmware(&args); - return (args.ms); + return (OUT(args.ms)); } /* * Device tree functions */ /* Return the next sibling of this node or 0. */ phandle_t OF_peer(phandle_t node) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t next; - } args = { - (cell_t)"peer", - 1, - 1, - }; + } args = {}; + SETUP(args, "peer", 1, 1); args.node = node; if (openfirmware(&args) == -1) return (-1); return (args.next); } /* Return the first child of this node or 0. */ phandle_t OF_child(phandle_t node) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t child; - } args = { - (cell_t)"child", - 1, - 1, - }; + } args = {}; + SETUP(args, "child", 1, 1); args.node = node; if (openfirmware(&args) == -1) return (-1); return (args.child); } /* Return the parent of this node or 0. */ phandle_t OF_parent(phandle_t node) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t parent; - } args = { - (cell_t)"parent", - 1, - 1, - }; + } args = {}; + SETUP(args, "parent", 1, 1); args.node = node; if (openfirmware(&args) == -1) return (-1); return (args.parent); } /* Return the package handle that corresponds to an instance handle. */ phandle_t OF_instance_to_package(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t package; - } args = { - (cell_t)"instance-to-package", - 1, - 1, - }; + } args = {}; + SETUP(args, "instance-to-package", 1, 1); args.instance = instance; if (openfirmware(&args) == -1) return (-1); return (args.package); } /* Get the length of a property of a package. */ int OF_getproplen(phandle_t package, const char *propname) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t proplen; - } args = { - (cell_t)"getproplen", - 2, - 1, - }; + } args = {}; + SETUP(args, "getproplen", 2, 1); args.package = package; - args.propname = (cell_t)propname; + args.propname = IN(propname); if (openfirmware(&args) == -1) return (-1); - return (args.proplen); + return (OUT(args.proplen)); } /* Get the value of a property of a package. */ int OF_getprop(phandle_t package, const char *propname, void *buf, int buflen) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t buf; cell_t buflen; cell_t size; - } args = { - (cell_t)"getprop", - 4, - 1, - }; + } args = {}; + SETUP(args, "getprop", 4, 1); args.package = package; - args.propname = (cell_t)propname; - args.buf = (cell_t)buf; - args.buflen = buflen; + args.propname = IN(propname); + args.buf = IN(buf); + args.buflen = IN(buflen); if (openfirmware(&args) == -1) return (-1); - return (args.size); + return (OUT(args.size)); +} + +/* Decode a binary property from a package. */ +int +OF_getencprop(phandle_t package, const char *propname, cell_t *buf, int buflen) +{ + int retval, i; + retval = OF_getprop(package, propname, buf, buflen); + if (retval == -1) + return (retval); + + for (i = 0; i < buflen/4; i++) + buf[i] = be32toh((uint32_t)buf[i]); + + return (retval); } /* Get the next property of a package. */ int OF_nextprop(phandle_t package, const char *previous, char *buf) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t previous; cell_t buf; cell_t flag; - } args = { - (cell_t)"nextprop", - 3, - 1, - }; + } args = {}; + SETUP(args, "nextprop", 3, 1); args.package = package; - args.previous = (cell_t)previous; - args.buf = (cell_t)buf; + args.previous = IN(previous); + args.buf = IN(buf); if (openfirmware(&args) == -1) return (-1); - return (args.flag); + return (OUT(args.flag)); } /* Set the value of a property of a package. */ /* XXX Has a bug on FirePower */ int OF_setprop(phandle_t package, const char *propname, void *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t buf; cell_t len; cell_t size; - } args = { - (cell_t)"setprop", - 4, - 1, - }; + } args = {}; + SETUP(args, "setprop", 4, 1); args.package = package; - args.propname = (cell_t)propname; - args.buf = (cell_t)buf; - args.len = len; + args.propname = IN(propname); + args.buf = IN(buf); + args.len = IN(len); if (openfirmware(&args) == -1) return (-1); - return (args.size); + return (OUT(args.size)); } /* Convert a device specifier to a fully qualified pathname. */ int OF_canon(const char *device, char *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t buf; cell_t len; cell_t size; - } args = { - (cell_t)"canon", - 3, - 1, - }; - - args.device = (cell_t)device; - args.buf = (cell_t)buf; - args.len = len; + } args = {}; + SETUP(args, "canon", 3, 1); + + args.device = IN(device); + args.buf = IN(buf); + args.len = IN(len); if (openfirmware(&args) == -1) return (-1); - return (args.size); + return (OUT(args.size)); } /* Return a package handle for the specified device. */ phandle_t OF_finddevice(const char *device) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t package; - } args = { - (cell_t)"finddevice", - 1, - 1, - }; + } args = {}; + SETUP(args, "finddevice", 1, 1); - args.device = (cell_t)device; + args.device = IN(device); if (openfirmware(&args) == -1) return (-1); return (args.package); } /* Return the fully qualified pathname corresponding to an instance. */ int OF_instance_to_path(ihandle_t instance, char *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t buf; cell_t len; cell_t size; - } args = { - (cell_t)"instance-to-path", - 3, - 1, - }; + } args = {}; + SETUP(args, "instance-to-path", 3, 1); args.instance = instance; - args.buf = (cell_t)buf; - args.len = len; + args.buf = IN(buf); + args.len = IN(len); if (openfirmware(&args) == -1) return (-1); - return (args.size); + return (OUT(args.size)); } /* Return the fully qualified pathname corresponding to a package. */ int OF_package_to_path(phandle_t package, char *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t buf; cell_t len; cell_t size; - } args = { - (cell_t)"package-to-path", - 3, - 1, - }; + } args = {}; + SETUP(args, "package-to-path", 3, 1); args.package = package; - args.buf = (cell_t)buf; - args.len = len; + args.buf = IN(buf); + args.len = IN(len); if (openfirmware(&args) == -1) return (-1); - return (args.size); + return (OUT(args.size)); } /* Call the method in the scope of a given instance. */ int OF_call_method(char *method, ihandle_t instance, int nargs, int nreturns, ...) { va_list ap; static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t method; cell_t instance; cell_t args_n_results[12]; - } args = { - (cell_t)"call-method", - 2, - 1, - }; + } args = {}; + SETUP(args, "call-method", nargs + 2, nreturns + 1); cell_t *cp; int n; if (nargs > 6) return (-1); - args.nargs = nargs + 2; - args.nreturns = nreturns + 1; - args.method = (cell_t)method; + args.method = IN(method); args.instance = instance; va_start(ap, nreturns); for (cp = (cell_t *)(args.args_n_results + (n = nargs)); --n >= 0;) - *--cp = va_arg(ap, cell_t); + *--cp = IN(va_arg(ap, cell_t)); if (openfirmware(&args) == -1) return (-1); if (args.args_n_results[nargs]) - return (args.args_n_results[nargs]); - for (cp = (cell_t *)(args.args_n_results + nargs + (n = args.nreturns)); - --n > 0;) - *va_arg(ap, cell_t *) = *--cp; + return (OUT(args.args_n_results[nargs])); + /* XXX what if ihandles or phandles are returned */ + for (cp = (cell_t *)(args.args_n_results + nargs + + (n = be32toh(args.nreturns))); --n > 0;) + *va_arg(ap, cell_t *) = OUT(*--cp); va_end(ap); return (0); } /* * Device I/O functions */ /* Open an instance for a device. */ ihandle_t OF_open(char *device) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t instance; - } args = { - (cell_t)"open", - 1, - 1, - }; + } args = {}; + SETUP(args, "open", 1, 1); - args.device = (cell_t)device; + args.device = IN(device); if (openfirmware(&args) == -1 || args.instance == 0) { return (-1); } return (args.instance); } /* Close an instance. */ void OF_close(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; - } args = { - (cell_t)"close", - 1, - }; + } args = {}; + SETUP(args, "close", 1, 0); args.instance = instance; openfirmware(&args); } /* Read from an instance. */ int OF_read(ihandle_t instance, void *addr, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t addr; cell_t len; cell_t actual; - } args = { - (cell_t)"read", - 3, - 1, - }; + } args = {}; + SETUP(args, "read", 3, 1); args.instance = instance; - args.addr = (cell_t)addr; - args.len = len; + args.addr = IN(addr); + args.len = IN(len); #if defined(OPENFIRM_DEBUG) printf("OF_read: called with instance=%08x, addr=%p, len=%d\n", - args.instance, args.addr, args.len); + instance, addr, len); #endif if (openfirmware(&args) == -1) return (-1); #if defined(OPENFIRM_DEBUG) printf("OF_read: returning instance=%d, addr=%p, len=%d, actual=%d\n", - args.instance, args.addr, args.len, args.actual); + args.instance, OUT(args.addr), OUT(args.len), OUT(args.actual)); #endif - return (args.actual); + return (OUT(args.actual)); } /* Write to an instance. */ int OF_write(ihandle_t instance, void *addr, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t addr; cell_t len; cell_t actual; - } args = { - (cell_t)"write", - 3, - 1, - }; + } args = {}; + SETUP(args, "write", 3, 1); args.instance = instance; - args.addr = (cell_t)addr; - args.len = len; + args.addr = IN(addr); + args.len = IN(len); if (openfirmware(&args) == -1) return (-1); - return (args.actual); + return (OUT(args.actual)); } /* Seek to a position. */ int OF_seek(ihandle_t instance, uint64_t pos) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t poshi; cell_t poslo; cell_t status; - } args = { - (cell_t)"seek", - 3, - 1, - }; + } args = {}; + SETUP(args, "seek", 3, 1); args.instance = instance; - args.poshi = pos >> 32; - args.poslo = pos; + args.poshi = IN(((uint64_t)pos >> 32)); + args.poslo = IN(pos); if (openfirmware(&args) == -1) return (-1); - return (args.status); + return (OUT(args.status)); } /* Blocks. */ unsigned int OF_blocks(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t result; cell_t blocks; - } args = { - (cell_t)"#blocks", - 2, - 1, - }; + } args = {}; + SETUP(args, "#blocks", 2, 1); args.instance = instance; if (openfirmware(&args) == -1) return ((unsigned int)-1); - return (args.blocks); + return (OUT(args.blocks)); } /* Block size. */ int OF_block_size(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t result; cell_t size; - } args = { - (cell_t)"block-size", - 2, - 1, - }; + } args = {}; + SETUP(args, "block-size", 2, 1); args.instance = instance; if (openfirmware(&args) == -1) return (512); - return (args.size); + return (OUT(args.size)); } /* * Memory functions */ /* Claim an area of memory. */ void * OF_claim(void *virt, u_int size, u_int align) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; cell_t align; cell_t baseaddr; - } args = { - (cell_t)"claim", - 3, - 1, - }; - - args.virt = (cell_t)virt; - args.size = size; - args.align = align; + } args = {}; + SETUP(args, "claim", 3, 1); + + args.virt = IN(virt); + args.size = IN(size); + args.align = IN(align); if (openfirmware(&args) == -1) return ((void *)-1); - return ((void *)args.baseaddr); + return ((void *)OUT(args.baseaddr)); } /* Release an area of memory. */ void OF_release(void *virt, u_int size) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; - } args = { - (cell_t)"release", - 2, - }; + } args = {}; + SETUP(args, "release", 2, 0); - args.virt = (cell_t)virt; - args.size = size; + args.virt = IN(virt); + args.size = IN(size); openfirmware(&args); } /* * Control transfer functions */ /* Reset the system and call "boot ". */ void OF_boot(char *bootspec) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t bootspec; - } args = { - (cell_t)"boot", - 1, - }; + } args = {}; + SETUP(args, "boot", 1, 0); - args.bootspec = (cell_t)bootspec; + args.bootspec = IN(bootspec); openfirmware(&args); for (;;) /* just in case */ ; } /* Suspend and drop back to the Open Firmware interface. */ void OF_enter() { static struct { cell_t name; cell_t nargs; cell_t nreturns; - } args = { - (cell_t)"enter", - }; + } args = {}; + SETUP(args, "enter", 0, 0); openfirmware(&args); /* We may come back. */ } /* Shut down and drop back to the Open Firmware interface. */ void OF_exit() { static struct { cell_t name; cell_t nargs; cell_t nreturns; - } args = { - (cell_t)"exit", - }; + } args = {}; + SETUP(args, "exit", 0, 0); openfirmware(&args); for (;;) /* just in case */ ; } void OF_quiesce() { static struct { cell_t name; cell_t nargs; cell_t nreturns; - } args = { - (cell_t)"quiesce", - }; + } args = {}; + SETUP(args, "quiesce", 0, 0); openfirmware(&args); } /* Free bytes starting at , then call with . */ #if 0 void OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; cell_t entry; cell_t arg; cell_t len; - } args = { - (cell_t)"chain", - 5, - }; - - args.virt = (cell_t)virt; - args.size = size; - args.entry = (cell_t)entry; - args.arg = (cell_t)arg; - args.len = len; + } args = {}; + SETUP(args, "chain", 5, 0); + + args.virt = IN(virt); + args.size = IN(size); + args.entry = IN(entry); + args.arg = IN(arg); + args.len = IN(len); openfirmware(&args); } #else void OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len) { /* * This is a REALLY dirty hack till the firmware gets this going */ #if 0 if (size > 0) OF_release(virt, size); #endif entry(0, 0, openfirmware, arg, len); } #endif diff --git a/stand/libofw/openfirm.h b/stand/libofw/openfirm.h index b83cf4b0b27f..0981dbf093eb 100644 --- a/stand/libofw/openfirm.h +++ b/stand/libofw/openfirm.h @@ -1,124 +1,125 @@ /* $NetBSD: openfirm.h,v 1.1 1998/05/15 10:16:00 tsubai Exp $ */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /*- * Copyright (C) 2000 Benno Rice. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Benno Rice ``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 TOOLS GMBH 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 _OPENFIRM_H_ #define _OPENFIRM_H_ /* * Prototypes for Open Firmware Interface Routines */ #include #include -typedef unsigned int ihandle_t; -typedef unsigned int phandle_t; -typedef unsigned long int cell_t; +typedef uint32_t ihandle_t; +typedef uint32_t phandle_t; +typedef uint32_t cell_t; extern int (*openfirmware)(void *); extern phandle_t chosen; extern ihandle_t memory, mmu; extern int real_mode; /* * This isn't actually an Open Firmware function, but it seemed like the right * place for it to go. */ void OF_init(int (*openfirm)(void *)); /* Generic functions */ int OF_test(char *); void OF_quiesce(); /* Disable firmware */ /* Device tree functions */ phandle_t OF_peer(phandle_t); phandle_t OF_child(phandle_t); phandle_t OF_parent(phandle_t); phandle_t OF_instance_to_package(ihandle_t); int OF_getproplen(phandle_t, const char *); int OF_getprop(phandle_t, const char *, void *, int); +int OF_getencprop(phandle_t, const char *, cell_t *, int); int OF_nextprop(phandle_t, const char *, char *); int OF_setprop(phandle_t, const char *, void *, int); int OF_canon(const char *, char *, int); phandle_t OF_finddevice(const char *); int OF_instance_to_path(ihandle_t, char *, int); int OF_package_to_path(phandle_t, char *, int); int OF_call_method(char *, ihandle_t, int, int, ...); /* Device I/O functions */ ihandle_t OF_open(char *); void OF_close(ihandle_t); int OF_read(ihandle_t, void *, int); int OF_write(ihandle_t, void *, int); int OF_seek(ihandle_t, u_quad_t); unsigned int OF_blocks(ihandle_t); int OF_block_size(ihandle_t); /* Memory functions */ void *OF_claim(void *, u_int, u_int); void OF_release(void *, u_int); /* Control transfer functions */ void OF_boot(char *); void OF_enter(void); void OF_exit(void) __attribute__((noreturn)); void OF_chain(void *, u_int, void (*)(), void *, u_int); /* Time function */ int OF_milliseconds(void); #endif /* _OPENFIRM_H_ */ diff --git a/stand/powerpc/Makefile b/stand/powerpc/Makefile index 888fe0e97028..a16d3933ff7e 100644 --- a/stand/powerpc/Makefile +++ b/stand/powerpc/Makefile @@ -1,13 +1,17 @@ # $FreeBSD$ NO_OBJ=t .include -SUBDIR.yes= boot1.chrp ofw uboot +SUBDIR.yes= boot1.chrp ofw + +.if "${MACHINE_ARCH}" != "powerpc64le" +SUBDIR.${MK_FDT}+= uboot +.endif .if "${MACHINE_ARCH}" == "powerpc64" SUBDIR.${MK_FDT}+= kboot .endif .include diff --git a/stand/powerpc/boot1.chrp/boot1.c b/stand/powerpc/boot1.chrp/boot1.c index 4d152efe1a70..ed7c55d11d6f 100644 --- a/stand/powerpc/boot1.chrp/boot1.c +++ b/stand/powerpc/boot1.chrp/boot1.c @@ -1,777 +1,867 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * Copyright (c) 2001 Robert Drehmel * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ #include __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include #include "paths.h" #define BSIZEMAX 16384 typedef int putc_func_t(char c, void *arg); typedef int32_t ofwh_t; struct sp_data { char *sp_buf; u_int sp_len; u_int sp_size; }; static const char digits[] = "0123456789abcdef"; static char bootpath[128]; static char bootargs[128]; static ofwh_t bootdev; static struct fs fs; static char blkbuf[BSIZEMAX]; static unsigned int fsblks; static uint32_t fs_off; int main(int ac, char **av); static void exit(int) __dead2; static void load(const char *); static int dskread(void *, uint64_t, int); static void usage(void); static void bcopy(const void *src, void *dst, size_t len); static void bzero(void *b, size_t len); static int domount(const char *device, int quiet); static void panic(const char *fmt, ...) __dead2; static int printf(const char *fmt, ...); static int putchar(char c, void *arg); static int vprintf(const char *fmt, va_list ap); static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap); static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap); static int __putc(char c, void *arg); static int __puts(const char *s, putc_func_t *putc, void *arg); static int __sputc(char c, void *arg); static char *__uitoa(char *buf, u_int val, int base); static char *__ultoa(char *buf, u_long val, int base); /* * Open Firmware interface functions */ typedef uint32_t ofwcell_t; typedef uint32_t u_ofwh_t; -typedef int (*ofwfp_t)(void *); +typedef int (*ofwfp_t)(ofwcell_t *); ofwfp_t ofw; /* the prom Open Firmware entry */ ofwh_t chosenh; -void ofw_init(void *, int, int (*)(void *), char *, int); +void ofw_init(void *, int, ofwfp_t, char *, int); static ofwh_t ofw_finddevice(const char *); static ofwh_t ofw_open(const char *); static int ofw_close(ofwh_t); static int ofw_getprop(ofwh_t, const char *, void *, size_t); static int ofw_setprop(ofwh_t, const char *, void *, size_t); static int ofw_read(ofwh_t, void *, size_t); static int ofw_write(ofwh_t, const void *, size_t); static int ofw_claim(void *virt, size_t len, u_int align); static int ofw_seek(ofwh_t, uint64_t); static void ofw_exit(void) __dead2; ofwh_t bootdevh; ofwh_t stdinh, stdouth; +/* + * Note about the entry point: + * + * For some odd reason, the first page of the load appears to have trouble + * when entering in LE. The first five instructions decode weirdly. + * I suspect it is some cache weirdness between the ELF headers and .text. + * + * Ensure we have a gap between the start of .text and the entry as a + * workaround. + */ __asm(" \n\ .data \n\ .align 4 \n\ stack: \n\ .space 16384 \n\ \n\ .text \n\ + /* SLOF cache hack */ \n\ + .space 4096 \n\ .globl _start \n\ _start: \n\ lis %r1,stack@ha \n\ addi %r1,%r1,stack@l \n\ addi %r1,%r1,8192 \n\ \n\ b ofw_init \n\ "); +ofwfp_t realofw; + +#if BYTE_ORDER == LITTLE_ENDIAN +/* + * Minimal endianness-swap trampoline for LE. + */ +__attribute__((naked)) int +ofwtramp(void *buf, ofwfp_t cb) +{ +__asm(" \n\ + mflr %r0 \n\ + stw %r0, 4(%r1) \n\ + stwu %r1, -16(%r1) \n\ + stw %r30, 8(%r1) \n\ + /* Save current MSR for restoration post-call. */ \n\ + mfmsr %r30 \n\ + mr %r5, %r30 \n\ + /* Remove LE bit from MSR. */ \n\ + clrrwi %r5, %r5, 1 \n\ + mtsrr0 %r4 \n\ + mtsrr1 %r5 \n\ + bcl 20, 31, .+4 /* LOAD_LR_NIA */ \n\ +1: \n\ + mflr %r4 \n\ + addi %r4, %r4, (2f - 1b) \n\ + mtlr %r4 \n\ + /* Switch to BE and transfer control to OF entry */ \n\ + rfid \n\ +2: \n\ + /* Control is returned here, but in BE. */ \n\ + .long 0x05009f42 /* LOAD_LR_NIA */\n\ + /* 0: */\n\ + .long 0xa603db7f /* mtsrr1 %r30 */\n\ + .long 0xa602c87f /* mflr %r30 */\n\ + .long 0x1400de3b /* addi %r30, %r30, (1f - 0b) */\n\ + .long 0xa603da7f /* mtsrr0 %r30 */\n\ + .long 0x2400004c /* rfid */\n\ + /* 1: */\n\ +1: \n\ + /* Back to normal. Tidy up for return. */ \n\ + lwz %r30, 8(%r1) \n\ + lwz %r0, 20(%r1) \n\ + addi %r1, %r1, 16 \n\ + mtlr %r0 \n\ + blr \n\ +"); +} + +/* + * Little-endian OFW entrypoint replacement. + * + * We are doing all the byteswapping in one place here to save space. + * This means instance handles will be byteswapped as well. + */ +int +call_ofw(ofwcell_t* buf) +{ + int ret, i, ncells; + + ncells = 3 + buf[1] + buf[2]; + for (i = 0; i < ncells; i++) + buf[i] = htobe32(buf[i]); + + ret = (ofwtramp(buf, realofw)); + for (i = 0; i < ncells; i++) + buf[i] = be32toh(buf[i]); + return (ret); +} +#endif + void -ofw_init(void *vpd, int res, int (*openfirm)(void *), char *arg, int argl) +ofw_init(void *vpd, int res, ofwfp_t openfirm, char *arg, int argl) { char *av[16]; char *p; int ac; - ofw = openfirm; +#if BYTE_ORDER == LITTLE_ENDIAN + realofw = openfirm; + ofw = call_ofw; +#else + realofw = ofw = openfirm; +#endif chosenh = ofw_finddevice("/chosen"); ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh)); + stdinh = be32toh(stdinh); ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth)); + stdouth = be32toh(stdouth); ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs)); ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath)); bootargs[sizeof(bootargs) - 1] = '\0'; bootpath[sizeof(bootpath) - 1] = '\0'; p = bootpath; while (*p != '\0') { /* Truncate partition ID */ if (*p == ':') { ofw_close(bootdev); *(++p) = '\0'; break; } p++; } ac = 0; p = bootargs; for (;;) { while (*p == ' ' && *p != '\0') p++; if (*p == '\0' || ac >= 16) break; av[ac++] = p; while (*p != ' ' && *p != '\0') p++; if (*p != '\0') *p++ = '\0'; } exit(main(ac, av)); } static ofwh_t ofw_finddevice(const char *name) { ofwcell_t args[] = { (ofwcell_t)"finddevice", 1, 1, (ofwcell_t)name, 0 }; if ((*ofw)(args)) { printf("ofw_finddevice: name=\"%s\"\n", name); return (1); } return (args[4]); } static int ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"getprop", 4, 1, (u_ofwh_t)ofwh, (ofwcell_t)name, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n", ofwh, buf, len); return (1); } return (0); } static int ofw_setprop(ofwh_t ofwh, const char *name, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"setprop", 4, 1, (u_ofwh_t)ofwh, (ofwcell_t)name, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_setprop: ofwh=0x%x buf=%p len=%u\n", ofwh, buf, len); return (1); } return (0); } static ofwh_t ofw_open(const char *path) { ofwcell_t args[] = { (ofwcell_t)"open", 1, 1, (ofwcell_t)path, 0 }; if ((*ofw)(args)) { printf("ofw_open: path=\"%s\"\n", path); return (-1); } return (args[4]); } static int ofw_close(ofwh_t devh) { ofwcell_t args[] = { (ofwcell_t)"close", 1, 0, (u_ofwh_t)devh }; if ((*ofw)(args)) { printf("ofw_close: devh=0x%x\n", devh); return (1); } return (0); } static int ofw_claim(void *virt, size_t len, u_int align) { ofwcell_t args[] = { (ofwcell_t)"claim", 3, 1, (ofwcell_t)virt, len, align, 0, 0 }; if ((*ofw)(args)) { printf("ofw_claim: virt=%p len=%u\n", virt, len); return (1); } return (0); } static int ofw_read(ofwh_t devh, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"read", 3, 1, (u_ofwh_t)devh, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len); return (1); } return (0); } static int ofw_write(ofwh_t devh, const void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"write", 3, 1, (u_ofwh_t)devh, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len); return (1); } return (0); } static int ofw_seek(ofwh_t devh, uint64_t off) { ofwcell_t args[] = { (ofwcell_t)"seek", 3, 1, (u_ofwh_t)devh, off >> 32, off, 0 }; if ((*ofw)(args)) { printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off); return (1); } return (0); } static void ofw_exit(void) { ofwcell_t args[3]; args[0] = (ofwcell_t)"exit"; args[1] = 0; args[2] = 0; for (;;) (*ofw)(args); } static void bcopy(const void *src, void *dst, size_t len) { const char *s = src; char *d = dst; while (len-- != 0) *d++ = *s++; } static void memcpy(void *dst, const void *src, size_t len) { bcopy(src, dst, len); } static void bzero(void *b, size_t len) { char *p = b; while (len-- != 0) *p++ = 0; } static int strcmp(const char *s1, const char *s2) { for (; *s1 == *s2 && *s1; s1++, s2++) ; return ((u_char)*s1 - (u_char)*s2); } #include "ufsread.c" int main(int ac, char **av) { const char *path; char bootpath_full[255]; int i, len; path = PATH_LOADER; for (i = 0; i < ac; i++) { switch (av[i][0]) { case '-': switch (av[i][1]) { default: usage(); } break; default: path = av[i]; break; } } printf(" \n>> FreeBSD/powerpc Open Firmware boot block\n" " Boot path: %s\n" " Boot loader: %s\n", bootpath, path); len = 0; while (bootpath[len] != '\0') len++; memcpy(bootpath_full,bootpath,len+1); if (bootpath_full[len-1] != ':') { /* First try full volume */ if (domount(bootpath_full,1) == 0) goto out; /* Add a : so that we try partitions if that fails */ if (bootdev > 0) ofw_close(bootdev); bootpath_full[len] = ':'; len += 1; } /* Loop through first 16 partitions to find a UFS one */ for (i = 0; i < 16; i++) { if (i < 10) { bootpath_full[len] = i + '0'; bootpath_full[len+1] = '\0'; } else { bootpath_full[len] = '1'; bootpath_full[len+1] = i - 10 + '0'; bootpath_full[len+2] = '\0'; } if (domount(bootpath_full,1) >= 0) break; if (bootdev > 0) ofw_close(bootdev); } if (i >= 16) panic("domount"); out: printf(" Boot volume: %s\n",bootpath_full); ofw_setprop(chosenh, "bootargs", bootpath_full, len+2); load(path); return (1); } static void usage(void) { printf("usage: boot device [/path/to/loader]\n"); exit(1); } static void exit(int code) { ofw_exit(); } static struct dmadat __dmadat; static int domount(const char *device, int quiet) { dmadat = &__dmadat; if ((bootdev = ofw_open(device)) == -1) { printf("domount: can't open device\n"); return (-1); } if (fsread(0, NULL, 0)) { if (!quiet) printf("domount: can't read superblock\n"); return (-1); } return (0); } static void load(const char *fname) { Elf32_Ehdr eh; Elf32_Phdr ph; caddr_t p; ufs_ino_t ino; int i; if ((ino = lookup(fname)) == 0) { printf("File %s not found\n", fname); return; } if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) { printf("Can't read elf header\n"); return; } if (!IS_ELF(eh)) { printf("Not an ELF file\n"); return; } for (i = 0; i < eh.e_phnum; i++) { fs_off = eh.e_phoff + i * eh.e_phentsize; if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) { printf("Can't read program header %d\n", i); return; } if (ph.p_type != PT_LOAD) continue; fs_off = ph.p_offset; p = (caddr_t)ph.p_vaddr; ofw_claim(p,(ph.p_filesz > ph.p_memsz) ? ph.p_filesz : ph.p_memsz,0); if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) { printf("Can't read content of section %d\n", i); return; } if (ph.p_filesz != ph.p_memsz) bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz); __syncicache(p, ph.p_memsz); } ofw_close(bootdev); - (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0, - ofw,NULL,0); + (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0, + realofw, NULL, 0); } static int dskread(void *buf, uint64_t lba, int nblk) { /* * The Open Firmware should open the correct partition for us. * That means, if we read from offset zero on an open instance handle, * we should read from offset zero of that partition. */ ofw_seek(bootdev, lba * DEV_BSIZE); ofw_read(bootdev, buf, nblk * DEV_BSIZE); return (0); } static void panic(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof buf, fmt, ap); printf("panic: %s\n", buf); va_end(ap); exit(1); } static int printf(const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vprintf(fmt, ap); va_end(ap); return (ret); } static int putchar(char c, void *arg) { char buf; if (c == '\n') { buf = '\r'; ofw_write(stdouth, &buf, 1); } buf = c; ofw_write(stdouth, &buf, 1); return (1); } static int vprintf(const char *fmt, va_list ap) { int ret; ret = __printf(fmt, putchar, 0, ap); return (ret); } static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) { struct sp_data sp; int ret; sp.sp_buf = str; sp.sp_len = 0; sp.sp_size = sz; ret = __printf(fmt, __sputc, &sp, ap); return (ret); } static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap) { char buf[(sizeof(long) * 8) + 1]; char *nbuf; u_long ul; u_int ui; int lflag; int sflag; char *s; int pad; int ret; int c; nbuf = &buf[sizeof buf - 1]; ret = 0; while ((c = *fmt++) != 0) { if (c != '%') { ret += putc(c, arg); continue; } lflag = 0; sflag = 0; pad = 0; reswitch: c = *fmt++; switch (c) { case '#': sflag = 1; goto reswitch; case '%': ret += putc('%', arg); break; case 'c': c = va_arg(ap, int); ret += putc(c, arg); break; case 'd': if (lflag == 0) { ui = (u_int)va_arg(ap, int); if (ui < (int)ui) { ui = -ui; ret += putc('-', arg); } s = __uitoa(nbuf, ui, 10); } else { ul = (u_long)va_arg(ap, long); if (ul < (long)ul) { ul = -ul; ret += putc('-', arg); } s = __ultoa(nbuf, ul, 10); } ret += __puts(s, putc, arg); break; case 'l': lflag = 1; goto reswitch; case 'o': if (lflag == 0) { ui = (u_int)va_arg(ap, u_int); s = __uitoa(nbuf, ui, 8); } else { ul = (u_long)va_arg(ap, u_long); s = __ultoa(nbuf, ul, 8); } ret += __puts(s, putc, arg); break; case 'p': ul = (u_long)va_arg(ap, void *); s = __ultoa(nbuf, ul, 16); ret += __puts("0x", putc, arg); ret += __puts(s, putc, arg); break; case 's': s = va_arg(ap, char *); ret += __puts(s, putc, arg); break; case 'u': if (lflag == 0) { ui = va_arg(ap, u_int); s = __uitoa(nbuf, ui, 10); } else { ul = va_arg(ap, u_long); s = __ultoa(nbuf, ul, 10); } ret += __puts(s, putc, arg); break; case 'x': if (lflag == 0) { ui = va_arg(ap, u_int); s = __uitoa(nbuf, ui, 16); } else { ul = va_arg(ap, u_long); s = __ultoa(nbuf, ul, 16); } if (sflag) ret += __puts("0x", putc, arg); ret += __puts(s, putc, arg); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pad = pad * 10 + c - '0'; goto reswitch; default: break; } } return (ret); } static int __sputc(char c, void *arg) { struct sp_data *sp; sp = arg; if (sp->sp_len < sp->sp_size) sp->sp_buf[sp->sp_len++] = c; sp->sp_buf[sp->sp_len] = '\0'; return (1); } static int __puts(const char *s, putc_func_t *putc, void *arg) { const char *p; int ret; ret = 0; for (p = s; *p != '\0'; p++) ret += putc(*p, arg); return (ret); } static char * __uitoa(char *buf, u_int ui, int base) { char *p; p = buf; *p = '\0'; do *--p = digits[ui % base]; while ((ui /= base) != 0); return (p); } static char * __ultoa(char *buf, u_long ul, int base) { char *p; p = buf; *p = '\0'; do *--p = digits[ul % base]; while ((ul /= base) != 0); return (p); } diff --git a/stand/powerpc/ofw/Makefile b/stand/powerpc/ofw/Makefile index a35a7c7c56bf..6d75bb882d2b 100644 --- a/stand/powerpc/ofw/Makefile +++ b/stand/powerpc/ofw/Makefile @@ -1,56 +1,66 @@ # $FreeBSD$ LOADER_CD9660_SUPPORT?= yes LOADER_EXT2FS_SUPPORT?= no LOADER_MSDOS_SUPPORT?= no LOADER_UFS_SUPPORT?= yes LOADER_NET_SUPPORT?= yes LOADER_NFS_SUPPORT?= yes LOADER_TFTP_SUPPORT?= no LOADER_GZIP_SUPPORT?= yes LOADER_BZIP2_SUPPORT?= no .include PROG= loader NEWVERSWHAT= "Open Firmware loader" ${MACHINE_ARCH} INSTALLFLAGS= -b # Architecture-specific loader code SRCS= conf.c vers.c main.c elf_freebsd.c ppc64_elf_freebsd.c start.c SRCS+= ucmpdi2.c gfx_fb.c CFLAGS.gfx_fb.c += -I${SRCTOP}/contrib/pnglite CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/teken .include "${BOOTSRC}/fdt.mk" .if ${MK_FDT} == "yes" SRCS+= ofwfdt.c .endif -.if ${MACHINE_ARCH} == "powerpc64" +.if ${MACHINE_ARCH:Mpowerpc64*} != "" SRCS+= cas.c CFLAGS+= -DCAS .endif +.if ${MACHINE_ARCH} == "powerpc64le" +SRCS+= trampolineLE.S +.endif + HELP_FILES= ${FDTSRC}/help.fdt # Always add MI sources .include "${BOOTSRC}/loader.mk" .PATH: ${SYSDIR}/libkern # load address. set in linker script RELOC?= 0x1C00000 CFLAGS+= -DRELOC=${RELOC} -g -LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc +LDFLAGS= -nostdlib -static + +.if ${MACHINE_ARCH} == "powerpc64le" +LDFLAGS+= -T ${.CURDIR}/ldscript.powerpcle +.else +LDFLAGS+= -T ${.CURDIR}/ldscript.powerpc +.endif # Open Firmware standalone support library LIBOFW= ${BOOTOBJ}/libofw/libofw.a CFLAGS+= -I${BOOTSRC}/libofw DPADD= ${LDR_INTERP} ${LIBOFW} ${LIBFDT} ${LIBSA} LDADD= ${LDR_INTERP} ${LIBOFW} ${LIBFDT} ${LIBSA} .include diff --git a/stand/powerpc/ofw/cas.c b/stand/powerpc/ofw/cas.c index 4f36a147f36d..eaf69566fca7 100644 --- a/stand/powerpc/ofw/cas.c +++ b/stand/powerpc/ofw/cas.c @@ -1,259 +1,261 @@ /*- * Copyright (c) 2019 Leandro Lupori * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include +#include + /* #define CAS_DEBUG */ #ifdef CAS_DEBUG #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) #else #define DPRINTF(fmt, ...) do { ; } while (0) #endif /* PVR */ #define PVR_CPU_P8E 0x004b0000 #define PVR_CPU_P8NVL 0x004c0000 #define PVR_CPU_P8 0x004d0000 #define PVR_CPU_P9 0x004e0000 #define PVR_CPU_MASK 0xffff0000 #define PVR_ISA_207 0x0f000004 #define PVR_ISA_300 0x0f000005 #define PVR_ISA_MASK 0xffffffff /* loader version of kernel's CPU_MAXSIZE */ #define MAX_CPUS ((uint32_t)256u) /* Option Vectors' settings */ /* length of ignored OV */ #define OV_IGN_LEN 0 /* byte 1 (of any OV) */ #define OV_IGN 0x80 /* Option Vector 5 */ /* byte 2 */ #define OV5_LPAR 0x80 #define OV5_SPLPAR 0x40 #define OV5_DRMEM 0x20 #define OV5_LP 0x10 #define OV5_ALPHA_PART 0x08 #define OV5_DMA_DELAY 0x04 #define OV5_DONATE_CPU 0x02 #define OV5_MSI 0x01 /* 9-12: max cpus */ #define OV5_MAX_CPUS(n) ((MAX_CPUS >> (3*8 - (n)*8)) & 0xff) /* 13-14: LoPAPR Level */ #define LOPAPR_LEVEL 0x0101 /* 1.1 */ #define OV5_LOPAPR_LEVEL(n) ((LOPAPR_LEVEL >> (8 - (n)*8)) & 0xff) /* byte 17: Platform Facilities */ #define OV5_RNG 0x80 #define OV5_COMP_ENG 0x40 #define OV5_ENC_ENG 0x20 /* byte 21: Sub-Processors */ #define OV5_NO_SUBPROCS 0 #define OV5_SUBPROCS 1 /* byte 23: interrupt controller */ #define OV5_INTC_XICS 0 /* byte 24: MMU */ #define OV5_MMU_INDEX 24 #define OV5_MMU_HPT 0 #define OV5_MMU_RADIX 0x40 #define OV5_MMU_EITHER 0x80 #define OV5_MMU_DYNAMIC 0xc0 /* byte 25: HPT MMU Extensions */ #define OV5_HPT_EXT_INDEX 25 #define OV5_HPT_GTSE 0x40 /* byte 26: Radix MMU Extensions */ #define OV5_RADIX_EXT_INDEX 26 #define OV5_RADIX_GTSE 0x40 struct pvr { uint32_t mask; uint32_t val; }; struct opt_vec_ignore { char data[2]; } __packed; struct opt_vec4 { char data[3]; } __packed; struct opt_vec5 { char data[27]; } __packed; static struct ibm_arch_vec { struct pvr pvr_list[7]; uint8_t num_opts; struct opt_vec_ignore vec1; struct opt_vec_ignore vec2; struct opt_vec_ignore vec3; struct opt_vec4 vec4; struct opt_vec5 vec5; } __packed ibm_arch_vec = { /* pvr_list */ { - { PVR_CPU_MASK, PVR_CPU_P8 }, /* POWER8 */ - { PVR_CPU_MASK, PVR_CPU_P8E }, /* POWER8E */ - { PVR_CPU_MASK, PVR_CPU_P8NVL }, /* POWER8NVL */ - { PVR_CPU_MASK, PVR_CPU_P9 }, /* POWER9 */ - { PVR_ISA_MASK, PVR_ISA_207 }, /* All ISA 2.07 */ - { PVR_ISA_MASK, PVR_ISA_300 }, /* All ISA 3.00 */ + { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8) }, + { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8E) }, + { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8NVL) }, + { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P9) }, + { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_207) }, + { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_300) }, { 0, 0xffffffffu } /* terminator */ }, 4, /* num_opts (4 actually means 5 option vectors) */ { OV_IGN_LEN, OV_IGN }, /* OV1 */ { OV_IGN_LEN, OV_IGN }, /* OV2 */ { OV_IGN_LEN, OV_IGN }, /* OV3 */ /* OV4 (can't be ignored) */ { sizeof(struct opt_vec4) - 2, /* length (n-2) */ 0, 10 /* Minimum VP entitled capacity percentage * 100 * (if absent assume 10%) */ }, /* OV5 */ { sizeof(struct opt_vec5) - 2, /* length (n-2) */ 0, /* don't ignore */ OV5_LPAR | OV5_SPLPAR | OV5_LP | OV5_MSI, 0, 0, /* Cooperative Memory Over-commitment */ 0, /* Associativity Information Option */ 0, /* Binary Option Controls */ 0, /* Reserved */ 0, /* Reserved */ OV5_MAX_CPUS(0), OV5_MAX_CPUS(1), /* 10 */ OV5_MAX_CPUS(2), OV5_MAX_CPUS(3), OV5_LOPAPR_LEVEL(0), OV5_LOPAPR_LEVEL(1), 0, /* Reserved */ 0, /* Reserved */ 0, /* Platform Facilities */ 0, /* Reserved */ 0, /* Reserved */ 0, /* Reserved */ /* 20 */ OV5_NO_SUBPROCS, 0, /* DRMEM_V2 */ OV5_INTC_XICS, OV5_MMU_HPT, 0, 0 } }; int ppc64_cas(void) { phandle_t pkg; ihandle_t inst; cell_t err; uint8_t buf[16], idx, val; int i, len, rc, radix_mmu; const char *var; char *ov5; pkg = OF_finddevice("/chosen"); if (pkg == -1) { printf("cas: couldn't find /chosen\n"); return (-1); } len = OF_getprop(pkg, "ibm,arch-vec-5-platform-support", buf, sizeof(buf)); if (len == -1) /* CAS not supported */ return (0); radix_mmu = 0; ov5 = ibm_arch_vec.vec5.data; for (i = 0; i < len; i += 2) { idx = buf[i]; val = buf[i + 1]; DPRINTF("idx 0x%02x val 0x%02x\n", idx, val); switch (idx) { case OV5_MMU_INDEX: /* * Note that testing for OV5_MMU_RADIX/OV5_MMU_EITHER * also covers OV5_MMU_DYNAMIC. */ if ((val & OV5_MMU_RADIX) || (val & OV5_MMU_EITHER)) radix_mmu = 1; break; case OV5_RADIX_EXT_INDEX: if (val & OV5_RADIX_GTSE) ov5[idx] = OV5_RADIX_GTSE; break; case OV5_HPT_EXT_INDEX: default: break; } } if (radix_mmu && (var = getenv("radix_mmu")) != NULL && var[0] != '0') ov5[OV5_MMU_INDEX] = OV5_MMU_RADIX; else radix_mmu = 0; inst = OF_open("/"); if (inst == -1) { printf("cas: failed to open / node\n"); return (-1); } DPRINTF("MMU 0x%02x RADIX_EXT 0x%02x\n", ov5[OV5_MMU_INDEX], ov5[OV5_RADIX_EXT_INDEX]); rc = OF_call_method("ibm,client-architecture-support", inst, 1, 1, &ibm_arch_vec, &err); if (rc != 0 || err) { printf("cas: CAS method returned an error: rc %d err %jd\n", rc, (intmax_t)err); rc = -1; } OF_close(inst); printf("cas: selected %s MMU\n", radix_mmu ? "radix" : "hash"); return (rc); } diff --git a/stand/powerpc/ofw/ldscript.powerpcle b/stand/powerpc/ofw/ldscript.powerpcle new file mode 100644 index 000000000000..813459b8c242 --- /dev/null +++ b/stand/powerpc/ofw/ldscript.powerpcle @@ -0,0 +1,142 @@ +/* $FreeBSD$ */ + +OUTPUT_FORMAT("elf32-powerpcle-freebsd", "elf32-powerpcle-freebsd", + "elf32-powerpcle-freebsd") +OUTPUT_ARCH(powerpcle:common) +ENTRY(_start) +SEARCH_DIR(/usr/lib); +PROVIDE (__stack = 0); +PHDRS +{ + text PT_LOAD; +} +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x02c00000 + SIZEOF_HEADERS; + .interp : { *(.interp) } :text + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rela.got : { *(.rela.got) } + .rela.got1 : { *(.rela.got1) } + .rela.got2 : { *(.rela.got2) } + .rela.ctors : { *(.rela.ctors) } + .rela.dtors : { *(.rela.dtors) } + .rela.init : { *(.rela.init) } + .rela.fini : { *(.rela.fini) } + .rela.bss : { *(.rela.bss) } + .rela.plt : { *(.rela.plt) } + .rela.sdata : { *(.rela.sdata) } + .rela.sbss : { *(.rela.sbss) } + .rela.sdata2 : { *(.rela.sdata2) } + .rela.sbss2 : { *(.rela.sbss2) } + .text : + { + *(.text) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0 + _etext = .; + PROVIDE (etext = .); + .init : { *(.init) } =0 + .fini : { *(.fini) } =0 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + .sdata2 : { *(.sdata2) } + .sbss2 : { *(.sbss2) } + /* Adjust the address for the data segment to the next page up. */ + . = ((. + 0x1000) & ~(0x1000 - 1)); + .data : + { + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .got1 : { *(.got1) } + .dynamic : { *(.dynamic) } + /* Put .ctors and .dtors next to the .got2 section, so that the pointers + get relocated with -mrelocatable. Also put in the .fixup pointers. + The current compiler no longer needs this, but keep it around for 2.7.2 */ + PROVIDE (_GOT2_START_ = .); + .got2 : { *(.got2) } + PROVIDE (__CTOR_LIST__ = .); + .ctors : { *(.ctors) } + PROVIDE (__CTOR_END__ = .); + PROVIDE (__DTOR_LIST__ = .); + .dtors : { *(.dtors) } + PROVIDE (__DTOR_END__ = .); + PROVIDE (_FIXUP_START_ = .); + .fixup : { *(.fixup) } + PROVIDE (_FIXUP_END_ = .); + PROVIDE (_GOT2_END_ = .); + PROVIDE (_GOT_START_ = .); + .got : { *(.got) } + .got.plt : { *(.got.plt) } + PROVIDE (_GOT_END_ = .); + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + .sbss : + { + PROVIDE (__sbss_start = .); + *(.sbss) + *(.scommon) + *(.dynsbss) + PROVIDE (__sbss_end = .); + } + .plt : { *(.plt) } + .bss : + { + PROVIDE (__bss_start = .); + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} + diff --git a/stand/powerpc/ofw/main.c b/stand/powerpc/ofw/main.c index 1686f1b03581..19b73161c18b 100644 --- a/stand/powerpc/ofw/main.c +++ b/stand/powerpc/ofw/main.c @@ -1,219 +1,250 @@ /*- * Copyright (c) 2000 Benno Rice * Copyright (c) 2000 Stephane Potvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); +#include + #include #include "openfirm.h" #include "libofw.h" #include "bootstrap.h" +#include #include struct arch_switch archsw; /* MI/MD interface boundary */ extern char end[]; uint32_t acells, scells; static char bootargs[128]; #define HEAP_SIZE 0x800000 static char heap[HEAP_SIZE]; // In BSS, so uses no space #define OF_puts(fd, text) OF_write(fd, text, strlen(text)) static __inline register_t mfmsr(void) { register_t value; __asm __volatile ("mfmsr %0" : "=r"(value)); return (value); } void init_heap(void) { bzero(heap, HEAP_SIZE); setheap(heap, (void *)((uintptr_t)heap + HEAP_SIZE)); } uint64_t memsize(void) { phandle_t memoryp; cell_t reg[24]; int i, sz; uint64_t memsz; memsz = 0; memoryp = OF_instance_to_package(memory); - sz = OF_getprop(memoryp, "reg", ®, sizeof(reg)); + sz = OF_getencprop(memoryp, "reg", ®[0], sizeof(reg)); sz /= sizeof(reg[0]); for (i = 0; i < sz; i += (acells + scells)) { if (scells > 1) memsz += (uint64_t)reg[i + acells] << 32; memsz += reg[i + acells + scells - 1]; } return (memsz); } #ifdef CAS extern int ppc64_cas(void); static int ppc64_autoload(void) { const char *cas; if ((cas = getenv("cas")) && cas[0] == '1') if (ppc64_cas() != 0) return (-1); return (ofw_autoload()); } #endif +#if BYTE_ORDER == LITTLE_ENDIAN +/* + * In Little-endian, we cannot just branch to the client interface. Since + * the client interface is big endian, we have to rfid to it. + * Likewise, when execution resumes, we are in the wrong endianness so + * we must do a fixup before returning to the caller. + */ +static int (*openfirmware_entry)(void *); +extern int openfirmware_trampoline(void *buf, int (*cb)(void *)); + +/* + * Wrapper to pass the real entry point to our trampoline. + */ +static int +openfirmware_docall(void *buf) +{ + return openfirmware_trampoline(buf, openfirmware_entry); +} +#endif + int main(int (*openfirm)(void *)) { phandle_t root; int i; char bootpath[64]; char *ch; int bargc; char **bargv; /* * Initialise the Open Firmware routines by giving them the entry point. */ +#if BYTE_ORDER == LITTLE_ENDIAN + /* + * Use a trampoline entry point for endian fixups. + */ + openfirmware_entry = openfirm; + OF_init(openfirmware_docall); +#else OF_init(openfirm); +#endif root = OF_finddevice("/"); scells = acells = 1; - OF_getprop(root, "#address-cells", &acells, sizeof(acells)); - OF_getprop(root, "#size-cells", &scells, sizeof(scells)); + OF_getencprop(root, "#address-cells", &acells, sizeof(acells)); + OF_getencprop(root, "#size-cells", &scells, sizeof(scells)); /* * Initialise the heap as early as possible. Once this is done, * alloc() is usable. The stack is buried inside us, so this is * safe. */ init_heap(); /* * Set up console. */ cons_probe(); /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); printf("\n%s", bootprog_info); printf("Memory: %lldKB\n", memsize() / 1024); OF_getprop(chosen, "bootpath", bootpath, 64); ch = strchr(bootpath, ':'); *ch = '\0'; printf("Booted from: %s\n", bootpath); printf("\n"); /* * Only parse the first bootarg if present. It should * be simple to handle extra arguments */ OF_getprop(chosen, "bootargs", bootargs, sizeof(bootargs)); bargc = 0; parse(&bargc, &bargv, bootargs); if (bargc == 1) env_setenv("currdev", EV_VOLATILE, bargv[0], ofw_setcurrdev, env_nounset); else env_setenv("currdev", EV_VOLATILE, bootpath, ofw_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, bootpath, env_noset, env_nounset); setenv("LINES", "24", 1); /* optional */ /* * On non-Apple hardware, where it works reliably, pass flattened * device trees to the kernel by default instead of OF CI pointers. * Apple hardware is the only virtual-mode OF implementation in * existence, so far as I am aware, so use that as a flag. */ if (!(mfmsr() & PSL_DR)) setenv("usefdt", "1", 1); archsw.arch_getdev = ofw_getdev; archsw.arch_copyin = ofw_copyin; archsw.arch_copyout = ofw_copyout; archsw.arch_readin = ofw_readin; #ifdef CAS setenv("cas", "1", 0); archsw.arch_autoload = ppc64_autoload; #else archsw.arch_autoload = ofw_autoload; #endif interact(); /* doesn't return */ OF_exit(); return 0; } COMMAND_SET(halt, "halt", "halt the system", command_halt); static int command_halt(int argc, char *argv[]) { OF_exit(); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); int command_memmap(int argc, char **argv) { ofw_memmap(acells); return (CMD_OK); } diff --git a/stand/powerpc/ofw/ofwfdt.c b/stand/powerpc/ofw/ofwfdt.c index 92ae9f553134..ff1458ed2edf 100644 --- a/stand/powerpc/ofw/ofwfdt.c +++ b/stand/powerpc/ofw/ofwfdt.c @@ -1,220 +1,219 @@ /*- * Copyright (C) 2014-2015 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH 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 "bootstrap.h" extern int command_fdt_internal(int argc, char *argv[]); static int OF_hasprop(phandle_t node, const char *prop) { return (OF_getproplen(node, (char *)prop) > 0); } static void add_node_to_fdt(void *buffer, phandle_t node, int fdt_offset) { int i, child_offset, error; char name[255], *lastprop, *subname; void *propbuf; ssize_t proplen; lastprop = NULL; while (OF_nextprop(node, lastprop, name) > 0) { proplen = OF_getproplen(node, name); /* Detect and correct for errors and strangeness */ if (proplen < 0) proplen = 0; if (proplen > 1024) proplen = 1024; propbuf = malloc(proplen); if (propbuf == NULL) { printf("Cannot allocate memory for prop %s\n", name); return; } OF_getprop(node, name, propbuf, proplen); error = fdt_setprop(buffer, fdt_offset, name, propbuf, proplen); free(propbuf); lastprop = name; if (error) printf("Error %d adding property %s to " "node %d\n", error, name, fdt_offset); } if (!OF_hasprop(node, "phandle") && !OF_hasprop(node, "linux,phandle") && !OF_hasprop(node, "ibm,phandle")) fdt_setprop(buffer, fdt_offset, "phandle", &node, sizeof(node)); for (node = OF_child(node); node > 0; node = OF_peer(node)) { OF_package_to_path(node, name, sizeof(name)); subname = strrchr(name, '/'); subname++; child_offset = fdt_add_subnode(buffer, fdt_offset, subname); if (child_offset < 0) { printf("Error %d adding node %s (%s), skipping\n", child_offset, name, subname); continue; } add_node_to_fdt(buffer, node, child_offset); } } static void ofwfdt_fixups(void *fdtp) { int offset, len, i; phandle_t node; ihandle_t rtas; const void *prop; /* * Instantiate and add reservations for RTAS state if present */ offset = fdt_path_offset(fdtp, "/rtas"); if (offset > 0) { uint32_t base; void *rtasmem; char path[255]; node = OF_finddevice("/rtas"); OF_package_to_path(node, path, sizeof(path)); - OF_getprop(node, "rtas-size", &len, sizeof(len)); + OF_getencprop(node, "rtas-size", &len, sizeof(len)); /* Allocate memory */ rtasmem = OF_claim(0, len, 4096); /* Instantiate RTAS */ rtas = OF_open(path); base = 0; OF_call_method("instantiate-rtas", rtas, 1, 1, (cell_t)rtasmem, &base); /* Store info to FDT using Linux convention */ base = cpu_to_fdt32(base); fdt_setprop(fdtp, offset, "linux,rtas-entry", &base, sizeof(base)); base = cpu_to_fdt32((uint32_t)rtasmem); offset = fdt_path_offset(fdtp, "/rtas"); fdt_setprop(fdtp, offset, "linux,rtas-base", &base, sizeof(base)); /* Mark RTAS private data area reserved */ + base = fdt32_to_cpu(base); fdt_add_mem_rsv(fdtp, base, len); } else { /* * Remove /memory/available properties, which reflect long-gone * OF state. Note that this doesn't work if we need RTAS still, * since that's part of the firmware. */ offset = fdt_path_offset(fdtp, "/memory@0"); if (offset > 0) fdt_delprop(fdtp, offset, "available"); } /* * Convert stored ihandles under /chosen to xref phandles */ offset = fdt_path_offset(fdtp, "/chosen"); if (offset > 0) { const char *chosenprops[] = {"stdout", "stdin", "mmu", "cpu", NULL}; const uint32_t *ihand; for (i = 0; chosenprops[i] != NULL; i++) { ihand = fdt_getprop(fdtp, offset, chosenprops[i], &len); if (ihand != NULL && len == sizeof(*ihand)) { - node = OF_instance_to_package( - fdt32_to_cpu(*ihand)); + node = OF_instance_to_package(*ihand); if (OF_hasprop(node, "phandle")) OF_getprop(node, "phandle", &node, sizeof(node)); else if (OF_hasprop(node, "linux,phandle")) OF_getprop(node, "linux,phandle", &node, sizeof(node)); else if (OF_hasprop(node, "ibm,phandle")) OF_getprop(node, "ibm,phandle", &node, sizeof(node)); - node = cpu_to_fdt32(node); fdt_setprop(fdtp, offset, chosenprops[i], &node, sizeof(node)); } /* Refind node in case it moved */ offset = fdt_path_offset(fdtp, "/chosen"); } } } int fdt_platform_load_dtb(void) { void *buffer; size_t buflen = 409600; buffer = malloc(buflen); fdt_create_empty_tree(buffer, buflen); add_node_to_fdt(buffer, OF_peer(0), fdt_path_offset(buffer, "/")); ofwfdt_fixups(buffer); fdt_pack(buffer); fdt_load_dtb_addr(buffer); free(buffer); return (0); } void fdt_platform_load_overlays(void) { } void fdt_platform_fixups(void) { } static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); diff --git a/stand/powerpc/ofw/trampolineLE.S b/stand/powerpc/ofw/trampolineLE.S new file mode 100644 index 000000000000..cd940fb3dfee --- /dev/null +++ b/stand/powerpc/ofw/trampolineLE.S @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Brandon Bergren + * + * 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$ + */ + +#include + +/** + * int openfirmware_trampoline(void *buf, int (*cb)(void *)); + */ +ASENTRY_NOPROF(openfirmware_trampoline) + mflr %r0 + stw %r0, 4(%r1) + stwu %r1, -16(%r1) + stw %r30, 8(%r1) + /* Save current MSR for restoration post-call. */ + mfmsr %r30 + mr %r5, %r30 + /* Remove LE bit from MSR. */ + clrrwi %r5, %r5, 1 + mtsrr0 %r4 + mtsrr1 %r5 + LOAD_LR_NIA +1: + mflr %r4 + addi %r4, %r4, (2f - 1b) + mtlr %r4 + /* Switch to BE and transfer control to OF entry */ + rfid +2: + /* Control is returned here, but in BE. */ + .long 0x05009f42 /* LOAD_LR_NIA */ + /* 0: */ + .long 0xa603db7f /* mtsrr1 %r30 */ + .long 0xa602c87f /* mflr %r30 */ + .long 0x1400de3b /* addi %r30, %r30, (1f - 0b) */ + .long 0xa603da7f /* mtsrr0 %r30 */ + .long 0x2400004c /* rfid */ + /* 1: */ +1: + /* Back to normal. Tidy up for return. */ + lwz %r30, 8(%r1) + lwz %r0, 20(%r1) + addi %r1, %r1, 16 + mtlr %r0 + blr +ASEND(openfirmware_trampoline)