MIPS: Initial implementation of a VDSO
authorAlex Smith <alex.smith@imgtec.com>
Wed, 21 Oct 2015 08:54:38 +0000 (09:54 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 11 Nov 2015 07:36:36 +0000 (08:36 +0100)
Add an initial implementation of a proper (i.e. an ELF shared library)
VDSO. With this commit it does not export any symbols, it only replaces
the current signal return trampoline page. A later commit will add user
implementations of gettimeofday()/clock_gettime().

To support both new toolchains and old ones which don't generate ABI
flags section, we define its content manually and then use a tool
(genvdso) to patch up the section to have the correct name and type.
genvdso also extracts symbol offsets ({,rt_}sigreturn) needed by the
kernel, and generates a C file containing a "struct mips_vdso_image"
containing both the VDSO data and these offsets. This C file is
compiled into the kernel.

On 64-bit kernels we require a different VDSO for each supported ABI,
so we may build up to 3 different VDSOs. The VDSO to use is selected by
the mips_abi structure.

A kernel/user shared data page is created and mapped below the VDSO
image. This is currently empty, but will be used by the user time
function implementations which are added later.

[markos.chandras@imgtec.com:
- Add more comments
- Move abi detection in genvdso.h since it's the get_symbol function
that needs it.
- Add an R6 specific way to calculate the base address of VDSO in order
to avoid the branch instruction which affects performance.
- Do not patch .gnu.attributes since it's not needed for dynamic linking.
- Simplify Makefile a little bit.
- checkpatch fixes
- Restrict VDSO support for binutils < 2.25 for pre-R6
- Include atomic64.h for O32 variant on MIPS64]

Signed-off-by: Alex Smith <alex.smith@imgtec.com>
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/11337/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
19 files changed:
arch/mips/Kbuild
arch/mips/include/asm/abi.h
arch/mips/include/asm/elf.h
arch/mips/include/asm/processor.h
arch/mips/include/asm/vdso.h
arch/mips/include/uapi/asm/Kbuild
arch/mips/include/uapi/asm/auxvec.h [new file with mode: 0644]
arch/mips/kernel/signal.c
arch/mips/kernel/signal32.c
arch/mips/kernel/signal_n32.c
arch/mips/kernel/vdso.c
arch/mips/vdso/.gitignore [new file with mode: 0644]
arch/mips/vdso/Makefile [new file with mode: 0644]
arch/mips/vdso/elf.S [new file with mode: 0644]
arch/mips/vdso/genvdso.c [new file with mode: 0644]
arch/mips/vdso/genvdso.h [new file with mode: 0644]
arch/mips/vdso/sigreturn.S [new file with mode: 0644]
arch/mips/vdso/vdso.h [new file with mode: 0644]
arch/mips/vdso/vdso.lds.S [new file with mode: 0644]

index dd295335891a03dc2cfbb657883135749f38083e..5c3f688a5232f2690007a8e92a12ba76ae11b4a5 100644 (file)
@@ -17,6 +17,7 @@ obj- := $(platform-)
 obj-y += kernel/
 obj-y += mm/
 obj-y += net/
+obj-y += vdso/
 
 ifdef CONFIG_KVM
 obj-y += kvm/
index 37f84078e78abbe3f6b85841461c8bccc62d6c9a..940760844e2fe98a8e3958e975b1cbd674786675 100644 (file)
 
 #include <asm/signal.h>
 #include <asm/siginfo.h>
+#include <asm/vdso.h>
 
 struct mips_abi {
        int (* const setup_frame)(void *sig_return, struct ksignal *ksig,
                                  struct pt_regs *regs, sigset_t *set);
-       const unsigned long     signal_return_offset;
        int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig,
                                     struct pt_regs *regs, sigset_t *set);
-       const unsigned long     rt_signal_return_offset;
        const unsigned long     restart;
 
        unsigned        off_sc_fpregs;
        unsigned        off_sc_fpc_csr;
        unsigned        off_sc_used_math;
+
+       struct mips_vdso_image *vdso;
 };
 
 #endif /* _ASM_ABI_H */
index 53b26933b12cea365457f95172fbde6990043ff1..b01a6ff468e00aab5d185a9dd53023101b34f2b7 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef _ASM_ELF_H
 #define _ASM_ELF_H
 
+#include <linux/auxvec.h>
 #include <linux/fs.h>
 #include <uapi/linux/elf.h>
 
@@ -419,6 +420,12 @@ extern const char *__elf_platform;
 #define ELF_ET_DYN_BASE                (TASK_SIZE / 3 * 2)
 #endif
 
