Author: bdragon
Date: Thu May  7 19:32:49 2020
New Revision: 360794
URL: https://svnweb.freebsd.org/changeset/base/360794

Log:
  [PowerPC] kernel ifunc support for powerpc*, fix ppc64 relocation oddities.
  
  This is a general cleanup of the relocatable kernel support on powerpc,
  needed to enable kernel ifuncs.
  
   * Fix some relocatable issues in the kernel linker, and change to using
     a RELOCATABLE_KERNEL #define instead of #ifdef __powerpc__ for parts that
     other platforms can use in the future if they wish to have ET_DYN kernels.
  
   * Get rid of the DB_STOFFS hack now that the kernel is relocated to the DMAP
     properly across the board on powerpc64.
  
   * Add powerpc64 and powerpc32 ifunc functionality.
  
   * Allow AIM64 virtual mode OF kernels to run from the DMAP like other AIM64
     by implementing a virtual mode restart. This fixes the runtime address on
     PowerMac G5.
  
   * Fix symbol relocation problems on post-relocation kernels by relocating
     the symbol table.
  
   * Add an undocumented method for supplying kernel symbols on powernv and
     other powerpc machines using linux-style kernel/initrd loading -- If
     you pass the kernel in as the initrd as well, the copy resident in initrd
     will be used as a source for symbols when initializing the debugger.
     This method is subject to removal once we have a better way of doing this.
  
  Approved by:  jhibbits
  Relnotes:     yes
  Sponsored by: Tag1 Consulting, Inc.
  Differential Revision:        https://reviews.freebsd.org/D23156

Modified:
  head/sys/conf/kern.pre.mk
  head/sys/conf/ldscript.powerpc
  head/sys/conf/ldscript.powerpc64
  head/sys/conf/ldscript.powerpcspe
  head/sys/kern/link_elf.c
  head/sys/powerpc/aim/aim_machdep.c
  head/sys/powerpc/aim/locore64.S
  head/sys/powerpc/include/db_machdep.h
  head/sys/powerpc/include/param.h
  head/sys/powerpc/ofw/ofw_initrd.c
  head/sys/powerpc/powerpc/elf32_machdep.c
  head/sys/powerpc/powerpc/elf64_machdep.c
  head/sys/powerpc/powerpc/machdep.c

Modified: head/sys/conf/kern.pre.mk
==============================================================================
--- head/sys/conf/kern.pre.mk   Thu May  7 19:19:39 2020        (r360793)
+++ head/sys/conf/kern.pre.mk   Thu May  7 19:32:49 2020        (r360794)
@@ -161,9 +161,9 @@ LDFLAGS+=   --build-id=sha1
 .endif
 
 .if (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \
-    ${MACHINE_CPUARCH} == "i386") && \
+    ${MACHINE_CPUARCH} == "i386" || ${MACHINE} == "powerpc") && \
     defined(LINKER_FEATURES) && ${LINKER_FEATURES:Mifunc} == ""
-.error amd64/arm64/i386 kernel requires linker ifunc support
+.error amd64/arm64/i386/ppc* kernel requires linker ifunc support
 .endif
 .if ${MACHINE_CPUARCH} == "amd64"
 LDFLAGS+=      -z max-page-size=2097152

Modified: head/sys/conf/ldscript.powerpc
==============================================================================
--- head/sys/conf/ldscript.powerpc      Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/conf/ldscript.powerpc      Thu May  7 19:32:49 2020        
(r360794)
@@ -6,6 +6,11 @@ OUTPUT_ARCH(powerpc)
 ENTRY(__start)
 SEARCH_DIR(/usr/lib);
 PROVIDE (__stack = 0);
+PHDRS
+{
+       kernel PT_LOAD;
+       dynamic PT_DYNAMIC;
+}
 SECTIONS
 {
   /* Read-only sections, merged into text segment: */
@@ -21,7 +26,7 @@ SECTIONS
     /* .gnu.warning sections are handled specially by elf32.em.  */
     *(.gnu.warning)
     *(.gnu.linkonce.t*)
-  } =0
+  } :kernel =0
   _etext = .;
   PROVIDE (etext = .);
 
