aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorNathan Lynch <nathan_lynch@mentor.com>2015-03-25 14:14:22 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2015-03-27 18:20:45 -0400
commit8512287a8165592466cb9cb347ba94892e9c56a5 (patch)
tree329f53031adec8e28f10700be54cae0e62cb8620 /arch
parent1713ce7c43755fe8b0f31ea317513129bf784909 (diff)
ARM: 8330/1: add VDSO user-space code
Place VDSO-related user-space code in arch/arm/kernel/vdso/. It is almost completely written in C with some assembly helpers to load the data page address, sample the counter, and fall back to system calls when necessary. The VDSO can service gettimeofday and clock_gettime when CONFIG_ARM_ARCH_TIMER is enabled and the architected timer is present (and correctly configured). It reads the CP15-based virtual counter to compute high-resolution timestamps. Of particular note is that a post-processing step ("vdsomunge") is necessary to produce a shared object which is architecturally allowed to be used by both soft- and hard-float EABI programs. The 2012 edition of the ARM ABI defines Tag_ABI_VFP_args = 3 "Code is compatible with both the base and VFP variants; the user did not permit non-variadic functions to pass FP parameters/results." Unfortunately current toolchains do not support this tag, which is ideally what we would use. The best available option is to ensure that both EF_ARM_ABI_FLOAT_SOFT and EF_ARM_ABI_FLOAT_HARD are unset in the ELF header's e_flags, indicating that the shared object is "old" and should be accepted for backward compatibility's sake. While binutils < 2.24 appear to produce a vdso.so with both flags clear, 2.24 always sets EF_ARM_ABI_FLOAT_SOFT, with no way to inhibit this behavior. So we have to fix things up with a custom post-processing step. In fact, the VDSO code in glibc does much less validation (including checking these flags) than the code for handling conventional file-backed shared libraries, so this is a bit moot unless glibc's VDSO code becomes more strict. Signed-off-by: Nathan Lynch <nathan_lynch@mentor.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/kernel/asm-offsets.c5
-rw-r--r--arch/arm/vdso/.gitignore1
-rw-r--r--arch/arm/vdso/Makefile74
-rw-r--r--arch/arm/vdso/datapage.S15
-rw-r--r--arch/arm/vdso/vdso.S35
-rw-r--r--arch/arm/vdso/vdso.lds.S87
-rw-r--r--arch/arm/vdso/vdsomunge.c201
-rw-r--r--arch/arm/vdso/vgettimeofday.c282
8 files changed, 700 insertions, 0 deletions
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 2d2d6087b9b1..9147008f0d51 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -25,6 +25,7 @@
25#include <asm/memory.h> 25#include <asm/memory.h>
26#include <asm/procinfo.h> 26#include <asm/procinfo.h>
27#include <asm/suspend.h> 27#include <asm/suspend.h>
28#include <asm/vdso_datapage.h>
28#include <asm/hardware/cache-l2x0.h> 29#include <asm/hardware/cache-l2x0.h>
29#include <linux/kbuild.h> 30#include <linux/kbuild.h>
30 31
@@ -210,5 +211,9 @@ int main(void)
210#endif 211#endif
211 DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr)); 212 DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
212#endif 213#endif
214 BLANK();
215#ifdef CONFIG_VDSO
216 DEFINE(VDSO_DATA_SIZE, sizeof(union vdso_data_store));
217#endif
213 return 0; 218 return 0;
214} 219}
diff --git a/arch/arm/vdso/.gitignore b/arch/arm/vdso/.gitignore
new file mode 100644
index 000000000000..f8b69d84238e
--- /dev/null
+++ b/arch/arm/vdso/.gitignore
@@ -0,0 +1 @@
vdso.lds
diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile
new file mode 100644
index 000000000000..bab0a8be7924
--- /dev/null
+++ b/arch/arm/vdso/Makefile
@@ -0,0 +1,74 @@
1hostprogs-y := vdsomunge
2
3obj-vdso := vgettimeofday.o datapage.o
4
5# Build rules
6targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.so.raw vdso.lds
7obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
8
9ccflags-y := -shared -fPIC -fno-common -fno-builtin -fno-stack-protector
10ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 -DDISABLE_BRANCH_PROFILING
11ccflags-y += -Wl,--no-undefined $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
12
13obj-y += vdso.o
14extra-y += vdso.lds
15CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
16
17CFLAGS_REMOVE_vdso.o = -pg
18
19# Force -O2 to avoid libgcc dependencies
20CFLAGS_REMOVE_vgettimeofday.o = -pg -Os
21CFLAGS_vgettimeofday.o = -O2
22
23# Disable gcov profiling for VDSO code
24GCOV_PROFILE := n
25
26# Force dependency
27$(obj)/vdso.o : $(obj)/vdso.so
28
29# Link rule for the .so file
30$(obj)/vdso.so.raw: $(src)/vdso.lds $(obj-vdso) FORCE
31 $(call if_changed,vdsold)
32
33$(obj)/vdso.so.dbg: $(obj)/vdso.so.raw $(obj)/vdsomunge FORCE
34 $(call if_changed,vdsomunge)
35
36# Strip rule for the .so file
37$(obj)/%.so: OBJCOPYFLAGS := -S
38$(obj)/%.so: $(obj)/%.so.dbg FORCE
39 $(call if_changed,objcopy)
40
41# Actual build commands
42quiet_cmd_vdsold = VDSO $@
43 cmd_vdsold = $(CC) $(c_flags) -Wl,-T $(filter %.lds,$^) $(filter %.o,$^) \
44 $(call cc-ldoption, -Wl$(comma)--build-id) \
45 -Wl,-Bsymbolic -Wl,-z,max-page-size=4096 \
46 -Wl,-z,common-page-size=4096 -o $@
47
48quiet_cmd_vdsomunge = MUNGE $@
49 cmd_vdsomunge = $(objtree)/$(obj)/vdsomunge $< $@
50
51#
52# Install the unstripped copy of vdso.so.dbg. If our toolchain
53# supports build-id, install .build-id links as well.
54#
55# Cribbed from arch/x86/vdso/Makefile.
56#
57quiet_cmd_vdso_install = INSTALL $<
58define cmd_vdso_install
59 cp $< "$(MODLIB)/vdso/vdso.so"; \
60 if readelf -n $< | grep -q 'Build ID'; then \
61 buildid=`readelf -n $< |grep 'Build ID' |sed -e 's/^.*Build ID: \(.*\)$$/\1/'`; \
62 first=`echo $$buildid | cut -b-2`; \
63 last=`echo $$buildid | cut -b3-`; \
64 mkdir -p "$(MODLIB)/vdso/.build-id/$$first"; \
65 ln -sf "../../vdso.so" "$(MODLIB)/vdso/.build-id/$$first/$$last.debug"; \
66 fi
67endef
68
69$(MODLIB)/vdso: FORCE
70 @mkdir -p $(MODLIB)/vdso
71
72PHONY += vdso_install
73vdso_install: $(obj)/vdso.so.dbg $(MODLIB)/vdso FORCE
74 $(call cmd,vdso_install)
diff --git a/arch/arm/vdso/datapage.S b/arch/arm/vdso/datapage.S
new file mode 100644
index 000000000000..a2e60367931b
--- /dev/null
+++ b/arch/arm/vdso/datapage.S
@@ -0,0 +1,15 @@
1#include <linux/linkage.h>
2#include <asm/asm-offsets.h>
3
4 .align 2
5.L_vdso_data_ptr:
6 .long _start - . - VDSO_DATA_SIZE
7
8ENTRY(__get_datapage)
9 .fnstart
10 adr r0, .L_vdso_data_ptr
11 ldr r1, [r0]
12 add r0, r0, r1
13 bx lr
14 .fnend
15ENDPROC(__get_datapage)
diff --git a/arch/arm/vdso/vdso.S b/arch/arm/vdso/vdso.S
new file mode 100644
index 000000000000..b2b97e3e7bab
--- /dev/null
+++ b/arch/arm/vdso/vdso.S
@@ -0,0 +1,35 @@
1/*
2 * Adapted from arm64 version.
3 *
4 * Copyright (C) 2012 ARM Limited
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Will Deacon <will.deacon@arm.com>
19 */
20
21#include <linux/init.h>
22#include <linux/linkage.h>
23#include <linux/const.h>
24#include <asm/page.h>
25
26 __PAGE_ALIGNED_DATA
27
28 .globl vdso_start, vdso_end
29 .balign PAGE_SIZE
30vdso_start:
31 .incbin "arch/arm/vdso/vdso.so"
32 .balign PAGE_SIZE
33vdso_end:
34
35 .previous
diff --git a/arch/arm/vdso/vdso.lds.S b/arch/arm/vdso/vdso.lds.S
new file mode 100644
index 000000000000..89ca89f12d23
--- /dev/null
+++ b/arch/arm/vdso/vdso.lds.S
@@ -0,0 +1,87 @@
1/*
2 * Adapted from arm64 version.
3 *
4 * GNU linker script for the VDSO library.
5 *
6 * Copyright (C) 2012 ARM Limited
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Will Deacon <will.deacon@arm.com>
21 * Heavily based on the vDSO linker scripts for other archs.
22 */
23
24#include <linux/const.h>
25#include <asm/page.h>
26#include <asm/vdso.h>
27
28OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
29OUTPUT_ARCH(arm)
30
31SECTIONS
32{
33 PROVIDE(_start = .);
34
35 . = SIZEOF_HEADERS;
36
37 .hash : { *(.hash) } :text
38 .gnu.hash : { *(.gnu.hash) }
39 .dynsym : { *(.dynsym) }
40 .dynstr : { *(.dynstr) }
41 .gnu.version : { *(.gnu.version) }
42 .gnu.version_d : { *(.gnu.version_d) }
43 .gnu.version_r : { *(.gnu.version_r) }
44
45 .note : { *(.note.*) } :text :note
46
47
48 .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
49 .eh_frame : { KEEP (*(.eh_frame)) } :text
50
51 .dynamic : { *(.dynamic) } :text :dynamic
52
53 .rodata : { *(.rodata*) } :text
54
55 .text : { *(.text*) } :text =0xe7f001f2
56
57 .got : { *(.got) }
58 .rel.plt : { *(.rel.plt) }
59
60 /DISCARD/ : {
61 *(.note.GNU-stack)
62 *(.data .data.* .gnu.linkonce.d.* .sdata*)
63 *(.bss .sbss .dynbss .dynsbss)
64 }
65}
66
67/*
68 * We must supply the ELF program headers explicitly to get just one
69 * PT_LOAD segment, and set the flags explicitly to make segments read-only.
70 */
71PHDRS
72{
73 text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
74 dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
75 note PT_NOTE FLAGS(4); /* PF_R */
76 eh_frame_hdr PT_GNU_EH_FRAME;
77}
78
79VERSION
80{
81 LINUX_2.6 {
82 global:
83 __vdso_clock_gettime;
84 __vdso_gettimeofday;
85 local: *;
86 };
87}
diff --git a/arch/arm/vdso/vdsomunge.c b/arch/arm/vdso/vdsomunge.c
new file mode 100644
index 000000000000..9005b07296c8
--- /dev/null
+++ b/arch/arm/vdso/vdsomunge.c
@@ -0,0 +1,201 @@
1/*
2 * Copyright 2015 Mentor Graphics Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; version 2 of the
7 * License.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 *
18 * vdsomunge - Host program which produces a shared object
19 * architecturally specified to be usable by both soft- and hard-float
20 * programs.
21 *
22 * The Procedure Call Standard for the ARM Architecture (ARM IHI
23 * 0042E) says:
24 *
25 * 6.4.1 VFP and Base Standard Compatibility
26 *
27 * Code compiled for the VFP calling standard is compatible with
28 * the base standard (and vice-versa) if no floating-point or
29 * containerized vector arguments or results are used.
30 *
31 * And ELF for the ARM Architecture (ARM IHI 0044E) (Table 4-2) says:
32 *
33 * If both EF_ARM_ABI_FLOAT_XXXX bits are clear, conformance to the
34 * base procedure-call standard is implied.
35 *
36 * The VDSO is built with -msoft-float, as with the rest of the ARM
37 * kernel, and uses no floating point arguments or results. The build
38 * process will produce a shared object that may or may not have the
39 * EF_ARM_ABI_FLOAT_SOFT flag set (it seems to depend on the binutils
40 * version; binutils starting with 2.24 appears to set it). The
41 * EF_ARM_ABI_FLOAT_HARD flag should definitely not be set, and this
42 * program will error out if it is.
43 *
44 * If the soft-float flag is set, this program clears it. That's all
45 * it does.
46 */
47
48#define _GNU_SOURCE
49
50#include <byteswap.h>
51#include <elf.h>
52#include <errno.h>
53#include <error.h>
54#include <fcntl.h>
55#include <stdbool.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <sys/mman.h>
60#include <sys/stat.h>
61#include <sys/types.h>
62#include <unistd.h>
63
64#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
65#define HOST_ORDER ELFDATA2LSB
66#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
67#define HOST_ORDER ELFDATA2MSB
68#endif
69
70/* Some of the ELF constants we'd like to use were added to <elf.h>
71 * relatively recently.
72 */
73#ifndef EF_ARM_EABI_VER5
74#define EF_ARM_EABI_VER5 0x05000000
75#endif
76
77#ifndef EF_ARM_ABI_FLOAT_SOFT
78#define EF_ARM_ABI_FLOAT_SOFT 0x200
79#endif
80
81#ifndef EF_ARM_ABI_FLOAT_HARD
82#define EF_ARM_ABI_FLOAT_HARD 0x400
83#endif
84
85static const char *outfile;
86
87static void cleanup(void)
88{
89 if (error_message_count > 0 && outfile != NULL)
90 unlink(outfile);
91}
92
93static Elf32_Word read_elf_word(Elf32_Word word, bool swap)
94{
95 return swap ? bswap_32(word) : word;
96}
97
98static Elf32_Half read_elf_half(Elf32_Half half, bool swap)
99{
100 return swap ? bswap_16(half) : half;
101}
102
103static void write_elf_word(Elf32_Word val, Elf32_Word *dst, bool swap)
104{
105 *dst = swap ? bswap_32(val) : val;
106}
107
108int main(int argc, char **argv)
109{
110 const Elf32_Ehdr *inhdr;
111 bool clear_soft_float;
112 const char *infile;
113 Elf32_Word e_flags;
114 const void *inbuf;
115 struct stat stat;
116 void *outbuf;
117 bool swap;
118 int outfd;
119 int infd;
120
121 atexit(cleanup);
122
123 if (argc != 3)
124 error(EXIT_FAILURE, 0, "Usage: %s [infile] [outfile]", argv[0]);
125
126 infile = argv[1];
127 outfile = argv[2];
128
129 infd = open(infile, O_RDONLY);
130 if (infd < 0)
131 error(EXIT_FAILURE, errno, "Cannot open %s", infile);
132
133 if (fstat(infd, &stat) != 0)
134 error(EXIT_FAILURE, errno, "Failed stat for %s", infile);
135
136 inbuf = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, infd, 0);
137 if (inbuf == MAP_FAILED)
138 error(EXIT_FAILURE, errno, "Failed to map %s", infile);
139
140 close(infd);
141
142 inhdr = inbuf;
143
144 if (memcmp(&inhdr->e_ident, ELFMAG, SELFMAG) != 0)
145 error(EXIT_FAILURE, 0, "Not an ELF file");
146
147 if (inhdr->e_ident[EI_CLASS] != ELFCLASS32)
148 error(EXIT_FAILURE, 0, "Unsupported ELF class");
149
150 swap = inhdr->e_ident[EI_DATA] != HOST_ORDER;
151
152 if (read_elf_half(inhdr->e_type, swap) != ET_DYN)
153 error(EXIT_FAILURE, 0, "Not a shared object");
154
155 if (read_elf_half(inhdr->e_machine, swap) != EM_ARM) {
156 error(EXIT_FAILURE, 0, "Unsupported architecture %#x",
157 inhdr->e_machine);
158 }
159
160 e_flags = read_elf_word(inhdr->e_flags, swap);
161
162 if (EF_ARM_EABI_VERSION(e_flags) != EF_ARM_EABI_VER5) {
163 error(EXIT_FAILURE, 0, "Unsupported EABI version %#x",
164 EF_ARM_EABI_VERSION(e_flags));
165 }
166
167 if (e_flags & EF_ARM_ABI_FLOAT_HARD)
168 error(EXIT_FAILURE, 0,
169 "Unexpected hard-float flag set in e_flags");
170
171 clear_soft_float = !!(e_flags & EF_ARM_ABI_FLOAT_SOFT);
172
173 outfd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
174 if (outfd < 0)
175 error(EXIT_FAILURE, errno, "Cannot open %s", outfile);
176
177 if (ftruncate(outfd, stat.st_size) != 0)
178 error(EXIT_FAILURE, errno, "Cannot truncate %s", outfile);
179
180 outbuf = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
181 outfd, 0);
182 if (outbuf == MAP_FAILED)
183 error(EXIT_FAILURE, errno, "Failed to map %s", outfile);
184
185 close(outfd);
186
187 memcpy(outbuf, inbuf, stat.st_size);
188
189 if (clear_soft_float) {
190 Elf32_Ehdr *outhdr;
191
192 outhdr = outbuf;
193 e_flags &= ~EF_ARM_ABI_FLOAT_SOFT;
194 write_elf_word(e_flags, &outhdr->e_flags, swap);
195 }
196
197 if (msync(outbuf, stat.st_size, MS_SYNC) != 0)
198 error(EXIT_FAILURE, errno, "Failed to sync %s", outfile);
199
200 return EXIT_SUCCESS;
201}
diff --git a/arch/arm/vdso/vgettimeofday.c b/arch/arm/vdso/vgettimeofday.c
new file mode 100644
index 000000000000..79214d5ff097
--- /dev/null
+++ b/arch/arm/vdso/vgettimeofday.c
@@ -0,0 +1,282 @@
1/*
2 * Copyright 2015 Mentor Graphics Corporation.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; version 2 of the
7 * License.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/compiler.h>
19#include <linux/hrtimer.h>
20#include <linux/time.h>
21#include <asm/arch_timer.h>
22#include <asm/barrier.h>
23#include <asm/bug.h>
24#include <asm/page.h>
25#include <asm/unistd.h>
26#include <asm/vdso_datapage.h>
27
28#ifndef CONFIG_AEABI
29#error This code depends on AEABI system call conventions
30#endif
31
32extern struct vdso_data *__get_datapage(void);
33
34static notrace u32 __vdso_read_begin(const struct vdso_data *vdata)
35{
36 u32 seq;
37repeat:
38 seq = ACCESS_ONCE(vdata->seq_count);
39 if (seq & 1) {
40 cpu_relax();
41 goto repeat;
42 }
43 return seq;
44}
45
46static notrace u32 vdso_read_begin(const struct vdso_data *vdata)
47{
48 u32 seq;
49
50 seq = __vdso_read_begin(vdata);
51
52 smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */
53 return seq;
54}
55
56static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start)
57{
58 smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */
59 return vdata->seq_count != start;
60}
61
62static notrace long clock_gettime_fallback(clockid_t _clkid,
63 struct timespec *_ts)
64{
65 register struct timespec *ts asm("r1") = _ts;
66 register clockid_t clkid asm("r0") = _clkid;
67 register long ret asm ("r0");
68 register long nr asm("r7") = __NR_clock_gettime;
69
70 asm volatile(
71 " swi #0\n"
72 : "=r" (ret)
73 : "r" (clkid), "r" (ts), "r" (nr)
74 : "memory");
75
76 return ret;
77}
78
79static notrace int do_realtime_coarse(struct timespec *ts,
80 struct vdso_data *vdata)
81{
82 u32 seq;
83
84 do {
85 seq = vdso_read_begin(vdata);
86
87 ts->tv_sec = vdata->xtime_coarse_sec;
88 ts->tv_nsec = vdata->xtime_coarse_nsec;
89
90 } while (vdso_read_retry(vdata, seq));
91
92 return 0;
93}
94
95static notrace int do_monotonic_coarse(struct timespec *ts,
96 struct vdso_data *vdata)
97{
98 struct timespec tomono;
99 u32 seq;
100
101 do {
102 seq = vdso_read_begin(vdata);
103
104 ts->tv_sec = vdata->xtime_coarse_sec;
105 ts->tv_nsec = vdata->xtime_coarse_nsec;
106
107 tomono.tv_sec = vdata->wtm_clock_sec;
108 tomono.tv_nsec = vdata->wtm_clock_nsec;
109
110 } while (vdso_read_retry(vdata, seq));
111
112 ts->tv_sec += tomono.tv_sec;
113 timespec_add_ns(ts, tomono.tv_nsec);
114
115 return 0;
116}
117
118#ifdef CONFIG_ARM_ARCH_TIMER
119
120static notrace u64 get_ns(struct vdso_data *vdata)
121{
122 u64 cycle_delta;
123 u64 cycle_now;
124 u64 nsec;
125
126 cycle_now = arch_counter_get_cntvct();
127
128 cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask;
129
130 nsec = (cycle_delta * vdata->cs_mult) + vdata->xtime_clock_snsec;
131 nsec >>= vdata->cs_shift;
132
133 return nsec;
134}
135
136static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata)
137{
138 u64 nsecs;
139 u32 seq;
140
141 do {
142 seq = vdso_read_begin(vdata);
143
144 if (!vdata->tk_is_cntvct)
145 return -1;
146
147 ts->tv_sec = vdata->xtime_clock_sec;
148 nsecs = get_ns(vdata);
149
150 } while (vdso_read_retry(vdata, seq));
151
152 ts->tv_nsec = 0;
153 timespec_add_ns(ts, nsecs);
154
155 return 0;
156}
157
158static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata)
159{
160 struct timespec tomono;
161 u64 nsecs;
162 u32 seq;
163
164 do {
165 seq = vdso_read_begin(vdata);
166
167 if (!vdata->tk_is_cntvct)
168 return -1;
169
170 ts->tv_sec = vdata->xtime_clock_sec;
171 nsecs = get_ns(vdata);
172
173 tomono.tv_sec = vdata->wtm_clock_sec;
174 tomono.tv_nsec = vdata->wtm_clock_nsec;
175
176 } while (vdso_read_retry(vdata, seq));
177
178 ts->tv_sec += tomono.tv_sec;
179 ts->tv_nsec = 0;
180 timespec_add_ns(ts, nsecs + tomono.tv_nsec);
181
182 return 0;
183}
184
185#else /* CONFIG_ARM_ARCH_TIMER */
186
187static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata)
188{
189 return -1;
190}
191
192static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata)
193{
194 return -1;
195}
196
197#endif /* CONFIG_ARM_ARCH_TIMER */
198
199notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
200{
201 struct vdso_data *vdata;
202 int ret = -1;
203
204 vdata = __get_datapage();
205
206 switch (clkid) {
207 case CLOCK_REALTIME_COARSE:
208 ret = do_realtime_coarse(ts, vdata);
209 break;
210 case CLOCK_MONOTONIC_COARSE:
211 ret = do_monotonic_coarse(ts, vdata);
212 break;
213 case CLOCK_REALTIME:
214 ret = do_realtime(ts, vdata);
215 break;
216 case CLOCK_MONOTONIC:
217 ret = do_monotonic(ts, vdata);
218 break;
219 default:
220 break;
221 }
222
223 if (ret)
224 ret = clock_gettime_fallback(clkid, ts);
225
226 return ret;
227}
228
229static notrace long gettimeofday_fallback(struct timeval *_tv,
230 struct timezone *_tz)
231{
232 register struct timezone *tz asm("r1") = _tz;
233 register struct timeval *tv asm("r0") = _tv;
234 register long ret asm ("r0");
235 register long nr asm("r7") = __NR_gettimeofday;
236
237 asm volatile(
238 " swi #0\n"
239 : "=r" (ret)
240 : "r" (tv), "r" (tz), "r" (nr)
241 : "memory");
242
243 return ret;
244}
245
246notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
247{
248 struct timespec ts;
249 struct vdso_data *vdata;
250 int ret;
251
252 vdata = __get_datapage();
253
254 ret = do_realtime(&ts, vdata);
255 if (ret)
256 return gettimeofday_fallback(tv, tz);
257
258 if (tv) {
259 tv->tv_sec = ts.tv_sec;
260 tv->tv_usec = ts.tv_nsec / 1000;
261 }
262 if (tz) {
263 tz->tz_minuteswest = vdata->tz_minuteswest;
264 tz->tz_dsttime = vdata->tz_dsttime;
265 }
266
267 return ret;
268}
269
270/* Avoid unresolved references emitted by GCC */
271
272void __aeabi_unwind_cpp_pr0(void)
273{
274}
275
276void __aeabi_unwind_cpp_pr1(void)
277{
278}
279
280void __aeabi_unwind_cpp_pr2(void)
281{
282}