diff options
Diffstat (limited to 'arch/mips/include/asm')
-rw-r--r-- | arch/mips/include/asm/vdso.h | 78 | ||||
-rw-r--r-- | arch/mips/include/asm/vdso/gettimeofday.h | 151 | ||||
-rw-r--r-- | arch/mips/include/asm/vdso/vdso.h | 85 | ||||
-rw-r--r-- | arch/mips/include/asm/vdso/vsyscall.h | 43 |
4 files changed, 281 insertions, 76 deletions
diff --git a/arch/mips/include/asm/vdso.h b/arch/mips/include/asm/vdso.h index a013fa4a3682..cc7b516129a8 100644 --- a/arch/mips/include/asm/vdso.h +++ b/arch/mips/include/asm/vdso.h @@ -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 index 000000000000..aa20865b288b --- /dev/null +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -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 */ diff --git a/arch/mips/include/asm/vdso/vdso.h b/arch/mips/include/asm/vdso/vdso.h new file mode 100644 index 000000000000..526695bc65ee --- /dev/null +++ b/arch/mips/include/asm/vdso/vdso.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 Imagination Technologies + * Author: Alex Smith <alex.smith@imgtec.com> + */ + +#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 struct vdso_data *get_vdso_data(void) +{ + return (const struct vdso_data *)(get_vdso_base() - PAGE_SIZE); +} + +#ifdef CONFIG_CLKSRC_MIPS_GIC + +static inline void __iomem *get_gic(const struct vdso_data *data) +{ + return (void __iomem *)data - PAGE_SIZE; +} + +#endif /* CONFIG_CLKSRC_MIPS_GIC */ + +#endif /* __ASSEMBLY__ */ diff --git a/arch/mips/include/asm/vdso/vsyscall.h b/arch/mips/include/asm/vdso/vsyscall.h new file mode 100644 index 000000000000..195314732233 --- /dev/null +++ b/arch/mips/include/asm/vdso/vsyscall.h @@ -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 */ |