@@ -77,7 +82,7 @@ SECTIONS
   .got.plt        : { *(.got.plt) }
 
 
-  .dynamic        : { *(.dynamic) }
+  .dynamic        : { *(.dynamic) } :kernel :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  
*/
@@ -96,7 +101,7 @@ SECTIONS
   /* 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) }
+  .sdata     : { *(.sdata) } :kernel
   _edata  =  .;
   PROVIDE (edata = .);
   .sbss      :

Modified: head/sys/conf/ldscript.powerpc64
==============================================================================
--- head/sys/conf/ldscript.powerpc64    Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/conf/ldscript.powerpc64    Thu May  7 19:32:49 2020        
(r360794)
@@ -8,15 +8,15 @@ SEARCH_DIR(/usr/lib);
 PROVIDE (__stack = 0);
 PHDRS
 {
-       text PT_LOAD ;
-       dynamic PT_DYNAMIC ;
+       kernel PT_LOAD;
+       dynamic PT_DYNAMIC;
 }
 SECTIONS
 {
 
   /* Low-address wrapper for bootloaders (kexec/kboot) that can't parse ELF */
   . = kernbase - 0x100;
-  .kboot : { *(.text.kboot) } :text
+  .kboot : { *(.text.kboot) } :kernel
 
   /* Read-only sections, merged into text segment: */
   . = kernbase;
@@ -106,7 +106,7 @@ SECTIONS
   .got       : ALIGN(8) { __tocbase = .; *(.got) }
   .toc       : ALIGN(8) { *(.toc) }
 
-  .dynamic        : { *(.dynamic) } :text :dynamic
+  .dynamic        : { *(.dynamic) } :kernel :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  
*/
@@ -125,7 +125,7 @@ SECTIONS
   /* 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) }
+  .sdata     : { *(.sdata) } :kernel
   _edata  =  .;
   PROVIDE (edata = .);
   .sbss      :

Modified: head/sys/conf/ldscript.powerpcspe
==============================================================================
--- head/sys/conf/ldscript.powerpcspe   Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/conf/ldscript.powerpcspe   Thu May  7 19:32:49 2020        
(r360794)
@@ -6,6 +6,11 @@ OUTPUT_ARCH(powerpc)
 ENTRY(__start)
 SEARCH_DIR(/usr/lib);
 PROVIDE (__stack = 0);
+PHDRS
+{
+       kernel PT_LOAD;
+       dynamic PT_DYNAMIC;
+}
 SECTIONS
 {
   /* Read-only sections, merged into text segment: */
@@ -21,7 +26,7 @@ SECTIONS
     /* .gnu.warning sections are handled specially by elf32.em.  */
     *(.gnu.warning)
     *(.gnu.linkonce.t*)
-  } =0
+  } :kernel =0
   _etext = .;
   PROVIDE (etext = .);
 
@@ -78,7 +83,7 @@ SECTIONS
   .got.plt        : { *(.got.plt) }
 
 
