diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-07 15:33:32 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-13 16:26:21 -0400 |
commit | 4a556f4f56da3110b27e265b79f0e7582115445c (patch) | |
tree | f5af0b865c867160855e4722b49d7e850dddf944 | |
parent | 0c1d1917c547c8e787fb58e20e2de577453c980c (diff) |
tile: implement gettimeofday() via vDSO
This change creates the framework for vDSO calls, makes the existing
rt_sigreturn() mechanism use it, and adds a fast gettimeofday().
Now that we need to expose the vDSO address to userspace, we add
AT_SYSINFO_EHDR to the set of aux entries provided to userspace.
(You can disable any extra vDSO support by booting with vdso=0,
but the rt_sigreturn vDSO page will still be provided.)
Note that glibc has supported the tile vDSO since release 2.17.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
-rw-r--r-- | arch/tile/Kconfig | 3 | ||||
-rw-r--r-- | arch/tile/include/asm/elf.h | 5 | ||||
-rw-r--r-- | arch/tile/include/asm/mmu.h | 1 | ||||
-rw-r--r-- | arch/tile/include/asm/page.h | 8 | ||||
-rw-r--r-- | arch/tile/include/asm/processor.h | 6 | ||||
-rw-r--r-- | arch/tile/include/asm/sections.h | 4 | ||||
-rw-r--r-- | arch/tile/include/asm/vdso.h | 49 | ||||
-rw-r--r-- | arch/tile/include/uapi/asm/auxvec.h | 3 | ||||
-rw-r--r-- | arch/tile/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/tile/kernel/compat_signal.c | 3 | ||||
-rw-r--r-- | arch/tile/kernel/entry.S | 16 | ||||
-rw-r--r-- | arch/tile/kernel/signal.c | 3 | ||||
-rw-r--r-- | arch/tile/kernel/stack.c | 5 | ||||
-rw-r--r-- | arch/tile/kernel/time.c | 37 | ||||
-rw-r--r-- | arch/tile/kernel/vdso.c | 212 | ||||
-rw-r--r-- | arch/tile/kernel/vdso/Makefile | 118 | ||||
-rw-r--r-- | arch/tile/kernel/vdso/vdso.S | 28 | ||||
-rw-r--r-- | arch/tile/kernel/vdso/vdso.lds.S | 87 | ||||
-rw-r--r-- | arch/tile/kernel/vdso/vdso32.S | 28 | ||||
-rw-r--r-- | arch/tile/kernel/vdso/vgettimeofday.c | 107 | ||||
-rw-r--r-- | arch/tile/kernel/vdso/vrt_sigreturn.S | 30 | ||||
-rw-r--r-- | arch/tile/mm/elf.c | 37 |
22 files changed, 732 insertions, 62 deletions
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 1126b9d2f4cc..7b87318ee690 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig | |||
@@ -64,6 +64,9 @@ config HUGETLB_SUPER_PAGES | |||
64 | depends on HUGETLB_PAGE && TILEGX | 64 | depends on HUGETLB_PAGE && TILEGX |
65 | def_bool y | 65 | def_bool y |
66 | 66 | ||
67 | config GENERIC_TIME_VSYSCALL | ||
68 | def_bool y | ||
69 | |||
67 | # FIXME: tilegx can implement a more efficient rwsem. | 70 | # FIXME: tilegx can implement a more efficient rwsem. |
68 | config RWSEM_GENERIC_SPINLOCK | 71 | config RWSEM_GENERIC_SPINLOCK |
69 | def_bool y | 72 | def_bool y |
diff --git a/arch/tile/include/asm/elf.h b/arch/tile/include/asm/elf.h index ff8a93408823..31d854f1b83b 100644 --- a/arch/tile/include/asm/elf.h +++ b/arch/tile/include/asm/elf.h | |||
@@ -132,6 +132,11 @@ extern int dump_task_regs(struct task_struct *, elf_gregset_t *); | |||
132 | struct linux_binprm; | 132 | struct linux_binprm; |
133 | extern int arch_setup_additional_pages(struct linux_binprm *bprm, | 133 | extern int arch_setup_additional_pages(struct linux_binprm *bprm, |
134 | int executable_stack); | 134 | int executable_stack); |
135 | #define ARCH_DLINFO \ | ||
136 | do { \ | ||
137 | NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_BASE); \ | ||
138 | } while (0) | ||
139 | |||
135 | #ifdef CONFIG_COMPAT | 140 | #ifdef CONFIG_COMPAT |
136 | 141 | ||
137 | #define COMPAT_ELF_PLATFORM "tilegx-m32" | 142 | #define COMPAT_ELF_PLATFORM "tilegx-m32" |
diff --git a/arch/tile/include/asm/mmu.h b/arch/tile/include/asm/mmu.h index e2c789096795..0cab1182bde1 100644 --- a/arch/tile/include/asm/mmu.h +++ b/arch/tile/include/asm/mmu.h | |||
@@ -22,6 +22,7 @@ struct mm_context { | |||
22 | * semaphore but atomically, but it is conservatively set. | 22 | * semaphore but atomically, but it is conservatively set. |
23 | */ | 23 | */ |
24 | unsigned long priority_cached; | 24 | unsigned long priority_cached; |
25 | unsigned long vdso_base; | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | typedef struct mm_context mm_context_t; | 28 | typedef struct mm_context mm_context_t; |
diff --git a/arch/tile/include/asm/page.h b/arch/tile/include/asm/page.h index dd033a4fd627..b4f96c0024df 100644 --- a/arch/tile/include/asm/page.h +++ b/arch/tile/include/asm/page.h | |||
@@ -39,6 +39,12 @@ | |||
39 | #define HPAGE_MASK (~(HPAGE_SIZE - 1)) | 39 | #define HPAGE_MASK (~(HPAGE_SIZE - 1)) |
40 | 40 | ||
41 | /* | 41 | /* |
42 | * We do define AT_SYSINFO_EHDR to support vDSO, | ||
43 | * but don't use the gate mechanism. | ||
44 | */ | ||
45 | #define __HAVE_ARCH_GATE_AREA 1 | ||
46 | |||
47 | /* | ||
42 | * If the Kconfig doesn't specify, set a maximum zone order that | 48 | * If the Kconfig doesn't specify, set a maximum zone order that |
43 | * is enough so that we can create huge pages from small pages given | 49 | * is enough so that we can create huge pages from small pages given |
44 | * the respective sizes of the two page types. See <linux/mmzone.h>. | 50 | * the respective sizes of the two page types. See <linux/mmzone.h>. |
@@ -246,7 +252,7 @@ static inline __attribute_const__ int get_order(unsigned long size) | |||
246 | 252 | ||
247 | #endif /* __tilegx__ */ | 253 | #endif /* __tilegx__ */ |
248 | 254 | ||
249 | #ifndef __ASSEMBLY__ | 255 | #if !defined(__ASSEMBLY__) && !defined(VDSO_BUILD) |
250 | 256 | ||
251 | #ifdef CONFIG_HIGHMEM | 257 | #ifdef CONFIG_HIGHMEM |
252 | 258 | ||
diff --git a/arch/tile/include/asm/processor.h b/arch/tile/include/asm/processor.h index fed1c044fec8..461322b473b5 100644 --- a/arch/tile/include/asm/processor.h +++ b/arch/tile/include/asm/processor.h | |||
@@ -180,10 +180,10 @@ struct thread_struct { | |||
180 | #define TASK_SIZE TASK_SIZE_MAX | 180 | #define TASK_SIZE TASK_SIZE_MAX |
181 | #endif | 181 | #endif |
182 | 182 | ||
183 | /* We provide a minimal "vdso" a la x86; just the sigreturn code for now. */ | 183 | #define VDSO_BASE ((unsigned long)current->active_mm->context.vdso_base) |
184 | #define VDSO_BASE (TASK_SIZE - PAGE_SIZE) | 184 | #define VDSO_SYM(x) (VDSO_BASE + (unsigned long)(x)) |
185 | 185 | ||
186 | #define STACK_TOP VDSO_BASE | 186 | #define STACK_TOP TASK_SIZE |
187 | 187 | ||
188 | /* STACK_TOP_MAX is used temporarily in execve and should not check COMPAT. */ | 188 | /* STACK_TOP_MAX is used temporarily in execve and should not check COMPAT. */ |
189 | #define STACK_TOP_MAX TASK_SIZE_MAX | 189 | #define STACK_TOP_MAX TASK_SIZE_MAX |
diff --git a/arch/tile/include/asm/sections.h b/arch/tile/include/asm/sections.h index cc95276ef9c9..5d5d3b739a6b 100644 --- a/arch/tile/include/asm/sections.h +++ b/arch/tile/include/asm/sections.h | |||
@@ -25,6 +25,10 @@ extern char _sinitdata[], _einitdata[]; | |||
25 | /* Write-once data is writable only till the end of initialization. */ | 25 | /* Write-once data is writable only till the end of initialization. */ |
26 | extern char __w1data_begin[], __w1data_end[]; | 26 | extern char __w1data_begin[], __w1data_end[]; |
27 | 27 | ||
28 | extern char vdso_start[], vdso_end[]; | ||
29 | #ifdef CONFIG_COMPAT | ||
30 | extern char vdso32_start[], vdso32_end[]; | ||
31 | #endif | ||
28 | 32 | ||
29 | /* Not exactly sections, but PC comparison points in the code. */ | 33 | /* Not exactly sections, but PC comparison points in the code. */ |
30 | extern char __rt_sigreturn[], __rt_sigreturn_end[]; | 34 | extern char __rt_sigreturn[], __rt_sigreturn_end[]; |
diff --git a/arch/tile/include/asm/vdso.h b/arch/tile/include/asm/vdso.h new file mode 100644 index 000000000000..9f6a78d665fa --- /dev/null +++ b/arch/tile/include/asm/vdso.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #ifndef __TILE_VDSO_H__ | ||
16 | #define __TILE_VDSO_H__ | ||
17 | |||
18 | #include <linux/types.h> | ||
19 | |||
20 | /* | ||
21 | * Note about the vdso_data structure: | ||
22 | * | ||
23 | * NEVER USE THEM IN USERSPACE CODE DIRECTLY. The layout of the | ||
24 | * structure is supposed to be known only to the function in the vdso | ||
25 | * itself and may change without notice. | ||
26 | */ | ||
27 | |||
28 | struct vdso_data { | ||
29 | __u64 tz_update_count; /* Timezone atomicity ctr */ | ||
30 | __u64 tb_update_count; /* Timebase atomicity ctr */ | ||
31 | __u64 xtime_tod_stamp; /* TOD clock for xtime */ | ||
32 | __u64 xtime_clock_sec; /* Kernel time second */ | ||
33 | __u64 xtime_clock_nsec; /* Kernel time nanosecond */ | ||
34 | __u64 wtom_clock_sec; /* Wall to monotonic clock second */ | ||
35 | __u64 wtom_clock_nsec; /* Wall to monotonic clock nanosecond */ | ||
36 | __u32 mult; /* Cycle to nanosecond multiplier */ | ||
37 | __u32 shift; /* Cycle to nanosecond divisor (power of two) */ | ||
38 | __u32 tz_minuteswest; /* Minutes west of Greenwich */ | ||
39 | __u32 tz_dsttime; /* Type of dst correction */ | ||
40 | }; | ||
41 | |||
42 | extern struct vdso_data *vdso_data; | ||
43 | |||
44 | /* __vdso_rt_sigreturn is defined with the addresses in the vdso page. */ | ||
45 | extern void __vdso_rt_sigreturn(void); | ||
46 | |||
47 | extern int setup_vdso_pages(void); | ||
48 | |||
49 | #endif /* __TILE_VDSO_H__ */ | ||
diff --git a/arch/tile/include/uapi/asm/auxvec.h b/arch/tile/include/uapi/asm/auxvec.h index 1d393edb0641..c93e92709f14 100644 --- a/arch/tile/include/uapi/asm/auxvec.h +++ b/arch/tile/include/uapi/asm/auxvec.h | |||
@@ -15,6 +15,7 @@ | |||
15 | #ifndef _ASM_TILE_AUXVEC_H | 15 | #ifndef _ASM_TILE_AUXVEC_H |
16 | #define _ASM_TILE_AUXVEC_H | 16 | #define _ASM_TILE_AUXVEC_H |
17 | 17 | ||
18 | /* No extensions to auxvec */ | 18 | /* The vDSO location. */ |
19 | #define AT_SYSINFO_EHDR 33 | ||
19 | 20 | ||
20 | #endif /* _ASM_TILE_AUXVEC_H */ | 21 | #endif /* _ASM_TILE_AUXVEC_H */ |
diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile index 6846c4ef5bf1..5157d1c4b4ed 100644 --- a/arch/tile/kernel/Makefile +++ b/arch/tile/kernel/Makefile | |||
@@ -6,7 +6,7 @@ extra-y := vmlinux.lds head_$(BITS).o | |||
6 | obj-y := backtrace.o entry.o irq.o messaging.o \ | 6 | obj-y := backtrace.o entry.o irq.o messaging.o \ |
7 | pci-dma.o proc.o process.o ptrace.o reboot.o \ | 7 | pci-dma.o proc.o process.o ptrace.o reboot.o \ |
8 | setup.o signal.o single_step.o stack.o sys.o \ | 8 | setup.o signal.o single_step.o stack.o sys.o \ |
9 | sysfs.o time.o traps.o unaligned.o \ | 9 | sysfs.o time.o traps.o unaligned.o vdso.o \ |
10 | intvec_$(BITS).o regs_$(BITS).o tile-desc_$(BITS).o | 10 | intvec_$(BITS).o regs_$(BITS).o tile-desc_$(BITS).o |
11 | 11 | ||
12 | obj-$(CONFIG_HARDWALL) += hardwall.o | 12 | obj-$(CONFIG_HARDWALL) += hardwall.o |
@@ -21,3 +21,5 @@ else | |||
21 | obj-$(CONFIG_PCI) += pci.o | 21 | obj-$(CONFIG_PCI) += pci.o |
22 | endif | 22 | endif |
23 | obj-$(CONFIG_TILE_USB) += usb.o | 23 | obj-$(CONFIG_TILE_USB) += usb.o |
24 | |||
25 | obj-y += vdso/ | ||
diff --git a/arch/tile/kernel/compat_signal.c b/arch/tile/kernel/compat_signal.c index d0a052e725be..85e00b2f39bf 100644 --- a/arch/tile/kernel/compat_signal.c +++ b/arch/tile/kernel/compat_signal.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <asm/ucontext.h> | 32 | #include <asm/ucontext.h> |
33 | #include <asm/sigframe.h> | 33 | #include <asm/sigframe.h> |
34 | #include <asm/syscalls.h> | 34 | #include <asm/syscalls.h> |
35 | #include <asm/vdso.h> | ||
35 | #include <arch/interrupts.h> | 36 | #include <arch/interrupts.h> |
36 | 37 | ||
37 | struct compat_ucontext { | 38 | struct compat_ucontext { |
@@ -227,7 +228,7 @@ int compat_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
227 | if (err) | 228 | if (err) |
228 | goto give_sigsegv; | 229 | goto give_sigsegv; |
229 | 230 | ||
230 | restorer = VDSO_BASE; | 231 | restorer = VDSO_SYM(&__vdso_rt_sigreturn); |
231 | if (ka->sa.sa_flags & SA_RESTORER) | 232 | if (ka->sa.sa_flags & SA_RESTORER) |
232 | restorer = ptr_to_compat_reg(ka->sa.sa_restorer); | 233 | restorer = ptr_to_compat_reg(ka->sa.sa_restorer); |
233 | 234 | ||
diff --git a/arch/tile/kernel/entry.S b/arch/tile/kernel/entry.S index f116cb0bce20..3d9175992a20 100644 --- a/arch/tile/kernel/entry.S +++ b/arch/tile/kernel/entry.S | |||
@@ -27,22 +27,6 @@ STD_ENTRY(current_text_addr) | |||
27 | { move r0, lr; jrp lr } | 27 | { move r0, lr; jrp lr } |
28 | STD_ENDPROC(current_text_addr) | 28 | STD_ENDPROC(current_text_addr) |
29 | 29 | ||
30 | /* | ||
31 | * We don't run this function directly, but instead copy it to a page | ||
32 | * we map into every user process. See vdso_setup(). | ||
33 | * | ||
34 | * Note that libc has a copy of this function that it uses to compare | ||
35 | * against the PC when a stack backtrace ends, so if this code is | ||
36 | * changed, the libc implementation(s) should also be updated. | ||
37 | */ | ||
38 | .pushsection .data | ||
39 | ENTRY(__rt_sigreturn) | ||
40 | moveli TREG_SYSCALL_NR_NAME,__NR_rt_sigreturn | ||
41 | swint1 | ||
42 | ENDPROC(__rt_sigreturn) | ||
43 | ENTRY(__rt_sigreturn_end) | ||
44 | .popsection | ||
45 | |||
46 | STD_ENTRY(dump_stack) | 30 | STD_ENTRY(dump_stack) |
47 | { move r2, lr; lnk r1 } | 31 | { move r2, lr; lnk r1 } |
48 | { move r4, r52; addli r1, r1, dump_stack - . } | 32 | { move r4, r52; addli r1, r1, dump_stack - . } |
diff --git a/arch/tile/kernel/signal.c b/arch/tile/kernel/signal.c index 9531845bf661..2d1dbf38a9ab 100644 --- a/arch/tile/kernel/signal.c +++ b/arch/tile/kernel/signal.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <asm/ucontext.h> | 33 | #include <asm/ucontext.h> |
34 | #include <asm/sigframe.h> | 34 | #include <asm/sigframe.h> |
35 | #include <asm/syscalls.h> | 35 | #include <asm/syscalls.h> |
36 | #include <asm/vdso.h> | ||
36 | #include <arch/interrupts.h> | 37 | #include <arch/interrupts.h> |
37 | 38 | ||
38 | #define DEBUG_SIG 0 | 39 | #define DEBUG_SIG 0 |
@@ -190,7 +191,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
190 | if (err) | 191 | if (err) |
191 | goto give_sigsegv; | 192 | goto give_sigsegv; |
192 | 193 | ||
193 | restorer = VDSO_BASE; | 194 | restorer = VDSO_SYM(&__vdso_rt_sigreturn); |
194 | if (ka->sa.sa_flags & SA_RESTORER) | 195 | if (ka->sa.sa_flags & SA_RESTORER) |
195 | restorer = (unsigned long) ka->sa.sa_restorer; | 196 | restorer = (unsigned long) ka->sa.sa_restorer; |
196 | 197 | ||
diff --git a/arch/tile/kernel/stack.c b/arch/tile/kernel/stack.c index 176ffe48eee9..a9db923bb9eb 100644 --- a/arch/tile/kernel/stack.c +++ b/arch/tile/kernel/stack.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <asm/switch_to.h> | 29 | #include <asm/switch_to.h> |
30 | #include <asm/sigframe.h> | 30 | #include <asm/sigframe.h> |
31 | #include <asm/stack.h> | 31 | #include <asm/stack.h> |
32 | #include <asm/vdso.h> | ||
32 | #include <arch/abi.h> | 33 | #include <arch/abi.h> |
33 | #include <arch/interrupts.h> | 34 | #include <arch/interrupts.h> |
34 | 35 | ||
@@ -119,7 +120,7 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt) | |||
119 | /* Is the pc pointing to a sigreturn trampoline? */ | 120 | /* Is the pc pointing to a sigreturn trampoline? */ |
120 | static int is_sigreturn(unsigned long pc) | 121 | static int is_sigreturn(unsigned long pc) |
121 | { | 122 | { |
122 | return (pc == VDSO_BASE); | 123 | return current->mm && (pc == VDSO_SYM(&__vdso_rt_sigreturn)); |
123 | } | 124 | } |
124 | 125 | ||
125 | /* Return a pt_regs pointer for a valid signal handler frame */ | 126 | /* Return a pt_regs pointer for a valid signal handler frame */ |
@@ -128,7 +129,7 @@ static struct pt_regs *valid_sigframe(struct KBacktraceIterator* kbt, | |||
128 | { | 129 | { |
129 | BacktraceIterator *b = &kbt->it; | 130 | BacktraceIterator *b = &kbt->it; |
130 | 131 | ||
131 | if (b->pc == VDSO_BASE && b->sp < PAGE_OFFSET && | 132 | if (is_sigreturn(b->pc) && b->sp < PAGE_OFFSET && |
132 | b->sp % sizeof(long) == 0) { | 133 | b->sp % sizeof(long) == 0) { |
133 | int retval; | 134 | int retval; |
134 | pagefault_disable(); | 135 | pagefault_disable(); |
diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c index 5ac397ec6986..36dc1e1bc0a0 100644 --- a/arch/tile/kernel/time.c +++ b/arch/tile/kernel/time.c | |||
@@ -23,8 +23,10 @@ | |||
23 | #include <linux/smp.h> | 23 | #include <linux/smp.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/timekeeper_internal.h> | ||
26 | #include <asm/irq_regs.h> | 27 | #include <asm/irq_regs.h> |
27 | #include <asm/traps.h> | 28 | #include <asm/traps.h> |
29 | #include <asm/vdso.h> | ||
28 | #include <hv/hypervisor.h> | 30 | #include <hv/hypervisor.h> |
29 | #include <arch/interrupts.h> | 31 | #include <arch/interrupts.h> |
30 | #include <arch/spr_def.h> | 32 | #include <arch/spr_def.h> |
@@ -110,7 +112,6 @@ void __init time_init(void) | |||
110 | setup_tile_timer(); | 112 | setup_tile_timer(); |
111 | } | 113 | } |
112 | 114 | ||
113 | |||
114 | /* | 115 | /* |
115 | * Define the tile timer clock event device. The timer is driven by | 116 | * Define the tile timer clock event device. The timer is driven by |
116 | * the TILE_TIMER_CONTROL register, which consists of a 31-bit down | 117 | * the TILE_TIMER_CONTROL register, which consists of a 31-bit down |
@@ -237,3 +238,37 @@ cycles_t ns2cycles(unsigned long nsecs) | |||
237 | struct clock_event_device *dev = &__raw_get_cpu_var(tile_timer); | 238 | struct clock_event_device *dev = &__raw_get_cpu_var(tile_timer); |
238 | return ((u64)nsecs * dev->mult) >> dev->shift; | 239 | return ((u64)nsecs * dev->mult) >> dev->shift; |
239 | } | 240 | } |
241 | |||
242 | void update_vsyscall_tz(void) | ||
243 | { | ||
244 | /* Userspace gettimeofday will spin while this value is odd. */ | ||
245 | ++vdso_data->tz_update_count; | ||
246 | smp_wmb(); | ||
247 | vdso_data->tz_minuteswest = sys_tz.tz_minuteswest; | ||
248 | vdso_data->tz_dsttime = sys_tz.tz_dsttime; | ||
249 | smp_wmb(); | ||
250 | ++vdso_data->tz_update_count; | ||
251 | } | ||
252 | |||
253 | void update_vsyscall(struct timekeeper *tk) | ||
254 | { | ||
255 | struct timespec wall_time = tk_xtime(tk); | ||
256 | struct timespec *wtm = &tk->wall_to_monotonic; | ||
257 | struct clocksource *clock = tk->clock; | ||
258 | |||
259 | if (clock != &cycle_counter_cs) | ||
260 | return; | ||
261 | |||
262 | /* Userspace gettimeofday will spin while this value is odd. */ | ||
263 | ++vdso_data->tb_update_count; | ||
264 | smp_wmb(); | ||
265 | vdso_data->xtime_tod_stamp = clock->cycle_last; | ||
266 | vdso_data->xtime_clock_sec = wall_time.tv_sec; | ||
267 | vdso_data->xtime_clock_nsec = wall_time.tv_nsec; | ||
268 | vdso_data->wtom_clock_sec = wtm->tv_sec; | ||
269 | vdso_data->wtom_clock_nsec = wtm->tv_nsec; | ||
270 | vdso_data->mult = clock->mult; | ||
271 | vdso_data->shift = clock->shift; | ||
272 | smp_wmb(); | ||
273 | ++vdso_data->tb_update_count; | ||
274 | } | ||
diff --git a/arch/tile/kernel/vdso.c b/arch/tile/kernel/vdso.c new file mode 100644 index 000000000000..1533af24106e --- /dev/null +++ b/arch/tile/kernel/vdso.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/binfmts.h> | ||
16 | #include <linux/compat.h> | ||
17 | #include <linux/elf.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/pagemap.h> | ||
20 | |||
21 | #include <asm/vdso.h> | ||
22 | #include <asm/mman.h> | ||
23 | #include <asm/sections.h> | ||
24 | |||
25 | #include <arch/sim.h> | ||
26 | |||
27 | /* The alignment of the vDSO. */ | ||
28 | #define VDSO_ALIGNMENT PAGE_SIZE | ||
29 | |||
30 | |||
31 | static unsigned int vdso_pages; | ||
32 | static struct page **vdso_pagelist; | ||
33 | |||
34 | #ifdef CONFIG_COMPAT | ||
35 | static unsigned int vdso32_pages; | ||
36 | static struct page **vdso32_pagelist; | ||
37 | #endif | ||
38 | static int vdso_ready; | ||
39 | |||
40 | /* | ||
41 | * The vdso data page. | ||
42 | */ | ||
43 | static union { | ||
44 | struct vdso_data data; | ||
45 | u8 page[PAGE_SIZE]; | ||
46 | } vdso_data_store __page_aligned_data; | ||
47 | |||
48 | struct vdso_data *vdso_data = &vdso_data_store.data; | ||
49 | |||
50 | static unsigned int __read_mostly vdso_enabled = 1; | ||
51 | |||
52 | static struct page **vdso_setup(void *vdso_kbase, unsigned int pages) | ||
53 | { | ||
54 | int i; | ||
55 | struct page **pagelist; | ||
56 | |||
57 | pagelist = kzalloc(sizeof(struct page *) * (pages + 1), GFP_KERNEL); | ||
58 | BUG_ON(pagelist == NULL); | ||
59 | for (i = 0; i < pages - 1; i++) { | ||
60 | struct page *pg = virt_to_page(vdso_kbase + i*PAGE_SIZE); | ||
61 | ClearPageReserved(pg); | ||
62 | pagelist[i] = pg; | ||
63 | } | ||
64 | pagelist[pages - 1] = virt_to_page(vdso_data); | ||
65 | pagelist[pages] = NULL; | ||
66 | |||
67 | return pagelist; | ||
68 | } | ||
69 | |||
70 | static int __init vdso_init(void) | ||
71 | { | ||
72 | int data_pages = sizeof(vdso_data_store) >> PAGE_SHIFT; | ||
73 | |||
74 | /* | ||
75 | * We can disable vDSO support generally, but we need to retain | ||
76 | * one page to support the two-bundle (16-byte) rt_sigreturn path. | ||
77 | */ | ||
78 | if (!vdso_enabled) { | ||
79 | size_t offset = (unsigned long)&__vdso_rt_sigreturn; | ||
80 | static struct page *sigret_page; | ||
81 | sigret_page = alloc_page(GFP_KERNEL | __GFP_ZERO); | ||
82 | BUG_ON(sigret_page == NULL); | ||
83 | vdso_pagelist = &sigret_page; | ||
84 | vdso_pages = 1; | ||
85 | BUG_ON(offset >= PAGE_SIZE); | ||
86 | memcpy(page_address(sigret_page) + offset, | ||
87 | vdso_start + offset, 16); | ||
88 | #ifdef CONFIG_COMPAT | ||
89 | vdso32_pages = vdso_pages; | ||
90 | vdso32_pagelist = vdso_pagelist; | ||
91 | #endif | ||
92 | vdso_ready = 1; | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; | ||
97 | vdso_pages += data_pages; | ||
98 | vdso_pagelist = vdso_setup(vdso_start, vdso_pages); | ||
99 | |||
100 | #ifdef CONFIG_COMPAT | ||
101 | vdso32_pages = (vdso32_end - vdso32_start) >> PAGE_SHIFT; | ||
102 | vdso32_pages += data_pages; | ||
103 | vdso32_pagelist = vdso_setup(vdso32_start, vdso32_pages); | ||
104 | #endif | ||
105 | |||
106 | smp_wmb(); | ||
107 | vdso_ready = 1; | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | arch_initcall(vdso_init); | ||
112 | |||
113 | const char *arch_vma_name(struct vm_area_struct *vma) | ||
114 | { | ||
115 | if (vma->vm_mm && vma->vm_start == VDSO_BASE) | ||
116 | return "[vdso]"; | ||
117 | #ifndef __tilegx__ | ||
118 | if (vma->vm_start == MEM_USER_INTRPT) | ||
119 | return "[intrpt]"; | ||
120 | #endif | ||
121 | return NULL; | ||
122 | } | ||
123 | |||
124 | struct vm_area_struct *get_gate_vma(struct mm_struct *mm) | ||
125 | { | ||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | int in_gate_area(struct mm_struct *mm, unsigned long address) | ||
130 | { | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | int in_gate_area_no_mm(unsigned long address) | ||
135 | { | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | int setup_vdso_pages(void) | ||
140 | { | ||
141 | struct page **pagelist; | ||
142 | unsigned long pages; | ||
143 | struct mm_struct *mm = current->mm; | ||
144 | unsigned long vdso_base = 0; | ||
145 | int retval = 0; | ||
146 | |||
147 | if (!vdso_ready) | ||
148 | return 0; | ||
149 | |||
150 | mm->context.vdso_base = 0; | ||
151 | |||
152 | pagelist = vdso_pagelist; | ||
153 | pages = vdso_pages; | ||
154 | #ifdef CONFIG_COMPAT | ||
155 | if (is_compat_task()) { | ||
156 | pagelist = vdso32_pagelist; | ||
157 | pages = vdso32_pages; | ||
158 | } | ||
159 | #endif | ||
160 | |||
161 | /* | ||
162 | * vDSO has a problem and was disabled, just don't "enable" it for the | ||
163 | * process. | ||
164 | */ | ||
165 | if (pages == 0) | ||
166 | return 0; | ||
167 | |||
168 | vdso_base = get_unmapped_area(NULL, vdso_base, | ||
169 | (pages << PAGE_SHIFT) + | ||
170 | ((VDSO_ALIGNMENT - 1) & PAGE_MASK), | ||
171 | 0, 0); | ||
172 | if (IS_ERR_VALUE(vdso_base)) { | ||
173 | retval = vdso_base; | ||
174 | return retval; | ||
175 | } | ||
176 | |||
177 | /* Add required alignment. */ | ||
178 | vdso_base = ALIGN(vdso_base, VDSO_ALIGNMENT); | ||
179 | |||
180 | /* | ||
181 | * Put vDSO base into mm struct. We need to do this before calling | ||
182 | * install_special_mapping or the perf counter mmap tracking code | ||
183 | * will fail to recognise it as a vDSO (since arch_vma_name fails). | ||
184 | */ | ||
185 | mm->context.vdso_base = vdso_base; | ||
186 | |||
187 | /* | ||
188 | * our vma flags don't have VM_WRITE so by default, the process isn't | ||
189 | * allowed to write those pages. | ||
190 | * gdb can break that with ptrace interface, and thus trigger COW on | ||
191 | * those pages but it's then your responsibility to never do that on | ||
192 | * the "data" page of the vDSO or you'll stop getting kernel updates | ||
193 | * and your nice userland gettimeofday will be totally dead. | ||
194 | * It's fine to use that for setting breakpoints in the vDSO code | ||
195 | * pages though | ||
196 | */ | ||
197 | retval = install_special_mapping(mm, vdso_base, | ||
198 | pages << PAGE_SHIFT, | ||
199 | VM_READ|VM_EXEC | | ||
200 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | ||
201 | pagelist); | ||
202 | if (retval) | ||
203 | mm->context.vdso_base = 0; | ||
204 | |||
205 | return retval; | ||
206 | } | ||
207 | |||
208 | static __init int vdso_func(char *s) | ||
209 | { | ||
210 | return kstrtouint(s, 0, &vdso_enabled); | ||
211 | } | ||
212 | __setup("vdso=", vdso_func); | ||
diff --git a/arch/tile/kernel/vdso/Makefile b/arch/tile/kernel/vdso/Makefile new file mode 100644 index 000000000000..e2b7a2f4ee41 --- /dev/null +++ b/arch/tile/kernel/vdso/Makefile | |||
@@ -0,0 +1,118 @@ | |||
1 | # Symbols present in the vdso | ||
2 | vdso-syms = rt_sigreturn gettimeofday | ||
3 | |||
4 | # Files to link into the vdso | ||
5 | obj-vdso = $(patsubst %, v%.o, $(vdso-syms)) | ||
6 | |||
7 | # Build rules | ||
8 | targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.lds | ||
9 | obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) | ||
10 | |||
11 | # vdso32 is only for tilegx -m32 compat task. | ||
12 | VDSO32-$(CONFIG_COMPAT) := y | ||
13 | |||
14 | obj-y += vdso.o | ||
15 | obj-$(VDSO32-y) += vdso32.o | ||
16 | extra-y += vdso.lds | ||
17 | CPPFLAGS_vdso.lds += -P -C -U$(ARCH) | ||
18 | |||
19 | # vDSO code runs in userspace and -pg doesn't help with profiling anyway. | ||
20 | CFLAGS_REMOVE_vdso.o = -pg | ||
21 | CFLAGS_REMOVE_vdso32.o = -pg | ||
22 | CFLAGS_REMOVE_vrt_sigreturn.o = -pg | ||
23 | CFLAGS_REMOVE_vrt_sigreturn32.o = -pg | ||
24 | CFLAGS_REMOVE_vgettimeofday.o = -pg | ||
25 | CFLAGS_REMOVE_vgettimeofday32.o = -pg | ||
26 | |||
27 | ifdef CONFIG_FEEDBACK_COLLECT | ||
28 | # vDSO code runs in userspace, not collecting feedback data. | ||
29 | CFLAGS_REMOVE_vdso.o = -ffeedback-generate | ||
30 | CFLAGS_REMOVE_vdso32.o = -ffeedback-generate | ||
31 | CFLAGS_REMOVE_vrt_sigreturn.o = -ffeedback-generate | ||
32 | CFLAGS_REMOVE_vrt_sigreturn32.o = -ffeedback-generate | ||
33 | CFLAGS_REMOVE_vgettimeofday.o = -ffeedback-generate | ||
34 | CFLAGS_REMOVE_vgettimeofday32.o = -ffeedback-generate | ||
35 | endif | ||
36 | |||
37 | # Disable gcov profiling for VDSO code | ||
38 | GCOV_PROFILE := n | ||
39 | |||
40 | # Force dependency | ||
41 | $(obj)/vdso.o: $(obj)/vdso.so | ||
42 | |||
43 | # link rule for the .so file, .lds has to be first | ||
44 | SYSCFLAGS_vdso.so.dbg = $(c_flags) | ||
45 | $(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) | ||
46 | $(call if_changed,vdsold) | ||
47 | |||
48 | |||
49 | # We also create a special relocatable object that should mirror the symbol | ||
50 | # table and layout of the linked DSO. With ld -R we can then refer to | ||
51 | # these symbols in the kernel code rather than hand-coded addresses. | ||
52 | extra-y += vdso-syms.o | ||
53 | $(obj)/built-in.o: $(obj)/vdso-syms.o | ||
54 | $(obj)/built-in.o: ld_flags += -R $(obj)/vdso-syms.o | ||
55 | |||
56 | SYSCFLAGS_vdso.so.dbg = -shared -s -Wl,-soname=linux-vdso.so.1 \ | ||
57 | $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) | ||
58 | SYSCFLAGS_vdso_syms.o = -r | ||
59 | $(obj)/vdso-syms.o: $(src)/vdso.lds $(obj)/vrt_sigreturn.o FORCE | ||
60 | $(call if_changed,vdsold) | ||
61 | |||
62 | |||
63 | # strip rule for the .so file | ||
64 | $(obj)/%.so: OBJCOPYFLAGS := -S | ||
65 | $(obj)/%.so: $(obj)/%.so.dbg FORCE | ||
66 | $(call if_changed,objcopy) | ||
67 | |||
68 | # actual build commands | ||
69 | # The DSO images are built using a special linker script | ||
70 | # Add -lgcc so tilepro gets static muldi3 and lshrdi3 definitions. | ||
71 | # Make sure only to export the intended __vdso_xxx symbol offsets. | ||
72 | quiet_cmd_vdsold = VDSOLD $@ | ||
73 | cmd_vdsold = $(CC) $(KCFLAGS) -nostdlib $(SYSCFLAGS_$(@F)) \ | ||
74 | -Wl,-T,$(filter-out FORCE,$^) -o $@.tmp -lgcc && \ | ||
75 | $(CROSS_COMPILE)objcopy \ | ||
76 | $(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@ | ||
77 | |||
78 | # install commands for the unstripped file | ||
79 | quiet_cmd_vdso_install = INSTALL $@ | ||
80 | cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ | ||
81 | |||
82 | vdso.so: $(obj)/vdso.so.dbg | ||
83 | @mkdir -p $(MODLIB)/vdso | ||
84 | $(call cmd,vdso_install) | ||
85 | |||
86 | vdso32.so: $(obj)/vdso32.so.dbg | ||
87 | $(call cmd,vdso_install) | ||
88 | |||
89 | vdso_install: vdso.so | ||
90 | vdso32_install: vdso32.so | ||
91 | |||
92 | |||
93 | KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) | ||
94 | KBUILD_AFLAGS_32 += -m32 -s | ||
95 | KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS)) | ||
96 | KBUILD_CFLAGS_32 += -m32 -fPIC -shared | ||
97 | |||
98 | obj-vdso32 = $(patsubst %, v%32.o, $(vdso-syms)) | ||
99 | obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32)) | ||
100 | |||
101 | targets += $(obj-vdso32) vdso32.so vdso32.so.dbg | ||
102 | |||
103 | $(obj-vdso32:%=%): KBUILD_AFLAGS = $(KBUILD_AFLAGS_32) | ||
104 | $(obj-vdso32:%=%): KBUILD_CFLAGS = $(KBUILD_CFLAGS_32) | ||
105 | |||
106 | $(obj)/vgettimeofday32.o: $(obj)/vgettimeofday.c | ||
107 | $(call if_changed,cc_o_c) | ||
108 | |||
109 | $(obj)/vrt_sigreturn32.o: $(obj)/vrt_sigreturn.S | ||
110 | $(call if_changed,as_o_S) | ||
111 | |||
112 | # Force dependency | ||
113 | $(obj)/vdso32.o: $(obj)/vdso32.so | ||
114 | |||
115 | SYSCFLAGS_vdso32.so.dbg = -m32 -shared -s -Wl,-soname=linux-vdso32.so.1 \ | ||
116 | $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) | ||
117 | $(obj)/vdso32.so.dbg: $(src)/vdso.lds $(obj-vdso32) | ||
118 | $(call if_changed,vdsold) | ||
diff --git a/arch/tile/kernel/vdso/vdso.S b/arch/tile/kernel/vdso/vdso.S new file mode 100644 index 000000000000..3467adb41630 --- /dev/null +++ b/arch/tile/kernel/vdso/vdso.S | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/linkage.h> | ||
17 | #include <asm/page.h> | ||
18 | |||
19 | __PAGE_ALIGNED_DATA | ||
20 | |||
21 | .global vdso_start, vdso_end | ||
22 | .align PAGE_SIZE | ||
23 | vdso_start: | ||
24 | .incbin "arch/tile/kernel/vdso/vdso.so" | ||
25 | .align PAGE_SIZE | ||
26 | vdso_end: | ||
27 | |||
28 | .previous | ||
diff --git a/arch/tile/kernel/vdso/vdso.lds.S b/arch/tile/kernel/vdso/vdso.lds.S new file mode 100644 index 000000000000..041cd6c39c83 --- /dev/null +++ b/arch/tile/kernel/vdso/vdso.lds.S | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #define VDSO_VERSION_STRING LINUX_2.6 | ||
16 | |||
17 | |||
18 | OUTPUT_ARCH(tile) | ||
19 | |||
20 | /* The ELF entry point can be used to set the AT_SYSINFO value. */ | ||
21 | ENTRY(__vdso_rt_sigreturn); | ||
22 | |||
23 | |||
24 | SECTIONS | ||
25 | { | ||
26 | . = SIZEOF_HEADERS; | ||
27 | |||
28 | .hash : { *(.hash) } :text | ||
29 | .gnu.hash : { *(.gnu.hash) } | ||
30 | .dynsym : { *(.dynsym) } | ||
31 | .dynstr : { *(.dynstr) } | ||
32 | .gnu.version : { *(.gnu.version) } | ||
33 | .gnu.version_d : { *(.gnu.version_d) } | ||
34 | .gnu.version_r : { *(.gnu.version_r) } | ||
35 | |||
36 | .note : { *(.note.*) } :text :note | ||
37 | .dynamic : { *(.dynamic) } :text :dynamic | ||
38 | |||
39 | .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr | ||
40 | .eh_frame : { KEEP (*(.eh_frame)) } :text | ||
41 | |||
42 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } | ||
43 | |||
44 | /* | ||
45 | * This linker script is used both with -r and with -shared. | ||
46 | * For the layouts to match, we need to skip more than enough | ||
47 | * space for the dynamic symbol table et al. If this amount | ||
48 | * is insufficient, ld -shared will barf. Just increase it here. | ||
49 | */ | ||
50 | . = 0x1000; | ||
51 | .text : { *(.text .text.*) } :text | ||
52 | |||
53 | .data : { | ||
54 | *(.got.plt) *(.got) | ||
55 | *(.data .data.* .gnu.linkonce.d.*) | ||
56 | *(.dynbss) | ||
57 | *(.bss .bss.* .gnu.linkonce.b.*) | ||
58 | } | ||
59 | } | ||
60 | |||
61 | |||
62 | /* | ||
63 | * We must supply the ELF program headers explicitly to get just one | ||
64 | * PT_LOAD segment, and set the flags explicitly to make segments read-only. | ||
65 | */ | ||
66 | PHDRS | ||
67 | { | ||
68 | text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ | ||
69 | dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ | ||
70 | note PT_NOTE FLAGS(4); /* PF_R */ | ||
71 | eh_frame_hdr PT_GNU_EH_FRAME; | ||
72 | } | ||
73 | |||
74 | |||
75 | /* | ||
76 | * This controls what userland symbols we export from the vDSO. | ||
77 | */ | ||
78 | VERSION | ||
79 | { | ||
80 | VDSO_VERSION_STRING { | ||
81 | global: | ||
82 | __vdso_rt_sigreturn; | ||
83 | __vdso_gettimeofday; | ||
84 | gettimeofday; | ||
85 | local:*; | ||
86 | }; | ||
87 | } | ||
diff --git a/arch/tile/kernel/vdso/vdso32.S b/arch/tile/kernel/vdso/vdso32.S new file mode 100644 index 000000000000..1d1ac3257e11 --- /dev/null +++ b/arch/tile/kernel/vdso/vdso32.S | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright 2013 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/linkage.h> | ||
17 | #include <asm/page.h> | ||
18 | |||
19 | __PAGE_ALIGNED_DATA | ||
20 | |||
21 | .global vdso32_start, vdso32_end | ||
22 | .align PAGE_SIZE | ||
23 | vdso32_start: | ||
24 | .incbin "arch/tile/kernel/vdso/vdso32.so" | ||
25 | .align PAGE_SIZE | ||
26 | vdso32_end: | ||
27 | |||
28 | .previous | ||
diff --git a/arch/tile/kernel/vdso/vgettimeofday.c b/arch/tile/kernel/vdso/vgettimeofday.c new file mode 100644 index 000000000000..51ec8e46f5f9 --- /dev/null +++ b/arch/tile/kernel/vdso/vgettimeofday.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #define VDSO_BUILD /* avoid some shift warnings for -m32 in <asm/page.h> */ | ||
16 | #include <linux/time.h> | ||
17 | #include <asm/timex.h> | ||
18 | #include <asm/vdso.h> | ||
19 | |||
20 | #if CHIP_HAS_SPLIT_CYCLE() | ||
21 | static inline cycles_t get_cycles_inline(void) | ||
22 | { | ||
23 | unsigned int high = __insn_mfspr(SPR_CYCLE_HIGH); | ||
24 | unsigned int low = __insn_mfspr(SPR_CYCLE_LOW); | ||
25 | unsigned int high2 = __insn_mfspr(SPR_CYCLE_HIGH); | ||
26 | |||
27 | while (unlikely(high != high2)) { | ||
28 | low = __insn_mfspr(SPR_CYCLE_LOW); | ||
29 | high = high2; | ||
30 | high2 = __insn_mfspr(SPR_CYCLE_HIGH); | ||
31 | } | ||
32 | |||
33 | return (((cycles_t)high) << 32) | low; | ||
34 | } | ||
35 | #define get_cycles get_cycles_inline | ||
36 | #endif | ||
37 | |||
38 | /* | ||
39 | * Find out the vDSO data page address in the process address space. | ||
40 | */ | ||
41 | inline unsigned long get_datapage(void) | ||
42 | { | ||
43 | unsigned long ret; | ||
44 | |||
45 | /* vdso data page located in the 2nd vDSO page. */ | ||
46 | asm volatile ("lnk %0" : "=r"(ret)); | ||
47 | ret &= ~(PAGE_SIZE - 1); | ||
48 | ret += PAGE_SIZE; | ||
49 | |||
50 | return ret; | ||
51 | } | ||
52 | |||
53 | int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | ||
54 | { | ||
55 | cycles_t cycles; | ||
56 | unsigned long count, sec, ns; | ||
57 | volatile struct vdso_data *vdso_data; | ||
58 | |||
59 | vdso_data = (struct vdso_data *)get_datapage(); | ||
60 | /* The use of the timezone is obsolete, normally tz is NULL. */ | ||
61 | if (unlikely(tz != NULL)) { | ||
62 | while (1) { | ||
63 | /* Spin until the update finish. */ | ||
64 | count = vdso_data->tz_update_count; | ||
65 | if (count & 1) | ||
66 | continue; | ||
67 | |||
68 | tz->tz_minuteswest = vdso_data->tz_minuteswest; | ||
69 | tz->tz_dsttime = vdso_data->tz_dsttime; | ||
70 | |||
71 | /* Check whether updated, read again if so. */ | ||
72 | if (count == vdso_data->tz_update_count) | ||
73 | break; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (unlikely(tv == NULL)) | ||
78 | return 0; | ||
79 | |||
80 | while (1) { | ||
81 | /* Spin until the update finish. */ | ||
82 | count = vdso_data->tb_update_count; | ||
83 | if (count & 1) | ||
84 | continue; | ||
85 | |||
86 | cycles = (get_cycles() - vdso_data->xtime_tod_stamp); | ||
87 | ns = (cycles * vdso_data->mult) >> vdso_data->shift; | ||
88 | sec = vdso_data->xtime_clock_sec; | ||
89 | ns += vdso_data->xtime_clock_nsec; | ||
90 | if (ns >= NSEC_PER_SEC) { | ||
91 | ns -= NSEC_PER_SEC; | ||
92 | sec += 1; | ||
93 | } | ||
94 | |||
95 | /* Check whether updated, read again if so. */ | ||
96 | if (count == vdso_data->tb_update_count) | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | tv->tv_sec = sec; | ||
101 | tv->tv_usec = ns / 1000; | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | int gettimeofday(struct timeval *tv, struct timezone *tz) | ||
107 | __attribute__((weak, alias("__vdso_gettimeofday"))); | ||
diff --git a/arch/tile/kernel/vdso/vrt_sigreturn.S b/arch/tile/kernel/vdso/vrt_sigreturn.S new file mode 100644 index 000000000000..6326caf4a039 --- /dev/null +++ b/arch/tile/kernel/vdso/vrt_sigreturn.S | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
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. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/linkage.h> | ||
16 | #include <arch/abi.h> | ||
17 | #include <asm/unistd.h> | ||
18 | |||
19 | /* | ||
20 | * Note that libc has a copy of this function that it uses to compare | ||
21 | * against the PC when a stack backtrace ends, so if this code is | ||
22 | * changed, the libc implementation(s) should also be updated. | ||
23 | */ | ||
24 | ENTRY(__vdso_rt_sigreturn) | ||
25 | moveli TREG_SYSCALL_NR_NAME, __NR_rt_sigreturn | ||
26 | swint1 | ||
27 | /* We don't use ENDPROC to avoid tagging this symbol as FUNC, | ||
28 | * which confuses the perf tool. | ||
29 | */ | ||
30 | END(__vdso_rt_sigreturn) | ||
diff --git a/arch/tile/mm/elf.c b/arch/tile/mm/elf.c index 1691b81b2b0c..23f044e8a7ab 100644 --- a/arch/tile/mm/elf.c +++ b/arch/tile/mm/elf.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <asm/pgtable.h> | 21 | #include <asm/pgtable.h> |
22 | #include <asm/pgalloc.h> | 22 | #include <asm/pgalloc.h> |
23 | #include <asm/sections.h> | 23 | #include <asm/sections.h> |
24 | #include <asm/vdso.h> | ||
24 | #include <arch/sim.h> | 25 | #include <arch/sim.h> |
25 | 26 | ||
26 | /* Notify a running simulator, if any, that an exec just occurred. */ | 27 | /* Notify a running simulator, if any, that an exec just occurred. */ |
@@ -102,37 +103,10 @@ static void sim_notify_interp(unsigned long load_addr) | |||
102 | } | 103 | } |
103 | 104 | ||
104 | 105 | ||
105 | /* Kernel address of page used to map read-only kernel data into userspace. */ | ||
106 | static void *vdso_page; | ||
107 | |||
108 | /* One-entry array used for install_special_mapping. */ | ||
109 | static struct page *vdso_pages[1]; | ||
110 | |||
111 | static int __init vdso_setup(void) | ||
112 | { | ||
113 | vdso_page = (void *)get_zeroed_page(GFP_ATOMIC); | ||
114 | memcpy(vdso_page, __rt_sigreturn, __rt_sigreturn_end - __rt_sigreturn); | ||
115 | vdso_pages[0] = virt_to_page(vdso_page); | ||
116 | return 0; | ||
117 | } | ||
118 | device_initcall(vdso_setup); | ||
119 | |||
120 | const char *arch_vma_name(struct vm_area_struct *vma) | ||
121 | { | ||
122 | if (vma->vm_private_data == vdso_pages) | ||
123 | return "[vdso]"; | ||
124 | #ifndef __tilegx__ | ||
125 | if (vma->vm_start == MEM_USER_INTRPT) | ||
126 | return "[intrpt]"; | ||
127 | #endif | ||
128 | return NULL; | ||
129 | } | ||
130 | |||
131 | int arch_setup_additional_pages(struct linux_binprm *bprm, | 106 | int arch_setup_additional_pages(struct linux_binprm *bprm, |
132 | int executable_stack) | 107 | int executable_stack) |
133 | { | 108 | { |
134 | struct mm_struct *mm = current->mm; | 109 | struct mm_struct *mm = current->mm; |
135 | unsigned long vdso_base; | ||
136 | int retval = 0; | 110 | int retval = 0; |
137 | 111 | ||
138 | down_write(&mm->mmap_sem); | 112 | down_write(&mm->mmap_sem); |
@@ -145,14 +119,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, | |||
145 | if (!notify_exec(mm)) | 119 | if (!notify_exec(mm)) |
146 | sim_notify_exec(bprm->filename); | 120 | sim_notify_exec(bprm->filename); |
147 | 121 | ||
148 | /* | 122 | retval = setup_vdso_pages(); |
149 | * MAYWRITE to allow gdb to COW and set breakpoints | ||
150 | */ | ||
151 | vdso_base = VDSO_BASE; | ||
152 | retval = install_special_mapping(mm, vdso_base, PAGE_SIZE, | ||
153 | VM_READ|VM_EXEC| | ||
154 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | ||
155 | vdso_pages); | ||
156 | 123 | ||
157 | #ifndef __tilegx__ | 124 | #ifndef __tilegx__ |
158 | /* | 125 | /* |