162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2018 ARM Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#ifndef __ASM_VDSO_GETTIMEOFDAY_H
662306a36Sopenharmony_ci#define __ASM_VDSO_GETTIMEOFDAY_H
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#ifndef __ASSEMBLY__
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/barrier.h>
1162306a36Sopenharmony_ci#include <asm/unistd.h>
1262306a36Sopenharmony_ci#include <asm/errno.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/vdso/compat_barrier.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define VDSO_HAS_CLOCK_GETRES		1
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define BUILD_VDSO32			1
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic __always_inline
2162306a36Sopenharmony_ciint gettimeofday_fallback(struct __kernel_old_timeval *_tv,
2262306a36Sopenharmony_ci			  struct timezone *_tz)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	register struct timezone *tz asm("r1") = _tz;
2562306a36Sopenharmony_ci	register struct __kernel_old_timeval *tv asm("r0") = _tv;
2662306a36Sopenharmony_ci	register long ret asm ("r0");
2762306a36Sopenharmony_ci	register long nr asm("r7") = __NR_compat_gettimeofday;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	asm volatile(
3062306a36Sopenharmony_ci	"	swi #0\n"
3162306a36Sopenharmony_ci	: "=r" (ret)
3262306a36Sopenharmony_ci	: "r" (tv), "r" (tz), "r" (nr)
3362306a36Sopenharmony_ci	: "memory");
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return ret;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic __always_inline
3962306a36Sopenharmony_cilong clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	register struct __kernel_timespec *ts asm("r1") = _ts;
4262306a36Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
4362306a36Sopenharmony_ci	register long ret asm ("r0");
4462306a36Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_gettime64;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	asm volatile(
4762306a36Sopenharmony_ci	"	swi #0\n"
4862306a36Sopenharmony_ci	: "=r" (ret)
4962306a36Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
5062306a36Sopenharmony_ci	: "memory");
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return ret;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic __always_inline
5662306a36Sopenharmony_cilong clock_gettime32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	register struct old_timespec32 *ts asm("r1") = _ts;
5962306a36Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
6062306a36Sopenharmony_ci	register long ret asm ("r0");
6162306a36Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_gettime;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	asm volatile(
6462306a36Sopenharmony_ci	"	swi #0\n"
6562306a36Sopenharmony_ci	: "=r" (ret)
6662306a36Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
6762306a36Sopenharmony_ci	: "memory");
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return ret;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic __always_inline
7362306a36Sopenharmony_ciint clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	register struct __kernel_timespec *ts asm("r1") = _ts;
7662306a36Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
7762306a36Sopenharmony_ci	register long ret asm ("r0");
7862306a36Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_getres_time64;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	asm volatile(
8162306a36Sopenharmony_ci	"       swi #0\n"
8262306a36Sopenharmony_ci	: "=r" (ret)
8362306a36Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
8462306a36Sopenharmony_ci	: "memory");
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ret;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic __always_inline
9062306a36Sopenharmony_ciint clock_getres32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	register struct old_timespec32 *ts asm("r1") = _ts;
9362306a36Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
9462306a36Sopenharmony_ci	register long ret asm ("r0");
9562306a36Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_getres;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	asm volatile(
9862306a36Sopenharmony_ci	"       swi #0\n"
9962306a36Sopenharmony_ci	: "=r" (ret)
10062306a36Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
10162306a36Sopenharmony_ci	: "memory");
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return ret;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
10762306a36Sopenharmony_ci						 const struct vdso_data *vd)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u64 res;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * Core checks for mode already, so this raced against a concurrent
11362306a36Sopenharmony_ci	 * update. Return something. Core will do another round and then
11462306a36Sopenharmony_ci	 * see the mode change and fallback to the syscall.
11562306a36Sopenharmony_ci	 */
11662306a36Sopenharmony_ci	if (clock_mode != VDSO_CLOCKMODE_ARCHTIMER)
11762306a36Sopenharmony_ci		return 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/*
12062306a36Sopenharmony_ci	 * This isb() is required to prevent that the counter value
12162306a36Sopenharmony_ci	 * is speculated.
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	isb();
12462306a36Sopenharmony_ci	asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (res));
12562306a36Sopenharmony_ci	/*
12662306a36Sopenharmony_ci	 * This isb() is required to prevent that the seq lock is
12762306a36Sopenharmony_ci	 * speculated.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	isb();
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return res;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic __always_inline const struct vdso_data *__arch_get_vdso_data(void)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	const struct vdso_data *ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/*
13962306a36Sopenharmony_ci	 * This simply puts &_vdso_data into ret. The reason why we don't use
14062306a36Sopenharmony_ci	 * `ret = _vdso_data` is that the compiler tends to optimise this in a
14162306a36Sopenharmony_ci	 * very suboptimal way: instead of keeping &_vdso_data in a register,
14262306a36Sopenharmony_ci	 * it goes through a relocation almost every time _vdso_data must be
14362306a36Sopenharmony_ci	 * accessed (even in subfunctions). This is both time and space
14462306a36Sopenharmony_ci	 * consuming: each relocation uses a word in the code section, and it
14562306a36Sopenharmony_ci	 * has to be loaded at runtime.
14662306a36Sopenharmony_ci	 *
14762306a36Sopenharmony_ci	 * This trick hides the assignment from the compiler. Since it cannot
14862306a36Sopenharmony_ci	 * track where the pointer comes from, it will only use one relocation
14962306a36Sopenharmony_ci	 * where __arch_get_vdso_data() is called, and then keep the result in
15062306a36Sopenharmony_ci	 * a register.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	asm volatile("mov %0, %1" : "=r"(ret) : "r"(_vdso_data));
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return ret;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#ifdef CONFIG_TIME_NS
15862306a36Sopenharmony_cistatic __always_inline
15962306a36Sopenharmony_ciconst struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	const struct vdso_data *ret;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* See __arch_get_vdso_data(). */
16462306a36Sopenharmony_ci	asm volatile("mov %0, %1" : "=r"(ret) : "r"(_timens_data));
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return ret;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci#endif
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic inline bool vdso_clocksource_ok(const struct vdso_data *vd)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return vd->clock_mode == VDSO_CLOCKMODE_ARCHTIMER;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci#define vdso_clocksource_ok	vdso_clocksource_ok
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#endif /* !__ASSEMBLY__ */
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
179