mips: Add support for generic vDSO
authorVincenzo Frascino <vincenzo.frascino@arm.com>
Fri, 21 Jun 2019 09:52:46 +0000 (10:52 +0100)
committerPaul Burton <paul.burton@mips.com>
Fri, 26 Jul 2019 04:45:05 +0000 (21:45 -0700)
The mips vDSO library requires some adaptations to take advantage of the
newly introduced generic vDSO library.

Introduce the following changes:
 - Modification of vdso.c to be compliant with the common vdso datapage
 - Use of lib/vdso for gettimeofday

Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Paul Burton <paul.burton@mips.com>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
[paul.burton@mips.com: Prepend $(src) to config-n32-o32-env.c path.]
Signed-off-by: Paul Burton <paul.burton@mips.com>
arch/mips/Kconfig
arch/mips/include/asm/vdso.h
arch/mips/include/asm/vdso/gettimeofday.h [new file with mode: 0644]
arch/mips/include/asm/vdso/vdso.h [moved from arch/mips/vdso/vdso.h with 89% similarity]
arch/mips/include/asm/vdso/vsyscall.h [new file with mode: 0644]
arch/mips/kernel/vdso.c
arch/mips/vdso/Makefile
arch/mips/vdso/config-n32-o32-env.c [new file with mode: 0644]
arch/mips/vdso/elf.S
arch/mips/vdso/sigreturn.S
arch/mips/vdso/vgettimeofday.c [new file with mode: 0644]

index 947c75684897df8847f2de642a22e9c31872a20f..99078c1b5273967e3f7d0c1dd9fe6c5ff1d80d55 100644 (file)
@@ -22,6 +22,7 @@ config MIPS
        select GENERIC_CLOCKEVENTS
        select GENERIC_CMOS_UPDATE
        select GENERIC_CPU_AUTOPROBE
+       select GENERIC_GETTIMEOFDAY
        select GENERIC_IOMAP
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
@@ -74,6 +75,7 @@ config MIPS
        select HAVE_STACKPROTECTOR
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT || !SMP
+       select HAVE_GENERIC_VDSO
        select IRQ_FORCED_THREADING
        select ISA if EISA
        select MODULES_USE_ELF_RELA if MODULES && 64BIT
index a013fa4a3682425077daf855d89d96ca396ba22e..cc7b516129a8fbc43bf532a85fac26801a2ba753 100644 (file)
@@ -8,6 +8,7 @@
 #define __ASM_VDSO_H
 
 #include <linux/mm_types.h>
+#include <vdso/datapage.h>
 
 #include <asm/barrier.h>
 
@@ -49,84 +50,9 @@ extern struct mips_vdso_image vdso_image_o32;
 extern struct mips_vdso_image vdso_image_n32;
 #endif
 
-/**
- * union mips_vdso_data - Data provided by the kernel for the VDSO.
- * @xtime_sec:         Current real time (seconds part).
- * @xtime_nsec:                Current real time (nanoseconds part, shifted).
- * @wall_to_mono_sec:  Wall-to-monotonic offset (seconds part).
- * @wall_to_mono_nsec: Wall-to-monotonic offset (nanoseconds part).
- * @seq_count:         Counter to synchronise updates (odd = updating).
- * @cs_shift:          Clocksource shift value.
- * @clock_mode:                Clocksource to use for time functions.
- * @cs_mult:           Clocksource multiplier value.
- * @cs_cycle_last:     Clock cycle value at last update.
- * @cs_mask:           Clocksource mask value.
- * @tz_minuteswest:    Minutes west of Greenwich (from timezone).
- * @tz_dsttime:                Type of DST correction (from timezone).
- *
- * This structure contains data needed by functions within the VDSO. It is
- * populated by the kernel and mapped read-only into user memory. The time
- * fields are mirrors of internal data from the timekeeping infrastructure.
- *
- * 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 {
-               u64 xtime_sec;
-               u64 xtime_nsec;
-               u64 wall_to_mono_sec;
-               u64 wall_to_mono_nsec;
-               u32 seq_count;
-               u32 cs_shift;
-               u8 clock_mode;
-               u32 cs_mult;
-               u64 cs_cycle_last;
-               u64 cs_mask;
-               s32 tz_minuteswest;
-               s32 tz_dsttime;
-       };
-
+       struct vdso_data data[CS_BASES];
        u8 page[PAGE_SIZE];
 };
 
-static inline u32 vdso_data_read_begin(const union mips_vdso_data *data)
-{
-       u32 seq;
-
-       while (true) {
-               seq = READ_ONCE(data->seq_count);
-               if (likely(!(seq & 1))) {
-                       /* Paired with smp_wmb() in vdso_data_write_*(). */
-                       smp_rmb();
-                       return seq;
-               }
-
-               cpu_relax();
-       }
-}
-
-static inline bool vdso_data_read_retry(const union mips_vdso_data *data,
-                                       u32 start_seq)
-{
-       /* Paired with smp_wmb() in vdso_data_write_*(). */
-       smp_rmb();
-       return unlikely(data->seq_count != start_seq);
-}
-
-static inline void vdso_data_write_begin(union mips_vdso_data *data)
-{
-       ++data->seq_count;
-
-       /* Ensure sequence update is written before other data page values. */
-       smp_wmb();
-}
-
-static inline void vdso_data_write_end(union mips_vdso_data *data)
-{
-       /* Ensure data values are written before updating sequence again. */
-       smp_wmb();
-       ++data->seq_count;
-}
-
 #endif /* __ASM_VDSO_H */
diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h
new file mode 100644 (file)
index 0000000..aa20865
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 ARM Limited
+ * 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_VDSO_GETTIMEOFDAY_H
+#define __ASM_VDSO_GETTIMEOFDAY_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+#include <linux/time.h>
+
+#include <asm/vdso/vdso.h>
+#include <asm/clocksource.h>
+#include <asm/io.h>
+#include <asm/unistd.h>
+#include <asm/vdso.h>
+
+#ifdef CONFIG_MIPS_CLOCK_VSYSCALL
+
+static __always_inline long gettimeofday_fallback(
+                               struct __kernel_old_timeval *_tv,
+                               struct timezone *_tz)
+{
+       register struct timezone *tz asm("a1") = _tz;
+       register struct __kernel_old_timeval *tv asm("a0") = _tv;
+       register long ret asm("v0");
+       register long nr asm("v0") = __NR_gettimeofday;
+       register long error asm("a3");
+
+       asm volatile(
+       "       syscall\n"
+       : "=r" (ret), "=r" (error)
+       : "r" (tv), "r" (tz), "r" (nr)
+       : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
+         "$14", "$15", "$24", "$25", "hi", "lo", "memory");
+
+       return error ? -ret : ret;
+}
+
+#else
+
+static __always_inline long gettimeofday_fallback(
+                               struct __kernel_old_timeval *_tv,
+                               struct timezone *_tz)
+{
+       return -1;
+}
+
+#endif
+
+static __always_inline long clock_gettime_fallback(
+                                       clockid_t _clkid,
+                                       struct __kernel_timespec *_ts)
+{
+       register struct __kernel_timespec *ts asm("a1") = _ts;
+       register clockid_t clkid asm("a0") = _clkid;
+       register long ret asm("v0");
+#if _MIPS_SIM == _MIPS_SIM_ABI64
+       register long nr asm("v0") = __NR_clock_gettime;
+#else
+       register long nr asm("v0") = __NR_clock_gettime64;
+#endif
+       register long error asm("a3");
+
+       asm volatile(
+       "       syscall\n"
+       : "=r" (ret), "=r" (error)
+       : "r" (clkid), "r" (ts), "r" (nr)
+       : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
+         "$14", "$15", "$24", "$25", "hi", "lo", "memory");
+
+       return error ? -ret : ret;
+}
+
+#ifdef CONFIG_CSRC_R4K
+
+static __always_inline u64 read_r4k_count(void)
+{
+       unsigned int count;
+
+       __asm__ __volatile__(
+       "       .set push\n"
+       "       .set mips32r2\n"
+       "       rdhwr   %0, $2\n"
+       "       .set pop\n"
+       : "=r" (count));
+
+       return count;
+}
+
+#endif
+
+#ifdef CONFIG_CLKSRC_MIPS_GIC
+
+static __always_inline u64 read_gic_count(const struct vdso_data *data)
+{
+       void __iomem *gic = get_gic(data);
+       u32 hi, hi2, lo;
+
+       do {
+               hi = __raw_readl(gic + sizeof(lo));
+               lo = __raw_readl(gic);
+               hi2 = __raw_readl(gic + sizeof(lo));
+       } while (hi2 != hi);
+
+       return (((u64)hi) << 32) + lo;
+}
+
+#endif
+
+static __always_inline u64 __arch_get_hw_counter(s32 clock_mode)
+{
+#ifdef CONFIG_CLKSRC_MIPS_GIC
+       const struct vdso_data *data = get_vdso_data();
+#endif
+       u64 cycle_now;
+
+       switch (clock_mode) {
+#ifdef CONFIG_CSRC_R4K
+       case VDSO_CLOCK_R4K:
+               cycle_now = read_r4k_count();
+               break;
+#endif
+#ifdef CONFIG_CLKSRC_MIPS_GIC
+       case VDSO_CLOCK_GIC:
+               cycle_now = read_gic_count(data);
+               break;
+#endif
+       default:
+               cycle_now = 0;
+               break;
+       }
+
+       return cycle_now;
+}
+
+static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
+{
+       return get_vdso_data();
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
similarity index 89%
rename from arch/mips/vdso/vdso.h
rename to arch/mips/include/asm/vdso/vdso.h
index 14b1931be69c3acf78afc794d4b3f0a3bbac5ecc..526695bc65ee52f663fb996fafabc760b9897626 100644 (file)
@@ -68,14 +68,14 @@ static inline unsigned long get_vdso_base(void)
        return addr;
 }
 
