18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2018 ARM Limited
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#ifndef __ASM_VDSO_GETTIMEOFDAY_H
68c2ecf20Sopenharmony_ci#define __ASM_VDSO_GETTIMEOFDAY_H
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/barrier.h>
118c2ecf20Sopenharmony_ci#include <asm/unistd.h>
128c2ecf20Sopenharmony_ci#include <asm/errno.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/vdso/compat_barrier.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define VDSO_HAS_CLOCK_GETRES		1
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define BUILD_VDSO32			1
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic __always_inline
218c2ecf20Sopenharmony_ciint gettimeofday_fallback(struct __kernel_old_timeval *_tv,
228c2ecf20Sopenharmony_ci			  struct timezone *_tz)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	register struct timezone *tz asm("r1") = _tz;
258c2ecf20Sopenharmony_ci	register struct __kernel_old_timeval *tv asm("r0") = _tv;
268c2ecf20Sopenharmony_ci	register long ret asm ("r0");
278c2ecf20Sopenharmony_ci	register long nr asm("r7") = __NR_compat_gettimeofday;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	asm volatile(
308c2ecf20Sopenharmony_ci	"	swi #0\n"
318c2ecf20Sopenharmony_ci	: "=r" (ret)
328c2ecf20Sopenharmony_ci	: "r" (tv), "r" (tz), "r" (nr)
338c2ecf20Sopenharmony_ci	: "memory");
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return ret;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic __always_inline
398c2ecf20Sopenharmony_cilong clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	register struct __kernel_timespec *ts asm("r1") = _ts;
428c2ecf20Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
438c2ecf20Sopenharmony_ci	register long ret asm ("r0");
448c2ecf20Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_gettime64;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	asm volatile(
478c2ecf20Sopenharmony_ci	"	swi #0\n"
488c2ecf20Sopenharmony_ci	: "=r" (ret)
498c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
508c2ecf20Sopenharmony_ci	: "memory");
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return ret;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic __always_inline
568c2ecf20Sopenharmony_cilong clock_gettime32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	register struct old_timespec32 *ts asm("r1") = _ts;
598c2ecf20Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
608c2ecf20Sopenharmony_ci	register long ret asm ("r0");
618c2ecf20Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_gettime;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	asm volatile(
648c2ecf20Sopenharmony_ci	"	swi #0\n"
658c2ecf20Sopenharmony_ci	: "=r" (ret)
668c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
678c2ecf20Sopenharmony_ci	: "memory");
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return ret;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic __always_inline
738c2ecf20Sopenharmony_ciint clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	register struct __kernel_timespec *ts asm("r1") = _ts;
768c2ecf20Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
778c2ecf20Sopenharmony_ci	register long ret asm ("r0");
788c2ecf20Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_getres_time64;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	asm volatile(
818c2ecf20Sopenharmony_ci	"       swi #0\n"
828c2ecf20Sopenharmony_ci	: "=r" (ret)
838c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
848c2ecf20Sopenharmony_ci	: "memory");
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return ret;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic __always_inline
908c2ecf20Sopenharmony_ciint clock_getres32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	register struct old_timespec32 *ts asm("r1") = _ts;
938c2ecf20Sopenharmony_ci	register clockid_t clkid asm("r0") = _clkid;
948c2ecf20Sopenharmony_ci	register long ret asm ("r0");
958c2ecf20Sopenharmony_ci	register long nr asm("r7") = __NR_compat_clock_getres;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	asm volatile(
988c2ecf20Sopenharmony_ci	"       swi #0\n"
998c2ecf20Sopenharmony_ci	: "=r" (ret)
1008c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
1018c2ecf20Sopenharmony_ci	: "memory");
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return ret;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
1078c2ecf20Sopenharmony_ci						 const struct vdso_data *vd)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u64 res;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/*
1128c2ecf20Sopenharmony_ci	 * Core checks for mode already, so this raced against a concurrent
1138c2ecf20Sopenharmony_ci	 * update. Return something. Core will do another round and then
1148c2ecf20Sopenharmony_ci	 * see the mode change and fallback to the syscall.
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	if (clock_mode != VDSO_CLOCKMODE_ARCHTIMER)
1178c2ecf20Sopenharmony_ci		return 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/*
1208c2ecf20Sopenharmony_ci	 * This isb() is required to prevent that the counter value
1218c2ecf20Sopenharmony_ci	 * is speculated.
1228c2ecf20Sopenharmony_ci	 */
1238c2ecf20Sopenharmony_ci	isb();
1248c2ecf20Sopenharmony_ci	asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (res));
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * This isb() is required to prevent that the seq lock is
1278c2ecf20Sopenharmony_ci	 * speculated.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	isb();
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return res;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic __always_inline const struct vdso_data *__arch_get_vdso_data(void)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	const struct vdso_data *ret;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/*
1398c2ecf20Sopenharmony_ci	 * This simply puts &_vdso_data into ret. The reason why we don't use
1408c2ecf20Sopenharmony_ci	 * `ret = _vdso_data` is that the compiler tends to optimise this in a
1418c2ecf20Sopenharmony_ci	 * very suboptimal way: instead of keeping &_vdso_data in a register,
1428c2ecf20Sopenharmony_ci	 * it goes through a relocation almost every time _vdso_data must be
1438c2ecf20Sopenharmony_ci	 * accessed (even in subfunctions). This is both time and space
1448c2ecf20Sopenharmony_ci	 * consuming: each relocation uses a word in the code section, and it
1458c2ecf20Sopenharmony_ci	 * has to be loaded at runtime.
1468c2ecf20Sopenharmony_ci	 *
1478c2ecf20Sopenharmony_ci	 * This trick hides the assignment from the compiler. Since it cannot
1488c2ecf20Sopenharmony_ci	 * track where the pointer comes from, it will only use one relocation
1498c2ecf20Sopenharmony_ci	 * where __arch_get_vdso_data() is called, and then keep the result in
1508c2ecf20Sopenharmony_ci	 * a register.
1518c2ecf20Sopenharmony_ci	 */
1528c2ecf20Sopenharmony_ci	asm volatile("mov %0, %1" : "=r"(ret) : "r"(_vdso_data));
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return ret;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci#ifdef CONFIG_TIME_NS
1588c2ecf20Sopenharmony_cistatic __always_inline const struct vdso_data *__arch_get_timens_vdso_data(void)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	const struct vdso_data *ret;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* See __arch_get_vdso_data(). */
1638c2ecf20Sopenharmony_ci	asm volatile("mov %0, %1" : "=r"(ret) : "r"(_timens_data));
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return ret;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci#endif
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic inline bool vdso_clocksource_ok(const struct vdso_data *vd)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	return vd->clock_mode == VDSO_CLOCKMODE_ARCHTIMER;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci#define vdso_clocksource_ok	vdso_clocksource_ok
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci#endif /* !__ASSEMBLY__ */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
178