1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright (C) 2018 ARM Limited
4  */
5 #ifndef __ASM_VDSO_GETTIMEOFDAY_H
6 #define __ASM_VDSO_GETTIMEOFDAY_H
7 
8 #ifndef __ASSEMBLY__
9 
10 #include <asm/alternative.h>
11 #include <asm/barrier.h>
12 #include <asm/unistd.h>
13 #include <asm/sysreg.h>
14 
15 #define VDSO_HAS_CLOCK_GETRES		1
16 
17 static __always_inline
18 int gettimeofday_fallback(struct __kernel_old_timeval *_tv,
19 			  struct timezone *_tz)
20 {
21 	register struct timezone *tz asm("x1") = _tz;
22 	register struct __kernel_old_timeval *tv asm("x0") = _tv;
23 	register long ret asm ("x0");
24 	register long nr asm("x8") = __NR_gettimeofday;
25 
26 	asm volatile(
27 	"       svc #0\n"
28 	: "=r" (ret)
29 	: "r" (tv), "r" (tz), "r" (nr)
30 	: "memory");
31 
32 	return ret;
33 }
34 
35 static __always_inline
36 long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
37 {
38 	register struct __kernel_timespec *ts asm("x1") = _ts;
39 	register clockid_t clkid asm("x0") = _clkid;
40 	register long ret asm ("x0");
41 	register long nr asm("x8") = __NR_clock_gettime;
42 
43 	asm volatile(
44 	"       svc #0\n"
45 	: "=r" (ret)
46 	: "r" (clkid), "r" (ts), "r" (nr)
47 	: "memory");
48 
49 	return ret;
50 }
51 
52 static __always_inline
53 int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
54 {
55 	register struct __kernel_timespec *ts asm("x1") = _ts;
56 	register clockid_t clkid asm("x0") = _clkid;
57 	register long ret asm ("x0");
58 	register long nr asm("x8") = __NR_clock_getres;
59 
60 	asm volatile(
61 	"       svc #0\n"
62 	: "=r" (ret)
63 	: "r" (clkid), "r" (ts), "r" (nr)
64 	: "memory");
65 
66 	return ret;
67 }
68 
69 static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
70 						 const struct vdso_data *vd)
71 {
72 	u64 res;
73 
74 	/*
75 	 * Core checks for mode already, so this raced against a concurrent
76 	 * update. Return something. Core will do another round and then
77 	 * see the mode change and fallback to the syscall.
78 	 */
79 	if (clock_mode == VDSO_CLOCKMODE_NONE)
80 		return 0;
81 
82 	/*
83 	 * If FEAT_ECV is available, use the self-synchronizing counter.
84 	 * Otherwise the isb is required to prevent that the counter value
85 	 * is speculated.
86 	*/
87 	asm volatile(
88 	ALTERNATIVE("isb\n"
89 		    "mrs %0, cntvct_el0",
90 		    "nop\n"
91 		    __mrs_s("%0", SYS_CNTVCTSS_EL0),
92 		    ARM64_HAS_ECV)
93 	: "=r" (res)
94 	:
95 	: "memory");
96 
97 	arch_counter_enforce_ordering(res);
98 
99 	return res;
100 }
101 
102 static __always_inline
103 const struct vdso_data *__arch_get_vdso_data(void)
104 {
105 	return _vdso_data;
106 }
107 
108 #ifdef CONFIG_TIME_NS
109 static __always_inline
110 const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd)
111 {
112 	return _timens_data;
113 }
114 #endif
115 
116 #endif /* !__ASSEMBLY__ */
117 
118 #endif /* __ASM_VDSO_GETTIMEOFDAY_H */
119