-static inline const union mips_vdso_data *get_vdso_data(void)
+static inline const struct vdso_data *get_vdso_data(void)
 {
-       return (const union mips_vdso_data *)(get_vdso_base() - PAGE_SIZE);
+       return (const struct vdso_data *)(get_vdso_base() - PAGE_SIZE);
 }
 
 #ifdef CONFIG_CLKSRC_MIPS_GIC
 
-static inline void __iomem *get_gic(const union mips_vdso_data *data)
+static inline void __iomem *get_gic(const struct vdso_data *data)
 {
        return (void __iomem *)data - PAGE_SIZE;
 }
diff --git a/arch/mips/include/asm/vdso/vsyscall.h b/arch/mips/include/asm/vdso/vsyscall.h
new file mode 100644 (file)
index 0000000..1953147
--- /dev/null
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_VDSO_VSYSCALL_H
+#define __ASM_VDSO_VSYSCALL_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/timekeeper_internal.h>
+#include <vdso/datapage.h>
+
+extern struct vdso_data *vdso_data;
+
+/*
+ * Update the vDSO data page to keep in sync with kernel timekeeping.
+ */
+static __always_inline
+struct vdso_data *__mips_get_k_vdso_data(void)
+{
+       return vdso_data;
+}
+#define __arch_get_k_vdso_data __mips_get_k_vdso_data
+
+static __always_inline
+int __mips_get_clock_mode(struct timekeeper *tk)
+{
+       u32 clock_mode = tk->tkr_mono.clock->archdata.vdso_clock_mode;
+
+       return clock_mode;
+}
+#define __arch_get_clock_mode __mips_get_clock_mode
+
+static __always_inline
+int __mips_use_vsyscall(struct vdso_data *vdata)
+{
+       return (vdata[CS_HRES_COARSE].clock_mode != VDSO_CLOCK_NONE);
+}
+#define __arch_use_vsyscall __mips_use_vsyscall
+
+/* The asm-generic header needs to be included after the definitions above */
+#include <asm-generic/vdso/vsyscall.h>
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __ASM_VDSO_VSYSCALL_H */
index 3a372686ffcaa58079d9d92040e699b18986d907..bc35f8499111b30be22bc4e8891c5459714ec088 100644 (file)
 #include <asm/mips-cps.h>
 #include <asm/page.h>
 #include <asm/vdso.h>
+#include <vdso/helpers.h>
+#include <vdso/vsyscall.h>
 
 /* Kernel-provided data used by the VDSO. */