-  .dynamic        : { *(.dynamic) }
+  .dynamic        : { *(.dynamic) } :kernel :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  
*/
@@ -97,7 +102,7 @@ SECTIONS
   /* 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) }
+  .sdata     : { *(.sdata) } :kernel
   _edata  =  .;
   PROVIDE (edata = .);
   .sbss      :

Modified: head/sys/kern/link_elf.c
==============================================================================
--- head/sys/kern/link_elf.c    Thu May  7 19:19:39 2020        (r360793)
+++ head/sys/kern/link_elf.c    Thu May  7 19:32:49 2020        (r360794)
@@ -388,7 +388,9 @@ link_elf_link_common_finish(linker_file_t lf)
        return (0);
 }
 
+#ifdef RELOCATABLE_KERNEL
 extern vm_offset_t __startkernel, __endkernel;
+#endif
 
 static unsigned long kern_relbase = KERNBASE;
 
@@ -424,7 +426,7 @@ link_elf_init(void* arg)
 
        ef = (elf_file_t) linker_kernel_file;
        ef->preloaded = 1;
-#ifdef __powerpc__
+#ifdef RELOCATABLE_KERNEL
        ef->address = (caddr_t) (__startkernel - KERNBASE);
 #else
        ef->address = 0;
@@ -436,7 +438,7 @@ link_elf_init(void* arg)
 
        if (dp != NULL)
                parse_dynamic(ef);
-#ifdef __powerpc__
+#ifdef RELOCATABLE_KERNEL
        linker_kernel_file->address = (caddr_t)__startkernel;
        linker_kernel_file->size = (intptr_t)(__endkernel - __startkernel);
        kern_relbase = (unsigned long)__startkernel;
@@ -1860,7 +1862,7 @@ link_elf_strtab_get(linker_file_t lf, caddr_t *strtab)
        return (ef->ddbstrcnt);
 }
 
-#if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
+#if defined(__i386__) || defined(__amd64__) || defined(__aarch64__) || 
defined(__powerpc__)
 /*
  * Use this lookup routine when performing relocations early during boot.
  * The generic lookup routine depends on kobj, which is not initialized
@@ -1896,8 +1898,14 @@ link_elf_ireloc(caddr_t kmdp)
 
        ef->modptr = kmdp;
        ef->dynamic = (Elf_Dyn *)&_DYNAMIC;
-       parse_dynamic(ef);
+
+#ifdef RELOCATABLE_KERNEL
+       ef->address = (caddr_t) (__startkernel - KERNBASE);
+#else
        ef->address = 0;
+#endif
+       parse_dynamic(ef);
+
        link_elf_preload_parse_symbols(ef);
        relocate_file1(ef, elf_lookup_ifunc, elf_reloc, true);
 }

Modified: head/sys/powerpc/aim/aim_machdep.c
==============================================================================
--- head/sys/powerpc/aim/aim_machdep.c  Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/aim/aim_machdep.c  Thu May  7 19:32:49 2020        
(r360794)
@@ -161,6 +161,7 @@ extern void *dsmisstrap, *dsmisssize;
 
 extern void *ap_pcpu;
 extern void __restartkernel(vm_offset_t, vm_offset_t, vm_offset_t, void *, 
uint32_t, register_t offset, register_t msr);
+extern void __restartkernel_virtual(vm_offset_t, vm_offset_t, vm_offset_t, 
void *, uint32_t, register_t offset, register_t msr);
 
 void aim_early_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry,
     void *mdp, uint32_t mdp_cookie);
@@ -184,13 +185,22 @@ aim_early_init(vm_offset_t fdt, vm_offset_t toc, vm_of
 
 #ifdef __powerpc64__
        /*
-        * If in real mode, relocate to high memory so that the kernel
+        * Relocate to high memory so that the kernel
         * can execute from the direct map.
+        *
+        * If we are in virtual mode already, use a special entry point
+        * that sets up a temporary DMAP to execute from until we can
+        * properly set up the MMU.
         */
-       if (!(mfmsr() & PSL_DR) &&
-           (vm_offset_t)&aim_early_init < DMAP_BASE_ADDRESS)
-               __restartkernel(fdt, 0, ofentry, mdp, mdp_cookie,
-                   DMAP_BASE_ADDRESS, mfmsr());
+       if ((vm_offset_t)&aim_early_init < DMAP_BASE_ADDRESS) {
+               if (mfmsr() & PSL_DR) {
+                       __restartkernel_virtual(fdt, 0, ofentry, mdp,
+                           mdp_cookie, DMAP_BASE_ADDRESS, mfmsr());
+               } else {
+                       __restartkernel(fdt, 0, ofentry, mdp, mdp_cookie,
+                           DMAP_BASE_ADDRESS, mfmsr());
+               }
+       }
 #endif
 
        /* Various very early CPU fix ups */

