diff options
author | Helge Deller <deller@gmx.de> | 2014-01-31 16:19:52 -0500 |
---|---|---|
committer | Helge Deller <deller@gmx.de> | 2014-02-02 15:00:13 -0500 |
commit | 9dabf60dc4abe6e06bebcc2ee46b4d76ec8741f2 (patch) | |
tree | 93b98d806b1941b91ac3de04b7bd730103d3fab8 | |
parent | f5a408d53edef3af07ac7697b8bc54a755628450 (diff) |
parisc: add flexible mmap memory layout support
Add support for the flexible mmap memory layout (as described in
http://lwn.net/Articles/91829). This is especially very interesting on
parisc since we currently only support 32bit userspace (even with a
64bit Linux kernel).
Signed-off-by: Helge Deller <deller@gmx.de>
-rw-r--r-- | arch/parisc/include/asm/elf.h | 4 | ||||
-rw-r--r-- | arch/parisc/include/asm/pgtable.h | 1 | ||||
-rw-r--r-- | arch/parisc/include/asm/processor.h | 2 | ||||
-rw-r--r-- | arch/parisc/include/asm/thread_info.h | 10 | ||||
-rw-r--r-- | arch/parisc/kernel/process.c | 21 | ||||
-rw-r--r-- | arch/parisc/kernel/sys_parisc.c | 238 |
6 files changed, 233 insertions, 43 deletions
diff --git a/arch/parisc/include/asm/elf.h b/arch/parisc/include/asm/elf.h index ad2b50397894..3391d061eccc 100644 --- a/arch/parisc/include/asm/elf.h +++ b/arch/parisc/include/asm/elf.h | |||
@@ -348,4 +348,8 @@ struct pt_regs; /* forward declaration... */ | |||
348 | 348 | ||
349 | #define ELF_HWCAP 0 | 349 | #define ELF_HWCAP 0 |
350 | 350 | ||
351 | struct mm_struct; | ||
352 | extern unsigned long arch_randomize_brk(struct mm_struct *); | ||
353 | #define arch_randomize_brk arch_randomize_brk | ||
354 | |||
351 | #endif | 355 | #endif |
diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h index 34899b5d959a..22b89d1edba7 100644 --- a/arch/parisc/include/asm/pgtable.h +++ b/arch/parisc/include/asm/pgtable.h | |||
@@ -511,6 +511,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, | |||
511 | /* We provide our own get_unmapped_area to provide cache coherency */ | 511 | /* We provide our own get_unmapped_area to provide cache coherency */ |
512 | 512 | ||
513 | #define HAVE_ARCH_UNMAPPED_AREA | 513 | #define HAVE_ARCH_UNMAPPED_AREA |
514 | #define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN | ||
514 | 515 | ||
515 | #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG | 516 | #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG |
516 | #define __HAVE_ARCH_PTEP_GET_AND_CLEAR | 517 | #define __HAVE_ARCH_PTEP_GET_AND_CLEAR |
diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index cc2290a3cace..198a86feb574 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h | |||
@@ -30,6 +30,8 @@ | |||
30 | #endif | 30 | #endif |
31 | #define current_text_addr() ({ void *pc; current_ia(pc); pc; }) | 31 | #define current_text_addr() ({ void *pc; current_ia(pc); pc; }) |
32 | 32 | ||
33 | #define HAVE_ARCH_PICK_MMAP_LAYOUT | ||
34 | |||
33 | #define TASK_SIZE_OF(tsk) ((tsk)->thread.task_size) | 35 | #define TASK_SIZE_OF(tsk) ((tsk)->thread.task_size) |
34 | #define TASK_SIZE TASK_SIZE_OF(current) | 36 | #define TASK_SIZE TASK_SIZE_OF(current) |
35 | #define TASK_UNMAPPED_BASE (current->thread.map_base) | 37 | #define TASK_UNMAPPED_BASE (current->thread.map_base) |
diff --git a/arch/parisc/include/asm/thread_info.h b/arch/parisc/include/asm/thread_info.h index d5f97ea3a4e1..4b9b10ce1f9d 100644 --- a/arch/parisc/include/asm/thread_info.h +++ b/arch/parisc/include/asm/thread_info.h | |||
@@ -76,6 +76,16 @@ struct thread_info { | |||
76 | #define _TIF_SYSCALL_TRACE_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \ | 76 | #define _TIF_SYSCALL_TRACE_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \ |
77 | _TIF_BLOCKSTEP | _TIF_SYSCALL_AUDIT) | 77 | _TIF_BLOCKSTEP | _TIF_SYSCALL_AUDIT) |
78 | 78 | ||
79 | #ifdef CONFIG_64BIT | ||
80 | # ifdef CONFIG_COMPAT | ||
81 | # define is_32bit_task() (test_thread_flag(TIF_32BIT)) | ||
82 | # else | ||
83 | # define is_32bit_task() (0) | ||
84 | # endif | ||
85 | #else | ||
86 | # define is_32bit_task() (1) | ||
87 | #endif | ||
88 | |||
79 | #endif /* __KERNEL__ */ | 89 | #endif /* __KERNEL__ */ |
80 | 90 | ||
81 | #endif /* _ASM_PARISC_THREAD_INFO_H */ | 91 | #endif /* _ASM_PARISC_THREAD_INFO_H */ |
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c index 55f92b614182..0bbbf0d3f608 100644 --- a/arch/parisc/kernel/process.c +++ b/arch/parisc/kernel/process.c | |||
@@ -13,7 +13,7 @@ | |||
13 | * Copyright (C) 2000 Grant Grundler <grundler with parisc-linux.org> | 13 | * Copyright (C) 2000 Grant Grundler <grundler with parisc-linux.org> |
14 | * Copyright (C) 2001 Alan Modra <amodra at parisc-linux.org> | 14 | * Copyright (C) 2001 Alan Modra <amodra at parisc-linux.org> |
15 | * Copyright (C) 2001-2002 Ryan Bradetich <rbrad at parisc-linux.org> | 15 | * Copyright (C) 2001-2002 Ryan Bradetich <rbrad at parisc-linux.org> |
16 | * Copyright (C) 2001-2007 Helge Deller <deller at parisc-linux.org> | 16 | * Copyright (C) 2001-2014 Helge Deller <deller@gmx.de> |
17 | * Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org> | 17 | * Copyright (C) 2002 Randolph Chung <tausq with parisc-linux.org> |
18 | * | 18 | * |
19 | * | 19 | * |
@@ -49,6 +49,7 @@ | |||
49 | #include <linux/kallsyms.h> | 49 | #include <linux/kallsyms.h> |
50 | #include <linux/uaccess.h> | 50 | #include <linux/uaccess.h> |
51 | #include <linux/rcupdate.h> | 51 | #include <linux/rcupdate.h> |
52 | #include <linux/random.h> | ||
52 | 53 | ||
53 | #include <asm/io.h> | 54 | #include <asm/io.h> |
54 | #include <asm/asm-offsets.h> | 55 | #include <asm/asm-offsets.h> |
@@ -286,3 +287,21 @@ void *dereference_function_descriptor(void *ptr) | |||
286 | return ptr; | 287 | return ptr; |
287 | } | 288 | } |
288 | #endif | 289 | #endif |
290 | |||
291 | static inline unsigned long brk_rnd(void) | ||
292 | { | ||
293 | /* 8MB for 32bit, 1GB for 64bit */ | ||
294 | if (is_32bit_task()) | ||
295 | return (get_random_int() & 0x7ffUL) << PAGE_SHIFT; | ||
296 | else | ||
297 | return (get_random_int() & 0x3ffffUL) << PAGE_SHIFT; | ||
298 | } | ||
299 | |||
300 | unsigned long arch_randomize_brk(struct mm_struct *mm) | ||
301 | { | ||
302 | unsigned long ret = PAGE_ALIGN(mm->brk + brk_rnd()); | ||
303 | |||
304 | if (ret < mm->brk) | ||
305 | return mm->brk; | ||
306 | return ret; | ||
307 | } | ||
diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index 0d3a9d4927b5..b7cadc4a06cd 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Copyright (C) 1999-2003 Matthew Wilcox <willy at parisc-linux.org> | 5 | * Copyright (C) 1999-2003 Matthew Wilcox <willy at parisc-linux.org> |
6 | * Copyright (C) 2000-2003 Paul Bame <bame at parisc-linux.org> | 6 | * Copyright (C) 2000-2003 Paul Bame <bame at parisc-linux.org> |
7 | * Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org> | 7 | * Copyright (C) 2001 Thomas Bogendoerfer <tsbogend at parisc-linux.org> |
8 | * Copyright (C) 1999-2014 Helge Deller <deller@gmx.de> | ||
8 | * | 9 | * |
9 | * | 10 | * |
10 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
@@ -23,6 +24,7 @@ | |||
23 | */ | 24 | */ |
24 | 25 | ||
25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
27 | #include <asm/elf.h> | ||
26 | #include <linux/file.h> | 28 | #include <linux/file.h> |
27 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
28 | #include <linux/linkage.h> | 30 | #include <linux/linkage.h> |
@@ -32,78 +34,230 @@ | |||
32 | #include <linux/syscalls.h> | 34 | #include <linux/syscalls.h> |
33 | #include <linux/utsname.h> | 35 | #include <linux/utsname.h> |
34 | #include <linux/personality.h> | 36 | #include <linux/personality.h> |
37 | #include <linux/random.h> | ||
35 | 38 | ||
36 | static unsigned long get_unshared_area(unsigned long addr, unsigned long len) | 39 | /* we construct an artificial offset for the mapping based on the physical |
40 | * address of the kernel mapping variable */ | ||
41 | #define GET_LAST_MMAP(filp) \ | ||
42 | (filp ? ((unsigned long) filp->f_mapping) >> 8 : 0UL) | ||
43 | #define SET_LAST_MMAP(filp, val) \ | ||
44 | { /* nothing */ } | ||
45 | |||
46 | static int get_offset(unsigned int last_mmap) | ||
37 | { | 47 | { |
38 | struct vm_unmapped_area_info info; | 48 | return (last_mmap & (SHMLBA-1)) >> PAGE_SHIFT; |
49 | } | ||
39 | 50 | ||
40 | info.flags = 0; | 51 | static unsigned long shared_align_offset(unsigned int last_mmap, |
41 | info.length = len; | 52 | unsigned long pgoff) |
42 | info.low_limit = PAGE_ALIGN(addr); | 53 | { |
43 | info.high_limit = TASK_SIZE; | 54 | return (get_offset(last_mmap) + pgoff) << PAGE_SHIFT; |
44 | info.align_mask = 0; | ||
45 | info.align_offset = 0; | ||
46 | return vm_unmapped_area(&info); | ||
47 | } | 55 | } |
48 | 56 | ||
49 | /* | 57 | static inline unsigned long COLOR_ALIGN(unsigned long addr, |
50 | * We need to know the offset to use. Old scheme was to look for | 58 | unsigned int last_mmap, unsigned long pgoff) |
51 | * existing mapping and use the same offset. New scheme is to use the | ||
52 | * address of the kernel data structure as the seed for the offset. | ||
53 | * We'll see how that works... | ||
54 | * | ||
55 | * The mapping is cacheline aligned, so there's no information in the bottom | ||
56 | * few bits of the address. We're looking for 10 bits (4MB / 4k), so let's | ||
57 | * drop the bottom 8 bits and use bits 8-17. | ||
58 | */ | ||
59 | static int get_offset(struct address_space *mapping) | ||
60 | { | 59 | { |
61 | return (unsigned long) mapping >> 8; | 60 | unsigned long base = (addr+SHMLBA-1) & ~(SHMLBA-1); |
61 | unsigned long off = (SHMLBA-1) & | ||
62 | (shared_align_offset(last_mmap, pgoff) << PAGE_SHIFT); | ||
63 | |||
64 | return base + off; | ||
62 | } | 65 | } |
63 | 66 | ||
64 | static unsigned long shared_align_offset(struct file *filp, unsigned long pgoff) | 67 | /* |
68 | * Top of mmap area (just below the process stack). | ||
69 | */ | ||
70 | |||
71 | static unsigned long mmap_upper_limit(void) | ||
65 | { | 72 | { |
66 | struct address_space *mapping = filp ? filp->f_mapping : NULL; | 73 | unsigned long stack_base; |
67 | 74 | ||
68 | return (get_offset(mapping) + pgoff) << PAGE_SHIFT; | 75 | /* Limit stack size to 1GB - see setup_arg_pages() in fs/exec.c */ |
76 | stack_base = rlimit_max(RLIMIT_STACK); | ||
77 | if (stack_base > (1 << 30)) | ||
78 | stack_base = 1 << 30; | ||
79 | |||
80 | return PAGE_ALIGN(STACK_TOP - stack_base); | ||
69 | } | 81 | } |
70 | 82 | ||
71 | static unsigned long get_shared_area(struct file *filp, unsigned long addr, | 83 | |
72 | unsigned long len, unsigned long pgoff) | 84 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, |
85 | unsigned long len, unsigned long pgoff, unsigned long flags) | ||
73 | { | 86 | { |
87 | struct mm_struct *mm = current->mm; | ||
88 | struct vm_area_struct *vma; | ||
89 | unsigned long task_size = TASK_SIZE; | ||
90 | int do_color_align, last_mmap; | ||
74 | struct vm_unmapped_area_info info; | 91 | struct vm_unmapped_area_info info; |
75 | 92 | ||
93 | if (len > task_size) | ||
94 | return -ENOMEM; | ||
95 | |||
96 | do_color_align = 0; | ||
97 | if (filp || (flags & MAP_SHARED)) | ||
98 | do_color_align = 1; | ||
99 | last_mmap = GET_LAST_MMAP(filp); | ||
100 | |||
101 | if (flags & MAP_FIXED) { | ||
102 | if ((flags & MAP_SHARED) && last_mmap && | ||
103 | (addr - shared_align_offset(last_mmap, pgoff)) | ||
104 | & (SHMLBA - 1)) | ||
105 | return -EINVAL; | ||
106 | goto found_addr; | ||
107 | } | ||
108 | |||
109 | if (addr) { | ||
110 | if (do_color_align && last_mmap) | ||
111 | addr = COLOR_ALIGN(addr, last_mmap, pgoff); | ||
112 | else | ||
113 | addr = PAGE_ALIGN(addr); | ||
114 | |||
115 | vma = find_vma(mm, addr); | ||
116 | if (task_size - len >= addr && | ||
117 | (!vma || addr + len <= vma->vm_start)) | ||
118 | goto found_addr; | ||
119 | } | ||
120 | |||
76 | info.flags = 0; | 121 | info.flags = 0; |
77 | info.length = len; | 122 | info.length = len; |
78 | info.low_limit = PAGE_ALIGN(addr); | 123 | info.low_limit = mm->mmap_legacy_base; |
79 | info.high_limit = TASK_SIZE; | 124 | info.high_limit = mmap_upper_limit(); |
80 | info.align_mask = PAGE_MASK & (SHMLBA - 1); | 125 | info.align_mask = last_mmap ? (PAGE_MASK & (SHMLBA - 1)) : 0; |
81 | info.align_offset = shared_align_offset(filp, pgoff); | 126 | info.align_offset = shared_align_offset(last_mmap, pgoff); |
82 | return vm_unmapped_area(&info); | 127 | addr = vm_unmapped_area(&info); |
128 | |||
129 | found_addr: | ||
130 | if (do_color_align && !last_mmap && !(addr & ~PAGE_MASK)) | ||
131 | SET_LAST_MMAP(filp, addr - (pgoff << PAGE_SHIFT)); | ||
132 | |||
133 | return addr; | ||
83 | } | 134 | } |
84 | 135 | ||
85 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | 136 | unsigned long |
86 | unsigned long len, unsigned long pgoff, unsigned long flags) | 137 | arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, |
138 | const unsigned long len, const unsigned long pgoff, | ||
139 | const unsigned long flags) | ||
87 | { | 140 | { |
141 | struct vm_area_struct *vma; | ||
142 | struct mm_struct *mm = current->mm; | ||
143 | unsigned long addr = addr0; | ||
144 | int do_color_align, last_mmap; | ||
145 | struct vm_unmapped_area_info info; | ||
146 | |||
147 | #ifdef CONFIG_64BIT | ||
148 | /* This should only ever run for 32-bit processes. */ | ||
149 | BUG_ON(!test_thread_flag(TIF_32BIT)); | ||
150 | #endif | ||
151 | |||
152 | /* requested length too big for entire address space */ | ||
88 | if (len > TASK_SIZE) | 153 | if (len > TASK_SIZE) |
89 | return -ENOMEM; | 154 | return -ENOMEM; |
155 | |||
156 | do_color_align = 0; | ||
157 | if (filp || (flags & MAP_SHARED)) | ||
158 | do_color_align = 1; | ||
159 | last_mmap = GET_LAST_MMAP(filp); | ||
160 | |||
90 | if (flags & MAP_FIXED) { | 161 | if (flags & MAP_FIXED) { |
91 | if ((flags & MAP_SHARED) && | 162 | if ((flags & MAP_SHARED) && last_mmap && |
92 | (addr - shared_align_offset(filp, pgoff)) & (SHMLBA - 1)) | 163 | (addr - shared_align_offset(last_mmap, pgoff)) |
164 | & (SHMLBA - 1)) | ||
93 | return -EINVAL; | 165 | return -EINVAL; |
94 | return addr; | 166 | goto found_addr; |
95 | } | 167 | } |
96 | if (!addr) | ||
97 | addr = TASK_UNMAPPED_BASE; | ||
98 | 168 | ||
99 | if (filp || (flags & MAP_SHARED)) | 169 | /* requesting a specific address */ |
100 | addr = get_shared_area(filp, addr, len, pgoff); | 170 | if (addr) { |
101 | else | 171 | if (do_color_align && last_mmap) |
102 | addr = get_unshared_area(addr, len); | 172 | addr = COLOR_ALIGN(addr, last_mmap, pgoff); |
173 | else | ||
174 | addr = PAGE_ALIGN(addr); | ||
175 | vma = find_vma(mm, addr); | ||
176 | if (TASK_SIZE - len >= addr && | ||
177 | (!vma || addr + len <= vma->vm_start)) | ||
178 | goto found_addr; | ||
179 | } | ||
180 | |||
181 | info.flags = VM_UNMAPPED_AREA_TOPDOWN; | ||
182 | info.length = len; | ||
183 | info.low_limit = PAGE_SIZE; | ||
184 | info.high_limit = mm->mmap_base; | ||
185 | info.align_mask = last_mmap ? (PAGE_MASK & (SHMLBA - 1)) : 0; | ||
186 | info.align_offset = shared_align_offset(last_mmap, pgoff); | ||
187 | addr = vm_unmapped_area(&info); | ||
188 | if (!(addr & ~PAGE_MASK)) | ||
189 | goto found_addr; | ||
190 | VM_BUG_ON(addr != -ENOMEM); | ||
191 | |||
192 | /* | ||
193 | * A failed mmap() very likely causes application failure, | ||
194 | * so fall back to the bottom-up function here. This scenario | ||
195 | * can happen with large stack limits and large mmap() | ||
196 | * allocations. | ||
197 | */ | ||
198 | return arch_get_unmapped_area(filp, addr0, len, pgoff, flags); | ||
199 | |||
200 | found_addr: | ||
201 | if (do_color_align && !last_mmap && !(addr & ~PAGE_MASK)) | ||
202 | SET_LAST_MMAP(filp, addr - (pgoff << PAGE_SHIFT)); | ||
103 | 203 | ||
104 | return addr; | 204 | return addr; |
105 | } | 205 | } |
106 | 206 | ||
207 | static int mmap_is_legacy(void) | ||
208 | { | ||
209 | if (current->personality & ADDR_COMPAT_LAYOUT) | ||
210 | return 1; | ||
211 | |||
212 | /* parisc stack always grows up - so a unlimited stack should | ||
213 | * not be an indicator to use the legacy memory layout. | ||
214 | * if (rlimit(RLIMIT_STACK) == RLIM_INFINITY) | ||
215 | * return 1; | ||
216 | */ | ||
217 | |||
218 | return sysctl_legacy_va_layout; | ||
219 | } | ||
220 | |||
221 | static unsigned long mmap_rnd(void) | ||
222 | { | ||
223 | unsigned long rnd = 0; | ||
224 | |||
225 | /* | ||
226 | * 8 bits of randomness in 32bit mmaps, 20 address space bits | ||
227 | * 28 bits of randomness in 64bit mmaps, 40 address space bits | ||
228 | */ | ||
229 | if (current->flags & PF_RANDOMIZE) { | ||
230 | if (is_32bit_task()) | ||
231 | rnd = get_random_int() % (1<<8); | ||
232 | else | ||
233 | rnd = get_random_int() % (1<<28); | ||
234 | } | ||
235 | return rnd << PAGE_SHIFT; | ||
236 | } | ||
237 | |||
238 | static unsigned long mmap_legacy_base(void) | ||
239 | { | ||
240 | return TASK_UNMAPPED_BASE + mmap_rnd(); | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * This function, called very early during the creation of a new | ||
245 | * process VM image, sets up which VM layout function to use: | ||
246 | */ | ||
247 | void arch_pick_mmap_layout(struct mm_struct *mm) | ||
248 | { | ||
249 | mm->mmap_legacy_base = mmap_legacy_base(); | ||
250 | mm->mmap_base = mmap_upper_limit(); | ||
251 | |||
252 | if (mmap_is_legacy()) { | ||
253 | mm->mmap_base = mm->mmap_legacy_base; | ||
254 | mm->get_unmapped_area = arch_get_unmapped_area; | ||
255 | } else { | ||
256 | mm->get_unmapped_area = arch_get_unmapped_area_topdown; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | |||
107 | asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len, | 261 | asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len, |
108 | unsigned long prot, unsigned long flags, unsigned long fd, | 262 | unsigned long prot, unsigned long flags, unsigned long fd, |
109 | unsigned long pgoff) | 263 | unsigned long pgoff) |