-static union mips_vdso_data vdso_data __page_aligned_data;
+static union mips_vdso_data mips_vdso_data __page_aligned_data;
+struct vdso_data *vdso_data = mips_vdso_data.data;
 
 /*
  * Mapping for the VDSO data/GIC pages. The real pages are mapped manually, as
@@ -66,34 +69,6 @@ static int __init init_vdso(void)
 }
 subsys_initcall(init_vdso);
 
-void update_vsyscall(struct timekeeper *tk)
-{
-       vdso_data_write_begin(&vdso_data);
-
-       vdso_data.xtime_sec = tk->xtime_sec;
-       vdso_data.xtime_nsec = tk->tkr_mono.xtime_nsec;
-       vdso_data.wall_to_mono_sec = tk->wall_to_monotonic.tv_sec;
-       vdso_data.wall_to_mono_nsec = tk->wall_to_monotonic.tv_nsec;
-       vdso_data.cs_shift = tk->tkr_mono.shift;
-
-       vdso_data.clock_mode = tk->tkr_mono.clock->archdata.vdso_clock_mode;
-       if (vdso_data.clock_mode != VDSO_CLOCK_NONE) {
-               vdso_data.cs_mult = tk->tkr_mono.mult;
-               vdso_data.cs_cycle_last = tk->tkr_mono.cycle_last;
-               vdso_data.cs_mask = tk->tkr_mono.mask;
-       }
-
-       vdso_data_write_end(&vdso_data);
-}
-
-void update_vsyscall_tz(void)
-{
-       if (vdso_data.clock_mode != VDSO_CLOCK_NONE) {
-               vdso_data.tz_minuteswest = sys_tz.tz_minuteswest;
-               vdso_data.tz_dsttime = sys_tz.tz_dsttime;
-       }
-}
-
 static unsigned long vdso_base(void)
 {
        unsigned long base;
@@ -163,7 +138,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
         */
        if (cpu_has_dc_aliases) {
                base = __ALIGN_MASK(base, shm_align_mask);
-               base += ((unsigned long)&vdso_data - gic_size) & shm_align_mask;
+               base += ((unsigned long)vdso_data - gic_size) & shm_align_mask;
        }
 
        data_addr = base + gic_size;
@@ -189,7 +164,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
 
        /* Map data page. */
        ret = remap_pfn_range(vma, data_addr,
-                             virt_to_phys(&vdso_data) >> PAGE_SHIFT,
+                             virt_to_phys(vdso_data) >> PAGE_SHIFT,
                              PAGE_SIZE, PAGE_READONLY);
        if (ret)
                goto out;
index 7221df24cb23c781249604ead6aae311d5fa6be0..de853c6aab28037267ba89d5934e449bd855f6e7 100644 (file)
@@ -1,6 +1,12 @@
 # SPDX-License-Identifier: GPL-2.0
 # Objects to go into the VDSO.
-obj-vdso-y := elf.o gettimeofday.o sigreturn.o
+
+# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before
+# the inclusion of generic Makefile.
+ARCH_REL_TYPE_ABS := R_MIPS_JUMP_SLOT|R_MIPS_GLOB_DAT
+include $(srctree)/lib/vdso/Makefile
+
+obj-vdso-y := elf.o vgettimeofday.o sigreturn.o
 
 # Common compiler flags between ABIs.
 ccflags-vdso := \
@@ -15,15 +21,31 @@ ifdef CONFIG_CC_IS_CLANG
 ccflags-vdso += $(filter --target=%,$(KBUILD_CFLAGS))
 endif
 
+#
+# The -fno-jump-tables flag only prevents the compiler from generating
+# jump tables but does not prevent the compiler from emitting absolute
+# offsets.
 cflags-vdso := $(ccflags-vdso) \
        $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \
-       -O2 -g -fPIC -fno-strict-aliasing -fno-common -fno-builtin -G 0 \
-       -DDISABLE_BRANCH_PROFILING \
+       -O3 -g -fPIC -fno-strict-aliasing -fno-common -fno-builtin -G 0 \
+       -fno-stack-protector -fno-jump-tables -DDISABLE_BRANCH_PROFILING \
        $(call cc-option, -fno-asynchronous-unwind-tables) \
        $(call cc-option, -fno-stack-protector)
 aflags-vdso := $(ccflags-vdso) \
        -D__ASSEMBLY__ -Wa,-gdwarf-2
 
+ifneq ($(c-gettimeofday-y),)
+CFLAGS_vgettimeofday.o = -include $(c-gettimeofday-y)
+
+# config-n32-o32-env.c prepares the environment to build a 32bit vDSO
+# library on a 64bit kernel.
+# Note: Needs to be included before than the generic library.
+CFLAGS_vgettimeofday-o32.o = -include $(src)/config-n32-o32-env.c -include $(c-gettimeofday-y)
+CFLAGS_vgettimeofday-n32.o = -include $(src)/config-n32-o32-env.c -include $(c-gettimeofday-y)
+endif
+
+CFLAGS_REMOVE_vgettimeofday.o = -pg
+
 #
 # 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
@@ -48,6 +70,8 @@ VDSO_LDFLAGS := \
        $(addprefix -Wl$(comma),$(filter -E%,$(KBUILD_CFLAGS))) \
        -nostdlib -shared -Wl,--hash-style=sysv -Wl,--build-id
 
+CFLAGS_REMOVE_vdso.o = -pg
+
 GCOV_PROFILE := n
 UBSAN_SANITIZE := n
 
