mips: Add support for generic vDSO
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>
This commit is contained in:

committed by
Paul Burton

parent
c2aeaaea17
commit
24640f233b
@@ -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 */
|
||||
|
151
arch/mips/include/asm/vdso/gettimeofday.h
Normal file
151
arch/mips/include/asm/vdso/gettimeofday.h
Normal file
@@ -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 */
|
85
arch/mips/include/asm/vdso/vdso.h
Normal file
85
arch/mips/include/asm/vdso/vdso.h
Normal file
@@ -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__ */
|
43
arch/mips/include/asm/vdso/vsyscall.h
Normal file
43
arch/mips/include/asm/vdso/vsyscall.h
Normal file
@@ -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 */
|
Reference in New Issue
Block a user