Modified: head/sys/powerpc/aim/locore64.S
==============================================================================
--- head/sys/powerpc/aim/locore64.S     Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/aim/locore64.S     Thu May  7 19:32:49 2020        
(r360794)
@@ -200,6 +200,57 @@ ASENTRY_NOPROF(__start)
        /* Unreachable */
        b       .
 
+ASENTRY_NOPROF(__restartkernel_virtual)
+       /*
+        * When coming in via this entry point, we need to alter the SLB to
+        * shadow the segment register emulation entries in DMAP space.
+        * We need to do this dance because we are running with virtual-mode
+        * OpenFirmware and have not yet taken over the MMU.
+        *
+        * Assumptions:
+        * 1) The kernel is currently identity-mapped.
+        * 2) We are currently executing at an address compatible with
+        *    real mode.
+        * 3) The first 16 SLB entries are emulating SRs.
+        * 4) The rest of the SLB is not in use.
+        * 5) OpenFirmware is not manipulating the SLB at runtime.
+        * 6) We are running on 64-bit AIM.
+        *
+        * Tested on a G5.
+        */
+       mfmsr   %r14
+       /* Switch to real mode because we are about to mess with the SLB. */
+       andi.   %r14, %r14, ~(PSL_DR|PSL_IR|PSL_ME|PSL_RI)@l
+       mtmsr   %r14
+       isync
+       /* Prepare variables for later use. */
+       li      %r14, 0
+       li      %r18, 0
+       oris    %r18, %r18, 0xc000
+       sldi    %r18, %r18, 32          /* r18: 0xc000000000000000 */
+1:
+       /*
+        * Loop over the first 16 SLB entries.
+        * Offset the SLBE into the DMAP, add 16 to the index, and write
+        * it back to the SLB.
+        */
+       /* XXX add more safety checks */
+       slbmfev %r15, %r14
+       slbmfee %r16, %r14
+       or      %r16, %r16, %r14        /* index is 0-15 */
+       ori     %r16, %r16, 0x10        /* add 16 to index. */
+       or      %r16, %r16, %r18        /* SLBE DMAP offset */
+       rldicr  %r17, %r16, 0, 37       /* Invalidation SLBE */
+
+       isync
+       slbie   %r17
+       /* isync */
+       slbmte  %r15, %r16
+       isync
+       addi    %r14, %r14, 1
+       cmpdi   %r14, 16
+       blt     1b
+
 ASENTRY_NOPROF(__restartkernel)
        /*
         * r3-r7: arguments to go to __start

Modified: head/sys/powerpc/include/db_machdep.h
==============================================================================
--- head/sys/powerpc/include/db_machdep.h       Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/include/db_machdep.h       Thu May  7 19:32:49 2020        
(r360794)
@@ -85,8 +85,4 @@ typedef       intptr_t        db_expr_t;      /* expression - 
signed */
 #define        inst_load(ins)          0
 #define        inst_store(ins)         0
 
-#ifdef __powerpc64__
-#define DB_STOFFS(offs)                ((offs) & ~DMAP_BASE_ADDRESS)
-#endif
-
 #endif /* _POWERPC_DB_MACHDEP_H_ */

Modified: head/sys/powerpc/include/param.h
==============================================================================
--- head/sys/powerpc/include/param.h    Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/include/param.h    Thu May  7 19:32:49 2020        
(r360794)
@@ -109,6 +109,8 @@
 
 #define        MAXPAGESIZES    1               /* maximum number of supported 
page sizes */
 
+#define        RELOCATABLE_KERNEL      1               /* kernel may relocate 
during startup */
+
 #ifndef KSTACK_PAGES
 #ifdef __powerpc64__
 #define        KSTACK_PAGES            8               /* includes pcb */

Modified: head/sys/powerpc/ofw/ofw_initrd.c
==============================================================================
--- head/sys/powerpc/ofw/ofw_initrd.c   Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/ofw/ofw_initrd.c   Thu May  7 19:32:49 2020        
(r360794)
@@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$");
 #include <vm/pmap.h>
 
 #include <machine/bus.h>