@@ -96,6 +120,7 @@ $(obj)/vdso.lds: KBUILD_CPPFLAGS := $(ccflags-vdso) $(native-abi)
 
 $(obj)/vdso.so.dbg.raw: $(obj)/vdso.lds $(obj-vdso) FORCE
        $(call if_changed,vdsold)
+       $(call if_changed,vdso_check)
 
 $(obj)/vdso-image.c: $(obj)/vdso.so.dbg.raw $(obj)/vdso.so.raw \
                      $(obj)/genvdso FORCE
@@ -134,6 +159,7 @@ $(obj)/vdso-o32.lds: $(src)/vdso.lds.S FORCE
 
 $(obj)/vdso-o32.so.dbg.raw: $(obj)/vdso-o32.lds $(obj-vdso-o32) FORCE
        $(call if_changed,vdsold)
+       $(call if_changed,vdso_check)
 
 $(obj)/vdso-o32-image.c: VDSO_NAME := o32
 $(obj)/vdso-o32-image.c: $(obj)/vdso-o32.so.dbg.raw $(obj)/vdso-o32.so.raw \
@@ -174,6 +200,7 @@ $(obj)/vdso-n32.lds: $(src)/vdso.lds.S FORCE
 
 $(obj)/vdso-n32.so.dbg.raw: $(obj)/vdso-n32.lds $(obj-vdso-n32) FORCE
        $(call if_changed,vdsold)
+       $(call if_changed,vdso_check)
 
 $(obj)/vdso-n32-image.c: VDSO_NAME := n32
 $(obj)/vdso-n32-image.c: $(obj)/vdso-n32.so.dbg.raw $(obj)/vdso-n32.so.raw \
diff --git a/arch/mips/vdso/config-n32-o32-env.c b/arch/mips/vdso/config-n32-o32-env.c
new file mode 100644 (file)
index 0000000..da4994b
--- /dev/null
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Configuration file for O32 and N32 binaries.
+ * Note: To be included before lib/vdso/gettimeofday.c
+ */
+#if defined(CONFIG_MIPS32_O32) || defined(CONFIG_MIPS32_N32)
+/*
+ * In case of a 32 bit VDSO for a 64 bit kernel fake a 32 bit kernel
+ * configuration.
+ */
+#undef CONFIG_64BIT
+
+#define CONFIG_32BIT 1
+#define CONFIG_GENERIC_ATOMIC64 1
+
+#endif
+
index e7543e8f426c3b14a124315b66f67383e005eb87..a25cb147f1caaf20e3f6a388d5f4498d421b883e 100644 (file)
@@ -4,7 +4,7 @@
  * Author: Alex Smith <alex.smith@imgtec.com>
  */
 
-#include "vdso.h"
+#include <asm/vdso/vdso.h>
 
 #include <asm/isa-rev.h>
 
index c3597632874bce35403084a97237b91ebd1e0347..e5c0ab98ab462406fee230aa4cf2322c566855de 100644 (file)
@@ -4,7 +4,7 @@
  * Author: Alex Smith <alex.smith@imgtec.com>
  */
 
-#include "vdso.h"
+#include <asm/vdso/vdso.h>
 
 #include <uapi/asm/unistd.h>
 
diff --git a/arch/mips/vdso/vgettimeofday.c b/arch/mips/vdso/vgettimeofday.c
new file mode 100644 (file)
index 0000000..1c46dac
--- /dev/null
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIPS64 and compat userspace implementations of gettimeofday()
+ * and similar.
+ *
+ * Copyright (C) 2015 Imagination Technologies
+ * Copyright (C) 2018 ARM Limited
+ *
+ */
+#include <linux/time.h>
+#include <linux/types.h>
+
+#if _MIPS_SIM != _MIPS_SIM_ABI64
+int __vdso_clock_gettime(clockid_t clock,
+                        struct old_timespec32 *ts)
+{
+       return __cvdso_clock_gettime32(clock, ts);
+}
+
+int __vdso_gettimeofday(struct __kernel_old_timeval *tv,
+                       struct timezone *tz)
+{
+       return __cvdso_gettimeofday(tv, tz);
+}
+
+#else
+
+int __vdso_clock_gettime(clockid_t clock,
+                        struct __kernel_timespec *ts)
+{
+       return __cvdso_clock_gettime(clock, ts);
+}
+
+int __vdso_gettimeofday(struct __kernel_old_timeval *tv,
+                       struct timezone *tz)
+{
+       return __cvdso_gettimeofday(tv, tz);
+}
+
+#endif