diff options
Diffstat (limited to 'arch/x86/vdso')
-rw-r--r-- | arch/x86/vdso/.gitignore | 2 | ||||
-rw-r--r-- | arch/x86/vdso/Makefile | 46 | ||||
-rw-r--r-- | arch/x86/vdso/vclock_gettime.c | 135 | ||||
-rw-r--r-- | arch/x86/vdso/vdso32-setup.c | 5 | ||||
-rw-r--r-- | arch/x86/vdso/vdsox32.S | 22 | ||||
-rw-r--r-- | arch/x86/vdso/vdsox32.lds.S | 28 | ||||
-rw-r--r-- | arch/x86/vdso/vma.c | 78 |
7 files changed, 239 insertions, 77 deletions
diff --git a/arch/x86/vdso/.gitignore b/arch/x86/vdso/.gitignore index 60274d5746e1..3282874bc61d 100644 --- a/arch/x86/vdso/.gitignore +++ b/arch/x86/vdso/.gitignore | |||
@@ -1,5 +1,7 @@ | |||
1 | vdso.lds | 1 | vdso.lds |
2 | vdso-syms.lds | 2 | vdso-syms.lds |
3 | vdsox32.lds | ||
4 | vdsox32-syms.lds | ||
3 | vdso32-syms.lds | 5 | vdso32-syms.lds |
4 | vdso32-syscall-syms.lds | 6 | vdso32-syscall-syms.lds |
5 | vdso32-sysenter-syms.lds | 7 | vdso32-sysenter-syms.lds |
diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile index 5d179502a52c..fd14be1d1472 100644 --- a/arch/x86/vdso/Makefile +++ b/arch/x86/vdso/Makefile | |||
@@ -3,21 +3,29 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | VDSO64-$(CONFIG_X86_64) := y | 5 | VDSO64-$(CONFIG_X86_64) := y |
6 | VDSOX32-$(CONFIG_X86_X32_ABI) := y | ||
6 | VDSO32-$(CONFIG_X86_32) := y | 7 | VDSO32-$(CONFIG_X86_32) := y |
7 | VDSO32-$(CONFIG_COMPAT) := y | 8 | VDSO32-$(CONFIG_COMPAT) := y |
8 | 9 | ||
9 | vdso-install-$(VDSO64-y) += vdso.so | 10 | vdso-install-$(VDSO64-y) += vdso.so |
11 | vdso-install-$(VDSOX32-y) += vdsox32.so | ||
10 | vdso-install-$(VDSO32-y) += $(vdso32-images) | 12 | vdso-install-$(VDSO32-y) += $(vdso32-images) |
11 | 13 | ||
12 | 14 | ||
13 | # files to link into the vdso | 15 | # files to link into the vdso |
14 | vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o | 16 | vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o |
15 | 17 | ||
18 | vobjs-$(VDSOX32-y) += $(vobjx32s-compat) | ||
19 | |||
20 | # Filter out x32 objects. | ||
21 | vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y)) | ||
22 | |||
16 | # files to link into kernel | 23 | # files to link into kernel |
17 | obj-$(VDSO64-y) += vma.o vdso.o | 24 | obj-$(VDSO64-y) += vma.o vdso.o |
25 | obj-$(VDSOX32-y) += vdsox32.o | ||
18 | obj-$(VDSO32-y) += vdso32.o vdso32-setup.o | 26 | obj-$(VDSO32-y) += vdso32.o vdso32-setup.o |
19 | 27 | ||
20 | vobjs := $(foreach F,$(vobjs-y),$(obj)/$F) | 28 | vobjs := $(foreach F,$(vobj64s),$(obj)/$F) |
21 | 29 | ||
22 | $(obj)/vdso.o: $(obj)/vdso.so | 30 | $(obj)/vdso.o: $(obj)/vdso.so |
23 | 31 | ||
@@ -73,6 +81,42 @@ $(obj)/%-syms.lds: $(obj)/%.so.dbg FORCE | |||
73 | $(call if_changed,vdsosym) | 81 | $(call if_changed,vdsosym) |
74 | 82 | ||
75 | # | 83 | # |
84 | # X32 processes use x32 vDSO to access 64bit kernel data. | ||
85 | # | ||
86 | # Build x32 vDSO image: | ||
87 | # 1. Compile x32 vDSO as 64bit. | ||
88 | # 2. Convert object files to x32. | ||
89 | # 3. Build x32 VDSO image with x32 objects, which contains 64bit codes | ||
90 | # so that it can reach 64bit address space with 64bit pointers. | ||
91 | # | ||
92 | |||
93 | targets += vdsox32-syms.lds | ||
94 | obj-$(VDSOX32-y) += vdsox32-syms.lds | ||
95 | |||
96 | CPPFLAGS_vdsox32.lds = $(CPPFLAGS_vdso.lds) | ||
97 | VDSO_LDFLAGS_vdsox32.lds = -Wl,-m,elf32_x86_64 \ | ||
98 | -Wl,-soname=linux-vdso.so.1 \ | ||
99 | -Wl,-z,max-page-size=4096 \ | ||
100 | -Wl,-z,common-page-size=4096 | ||
101 | |||
102 | vobjx32s-y := $(vobj64s:.o=-x32.o) | ||
103 | vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) | ||
104 | |||
105 | # Convert 64bit object file to x32 for x32 vDSO. | ||
106 | quiet_cmd_x32 = X32 $@ | ||
107 | cmd_x32 = $(OBJCOPY) -O elf32-x86-64 $< $@ | ||
108 | |||
109 | $(obj)/%-x32.o: $(obj)/%.o FORCE | ||
110 | $(call if_changed,x32) | ||
111 | |||
112 | targets += vdsox32.so vdsox32.so.dbg vdsox32.lds $(vobjx32s-y) | ||
113 | |||
114 | $(obj)/vdsox32.o: $(src)/vdsox32.S $(obj)/vdsox32.so | ||
115 | |||
116 | $(obj)/vdsox32.so.dbg: $(src)/vdsox32.lds $(vobjx32s) FORCE | ||
117 | $(call if_changed,vdso) | ||
118 | |||
119 | # | ||
76 | # Build multiple 32-bit vDSO images to choose from at boot time. | 120 | # Build multiple 32-bit vDSO images to choose from at boot time. |
77 | # | 121 | # |
78 | obj-$(VDSO32-y) += vdso32-syms.lds | 122 | obj-$(VDSO32-y) += vdso32-syms.lds |
diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c index 6bc0e723b6e8..885eff49d6ab 100644 --- a/arch/x86/vdso/vclock_gettime.c +++ b/arch/x86/vdso/vclock_gettime.c | |||
@@ -70,100 +70,98 @@ notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) | |||
70 | return ret; | 70 | return ret; |
71 | } | 71 | } |
72 | 72 | ||
73 | notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz) | ||
74 | { | ||
75 | long ret; | ||
76 | |||
77 | asm("syscall" : "=a" (ret) : | ||
78 | "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory"); | ||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | |||
73 | notrace static inline long vgetns(void) | 83 | notrace static inline long vgetns(void) |
74 | { | 84 | { |
75 | long v; | 85 | long v; |
76 | cycles_t cycles; | 86 | cycles_t cycles; |
77 | if (gtod->clock.vclock_mode == VCLOCK_TSC) | 87 | if (gtod->clock.vclock_mode == VCLOCK_TSC) |
78 | cycles = vread_tsc(); | 88 | cycles = vread_tsc(); |
79 | else | 89 | else if (gtod->clock.vclock_mode == VCLOCK_HPET) |
80 | cycles = vread_hpet(); | 90 | cycles = vread_hpet(); |
91 | else | ||
92 | return 0; | ||
81 | v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask; | 93 | v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask; |
82 | return (v * gtod->clock.mult) >> gtod->clock.shift; | 94 | return (v * gtod->clock.mult) >> gtod->clock.shift; |
83 | } | 95 | } |
84 | 96 | ||
85 | notrace static noinline int do_realtime(struct timespec *ts) | 97 | /* Code size doesn't matter (vdso is 4k anyway) and this is faster. */ |
98 | notrace static int __always_inline do_realtime(struct timespec *ts) | ||
86 | { | 99 | { |
87 | unsigned long seq, ns; | 100 | unsigned long seq, ns; |
101 | int mode; | ||
102 | |||
88 | do { | 103 | do { |
89 | seq = read_seqbegin(>od->lock); | 104 | seq = read_seqcount_begin(>od->seq); |
105 | mode = gtod->clock.vclock_mode; | ||
90 | ts->tv_sec = gtod->wall_time_sec; | 106 | ts->tv_sec = gtod->wall_time_sec; |
91 | ts->tv_nsec = gtod->wall_time_nsec; | 107 | ts->tv_nsec = gtod->wall_time_nsec; |
92 | ns = vgetns(); | 108 | ns = vgetns(); |
93 | } while (unlikely(read_seqretry(>od->lock, seq))); | 109 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
110 | |||
94 | timespec_add_ns(ts, ns); | 111 | timespec_add_ns(ts, ns); |
95 | return 0; | 112 | return mode; |
96 | } | 113 | } |
97 | 114 | ||
98 | notrace static noinline int do_monotonic(struct timespec *ts) | 115 | notrace static int do_monotonic(struct timespec *ts) |
99 | { | 116 | { |
100 | unsigned long seq, ns, secs; | 117 | unsigned long seq, ns; |
118 | int mode; | ||
119 | |||
101 | do { | 120 | do { |
102 | seq = read_seqbegin(>od->lock); | 121 | seq = read_seqcount_begin(>od->seq); |
103 | secs = gtod->wall_time_sec; | 122 | mode = gtod->clock.vclock_mode; |
104 | ns = gtod->wall_time_nsec + vgetns(); | 123 | ts->tv_sec = gtod->monotonic_time_sec; |
105 | secs += gtod->wall_to_monotonic.tv_sec; | 124 | ts->tv_nsec = gtod->monotonic_time_nsec; |
106 | ns += gtod->wall_to_monotonic.tv_nsec; | 125 | ns = vgetns(); |
107 | } while (unlikely(read_seqretry(>od->lock, seq))); | 126 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
108 | 127 | timespec_add_ns(ts, ns); | |
109 | /* wall_time_nsec, vgetns(), and wall_to_monotonic.tv_nsec | ||
110 | * are all guaranteed to be nonnegative. | ||
111 | */ | ||
112 | while (ns >= NSEC_PER_SEC) { | ||
113 | ns -= NSEC_PER_SEC; | ||
114 | ++secs; | ||
115 | } | ||
116 | ts->tv_sec = secs; | ||
117 | ts->tv_nsec = ns; | ||
118 | 128 | ||
119 | return 0; | 129 | return mode; |
120 | } | 130 | } |
121 | 131 | ||
122 | notrace static noinline int do_realtime_coarse(struct timespec *ts) | 132 | notrace static int do_realtime_coarse(struct timespec *ts) |
123 | { | 133 | { |
124 | unsigned long seq; | 134 | unsigned long seq; |
125 | do { | 135 | do { |
126 | seq = read_seqbegin(>od->lock); | 136 | seq = read_seqcount_begin(>od->seq); |
127 | ts->tv_sec = gtod->wall_time_coarse.tv_sec; | 137 | ts->tv_sec = gtod->wall_time_coarse.tv_sec; |
128 | ts->tv_nsec = gtod->wall_time_coarse.tv_nsec; | 138 | ts->tv_nsec = gtod->wall_time_coarse.tv_nsec; |
129 | } while (unlikely(read_seqretry(>od->lock, seq))); | 139 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
130 | return 0; | 140 | return 0; |
131 | } | 141 | } |
132 | 142 | ||
133 | notrace static noinline int do_monotonic_coarse(struct timespec *ts) | 143 | notrace static int do_monotonic_coarse(struct timespec *ts) |
134 | { | 144 | { |
135 | unsigned long seq, ns, secs; | 145 | unsigned long seq; |
136 | do { | 146 | do { |
137 | seq = read_seqbegin(>od->lock); | 147 | seq = read_seqcount_begin(>od->seq); |
138 | secs = gtod->wall_time_coarse.tv_sec; | 148 | ts->tv_sec = gtod->monotonic_time_coarse.tv_sec; |
139 | ns = gtod->wall_time_coarse.tv_nsec; | 149 | ts->tv_nsec = gtod->monotonic_time_coarse.tv_nsec; |
140 | secs += gtod->wall_to_monotonic.tv_sec; | 150 | } while (unlikely(read_seqcount_retry(>od->seq, seq))); |
141 | ns += gtod->wall_to_monotonic.tv_nsec; | ||
142 | } while (unlikely(read_seqretry(>od->lock, seq))); | ||
143 | |||
144 | /* wall_time_nsec and wall_to_monotonic.tv_nsec are | ||
145 | * guaranteed to be between 0 and NSEC_PER_SEC. | ||
146 | */ | ||
147 | if (ns >= NSEC_PER_SEC) { | ||
148 | ns -= NSEC_PER_SEC; | ||
149 | ++secs; | ||
150 | } | ||
151 | ts->tv_sec = secs; | ||
152 | ts->tv_nsec = ns; | ||
153 | 151 | ||
154 | return 0; | 152 | return 0; |
155 | } | 153 | } |
156 | 154 | ||
157 | notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) | 155 | notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) |
158 | { | 156 | { |
157 | int ret = VCLOCK_NONE; | ||
158 | |||
159 | switch (clock) { | 159 | switch (clock) { |
160 | case CLOCK_REALTIME: | 160 | case CLOCK_REALTIME: |
161 | if (likely(gtod->clock.vclock_mode != VCLOCK_NONE)) | 161 | ret = do_realtime(ts); |
162 | return do_realtime(ts); | ||
163 | break; | 162 | break; |
164 | case CLOCK_MONOTONIC: | 163 | case CLOCK_MONOTONIC: |
165 | if (likely(gtod->clock.vclock_mode != VCLOCK_NONE)) | 164 | ret = do_monotonic(ts); |
166 | return do_monotonic(ts); | ||
167 | break; | 165 | break; |
168 | case CLOCK_REALTIME_COARSE: | 166 | case CLOCK_REALTIME_COARSE: |
169 | return do_realtime_coarse(ts); | 167 | return do_realtime_coarse(ts); |
@@ -171,32 +169,33 @@ notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) | |||
171 | return do_monotonic_coarse(ts); | 169 | return do_monotonic_coarse(ts); |
172 | } | 170 | } |
173 | 171 | ||
174 | return vdso_fallback_gettime(clock, ts); | 172 | if (ret == VCLOCK_NONE) |
173 | return vdso_fallback_gettime(clock, ts); | ||
174 | return 0; | ||
175 | } | 175 | } |
176 | int clock_gettime(clockid_t, struct timespec *) | 176 | int clock_gettime(clockid_t, struct timespec *) |
177 | __attribute__((weak, alias("__vdso_clock_gettime"))); | 177 | __attribute__((weak, alias("__vdso_clock_gettime"))); |
178 | 178 | ||
179 | notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | 179 | notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) |
180 | { | 180 | { |
181 | long ret; | 181 | long ret = VCLOCK_NONE; |
182 | if (likely(gtod->clock.vclock_mode != VCLOCK_NONE)) { | 182 | |
183 | if (likely(tv != NULL)) { | 183 | if (likely(tv != NULL)) { |
184 | BUILD_BUG_ON(offsetof(struct timeval, tv_usec) != | 184 | BUILD_BUG_ON(offsetof(struct timeval, tv_usec) != |
185 | offsetof(struct timespec, tv_nsec) || | 185 | offsetof(struct timespec, tv_nsec) || |
186 | sizeof(*tv) != sizeof(struct timespec)); | 186 | sizeof(*tv) != sizeof(struct timespec)); |
187 | do_realtime((struct timespec *)tv); | 187 | ret = do_realtime((struct timespec *)tv); |
188 | tv->tv_usec /= 1000; | 188 | tv->tv_usec /= 1000; |
189 | } | ||
190 | if (unlikely(tz != NULL)) { | ||
191 | /* Avoid memcpy. Some old compilers fail to inline it */ | ||
192 | tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest; | ||
193 | tz->tz_dsttime = gtod->sys_tz.tz_dsttime; | ||
194 | } | ||
195 | return 0; | ||
196 | } | 189 | } |
197 | asm("syscall" : "=a" (ret) : | 190 | if (unlikely(tz != NULL)) { |
198 | "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory"); | 191 | /* Avoid memcpy. Some old compilers fail to inline it */ |
199 | return ret; | 192 | tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest; |
193 | tz->tz_dsttime = gtod->sys_tz.tz_dsttime; | ||
194 | } | ||
195 | |||
196 | if (ret == VCLOCK_NONE) | ||
197 | return vdso_fallback_gtod(tv, tz); | ||
198 | return 0; | ||
200 | } | 199 | } |
201 | int gettimeofday(struct timeval *, struct timezone *) | 200 | int gettimeofday(struct timeval *, struct timezone *) |
202 | __attribute__((weak, alias("__vdso_gettimeofday"))); | 201 | __attribute__((weak, alias("__vdso_gettimeofday"))); |
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index a944020fa859..66e6d9359826 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c | |||
@@ -311,6 +311,11 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
311 | int ret = 0; | 311 | int ret = 0; |
312 | bool compat; | 312 | bool compat; |
313 | 313 | ||
314 | #ifdef CONFIG_X86_X32_ABI | ||
315 | if (test_thread_flag(TIF_X32)) | ||
316 | return x32_setup_additional_pages(bprm, uses_interp); | ||
317 | #endif | ||
318 | |||
314 | if (vdso_enabled == VDSO_DISABLED) | 319 | if (vdso_enabled == VDSO_DISABLED) |
315 | return 0; | 320 | return 0; |
316 | 321 | ||
diff --git a/arch/x86/vdso/vdsox32.S b/arch/x86/vdso/vdsox32.S new file mode 100644 index 000000000000..d6b9a7f42a8a --- /dev/null +++ b/arch/x86/vdso/vdsox32.S | |||
@@ -0,0 +1,22 @@ | |||
1 | #include <asm/page_types.h> | ||
2 | #include <linux/linkage.h> | ||
3 | #include <linux/init.h> | ||
4 | |||
5 | __PAGE_ALIGNED_DATA | ||
6 | |||
7 | .globl vdsox32_start, vdsox32_end | ||
8 | .align PAGE_SIZE | ||
9 | vdsox32_start: | ||
10 | .incbin "arch/x86/vdso/vdsox32.so" | ||
11 | vdsox32_end: | ||
12 | .align PAGE_SIZE /* extra data here leaks to userspace. */ | ||
13 | |||
14 | .previous | ||
15 | |||
16 | .globl vdsox32_pages | ||
17 | .bss | ||
18 | .align 8 | ||
19 | .type vdsox32_pages, @object | ||
20 | vdsox32_pages: | ||
21 | .zero (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE * 8 | ||
22 | .size vdsox32_pages, .-vdsox32_pages | ||
diff --git a/arch/x86/vdso/vdsox32.lds.S b/arch/x86/vdso/vdsox32.lds.S new file mode 100644 index 000000000000..62272aa2ae0a --- /dev/null +++ b/arch/x86/vdso/vdsox32.lds.S | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Linker script for x32 vDSO. | ||
3 | * We #include the file to define the layout details. | ||
4 | * Here we only choose the prelinked virtual address. | ||
5 | * | ||
6 | * This file defines the version script giving the user-exported symbols in | ||
7 | * the DSO. We can define local symbols here called VDSO* to make their | ||
8 | * values visible using the asm-x86/vdso.h macros from the kernel proper. | ||
9 | */ | ||
10 | |||
11 | #define VDSO_PRELINK 0 | ||
12 | #include "vdso-layout.lds.S" | ||
13 | |||
14 | /* | ||
15 | * This controls what userland symbols we export from the vDSO. | ||
16 | */ | ||
17 | VERSION { | ||
18 | LINUX_2.6 { | ||
19 | global: | ||
20 | __vdso_clock_gettime; | ||
21 | __vdso_gettimeofday; | ||
22 | __vdso_getcpu; | ||
23 | __vdso_time; | ||
24 | local: *; | ||
25 | }; | ||
26 | } | ||
27 | |||
28 | VDSOX32_PRELINK = VDSO_PRELINK; | ||
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c index 17e18279649f..00aaf047b39f 100644 --- a/arch/x86/vdso/vma.c +++ b/arch/x86/vdso/vma.c | |||
@@ -24,7 +24,44 @@ extern unsigned short vdso_sync_cpuid; | |||
24 | extern struct page *vdso_pages[]; | 24 | extern struct page *vdso_pages[]; |
25 | static unsigned vdso_size; | 25 | static unsigned vdso_size; |
26 | 26 | ||
27 | static void __init patch_vdso(void *vdso, size_t len) | 27 | #ifdef CONFIG_X86_X32_ABI |
28 | extern char vdsox32_start[], vdsox32_end[]; | ||
29 | extern struct page *vdsox32_pages[]; | ||
30 | static unsigned vdsox32_size; | ||
31 | |||
32 | static void __init patch_vdsox32(void *vdso, size_t len) | ||
33 | { | ||
34 | Elf32_Ehdr *hdr = vdso; | ||
35 | Elf32_Shdr *sechdrs, *alt_sec = 0; | ||
36 | char *secstrings; | ||
37 | void *alt_data; | ||
38 | int i; | ||
39 | |||
40 | BUG_ON(len < sizeof(Elf32_Ehdr)); | ||
41 | BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0); | ||
42 | |||
43 | sechdrs = (void *)hdr + hdr->e_shoff; | ||
44 | secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
45 | |||
46 | for (i = 1; i < hdr->e_shnum; i++) { | ||
47 | Elf32_Shdr *shdr = &sechdrs[i]; | ||
48 | if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) { | ||
49 | alt_sec = shdr; | ||
50 | goto found; | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /* If we get here, it's probably a bug. */ | ||
55 | pr_warning("patch_vdsox32: .altinstructions not found\n"); | ||
56 | return; /* nothing to patch */ | ||
57 | |||
58 | found: | ||
59 | alt_data = (void *)hdr + alt_sec->sh_offset; | ||
60 | apply_alternatives(alt_data, alt_data + alt_sec->sh_size); | ||
61 | } | ||
62 | #endif | ||
63 | |||
64 | static void __init patch_vdso64(void *vdso, size_t len) | ||
28 | { | 65 | { |
29 | Elf64_Ehdr *hdr = vdso; | 66 | Elf64_Ehdr *hdr = vdso; |
30 | Elf64_Shdr *sechdrs, *alt_sec = 0; | 67 | Elf64_Shdr *sechdrs, *alt_sec = 0; |
@@ -47,7 +84,7 @@ static void __init patch_vdso(void *vdso, size_t len) | |||
47 | } | 84 | } |
48 | 85 | ||
49 | /* If we get here, it's probably a bug. */ | 86 | /* If we get here, it's probably a bug. */ |
50 | pr_warning("patch_vdso: .altinstructions not found\n"); | 87 | pr_warning("patch_vdso64: .altinstructions not found\n"); |
51 | return; /* nothing to patch */ | 88 | return; /* nothing to patch */ |
52 | 89 | ||
53 | found: | 90 | found: |
@@ -60,12 +97,20 @@ static int __init init_vdso(void) | |||
60 | int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE; | 97 | int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE; |
61 | int i; | 98 | int i; |
62 | 99 | ||
63 | patch_vdso(vdso_start, vdso_end - vdso_start); | 100 | patch_vdso64(vdso_start, vdso_end - vdso_start); |
64 | 101 | ||
65 | vdso_size = npages << PAGE_SHIFT; | 102 | vdso_size = npages << PAGE_SHIFT; |
66 | for (i = 0; i < npages; i++) | 103 | for (i = 0; i < npages; i++) |
67 | vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE); | 104 | vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE); |
68 | 105 | ||
106 | #ifdef CONFIG_X86_X32_ABI | ||
107 | patch_vdsox32(vdsox32_start, vdsox32_end - vdsox32_start); | ||
108 | npages = (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE; | ||
109 | vdsox32_size = npages << PAGE_SHIFT; | ||
110 | for (i = 0; i < npages; i++) | ||
111 | vdsox32_pages[i] = virt_to_page(vdsox32_start + i*PAGE_SIZE); | ||
112 | #endif | ||
113 | |||
69 | return 0; | 114 | return 0; |
70 | } | 115 | } |
71 | subsys_initcall(init_vdso); | 116 | subsys_initcall(init_vdso); |
@@ -103,7 +148,10 @@ static unsigned long vdso_addr(unsigned long start, unsigned len) | |||
103 | 148 | ||
104 | /* Setup a VMA at program startup for the vsyscall page. | 149 | /* Setup a VMA at program startup for the vsyscall page. |
105 | Not called for compat tasks */ | 150 | Not called for compat tasks */ |
106 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | 151 | static int setup_additional_pages(struct linux_binprm *bprm, |
152 | int uses_interp, | ||
153 | struct page **pages, | ||
154 | unsigned size) | ||
107 | { | 155 | { |
108 | struct mm_struct *mm = current->mm; | 156 | struct mm_struct *mm = current->mm; |
109 | unsigned long addr; | 157 | unsigned long addr; |
@@ -113,8 +161,8 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
113 | return 0; | 161 | return 0; |
114 | 162 | ||
115 | down_write(&mm->mmap_sem); | 163 | down_write(&mm->mmap_sem); |
116 | addr = vdso_addr(mm->start_stack, vdso_size); | 164 | addr = vdso_addr(mm->start_stack, size); |
117 | addr = get_unmapped_area(NULL, addr, vdso_size, 0, 0); | 165 | addr = get_unmapped_area(NULL, addr, size, 0, 0); |
118 | if (IS_ERR_VALUE(addr)) { | 166 | if (IS_ERR_VALUE(addr)) { |
119 | ret = addr; | 167 | ret = addr; |
120 | goto up_fail; | 168 | goto up_fail; |
@@ -122,10 +170,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
122 | 170 | ||
123 | current->mm->context.vdso = (void *)addr; | 171 | current->mm->context.vdso = (void *)addr; |
124 | 172 | ||
125 | ret = install_special_mapping(mm, addr, vdso_size, | 173 | ret = install_special_mapping(mm, addr, size, |
126 | VM_READ|VM_EXEC| | 174 | VM_READ|VM_EXEC| |
127 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | 175 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, |
128 | vdso_pages); | 176 | pages); |
129 | if (ret) { | 177 | if (ret) { |
130 | current->mm->context.vdso = NULL; | 178 | current->mm->context.vdso = NULL; |
131 | goto up_fail; | 179 | goto up_fail; |
@@ -136,6 +184,20 @@ up_fail: | |||
136 | return ret; | 184 | return ret; |
137 | } | 185 | } |
138 | 186 | ||
187 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | ||
188 | { | ||
189 | return setup_additional_pages(bprm, uses_interp, vdso_pages, | ||
190 | vdso_size); | ||
191 | } | ||
192 | |||
193 | #ifdef CONFIG_X86_X32_ABI | ||
194 | int x32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | ||
195 | { | ||
196 | return setup_additional_pages(bprm, uses_interp, vdsox32_pages, | ||
197 | vdsox32_size); | ||
198 | } | ||
199 | #endif | ||
200 | |||
139 | static __init int vdso_setup(char *s) | 201 | static __init int vdso_setup(char *s) |
140 | { | 202 | { |
141 | vdso_enabled = simple_strtoul(s, NULL, 0); | 203 | vdso_enabled = simple_strtoul(s, NULL, 0); |