+#include <machine/elf.h>
+#include <machine/param.h>
 
 #include <dev/ofw/openfirm.h>
 #include <dev/ofw/ofw_bus.h>
@@ -58,6 +60,8 @@ ofw_initrd_probe_and_attach(void *junk)
        vm_paddr_t start, end;
        pcell_t cell[2];
        ssize_t size;
+       u_char *taste;
+       Elf_Ehdr ehdr;
 
        if (!hw_direct_map)
                return;
@@ -91,7 +95,15 @@ ofw_initrd_probe_and_attach(void *junk)
        }
 
        if (end - start > 0) {
-               mfs_root = (u_char *) PHYS_TO_DMAP(start);
+               taste = (u_char*) PHYS_TO_DMAP(start);
+               memcpy(&ehdr, taste, sizeof(ehdr));
+
+               if (IS_ELF(ehdr)) {
+                       printf("ofw_initrd: initrd is kernel image!\n");
+                       return;
+               }
+
+               mfs_root = taste;
                mfs_root_size = end - start;
                printf("ofw_initrd: initrd loaded at 0x%08lx-0x%08lx\n",
                        start, end);

Modified: head/sys/powerpc/powerpc/elf32_machdep.c
==============================================================================
--- head/sys/powerpc/powerpc/elf32_machdep.c    Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/powerpc/elf32_machdep.c    Thu May  7 19:32:49 2020        
(r360794)
@@ -221,10 +221,10 @@ elf32_dump_thread(struct thread *td, void *dst, size_t
 
 #ifndef __powerpc64__
 bool
-elf_is_ifunc_reloc(Elf_Size r_info __unused)
+elf_is_ifunc_reloc(Elf_Size r_info)
 {
 
-       return (false);
+       return (ELF_R_TYPE(r_info) == R_PPC_IRELATIVE);
 }
 
 /* Process one elf relocation with addend. */
@@ -235,7 +235,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbas
        Elf_Addr *where;
        Elf_Half *hwhere;
        Elf_Addr addr;
-       Elf_Addr addend;
+       Elf_Addr addend, val;
        Elf_Word rtype, symidx;
        const Elf_Rela *rela;
        int error;
@@ -315,6 +315,13 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbas
                if (error != 0)
                        return -1;
                *where = elf_relocaddr(lf, addr + addend);
+               break;
+
+       case R_PPC_IRELATIVE:
+               addr = relocbase + addend;
+               val = ((Elf32_Addr (*)(void))addr)();
+               if (*where != val)
+                       *where = val;
                break;
 
        default:

Modified: head/sys/powerpc/powerpc/elf64_machdep.c
==============================================================================
--- head/sys/powerpc/powerpc/elf64_machdep.c    Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/powerpc/elf64_machdep.c    Thu May  7 19:32:49 2020        
(r360794)
@@ -282,10 +282,10 @@ elf64_dump_thread(struct thread *td, void *dst, size_t
 }
 
 bool
-elf_is_ifunc_reloc(Elf_Size r_info __unused)
+elf_is_ifunc_reloc(Elf_Size r_info)
 {
 
-       return (false);
+       return (ELF_R_TYPE(r_info) == R_PPC_IRELATIVE);
 }
 
 /* Process one elf relocation with addend. */
@@ -295,7 +295,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbas
 {
        Elf_Addr *where;
        Elf_Addr addr;
-       Elf_Addr addend;
+       Elf_Addr addend, val;
        Elf_Word rtype, symidx;
        const Elf_Rela *rela;
        int error;
@@ -340,6 +340,13 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbas
                *where = addr;
 #endif
                __asm __volatile("dcbst 0,%0; sync" :: "r"(where) : "memory");
+               break;
+
+       case R_PPC_IRELATIVE:
+               addr = relocbase + addend;
+               val = ((Elf64_Addr (*)(void))addr)();
+               if (*where != val)
+                       *where = val;
                break;
 
        default:

Modified: head/sys/powerpc/powerpc/machdep.c
==============================================================================
--- head/sys/powerpc/powerpc/machdep.c  Thu May  7 19:19:39 2020        
(r360793)
+++ head/sys/powerpc/powerpc/machdep.c  Thu May  7 19:32:49 2020        
(r360794)
@@ -113,6 +113,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/elf.h>
 #include <machine/fpu.h>
 #include <machine/hid.h>
+#include <machine/ifunc.h>
 #include <machine/kdb.h>
 #include <machine/md_var.h>
 #include <machine/metadata.h>
@@ -161,6 +162,8 @@ SYSCTL_INT(_machdep, CPU_CACHELINE, cacheline_size,
 uintptr_t      powerpc_init(vm_offset_t, vm_offset_t, vm_offset_t, void *,
                    uint32_t);
 
+static void    fake_preload_metadata(void);
+
 long           Maxmem = 0;
 long           realmem = 0;
 
@@ -246,6 +249,11 @@ void aim_early_init(vm_offset_t fdt, vm_offset_t toc, 
 void aim_cpu_init(vm_offset_t toc);
 void booke_cpu_init(void);
 
+#ifdef DDB
+static void    load_external_symtab(void);
+static void    displace_symbol_table(vm_offset_t, vm_offset_t, vm_offset_t);
+#endif
+
 uintptr_t
 powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry, void *mdp,
     uint32_t mdp_cookie)
@@ -254,10 +262,13 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
        struct cpuref   bsp;
        vm_offset_t     startkernel, endkernel;
        char            *env;
+       void            *kmdp = NULL;
         bool           ofw_bootargs = false;
+       bool            symbols_provided = false;
 #ifdef DDB
        vm_offset_t ksym_start;
        vm_offset_t ksym_end;
+       vm_offset_t ksym_sz;
 #endif
 
        /* First guess at start/end kernel positions */
@@ -287,15 +298,29 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
 #endif
 
        /*
+        * At this point, we are executing in our correct memory space.
+        * Book-E started there, and AIM has done an rfi and restarted
+        * execution from _start.
+        *
+        * We may still be in real mode, however. If we are running out of
+        * the direct map on 64 bit, this is possible to do.
+        */
+
+       /*
         * Parse metadata if present and fetch parameters.  Must be done
         * before console is inited so cninit gets the right value of
         * boothowto.
         */
        if (mdp != NULL) {
-               void *kmdp = NULL;
+               /*
+                * Starting up from loader.
+                *
+                * Full metadata has been provided, but we need to figure
+                * out the correct address to relocate it to.
+                */
                char *envp = NULL;
                uintptr_t md_offset = 0;
-               vm_paddr_t kernelendphys;
+               vm_paddr_t kernelstartphys, kernelendphys;
 
 #ifdef AIM
                if ((uintptr_t)&powerpc_init > DMAP_BASE_ADDRESS)
@@ -306,6 +331,7 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
 
                preload_metadata = mdp;
                if (md_offset > 0) {
+                       /* Translate phys offset into DMAP offset. */
                        preload_metadata += md_offset;
                        preload_bootstrap_relocate(md_offset);
                }
@@ -321,6 +347,9 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
                                if (fdt != 0)
                                        fdt += md_offset;
                        }
+                       kernelstartphys = MD_FETCH(kmdp, MODINFO_ADDR,
+                           vm_offset_t);
+                       /* kernelstartphys is already relocated. */
                        kernelendphys = MD_FETCH(kmdp, MODINFOMD_KERNEND,
                            vm_offset_t);
                        if (kernelendphys != 0)
@@ -329,13 +358,35 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
 #ifdef DDB
                        ksym_start = MD_FETCH(kmdp, MODINFOMD_SSYM, uintptr_t);
                        ksym_end = MD_FETCH(kmdp, MODINFOMD_ESYM, uintptr_t);
+                       ksym_sz = *(Elf_Size*)ksym_start;
+
+                       /*
+                        * Loader already handled displacing to the load
+                        * address, but we still need to displace it to the
+                        * DMAP.
+                        */
+                       displace_symbol_table(
+                           (vm_offset_t)(ksym_start + sizeof(Elf_Size)),
+                           ksym_sz, md_offset);
+
                        db_fetch_ksymtab(ksym_start, ksym_end);
+                       symbols_provided = true;
 #endif
                }
        } else {
+               /*
+                * Self-loading kernel, we have to fake up metadata.
+                *
+                * Since we are creating the metadata from the final
+                * memory space, we don't need to call
+                * preload_boostrap_relocate().
+                */
+               fake_preload_metadata();
+               kmdp = preload_search_by_type("elf kernel");
                init_static_kenv(init_kenv, sizeof(init_kenv));
                ofw_bootargs = true;
        }
+
        /* Store boot environment state */
        OF_initial_setup((void *)fdt, NULL, (int (*)(void *))ofentry);
 
@@ -365,6 +416,11 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
         */
        OF_bootstrap();
 
+#ifdef DDB
+       if (!symbols_provided && hw_direct_map)
+               load_external_symtab();
+#endif
+
        if (ofw_bootargs)
                ofw_parse_bootargs();
 
@@ -412,6 +468,7 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
         */
        pmap_bootstrap(startkernel, endkernel);
        mtmsr(psl_kernset & ~PSL_EE);
+       link_elf_ireloc(kmdp);
 
        /*
         * Initialize params/tunables that are derived from memsize
@@ -447,6 +504,178 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offs
 
        return (((uintptr_t)thread0.td_pcb -
            (sizeof(struct callframe) - 3*sizeof(register_t))) & ~15UL);
+}
+
+#ifdef DDB
+/*
+ * XXX Figure out where to move this.
+ */
+static void
+displace_symbol_table(vm_offset_t ksym_start,
+    vm_offset_t ksym_sz, vm_offset_t displacement) {
+       Elf_Sym *sym;
+
+       /*
+        * Relocate the symbol table to our final load address.
+        */
+       for (sym = (Elf_Sym *)ksym_start;
+           (vm_paddr_t)sym < (ksym_start + ksym_sz);
+           sym++) {
+               if (sym->st_name == 0 ||
+                   sym->st_shndx == SHN_UNDEF ||
+                   sym->st_value == 0)
+                       continue;
+               if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
+                   ELF_ST_TYPE(sym->st_info) != STT_FUNC &&
+                   ELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
+                       continue;
+               /* Skip relocating any implausible symbols */
+               if (sym->st_value > KERNBASE)
+                       sym->st_value += displacement;
+       }
+}
+
+/*
+ * On powernv, we might not have symbols loaded via loader. However, if the
+ * user passed the kernel in as the initrd as well, we can manually load it
+ * via reinterpreting the initrd copy of the kernel.
+ */
+static void
+load_external_symtab(void) {
+       phandle_t chosen;
+       vm_paddr_t start, end;
+       pcell_t cell[2];
+       ssize_t size;
+       u_char *kernelimg;
+
+       int i;
+
+       Elf_Ehdr *ehdr;
+       Elf_Phdr *phdr;
+       Elf_Shdr *shdr;
+
+        vm_offset_t ksym_start, ksym_sz, kstr_start, kstr_sz;
+
+       if (!hw_direct_map)
+               return;
+
+       chosen = OF_finddevice("/chosen");
+       if (chosen <= 0)
+               return;
+
+       if (!OF_hasprop(chosen, "linux,initrd-start") ||
+           !OF_hasprop(chosen, "linux,initrd-end"))
+               return;
+
+       size = OF_getencprop(chosen, "linux,initrd-start", cell, sizeof(cell));
+       if (size == 4)
+               start = cell[0];
+       else if (size == 8)
+               start = (uint64_t)cell[0] << 32 | cell[1];
+       else
+               return;
+
+       size = OF_getencprop(chosen, "linux,initrd-end", cell, sizeof(cell));
+       if (size == 4)
+               end = cell[0];
+       else if (size == 8)
+               end = (uint64_t)cell[0] << 32 | cell[1];
+       else
+               return;
+
+       if (!(end - start > 0))
+               return;
+
+       kernelimg = (u_char *) PHYS_TO_DMAP(start);
+
+       ehdr = (Elf_Ehdr *)kernelimg;
+
+       if (!IS_ELF(*ehdr))
+               return;
+
+       phdr = (Elf_Phdr *)(kernelimg + ehdr->e_phoff);
+       shdr = (Elf_Shdr *)(kernelimg + ehdr->e_shoff);
+
+       ksym_start = 0;
+       ksym_sz = 0;
+       kstr_start = 0;
+       kstr_sz = 0;
+       for (i = 0; i < ehdr->e_shnum; i++) {
+               if (shdr[i].sh_type == SHT_SYMTAB) {
+                       ksym_start = (vm_offset_t)(kernelimg +
+                           shdr[i].sh_offset);
+                       ksym_sz = (vm_offset_t)(shdr[i].sh_size);
+                       kstr_start = (vm_offset_t)(kernelimg +
+                           shdr[shdr[i].sh_link].sh_offset);
+                       kstr_sz = (vm_offset_t)
+                           (shdr[shdr[i].sh_link].sh_size);
+               }
+       }
+
+       if (ksym_start != 0 && kstr_start != 0 && ksym_sz != 0 &&
+           kstr_sz != 0 && ksym_start < kstr_start) {
+
+               displace_symbol_table(ksym_start, ksym_sz,
+                   (__startkernel - KERNBASE));
+               ksymtab = ksym_start;
+               ksymtab_size = ksym_sz;
+               kstrtab = kstr_start;
+       }
+
+};
+#endif
+
+/*
+ * When not being loaded from loader, we need to create our own metadata
+ * so we can interact with the kernel linker.
+ */
+static void
+fake_preload_metadata(void) {
+       /* We depend on dword alignment here. */
+       static uint32_t fake_preload[36] __aligned(8);
+       int i = 0;
+
+       fake_preload[i++] = MODINFO_NAME;
+       fake_preload[i++] = strlen("kernel") + 1;
+       strcpy((char*)&fake_preload[i], "kernel");
+       /* ['k' 'e' 'r' 'n'] ['e' 'l' '\0' ..] */
+       i += 2;
+
+       fake_preload[i++] = MODINFO_TYPE;
+       fake_preload[i++] = strlen("elf kernel") + 1;
+       strcpy((char*)&fake_preload[i], "elf kernel");
+       /* ['e' 'l' 'f' ' '] ['k' 'e' 'r' 'n'] ['e' 'l' '\0' ..] */
+       i += 3;
+
+#ifdef __powerpc64__
+       /* Padding -- Fields start on u_long boundaries */
+       fake_preload[i++] = 0;
+#endif
+
+       fake_preload[i++] = MODINFO_ADDR;
+       fake_preload[i++] = sizeof(vm_offset_t);
+       *(vm_offset_t *)&fake_preload[i] =
+           (vm_offset_t)(__startkernel);
+       i += (sizeof(vm_offset_t) / 4);
+
+       fake_preload[i++] = MODINFO_SIZE;
+       fake_preload[i++] = sizeof(vm_offset_t);
+       *(vm_offset_t *)&fake_preload[i] =
+           (vm_offset_t)(__endkernel) - (vm_offset_t)(__startkernel);
+       i += (sizeof(vm_offset_t) / 4);
+
+       /*
+        * MODINFOMD_SSYM and MODINFOMD_ESYM cannot be provided here,
+        * as the memory comes from outside the loaded ELF sections.
+        *
+        * If the symbols are being provided by other means (MFS), the
+        * tables will be loaded into the debugger directly.
+        */
+
+       /* Null field at end to mark end of data. */
+       fake_preload[i++] = 0;
+       fake_preload[i] = 0;
+       preload_metadata = (void*)fake_preload;
 }
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to