+#define ARCH_DLINFO                                                    \
+do {                                                                   \
+       NEW_AUX_ENT(AT_SYSINFO_EHDR,                                    \
+                   (unsigned long)current->mm->context.vdso);          \
+} while (0)
+
 #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
 struct linux_binprm;
 extern int arch_setup_additional_pages(struct linux_binprm *bprm,
index 59ee6dcf6eed9fd2b336347b0d674070baf9f298..3f832c3dd8f5f1cad76461b47ee00a0b7f85722d 100644 (file)
@@ -36,12 +36,6 @@ extern unsigned int vced_count, vcei_count;
  */
 #define HAVE_ARCH_PICK_MMAP_LAYOUT 1
 
-/*
- * A special page (the vdso) is mapped into all processes at the very
- * top of the virtual memory space.
- */
-#define SPECIAL_PAGES_SIZE PAGE_SIZE
-
 #ifdef CONFIG_32BIT
 #ifdef CONFIG_KVM_GUEST
 /* User space process size is limited to 1GB in KVM Guest Mode */
@@ -80,7 +74,7 @@ extern unsigned int vced_count, vcei_count;
 
 #endif
 
-#define STACK_TOP      ((TASK_SIZE & PAGE_MASK) - SPECIAL_PAGES_SIZE)
+#define STACK_TOP      (TASK_SIZE & PAGE_MASK)
 
 /*
  * This decides where the kernel will search for a free chunk of vm
index cca56aa40ff4a1cd475457c16783bc71855f43c5..db2d45be8f2ed314628bfa0ec1958221e6abf800 100644 (file)
@@ -1,29 +1,70 @@
 /*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
  *
- * Copyright (C) 2009 Cavium Networks
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
  */
 
 #ifndef __ASM_VDSO_H
 #define __ASM_VDSO_H
 
-#include <linux/types.h>
+#include <linux/mm_types.h>
 
+/**
+ * struct mips_vdso_image - Details of a VDSO image.
+ * @data: Pointer to VDSO image data (page-aligned).
+ * @size: Size of the VDSO image data (page-aligned).
+ * @off_sigreturn: Offset of the sigreturn() trampoline.
+ * @off_rt_sigreturn: Offset of the rt_sigreturn() trampoline.
+ * @mapping: Special mapping structure.
+ *
+ * This structure contains details of a VDSO image, including the image data
+ * and offsets of certain symbols required by the kernel. It is generated as
+ * part of the VDSO build process, aside from the mapping page array, which is
+ * populated at runtime.
+ */
+struct mips_vdso_image {
+       void *data;
+       unsigned long size;
 
-#ifdef CONFIG_32BIT
-struct mips_vdso {
-       u32 signal_trampoline[2];
-       u32 rt_signal_trampoline[2];
+       unsigned long off_sigreturn;
+       unsigned long off_rt_sigreturn;
+
+       struct vm_special_mapping mapping;
 };
-#else  /* !CONFIG_32BIT */
-struct mips_vdso {
-       u32 o32_signal_trampoline[2];
-       u32 o32_rt_signal_trampoline[2];
-       u32 rt_signal_trampoline[2];
-       u32 n32_rt_signal_trampoline[2];
+
+/*
+ * The following structures are auto-generated as part of the build for each
+ * ABI by genvdso, see arch/mips/vdso/Makefile.
+ */
+
+extern struct mips_vdso_image vdso_image;
+
+#ifdef CONFIG_MIPS32_O32
+extern struct mips_vdso_image vdso_image_o32;
+#endif
+
+#ifdef CONFIG_MIPS32_N32
+extern struct mips_vdso_image vdso_image_n32;
+#endif
+
+/**
+ * union mips_vdso_data - Data provided by the kernel for the VDSO.
+ *
+ * This structure contains data needed by functions within the VDSO. It is
+ * populated by the kernel and mapped read-only into user memory.
+ *
+ * Note: Care should be taken when modifying as the layout must remain the same
+ * for both 64- and 32-bit (for 32-bit userland on 64-bit kernel).
+ */
+union mips_vdso_data {
+       struct {
+       };
+
+       u8 page[PAGE_SIZE];
 };
-#endif /* CONFIG_32BIT */
 
 #endif /* __ASM_VDSO_H */
index 96fe7395ed8dc8df6d2675bf31626b45133c15e4..f2cf4146114679253195769bab10b859f41026d6 100644 (file)
@@ -1,9 +1,9 @@
 # UAPI Header export list
 include include/uapi/asm-generic/Kbuild.asm
 
-generic-y += auxvec.h
 generic-y += ipcbuf.h
 
+header-y += auxvec.h
 header-y += bitfield.h
 header-y += bitsperlong.h
 header-y += break.h
diff --git a/arch/mips/include/uapi/asm/auxvec.h b/arch/mips/include/uapi/asm/auxvec.h
new file mode 100644 (file)
index 0000000..c9c7195
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __ASM_AUXVEC_H
+#define __ASM_AUXVEC_H
+
+/* Location of VDSO image. */
+#define AT_SYSINFO_EHDR                33
+
+#endif /* __ASM_AUXVEC_H */
index 2fec67bfc457cc969056f6f46258c0f00992be83..bf792e2839a6f31b5cde496cb84892ded60d5b5f 100644 (file)
@@ -36,7 +36,6 @@
 #include <asm/ucontext.h>
 #include <asm/cpu-features.h>
 #include <asm/war.h>
-#include <asm/vdso.h>
 #include <asm/dsp.h>
 #include <asm/inst.h>
 #include <asm/msa.h>
@@ -752,16 +751,15 @@ static int setup_rt_frame(void *sig_return, struct ksignal *ksig,
 struct mips_abi mips_abi = {
 #ifdef CONFIG_TRAD_SIGNALS
        .setup_frame    = setup_frame,
-       .signal_return_offset = offsetof(struct mips_vdso, signal_trampoline),
 #endif
        .setup_rt_frame = setup_rt_frame,
-       .rt_signal_return_offset =
-               offsetof(struct mips_vdso, rt_signal_trampoline),
        .restart        = __NR_restart_syscall,
 
        .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
        .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
        .off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
+
+       .vdso           = &vdso_image,
 };
 
 static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
@@ -801,11 +799,11 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
        }
 
        if (sig_uses_siginfo(&ksig->ka))
-               ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset,
+               ret = abi->setup_rt_frame(vdso + abi->vdso->off_rt_sigreturn,
                                          ksig, regs, oldset);
        else
-               ret = abi->setup_frame(vdso + abi->signal_return_offset, ksig,
-                                      regs, oldset);
+               ret = abi->setup_frame(vdso + abi->vdso->off_sigreturn,
+                                      ksig, regs, oldset);
 
        signal_setup_done(ret, ksig, 0);
 }
index f7e89524e3166fc23b0ea7ae08c10137d75de74e..4909639aa35ba7fcb3de868c4c28ca92e8f7a54e 100644 (file)
@@ -31,7 +31,6 @@
 #include <asm/ucontext.h>
 #include <asm/fpu.h>
 #include <asm/war.h>
-#include <asm/vdso.h>
 #include <asm/dsp.h>
 
 #include "signal-common.h"
@@ -406,14 +405,12 @@ static int setup_rt_frame_32(void *sig_return, struct ksignal *ksig,
  */
 struct mips_abi mips_abi_32 = {
        .setup_frame    = setup_frame_32,
-       .signal_return_offset =
-               offsetof(struct mips_vdso, o32_signal_trampoline),
        .setup_rt_frame = setup_rt_frame_32,
-       .rt_signal_return_offset =
-               offsetof(struct mips_vdso, o32_rt_signal_trampoline),
        .restart        = __NR_O32_restart_syscall,
 
        .off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs),
        .off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr),
        .off_sc_used_math = offsetof(struct sigcontext32, sc_used_math),
+
+       .vdso           = &vdso_image_o32,
 };
index 0d017fdcaf07aa50c583e644f4dc1f21d9ab80ee..a7bc384305008ece50b78c9ff72364e54c7af2e5 100644 (file)
@@ -38,7 +38,6 @@
 #include <asm/fpu.h>
 #include <asm/cpu-features.h>
 #include <asm/war.h>
-#include <asm/vdso.h>
 
 #include "signal-common.h"
 
@@ -151,11 +150,11 @@ static int setup_rt_frame_n32(void *sig_return, struct ksignal *ksig,
 
 struct mips_abi mips_abi_n32 = {
        .setup_rt_frame = setup_rt_frame_n32,
-       .rt_signal_return_offset =
-               offsetof(struct mips_vdso, n32_rt_signal_trampoline),
        .restart        = __NR_N32_restart_syscall,
 
        .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
        .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
        .off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
+
+       .vdso           = &vdso_image_n32,
 };
index ed2a278722a9599e9edfaf501bda7f88b66690f7..56cc3c4377fbae7c5e70ba15b1b26b8cbff7c2c1 100644 (file)
 /*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
  *
- * Copyright (C) 2009, 2010 Cavium Networks, Inc.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
  */
 
-
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/init.h>
 #include <linux/binfmts.h>
 #include <linux/elf.h>
-#include <linux/vmalloc.h>
-#include <linux/unistd.h>
-#include <linux/random.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
 
+#include <asm/abi.h>
 #include <asm/vdso.h>
-#include <asm/uasm.h>
-#include <asm/processor.h>
+
+/* Kernel-provided data used by the VDSO. */
+static union mips_vdso_data vdso_data __page_aligned_data;
 
 /*
- * Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
+ * Mapping for the VDSO data pages. The real pages are mapped manually, as
+ * what we map and where within the area they are mapped is determined at
+ * runtime.
  */
-#define __NR_O32_sigreturn             4119
-#define __NR_O32_rt_sigreturn          4193
-#define __NR_N32_rt_sigreturn          6211
+static struct page *no_pages[] = { NULL };
+static struct vm_special_mapping vdso_vvar_mapping = {
+       .name = "[vvar]",
+       .pages = no_pages,
+};
 
-static struct page *vdso_page;
-
-static void __init install_trampoline(u32 *tramp, unsigned int sigreturn)
+static void __init init_vdso_image(struct mips_vdso_image *image)
 {
-       uasm_i_addiu(&tramp, 2, 0, sigreturn);  /* li v0, sigreturn */
-       uasm_i_syscall(&tramp, 0);
+       unsigned long num_pages, i;
+
+       BUG_ON(!PAGE_ALIGNED(image->data));
+       BUG_ON(!PAGE_ALIGNED(image->size));
+
+       num_pages = image->size / PAGE_SIZE;
+
+       for (i = 0; i < num_pages; i++) {
+               image->mapping.pages[i] =
+                       virt_to_page(image->data + (i * PAGE_SIZE));
+       }
 }
 
 static int __init init_vdso(void)
 {
-       struct mips_vdso *vdso;
-
-       vdso_page = alloc_page(GFP_KERNEL);
-       if (!vdso_page)
-               panic("Cannot allocate vdso");
-
-       vdso = vmap(&vdso_page, 1, 0, PAGE_KERNEL);
-       if (!vdso)
-               panic("Cannot map vdso");
-       clear_page(vdso);
-
-       install_trampoline(vdso->rt_signal_trampoline, __NR_rt_sigreturn);
-#ifdef CONFIG_32BIT
-       install_trampoline(vdso->signal_trampoline, __NR_sigreturn);
-#else
-       install_trampoline(vdso->n32_rt_signal_trampoline,
-                          __NR_N32_rt_sigreturn);
-       install_trampoline(vdso->o32_signal_trampoline, __NR_O32_sigreturn);
-       install_trampoline(vdso->o32_rt_signal_trampoline,
-                          __NR_O32_rt_sigreturn);
+       init_vdso_image(&vdso_image);
+
+#ifdef CONFIG_MIPS32_O32
+       init_vdso_image(&vdso_image_o32);
 #endif
 
-       vunmap(vdso);
+#ifdef CONFIG_MIPS32_N32
+       init_vdso_image(&vdso_image_n32);
+#endif
 
        return 0;
 }
 subsys_initcall(init_vdso);
 
-static unsigned long vdso_addr(unsigned long start)
-{
-       unsigned long offset = 0UL;
-
-       if (current->flags & PF_RANDOMIZE) {
-               offset = get_random_int();
-               offset <<= PAGE_SHIFT;
-               if (TASK_IS_32BIT_ADDR)
-                       offset &= 0xfffffful;
-               else
-                       offset &= 0xffffffful;
-       }
-
-       return STACK_TOP + offset;
-}
-
 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
 {
-       int ret;
-       unsigned long addr;
+       struct mips_vdso_image *image = current->thread.abi->vdso;
        struct mm_struct *mm = current->mm;
+       unsigned long base, vdso_addr;
+       struct vm_area_struct *vma;
+       int ret;
 
        down_write(&mm->mmap_sem);
 
-       addr = vdso_addr(mm->start_stack);
-
-       addr = get_unmapped_area(NULL, addr, PAGE_SIZE, 0, 0);
-       if (IS_ERR_VALUE(addr)) {
-               ret = addr;
-               goto up_fail;
+       base = get_unmapped_area(NULL, 0, PAGE_SIZE + image->size, 0, 0);
+       if (IS_ERR_VALUE(base)) {
+               ret = base;
+               goto out;
        }
 
-       ret = install_special_mapping(mm, addr, PAGE_SIZE,
-                                     VM_READ|VM_EXEC|
-                                     VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
-                                     &vdso_page);
+       vdso_addr = base + PAGE_SIZE;
+
+       vma = _install_special_mapping(mm, base, PAGE_SIZE,
+                                      VM_READ | VM_MAYREAD,
+                                      &vdso_vvar_mapping);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto out;
+       }
 
+       /* Map data page. */
+       ret = remap_pfn_range(vma, base,
+                             virt_to_phys(&vdso_data) >> PAGE_SHIFT,
+                             PAGE_SIZE, PAGE_READONLY);
        if (ret)
-               goto up_fail;
+               goto out;
+
+       /* Map VDSO image. */
+       vma = _install_special_mapping(mm, vdso_addr, image->size,
+                                      VM_READ | VM_EXEC |
+                                      VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
+                                      &image->mapping);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto out;
+       }
 
-       mm->context.vdso = (void *)addr;
+       mm->context.vdso = (void *)vdso_addr;
+       ret = 0;
 
-up_fail:
+out:
        up_write(&mm->mmap_sem);
        return ret;
 }
-
-const char *arch_vma_name(struct vm_area_struct *vma)
-{
-       if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso)
-               return "[vdso]";
-       return NULL;
-}
diff --git a/arch/mips/vdso/.gitignore b/arch/mips/vdso/.gitignore
new file mode 100644 (file)
index 0000000..5286a7d
--- /dev/null
@@ -0,0 +1,4 @@
+*.so*
+vdso-*image.c
+genvdso
+vdso*.lds
diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile
new file mode 100644 (file)
index 0000000..ef5f348
--- /dev/null
@@ -0,0 +1,160 @@
+# Objects to go into the VDSO.
+obj-vdso-y := elf.o gettimeofday.o sigreturn.o
+
+# Common compiler flags between ABIs.
+ccflags-vdso := \
+       $(filter -I%,$(KBUILD_CFLAGS)) \
+       $(filter -E%,$(KBUILD_CFLAGS)) \
+       $(filter -march=%,$(KBUILD_CFLAGS))
+cflags-vdso := $(ccflags-vdso) \
+       $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \
+       -O2 -g -fPIC -fno-common -fno-builtin -G 0 -DDISABLE_BRANCH_PROFILING \
+       $(call cc-option, -fno-stack-protector)
+aflags-vdso := $(ccflags-vdso) \
+       $(filter -I%,$(KBUILD_CFLAGS)) \
+       $(filter -E%,$(KBUILD_CFLAGS)) \
+       -D__ASSEMBLY__ -Wa,-gdwarf-2
+
+#
+# For the pre-R6 code in arch/mips/vdso/vdso.h for locating
+# the base address of VDSO, the linker will emit a R_MIPS_PC32
+# relocation in binutils > 2.25 but it will fail with older versions
+# because that relocation is not supported for that symbol. As a result
+# of which we are forced to disable the VDSO symbols when building
+# with < 2.25 binutils on pre-R6 kernels. For more references on why we
+# can't use other methods to get the base address of VDSO please refer to
+# the comments on that file.
+#
+ifndef CONFIG_CPU_MIPSR6
+  ifeq ($(call ld-ifversion, -gt, 22400000, y),)
+    $(warning MIPS VDSO requires binutils > 2.24)
+    obj-vdso-y := $(filter-out gettimeofday.o, $(obj-vdso-y))
+    ccflags-vdso += -DDISABLE_MIPS_VDSO
+  endif
+endif
+
+# VDSO linker flags.
+VDSO_LDFLAGS := \
+       -Wl,-Bsymbolic -Wl,--no-undefined -Wl,-soname=linux-vdso.so.1 \
+       -nostdlib -shared \
+       $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) \
+       $(call cc-ldoption, -Wl$(comma)--build-id)
+
+GCOV_PROFILE := n
+
+#
+# Shared build commands.
+#
+
+quiet_cmd_vdsold = VDSO    $@
+      cmd_vdsold = $(CC) $(c_flags) $(VDSO_LDFLAGS) \
+                   -Wl,-T $(filter %.lds,$^) $(filter %.o,$^) -o $@
+
+hostprogs-y := genvdso
+
+quiet_cmd_genvdso = GENVDSO $@
+define cmd_genvdso
+       cp $< $(<:%.dbg=%) && \
+       $(OBJCOPY) -S $< $(<:%.dbg=%) && \
+       $(obj)/genvdso $< $(<:%.dbg=%) $@ $(VDSO_NAME)
+endef
+
+#
+# Build native VDSO.
+#
+
+native-abi := $(filter -mabi=%,$(KBUILD_CFLAGS))
+
+targets += $(obj-vdso-y)
+targets += vdso.lds vdso.so.dbg vdso.so vdso-image.c
+
+obj-vdso := $(obj-vdso-y:%.o=$(obj)/%.o)
+
+$(obj-vdso): KBUILD_CFLAGS := $(cflags-vdso) $(native-abi)
+$(obj-vdso): KBUILD_AFLAGS := $(aflags-vdso) $(native-abi)
+
+$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(native-abi)
+
+$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE
+       $(call if_changed,vdsold)
+
+$(obj)/vdso-image.c: $(obj)/vdso.so.dbg $(obj)/genvdso FORCE
+       $(call if_changed,genvdso)
+
+obj-y += vdso-image.o
+
+#
+# Build O32 VDSO.
+#
+
+# Define these outside the ifdef to ensure they are picked up by clean.
+targets += $(obj-vdso-y:%.o=%-o32.o)
+targets += vdso-o32.lds vdso-o32.so.dbg vdso-o32.so vdso-o32-image.c
+
+ifdef CONFIG_MIPS32_O32
+
+obj-vdso-o32 := $(obj-vdso-y:%.o=$(obj)/%-o32.o)
+
+$(obj-vdso-o32): KBUILD_CFLAGS := $(cflags-vdso) -mabi=32
+$(obj-vdso-o32): KBUILD_AFLAGS := $(aflags-vdso) -mabi=32
+
+$(obj)/%-o32.o: $(src)/%.S FORCE
+       $(call if_changed_dep,as_o_S)
+
+$(obj)/%-o32.o: $(src)/%.c FORCE
+       $(call cmd,force_checksrc)
+       $(call if_changed_rule,cc_o_c)
+
+$(obj)/vdso-o32.lds: KBUILD_CPPFLAGS := -mabi=32
+$(obj)/vdso-o32.lds: $(src)/vdso.lds.S FORCE
+       $(call if_changed_dep,cpp_lds_S)
+
+$(obj)/vdso-o32.so.dbg: $(obj)/vdso-o32.lds $(obj-vdso-o32) FORCE
+       $(call if_changed,vdsold)
+
+$(obj)/vdso-o32-image.c: VDSO_NAME := o32
+$(obj)/vdso-o32-image.c: $(obj)/vdso-o32.so.dbg $(obj)/genvdso FORCE
+       $(call if_changed,genvdso)
+
+obj-y += vdso-o32-image.o
+
+endif
+
+#
+# Build N32 VDSO.
+#
+
+targets += $(obj-vdso-y:%.o=%-n32.o)
+targets += vdso-n32.lds vdso-n32.so.dbg vdso-n32.so vdso-n32-image.c
+
+ifdef CONFIG_MIPS32_N32
+
+obj-vdso-n32 := $(obj-vdso-y:%.o=$(obj)/%-n32.o)
+
+$(obj-vdso-n32): KBUILD_CFLAGS := $(cflags-vdso) -mabi=n32
+$(obj-vdso-n32): KBUILD_AFLAGS := $(aflags-vdso) -mabi=n32
+
+$(obj)/%-n32.o: $(src)/%.S FORCE
+       $(call if_changed_dep,as_o_S)
+
+$(obj)/%-n32.o: $(src)/%.c FORCE
+       $(call cmd,force_checksrc)
+       $(call if_changed_rule,cc_o_c)
+
+$(obj)/vdso-n32.lds: KBUILD_CPPFLAGS := -mabi=n32
+$(obj)/vdso-n32.lds: $(src)/vdso.lds.S FORCE
+       $(call if_changed_dep,cpp_lds_S)
+
+$(obj)/vdso-n32.so.dbg: $(obj)/vdso-n32.lds $(obj-vdso-n32) FORCE
+       $(call if_changed,vdsold)
+
+$(obj)/vdso-n32-image.c: VDSO_NAME := n32
+$(obj)/vdso-n32-image.c: $(obj)/vdso-n32.so.dbg $(obj)/genvdso FORCE
+       $(call if_changed,genvdso)
+
+obj-y += vdso-n32-image.o
+
+endif
+
+# FIXME: Need install rule for debug.
+# Needs to deal with dependency for generation of dbg by cmd_genvdso...
diff --git a/arch/mips/vdso/elf.S b/arch/mips/vdso/elf.S
new file mode 100644 (file)
index 0000000..be37bbb
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include "vdso.h"
+
+#include <linux/elfnote.h>
+#include <linux/version.h>
+
+ELFNOTE_START(Linux, 0, "a")
+       .long LINUX_VERSION_CODE
+ELFNOTE_END
+
+/*
+ * The .MIPS.abiflags section must be defined with the FP ABI flags set
+ * to 'any' to be able to link with both old and new libraries.
+ * Newer toolchains are capable of automatically generating this, but we want
+ * to work with older toolchains as well. Therefore, we define the contents of
+ * this section here (under different names), and then genvdso will patch
+ * it to have the correct name and type.
+ *
+ * We base the .MIPS.abiflags section on preprocessor definitions rather than
+ * CONFIG_* because we need to match the particular ABI we are building the
+ * VDSO for.
+ *
+ * See https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking
+ * for the .MIPS.abiflags section description.
+ */
+
+       .section .mips_abiflags, "a"
+       .align 3
+__mips_abiflags:
+       .hword  0               /* version */
+       .byte   __mips          /* isa_level */
+
+       /* isa_rev */
+#ifdef __mips_isa_rev
+       .byte   __mips_isa_rev
+#else
+       .byte   0
+#endif
+
+       /* gpr_size */
+#ifdef __mips64
+       .byte   2               /* AFL_REG_64 */
+#else
+       .byte   1               /* AFL_REG_32 */
+#endif
+
+       /* cpr1_size */
+#if (defined(__mips_isa_rev) && __mips_isa_rev >= 6) || defined(__mips64)
+       .byte   2               /* AFL_REG_64 */
+#else
+       .byte   1               /* AFL_REG_32 */
+#endif
+
+       .byte   0               /* cpr2_size (AFL_REG_NONE) */
+       .byte   0               /* fp_abi (Val_GNU_MIPS_ABI_FP_ANY) */
+       .word   0               /* isa_ext */
+       .word   0               /* ases */
+       .word   0               /* flags1 */
+       .word   0               /* flags2 */
diff --git a/arch/mips/vdso/genvdso.c b/arch/mips/vdso/genvdso.c
new file mode 100644 (file)
index 0000000..530a36f
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+/*
+ * This tool is used to generate the real VDSO images from the raw image. It
+ * first patches up the MIPS ABI flags and GNU attributes sections defined in
+ * elf.S to have the correct name and type. It then generates a C source file
+ * to be compiled into the kernel containing the VDSO image data and a
+ * mips_vdso_image struct for it, including symbol offsets extracted from the
+ * image.
+ *
+ * We need to be passed both a stripped and unstripped VDSO image. The stripped
+ * image is compiled into the kernel, but we must also patch up the unstripped
+ * image's ABI flags sections so that it can be installed and used for
+ * debugging.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <byteswap.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Define these in case the system elf.h is not new enough to have them. */
+#ifndef SHT_GNU_ATTRIBUTES
+# define SHT_GNU_ATTRIBUTES    0x6ffffff5
+#endif
+#ifndef SHT_MIPS_ABIFLAGS
+# define SHT_MIPS_ABIFLAGS     0x7000002a
+#endif
+
+enum {
+       ABI_O32 = (1 << 0),
+       ABI_N32 = (1 << 1),
+       ABI_N64 = (1 << 2),
+
+       ABI_ALL = ABI_O32 | ABI_N32 | ABI_N64,
+};
+
+/* Symbols the kernel requires offsets for. */
+static struct {
+       const char *name;
+       const char *offset_name;
+       unsigned int abis;
+} vdso_symbols[] = {
+       { "__vdso_sigreturn", "off_sigreturn", ABI_O32 },
+       { "__vdso_rt_sigreturn", "off_rt_sigreturn", ABI_ALL },
+       {}
+};
+
+static const char *program_name;
+static const char *vdso_name;
+static unsigned char elf_class;
+static unsigned int elf_abi;
+static bool need_swap;
+static FILE *out_file;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define HOST_ORDER            ELFDATA2LSB
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define HOST_ORDER            ELFDATA2MSB
+#endif
+
+#define BUILD_SWAP(bits)                                               \
+       static uint##bits##_t swap_uint##bits(uint##bits##_t val)       \
+       {                                                               \
+               return need_swap ? bswap_##bits(val) : val;             \
+       }
+
+BUILD_SWAP(16)
+BUILD_SWAP(32)
+BUILD_SWAP(64)
+
+#define __FUNC(name, bits) name##bits
+#define _FUNC(name, bits) __FUNC(name, bits)
+#define FUNC(name) _FUNC(name, ELF_BITS)
+
+#define __ELF(x, bits) Elf##bits##_##x
+#define _ELF(x, bits) __ELF(x, bits)
+#define ELF(x) _ELF(x, ELF_BITS)
+
+/*
+ * Include genvdso.h twice with ELF_BITS defined differently to get functions
+ * for both ELF32 and ELF64.
+ */
+
+#define ELF_BITS 64
+#include "genvdso.h"
+#undef ELF_BITS
+
+#define ELF_BITS 32
+#include "genvdso.h"
+#undef ELF_BITS
+
+static void *map_vdso(const char *path, size_t *_size)
+{
+       int fd;
+       struct stat stat;
+       void *addr;
+       const Elf32_Ehdr *ehdr;
+
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name,
+                       path, strerror(errno));
+               return NULL;
+       }
+
+       if (fstat(fd, &stat) != 0) {
+               fprintf(stderr, "%s: Failed to stat '%s': %s\n", program_name,
+                       path, strerror(errno));
+               return NULL;
+       }
+
+       addr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+                   0);
+       if (addr == MAP_FAILED) {
+               fprintf(stderr, "%s: Failed to map '%s': %s\n", program_name,
+                       path, strerror(errno));
+               return NULL;
+       }
+
+       /* ELF32/64 header formats are the same for the bits we're checking. */
+       ehdr = addr;
+
+       if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+               fprintf(stderr, "%s: '%s' is not an ELF file\n", program_name,
+                       path);
+               return NULL;
+       }
+
+       elf_class = ehdr->e_ident[EI_CLASS];
+       switch (elf_class) {
+       case ELFCLASS32:
+       case ELFCLASS64:
+               break;
+       default:
+               fprintf(stderr, "%s: '%s' has invalid ELF class\n",
+                       program_name, path);
+               return NULL;
+       }
+
+       switch (ehdr->e_ident[EI_DATA]) {
+       case ELFDATA2LSB:
+       case ELFDATA2MSB:
+               need_swap = ehdr->e_ident[EI_DATA] != HOST_ORDER;
+               break;
+       default:
+               fprintf(stderr, "%s: '%s' has invalid ELF data order\n",
+                       program_name, path);
+               return NULL;
+       }
+
+       if (swap_uint16(ehdr->e_machine) != EM_MIPS) {
+               fprintf(stderr,
+                       "%s: '%s' has invalid ELF machine (expected EM_MIPS)\n",
+                       program_name, path);
+               return NULL;
+       } else if (swap_uint16(ehdr->e_type) != ET_DYN) {
+               fprintf(stderr,
+                       "%s: '%s' has invalid ELF type (expected ET_DYN)\n",
+                       program_name, path);
+               return NULL;
+       }
+
+       *_size = stat.st_size;
+       return addr;
+}
+
+static bool patch_vdso(const char *path, void *vdso)
+{
+       if (elf_class == ELFCLASS64)
+               return patch_vdso64(path, vdso);
+       else
+               return patch_vdso32(path, vdso);
+}
+
+static bool get_symbols(const char *path, void *vdso)
+{
+       if (elf_class == ELFCLASS64)
+               return get_symbols64(path, vdso);
+       else
+               return get_symbols32(path, vdso);
+}
+
+int main(int argc, char **argv)
+{
+       const char *dbg_vdso_path, *vdso_path, *out_path;
+       void *dbg_vdso, *vdso;
+       size_t dbg_vdso_size, vdso_size, i;
+
+       program_name = argv[0];
+
+       if (argc < 4 || argc > 5) {
+               fprintf(stderr,
+                       "Usage: %s <debug VDSO> <stripped VDSO> <output file> [<name>]\n",
+                       program_name);
+               return EXIT_FAILURE;
+       }
+
+       dbg_vdso_path = argv[1];
+       vdso_path = argv[2];
+       out_path = argv[3];
+       vdso_name = (argc > 4) ? argv[4] : "";
+
+       dbg_vdso = map_vdso(dbg_vdso_path, &dbg_vdso_size);
+       if (!dbg_vdso)
+               return EXIT_FAILURE;
+
+       vdso = map_vdso(vdso_path, &vdso_size);
+       if (!vdso)
+               return EXIT_FAILURE;
+
+       /* Patch both the VDSOs' ABI flags sections. */
+       if (!patch_vdso(dbg_vdso_path, dbg_vdso))
+               return EXIT_FAILURE;
+       if (!patch_vdso(vdso_path, vdso))
+               return EXIT_FAILURE;
+
+       if (msync(dbg_vdso, dbg_vdso_size, MS_SYNC) != 0) {
+               fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name,
+                       dbg_vdso_path, strerror(errno));
+               return EXIT_FAILURE;
+       } else if (msync(vdso, vdso_size, MS_SYNC) != 0) {
+               fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name,
+                       vdso_path, strerror(errno));
+               return EXIT_FAILURE;
+       }
+
+       out_file = fopen(out_path, "w");
+       if (!out_file) {
+               fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name,
+                       out_path, strerror(errno));
+               return EXIT_FAILURE;
+       }
+
+       fprintf(out_file, "/* Automatically generated - do not edit */\n");
+       fprintf(out_file, "#include <linux/linkage.h>\n");
+       fprintf(out_file, "#include <linux/mm.h>\n");
+       fprintf(out_file, "#include <asm/vdso.h>\n");
+
+       /* Write out the stripped VDSO data. */
+       fprintf(out_file,
+               "static unsigned char vdso_data[PAGE_ALIGN(%zu)] __page_aligned_data = {\n\t",
+               vdso_size);
+       for (i = 0; i < vdso_size; i++) {
+               if (!(i % 10))
+                       fprintf(out_file, "\n\t");
+               fprintf(out_file, "0x%02x, ", ((unsigned char *)vdso)[i]);
+       }
+       fprintf(out_file, "\n};\n");
+
+       /* Preallocate a page array. */
+       fprintf(out_file,
+               "static struct page *vdso_pages[PAGE_ALIGN(%zu) / PAGE_SIZE];\n",
+               vdso_size);
+
+       fprintf(out_file, "struct mips_vdso_image vdso_image%s%s = {\n",
+               (vdso_name[0]) ? "_" : "", vdso_name);
+       fprintf(out_file, "\t.data = vdso_data,\n");
+       fprintf(out_file, "\t.size = PAGE_ALIGN(%zu),\n", vdso_size);
+       fprintf(out_file, "\t.mapping = {\n");
+       fprintf(out_file, "\t\t.name = \"[vdso]\",\n");
+       fprintf(out_file, "\t\t.pages = vdso_pages,\n");
+       fprintf(out_file, "\t},\n");
+
+       /* Calculate and write symbol offsets to <output file> */
+       if (!get_symbols(dbg_vdso_path, dbg_vdso)) {
+               unlink(out_path);
+               return EXIT_FAILURE;
+       }
+
+       fprintf(out_file, "};\n");
+
+       return EXIT_SUCCESS;
+}
diff --git a/arch/mips/vdso/genvdso.h b/arch/mips/vdso/genvdso.h
new file mode 100644 (file)
index 0000000..9433472
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+static inline bool FUNC(patch_vdso)(const char *path, void *vdso)
+{
+       const ELF(Ehdr) *ehdr = vdso;
+       void *shdrs;
+       ELF(Shdr) *shdr;
+       char *shstrtab, *name;
+       uint16_t sh_count, sh_entsize, i;
+       unsigned int local_gotno, symtabno, gotsym;
+       ELF(Dyn) *dyn = NULL;
+
+       shdrs = vdso + FUNC(swap_uint)(ehdr->e_shoff);
+       sh_count = swap_uint16(ehdr->e_shnum);
+       sh_entsize = swap_uint16(ehdr->e_shentsize);
+
+       shdr = shdrs + (sh_entsize * swap_uint16(ehdr->e_shstrndx));
+       shstrtab = vdso + FUNC(swap_uint)(shdr->sh_offset);
+
+       for (i = 0; i < sh_count; i++) {
+               shdr = shdrs + (i * sh_entsize);
+               name = shstrtab + swap_uint32(shdr->sh_name);
+
+               /*
+                * Ensure there are no relocation sections - ld.so does not
+                * relocate the VDSO so if there are relocations things will
+                * break.
+                */
+               switch (swap_uint32(shdr->sh_type)) {
+               case SHT_REL:
+               case SHT_RELA:
+                       fprintf(stderr,
+                               "%s: '%s' contains relocation sections\n",
+                               program_name, path);
+                       return false;
+               case SHT_DYNAMIC:
+                       dyn = vdso + FUNC(swap_uint)(shdr->sh_offset);
+                       break;
+               }
+
+               /* Check for existing sections. */
+               if (strcmp(name, ".MIPS.abiflags") == 0) {
+                       fprintf(stderr,
+                               "%s: '%s' already contains a '.MIPS.abiflags' section\n",
+                               program_name, path);
+                       return false;
+               }
+
+               if (strcmp(name, ".mips_abiflags") == 0) {
+                       strcpy(name, ".MIPS.abiflags");
+                       shdr->sh_type = swap_uint32(SHT_MIPS_ABIFLAGS);
+                       shdr->sh_entsize = shdr->sh_size;
+               }
+       }
+
+       /*
+        * Ensure the GOT has no entries other than the standard 2, for the same
+        * reason we check that there's no relocation sections above.
+        * The standard two entries are:
+        * - Lazy resolver
+        * - Module pointer
+        */
+       if (dyn) {
+               local_gotno = symtabno = gotsym = 0;
+
+               while (FUNC(swap_uint)(dyn->d_tag) != DT_NULL) {
+                       switch (FUNC(swap_uint)(dyn->d_tag)) {
+                       /*
+                        * This member holds the number of local GOT entries.
+                        */
+                       case DT_MIPS_LOCAL_GOTNO:
+                               local_gotno = FUNC(swap_uint)(dyn->d_un.d_val);
+                               break;
+                       /*
+                        * This member holds the number of entries in the
+                        * .dynsym section.
+                        */
+                       case DT_MIPS_SYMTABNO:
+                               symtabno = FUNC(swap_uint)(dyn->d_un.d_val);
+                               break;
+                       /*
+                        * This member holds the index of the first dynamic
+                        * symbol table entry that corresponds to an entry in
+                        * the GOT.
+                        */
+                       case DT_MIPS_GOTSYM:
+                               gotsym = FUNC(swap_uint)(dyn->d_un.d_val);
+                               break;
+                       }
+
+                       dyn++;
+               }
+
+               if (local_gotno > 2 || symtabno - gotsym) {
+                       fprintf(stderr,
+                               "%s: '%s' contains unexpected GOT entries\n",
+                               program_name, path);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static inline bool FUNC(get_symbols)(const char *path, void *vdso)
+{
+       const ELF(Ehdr) *ehdr = vdso;
+       void *shdrs, *symtab;
+       ELF(Shdr) *shdr;
+       const ELF(Sym) *sym;
+       char *strtab, *name;
+       uint16_t sh_count, sh_entsize, st_count, st_entsize, i, j;
+       uint64_t offset;
+       uint32_t flags;
+
+       shdrs = vdso + FUNC(swap_uint)(ehdr->e_shoff);
+       sh_count = swap_uint16(ehdr->e_shnum);
+       sh_entsize = swap_uint16(ehdr->e_shentsize);
+
+       for (i = 0; i < sh_count; i++) {
+               shdr = shdrs + (i * sh_entsize);
+
+               if (swap_uint32(shdr->sh_type) == SHT_SYMTAB)
+                       break;
+       }
+
+       if (i == sh_count) {
+               fprintf(stderr, "%s: '%s' has no symbol table\n", program_name,
+                       path);
+               return false;
+       }
+
+       /* Get flags */
+       flags = swap_uint32(ehdr->e_flags);
+       if (elf_class == ELFCLASS64)
+               elf_abi = ABI_N64;
+       else if (flags & EF_MIPS_ABI2)
+               elf_abi = ABI_N32;
+       else
+               elf_abi = ABI_O32;
+
+       /* Get symbol table. */
+       symtab = vdso + FUNC(swap_uint)(shdr->sh_offset);
+       st_entsize = FUNC(swap_uint)(shdr->sh_entsize);
+       st_count = FUNC(swap_uint)(shdr->sh_size) / st_entsize;
+
+       /* Get string table. */
+       shdr = shdrs + (swap_uint32(shdr->sh_link) * sh_entsize);
+       strtab = vdso + FUNC(swap_uint)(shdr->sh_offset);
+
+       /* Write offsets for symbols needed by the kernel. */
+       for (i = 0; vdso_symbols[i].name; i++) {
+               if (!(vdso_symbols[i].abis & elf_abi))
+                       continue;
+
+               for (j = 0; j < st_count; j++) {
+                       sym = symtab + (j * st_entsize);
+                       name = strtab + swap_uint32(sym->st_name);
+
+                       if (!strcmp(name, vdso_symbols[i].name)) {
+                               offset = FUNC(swap_uint)(sym->st_value);
+
+                               fprintf(out_file,
+                                       "\t.%s = 0x%" PRIx64 ",\n",
+                                       vdso_symbols[i].offset_name, offset);
+                               break;
+                       }
+               }
+
+               if (j == st_count) {
+                       fprintf(stderr,
+                               "%s: '%s' is missing required symbol '%s'\n",
+                               program_name, path, vdso_symbols[i].name);
+                       return false;
+               }
+       }
+
+       return true;
+}
diff --git a/arch/mips/vdso/sigreturn.S b/arch/mips/vdso/sigreturn.S
new file mode 100644 (file)
index 0000000..715bf59
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include "vdso.h"
+
+#include <uapi/asm/unistd.h>
+
+#include <asm/regdef.h>
+#include <asm/asm.h>
+
+       .section        .text
+       .cfi_sections   .debug_frame
+
+LEAF(__vdso_rt_sigreturn)
+       .cfi_startproc
+       .frame  sp, 0, ra
+       .mask   0x00000000, 0
+       .fmask  0x00000000, 0
+       .cfi_signal_frame
+
+       li      v0, __NR_rt_sigreturn
+       syscall
+
+       .cfi_endproc
+       END(__vdso_rt_sigreturn)
+
+#if _MIPS_SIM == _MIPS_SIM_ABI32
+
+LEAF(__vdso_sigreturn)
+       .cfi_startproc
+       .frame  sp, 0, ra
+       .mask   0x00000000, 0
+       .fmask  0x00000000, 0
+       .cfi_signal_frame
+
+       li      v0, __NR_sigreturn
+       syscall
+
+       .cfi_endproc
+       END(__vdso_sigreturn)
+
+#endif
diff --git a/arch/mips/vdso/vdso.h b/arch/mips/vdso/vdso.h
new file mode 100644 (file)
index 0000000..0bb6b1a
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <asm/sgidefs.h>
+
+#if _MIPS_SIM != _MIPS_SIM_ABI64 && defined(CONFIG_64BIT)
+
+/* Building 32-bit VDSO for the 64-bit kernel. Fake a 32-bit Kconfig. */
+#undef CONFIG_64BIT
+#define CONFIG_32BIT 1
+#ifndef __ASSEMBLY__
+#include <asm-generic/atomic64.h>
+#endif
+#endif
+
+#ifndef __ASSEMBLY__
+
+#include <asm/asm.h>
+#include <asm/page.h>
+#include <asm/vdso.h>
+
+static inline unsigned long get_vdso_base(void)
+{
+       unsigned long addr;
+
+       /*
+        * We can't use cpu_has_mips_r6 since it needs the cpu_data[]
+        * kernel symbol.
+        */
+#ifdef CONFIG_CPU_MIPSR6
+       /*
+        * lapc <symbol> is an alias to addiupc reg, <symbol> - .
+        *
+        * We can't use addiupc because there is no label-label
+        * support for the addiupc reloc
+        */
+       __asm__("lapc   %0, _start                      \n"
+               : "=r" (addr) : :);
+#else
+       /*
+        * Get the base load address of the VDSO. We have to avoid generating
+        * relocations and references to the GOT because ld.so does not peform
+        * relocations on the VDSO. We use the current offset from the VDSO base
+        * and perform a PC-relative branch which gives the absolute address in
+        * ra, and take the difference. The assembler chokes on
+        * "li %0, _start - .", so embed the offset as a word and branch over
+        * it.
+        *
+        */
+
+       __asm__(
+       "       .set push                               \n"
+       "       .set noreorder                          \n"
+       "       bal     1f                              \n"
+       "        nop                                    \n"
+       "       .word   _start - .                      \n"
+       "1:     lw      %0, 0($31)                      \n"
+       "       " STR(PTR_ADDU) " %0, $31, %0           \n"
+       "       .set pop                                \n"
+       : "=r" (addr)
+       :
+       : "$31");
+#endif /* CONFIG_CPU_MIPSR6 */
+
+       return addr;
+}
+
+static inline const union mips_vdso_data *get_vdso_data(void)
+{
+       return (const union mips_vdso_data *)(get_vdso_base() - PAGE_SIZE);
+}
+
+#endif /* __ASSEMBLY__ */
diff --git a/arch/mips/vdso/vdso.lds.S b/arch/mips/vdso/vdso.lds.S
new file mode 100644 (file)
index 0000000..21655b6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <asm/sgidefs.h>
+
+#if _MIPS_SIM == _MIPS_SIM_ABI64
+OUTPUT_FORMAT("elf64-tradlittlemips", "elf64-tradbigmips", "elf64-tradlittlemips")
+#elif _MIPS_SIM == _MIPS_SIM_NABI32
+OUTPUT_FORMAT("elf32-ntradlittlemips", "elf32-ntradbigmips", "elf32-ntradlittlemips")
+#else
+OUTPUT_FORMAT("elf32-tradlittlemips", "elf32-tradbigmips", "elf32-tradlittlemips")
+#endif
+
+OUTPUT_ARCH(mips)
+
+SECTIONS
+{
+       PROVIDE(_start = .);
+       . = SIZEOF_HEADERS;
+
+       /*
+        * In order to retain compatibility with older toolchains we provide the
+        * ABI flags section ourself. Newer assemblers will automatically
+        * generate .MIPS.abiflags sections so we discard such input sections,
+        * and then manually define our own section here. genvdso will patch
+        * this section to have the correct name/type.
+        */
+       .mips_abiflags  : { *(.mips_abiflags) }         :text :abiflags
+
+       .reginfo        : { *(.reginfo) }               :text :reginfo
+
+       .hash           : { *(.hash) }                  :text
+       .gnu.hash       : { *(.gnu.hash) }
+       .dynsym         : { *(.dynsym) }
+       .dynstr         : { *(.dynstr) }
+       .gnu.version    : { *(.gnu.version) }
+       .gnu.version_d  : { *(.gnu.version_d) }
+       .gnu.version_r  : { *(.gnu.version_r) }
+
+       .note           : { *(.note.*) }                :text :note
+
+       .text           : { *(.text*) }                 :text
+       PROVIDE (__etext = .);
+       PROVIDE (_etext = .);
+       PROVIDE (etext = .);
+
+       .eh_frame_hdr   : { *(.eh_frame_hdr) }          :text :eh_frame_hdr
+       .eh_frame       : { KEEP (*(.eh_frame)) }       :text
+
+       .dynamic        : { *(.dynamic) }               :text :dynamic
+
+       .rodata         : { *(.rodata*) }               :text
+
+       _end = .;
+       PROVIDE(end = .);
+
+       /DISCARD/       : {
+               *(.MIPS.abiflags)
+               *(.gnu.attributes)
+               *(.note.GNU-stack)
+               *(.data .data.* .gnu.linkonce.d.* .sdata*)
+               *(.bss .sbss .dynbss .dynsbss)
+       }
+}
+
+PHDRS
+{
+       /*
+        * Provide a PT_MIPS_ABIFLAGS header to assign the ABI flags section
+        * to. We can specify the header type directly here so no modification
+        * is needed later on.
+        */
+       abiflags        0x70000003;
+
+       /*
+        * The ABI flags header must exist directly after the PT_INTERP header,
+        * so we must explicitly place the PT_MIPS_REGINFO header after it to
+        * stop the linker putting one in at the start.
+        */
+       reginfo         0x70000000;
+
+       text            PT_LOAD         FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+       dynamic         PT_DYNAMIC      FLAGS(4);               /* PF_R */
+       note            PT_NOTE         FLAGS(4);               /* PF_R */
+       eh_frame_hdr    PT_GNU_EH_FRAME;
+}
+
+VERSION
+{
+       LINUX_2.6 {
+       local: *;
+       };
+}