diff options
| author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2006-06-21 15:38:17 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-06-28 12:59:46 -0400 |
| commit | 9641c7cc5a7f6d5c9dc9b43eea4e5f8c3c08c94e (patch) | |
| tree | 5ff57feabe0538d58404ec4918b0d94d75f69846 | |
| parent | 002547b4f86c27bfac5bae344b723d250857be6b (diff) | |
[ARM] nommu: uaccess tweaks
MMUless systems have only one address space for all threads, so
both the usual access_ok() checks, and the exception handling do
not make much sense.
Hence, discard the fixup and exception tables at link time, use
memcpy/memset for the user copy/clearing functions, and define
the permission check macros to be constants.
Some of this patch was derived from the equivalent patch by
Hyok S. Choi.
Signed-off-by: Hyok S. Choi <hyok.choi@samsung.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | arch/arm/kernel/armksyms.c | 7 | ||||
| -rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 8 | ||||
| -rw-r--r-- | arch/arm/lib/Makefile | 13 | ||||
| -rw-r--r-- | include/asm-arm/uaccess.h | 139 |
4 files changed, 108 insertions, 59 deletions
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index f8bb7abd3e9b..da69e660574b 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c | |||
| @@ -109,11 +109,13 @@ EXPORT_SYMBOL(memchr); | |||
| 109 | EXPORT_SYMBOL(__memzero); | 109 | EXPORT_SYMBOL(__memzero); |
| 110 | 110 | ||
| 111 | /* user mem (segment) */ | 111 | /* user mem (segment) */ |
| 112 | EXPORT_SYMBOL(__strnlen_user); | ||
| 113 | EXPORT_SYMBOL(__strncpy_from_user); | ||
| 114 | |||
| 115 | #ifdef CONFIG_MMU | ||
| 112 | EXPORT_SYMBOL(__copy_from_user); | 116 | EXPORT_SYMBOL(__copy_from_user); |
| 113 | EXPORT_SYMBOL(__copy_to_user); | 117 | EXPORT_SYMBOL(__copy_to_user); |
| 114 | EXPORT_SYMBOL(__clear_user); | 118 | EXPORT_SYMBOL(__clear_user); |
| 115 | EXPORT_SYMBOL(__strnlen_user); | ||
| 116 | EXPORT_SYMBOL(__strncpy_from_user); | ||
| 117 | 119 | ||
| 118 | EXPORT_SYMBOL(__get_user_1); | 120 | EXPORT_SYMBOL(__get_user_1); |
| 119 | EXPORT_SYMBOL(__get_user_2); | 121 | EXPORT_SYMBOL(__get_user_2); |
| @@ -123,6 +125,7 @@ EXPORT_SYMBOL(__put_user_1); | |||
| 123 | EXPORT_SYMBOL(__put_user_2); | 125 | EXPORT_SYMBOL(__put_user_2); |
| 124 | EXPORT_SYMBOL(__put_user_4); | 126 | EXPORT_SYMBOL(__put_user_4); |
| 125 | EXPORT_SYMBOL(__put_user_8); | 127 | EXPORT_SYMBOL(__put_user_8); |
| 128 | #endif | ||
| 126 | 129 | ||
| 127 | /* crypto hash */ | 130 | /* crypto hash */ |
| 128 | EXPORT_SYMBOL(sha_transform); | 131 | EXPORT_SYMBOL(sha_transform); |
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 2b254e88595c..2df9688a7028 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
| @@ -80,6 +80,10 @@ SECTIONS | |||
| 80 | *(.exit.text) | 80 | *(.exit.text) |
| 81 | *(.exit.data) | 81 | *(.exit.data) |
| 82 | *(.exitcall.exit) | 82 | *(.exitcall.exit) |
| 83 | #ifndef CONFIG_MMU | ||
| 84 | *(.fixup) | ||
| 85 | *(__ex_table) | ||
| 86 | #endif | ||
| 83 | } | 87 | } |
| 84 | 88 | ||
| 85 | .text : { /* Real text segment */ | 89 | .text : { /* Real text segment */ |
| @@ -87,7 +91,9 @@ SECTIONS | |||
| 87 | *(.text) | 91 | *(.text) |
| 88 | SCHED_TEXT | 92 | SCHED_TEXT |
| 89 | LOCK_TEXT | 93 | LOCK_TEXT |
| 94 | #ifdef CONFIG_MMU | ||
| 90 | *(.fixup) | 95 | *(.fixup) |
| 96 | #endif | ||
| 91 | *(.gnu.warning) | 97 | *(.gnu.warning) |
| 92 | *(.rodata) | 98 | *(.rodata) |
| 93 | *(.rodata.*) | 99 | *(.rodata.*) |
| @@ -142,7 +148,9 @@ SECTIONS | |||
| 142 | */ | 148 | */ |
| 143 | . = ALIGN(32); | 149 | . = ALIGN(32); |
| 144 | __start___ex_table = .; | 150 | __start___ex_table = .; |
| 151 | #ifdef CONFIG_MMU | ||
| 145 | *(__ex_table) | 152 | *(__ex_table) |
| 153 | #endif | ||
| 146 | __stop___ex_table = .; | 154 | __stop___ex_table = .; |
| 147 | 155 | ||
| 148 | /* | 156 | /* |
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 7b726b627ea5..30351cd4560d 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile | |||
| @@ -6,28 +6,31 @@ | |||
| 6 | 6 | ||
| 7 | lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ | 7 | lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ |
| 8 | csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ | 8 | csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ |
| 9 | copy_page.o delay.o findbit.o memchr.o memcpy.o \ | 9 | delay.o findbit.o memchr.o memcpy.o \ |
| 10 | memmove.o memset.o memzero.o setbit.o \ | 10 | memmove.o memset.o memzero.o setbit.o \ |
| 11 | strncpy_from_user.o strnlen_user.o \ | 11 | strncpy_from_user.o strnlen_user.o \ |
| 12 | strchr.o strrchr.o \ | 12 | strchr.o strrchr.o \ |
| 13 | testchangebit.o testclearbit.o testsetbit.o \ | 13 | testchangebit.o testclearbit.o testsetbit.o \ |
| 14 | getuser.o putuser.o clear_user.o \ | ||
| 15 | ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ | 14 | ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ |
| 16 | ucmpdi2.o lib1funcs.o div64.o sha1.o \ | 15 | ucmpdi2.o lib1funcs.o div64.o sha1.o \ |
| 17 | io-readsb.o io-writesb.o io-readsl.o io-writesl.o | 16 | io-readsb.o io-writesb.o io-readsl.o io-writesl.o |
| 18 | 17 | ||
| 18 | mmu-y := clear_user.o copy_page.o getuser.o putuser.o | ||
| 19 | |||
| 19 | # the code in uaccess.S is not preemption safe and | 20 | # the code in uaccess.S is not preemption safe and |
| 20 | # probably faster on ARMv3 only | 21 | # probably faster on ARMv3 only |
| 21 | ifeq ($(CONFIG_PREEMPT),y) | 22 | ifeq ($(CONFIG_PREEMPT),y) |
| 22 | lib-y += copy_from_user.o copy_to_user.o | 23 | mmu-y += copy_from_user.o copy_to_user.o |
| 23 | else | 24 | else |
| 24 | ifneq ($(CONFIG_CPU_32v3),y) | 25 | ifneq ($(CONFIG_CPU_32v3),y) |
| 25 | lib-y += copy_from_user.o copy_to_user.o | 26 | mmu-y += copy_from_user.o copy_to_user.o |
| 26 | else | 27 | else |
| 27 | lib-y += uaccess.o | 28 | mmu-y += uaccess.o |
| 28 | endif | 29 | endif |
| 29 | endif | 30 | endif |
| 30 | 31 | ||
| 32 | lib-$(CONFIG_MMU) += $(mmu-y) | ||
| 33 | |||
| 31 | ifeq ($(CONFIG_CPU_32v3),y) | 34 | ifeq ($(CONFIG_CPU_32v3),y) |
| 32 | lib-y += io-readsw-armv3.o io-writesw-armv3.o | 35 | lib-y += io-readsw-armv3.o io-writesw-armv3.o |
| 33 | else | 36 | else |
diff --git a/include/asm-arm/uaccess.h b/include/asm-arm/uaccess.h index f909dc75301a..87aba57a66c4 100644 --- a/include/asm-arm/uaccess.h +++ b/include/asm-arm/uaccess.h | |||
| @@ -41,15 +41,24 @@ struct exception_table_entry | |||
| 41 | extern int fixup_exception(struct pt_regs *regs); | 41 | extern int fixup_exception(struct pt_regs *regs); |
| 42 | 42 | ||
| 43 | /* | 43 | /* |
| 44 | * These two are intentionally not defined anywhere - if the kernel | ||
| 45 | * code generates any references to them, that's a bug. | ||
| 46 | */ | ||
| 47 | extern int __get_user_bad(void); | ||
| 48 | extern int __put_user_bad(void); | ||
| 49 | |||
| 50 | /* | ||
| 44 | * Note that this is actually 0x1,0000,0000 | 51 | * Note that this is actually 0x1,0000,0000 |
| 45 | */ | 52 | */ |
| 46 | #define KERNEL_DS 0x00000000 | 53 | #define KERNEL_DS 0x00000000 |
| 47 | #define USER_DS TASK_SIZE | ||
| 48 | |||
| 49 | #define get_ds() (KERNEL_DS) | 54 | #define get_ds() (KERNEL_DS) |
| 55 | |||
| 56 | #ifdef CONFIG_MMU | ||
| 57 | |||
| 58 | #define USER_DS TASK_SIZE | ||
| 50 | #define get_fs() (current_thread_info()->addr_limit) | 59 | #define get_fs() (current_thread_info()->addr_limit) |
| 51 | 60 | ||
| 52 | static inline void set_fs (mm_segment_t fs) | 61 | static inline void set_fs(mm_segment_t fs) |
| 53 | { | 62 | { |
| 54 | current_thread_info()->addr_limit = fs; | 63 | current_thread_info()->addr_limit = fs; |
| 55 | modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER); | 64 | modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER); |
| @@ -75,8 +84,6 @@ static inline void set_fs (mm_segment_t fs) | |||
| 75 | : "cc"); \ | 84 | : "cc"); \ |
| 76 | flag; }) | 85 | flag; }) |
| 77 | 86 | ||
| 78 | #define access_ok(type,addr,size) (__range_ok(addr,size) == 0) | ||
| 79 | |||
| 80 | /* | 87 | /* |
| 81 | * Single-value transfer routines. They automatically use the right | 88 | * Single-value transfer routines. They automatically use the right |
| 82 | * size if we just have the right pointer type. Note that the functions | 89 | * size if we just have the right pointer type. Note that the functions |
| @@ -87,20 +94,10 @@ static inline void set_fs (mm_segment_t fs) | |||
| 87 | * fixup code, but there are a few places where it intrudes on the | 94 | * fixup code, but there are a few places where it intrudes on the |
| 88 | * main code path. When we only write to user space, there is no | 95 | * main code path. When we only write to user space, there is no |
| 89 | * problem. | 96 | * problem. |
| 90 | * | ||
| 91 | * The "__xxx" versions of the user access functions do not verify the | ||
| 92 | * address space - it must have been done previously with a separate | ||
| 93 | * "access_ok()" call. | ||
| 94 | * | ||
| 95 | * The "xxx_error" versions set the third argument to EFAULT if an | ||
| 96 | * error occurs, and leave it unchanged on success. Note that these | ||
| 97 | * versions are void (ie, don't return a value as such). | ||
| 98 | */ | 97 | */ |
| 99 | |||
| 100 | extern int __get_user_1(void *); | 98 | extern int __get_user_1(void *); |
| 101 | extern int __get_user_2(void *); | 99 | extern int __get_user_2(void *); |
| 102 | extern int __get_user_4(void *); | 100 | extern int __get_user_4(void *); |
| 103 | extern int __get_user_bad(void); | ||
| 104 | 101 | ||
| 105 | #define __get_user_x(__r2,__p,__e,__s,__i...) \ | 102 | #define __get_user_x(__r2,__p,__e,__s,__i...) \ |
| 106 | __asm__ __volatile__ ( \ | 103 | __asm__ __volatile__ ( \ |
| @@ -131,6 +128,74 @@ extern int __get_user_bad(void); | |||
| 131 | __e; \ | 128 | __e; \ |
| 132 | }) | 129 | }) |
| 133 | 130 | ||
| 131 | extern int __put_user_1(void *, unsigned int); | ||
| 132 | extern int __put_user_2(void *, unsigned int); | ||
| 133 | extern int __put_user_4(void *, unsigned int); | ||
| 134 | extern int __put_user_8(void *, unsigned long long); | ||
| 135 | |||
| 136 | #define __put_user_x(__r2,__p,__e,__s) \ | ||
| 137 | __asm__ __volatile__ ( \ | ||
| 138 | __asmeq("%0", "r0") __asmeq("%2", "r2") \ | ||
| 139 | "bl __put_user_" #__s \ | ||
| 140 | : "=&r" (__e) \ | ||
| 141 | : "0" (__p), "r" (__r2) \ | ||
| 142 | : "ip", "lr", "cc") | ||
| 143 | |||
| 144 | #define put_user(x,p) \ | ||
| 145 | ({ \ | ||
| 146 | const register typeof(*(p)) __r2 asm("r2") = (x); \ | ||
| 147 | const register typeof(*(p)) __user *__p asm("r0") = (p);\ | ||
| 148 | register int __e asm("r0"); \ | ||
| 149 | switch (sizeof(*(__p))) { \ | ||
| 150 | case 1: \ | ||
| 151 | __put_user_x(__r2, __p, __e, 1); \ | ||
| 152 | break; \ | ||
| 153 | case 2: \ | ||
| 154 | __put_user_x(__r2, __p, __e, 2); \ | ||
| 155 | break; \ | ||
| 156 | case 4: \ | ||
| 157 | __put_user_x(__r2, __p, __e, 4); \ | ||
| 158 | break; \ | ||
| 159 | case 8: \ | ||
| 160 | __put_user_x(__r2, __p, __e, 8); \ | ||
| 161 | break; \ | ||
| 162 | default: __e = __put_user_bad(); break; \ | ||
| 163 | } \ | ||
| 164 | __e; \ | ||
| 165 | }) | ||
| 166 | |||
| 167 | #else /* CONFIG_MMU */ | ||
| 168 | |||
| 169 | /* | ||
| 170 | * uClinux has only one addr space, so has simplified address limits. | ||
| 171 | */ | ||
| 172 | #define USER_DS KERNEL_DS | ||
| 173 | |||
| 174 | #define segment_eq(a,b) (1) | ||
| 175 | #define __addr_ok(addr) (1) | ||
| 176 | #define __range_ok(addr,size) (0) | ||
| 177 | #define get_fs() (KERNEL_DS) | ||
| 178 | |||
| 179 | static inline void set_fs(mm_segment_t fs) | ||
| 180 | { | ||
| 181 | } | ||
| 182 | |||
| 183 | #define get_user(x,p) __get_user(x,p) | ||
| 184 | #define put_user(x,p) __put_user(x,p) | ||
| 185 | |||
| 186 | #endif /* CONFIG_MMU */ | ||
| 187 | |||
| 188 | #define access_ok(type,addr,size) (__range_ok(addr,size) == 0) | ||
| 189 | |||
| 190 | /* | ||
| 191 | * The "__xxx" versions of the user access functions do not verify the | ||
| 192 | * address space - it must have been done previously with a separate | ||
| 193 | * "access_ok()" call. | ||
| 194 | * | ||
| 195 | * The "xxx_error" versions set the third argument to EFAULT if an | ||
| 196 | * error occurs, and leave it unchanged on success. Note that these | ||
| 197 | * versions are void (ie, don't return a value as such). | ||
| 198 | */ | ||
| 134 | #define __get_user(x,ptr) \ | 199 | #define __get_user(x,ptr) \ |
| 135 | ({ \ | 200 | ({ \ |
| 136 | long __gu_err = 0; \ | 201 | long __gu_err = 0; \ |
| @@ -212,43 +277,6 @@ do { \ | |||
| 212 | : "r" (addr), "i" (-EFAULT) \ | 277 | : "r" (addr), "i" (-EFAULT) \ |
| 213 | : "cc") | 278 | : "cc") |
| 214 | 279 | ||
| 215 | extern int __put_user_1(void *, unsigned int); | ||
| 216 | extern int __put_user_2(void *, unsigned int); | ||
| 217 | extern int __put_user_4(void *, unsigned int); | ||
| 218 | extern int __put_user_8(void *, unsigned long long); | ||
| 219 | extern int __put_user_bad(void); | ||
| 220 | |||
| 221 | #define __put_user_x(__r2,__p,__e,__s) \ | ||
| 222 | __asm__ __volatile__ ( \ | ||
| 223 | __asmeq("%0", "r0") __asmeq("%2", "r2") \ | ||
| 224 | "bl __put_user_" #__s \ | ||
| 225 | : "=&r" (__e) \ | ||
| 226 | : "0" (__p), "r" (__r2) \ | ||
| 227 | : "ip", "lr", "cc") | ||
| 228 | |||
| 229 | #define put_user(x,p) \ | ||
| 230 | ({ \ | ||
| 231 | const register typeof(*(p)) __r2 asm("r2") = (x); \ | ||
| 232 | const register typeof(*(p)) __user *__p asm("r0") = (p);\ | ||
| 233 | register int __e asm("r0"); \ | ||
| 234 | switch (sizeof(*(__p))) { \ | ||
| 235 | case 1: \ | ||
| 236 | __put_user_x(__r2, __p, __e, 1); \ | ||
| 237 | break; \ | ||
| 238 | case 2: \ | ||
| 239 | __put_user_x(__r2, __p, __e, 2); \ | ||
| 240 | break; \ | ||
| 241 | case 4: \ | ||
| 242 | __put_user_x(__r2, __p, __e, 4); \ | ||
| 243 | break; \ | ||
| 244 | case 8: \ | ||
| 245 | __put_user_x(__r2, __p, __e, 8); \ | ||
| 246 | break; \ | ||
| 247 | default: __e = __put_user_bad(); break; \ | ||
| 248 | } \ | ||
| 249 | __e; \ | ||
| 250 | }) | ||
| 251 | |||
| 252 | #define __put_user(x,ptr) \ | 280 | #define __put_user(x,ptr) \ |
| 253 | ({ \ | 281 | ({ \ |
| 254 | long __pu_err = 0; \ | 282 | long __pu_err = 0; \ |
| @@ -354,9 +382,16 @@ do { \ | |||
| 354 | : "cc") | 382 | : "cc") |
| 355 | 383 | ||
| 356 | 384 | ||
| 385 | #ifdef CONFIG_MMU | ||
| 357 | extern unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n); | 386 | extern unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n); |
| 358 | extern unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n); | 387 | extern unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n); |
| 359 | extern unsigned long __clear_user(void __user *addr, unsigned long n); | 388 | extern unsigned long __clear_user(void __user *addr, unsigned long n); |
| 389 | #else | ||
| 390 | #define __copy_from_user(to,from,n) (memcpy(to, (void __force *)from, n), 0) | ||
| 391 | #define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0) | ||
| 392 | #define __clear_user(addr,n) (memset((void __force *)addr, 0, n), 0) | ||
| 393 | #endif | ||
| 394 | |||
| 360 | extern unsigned long __strncpy_from_user(char *to, const char __user *from, unsigned long count); | 395 | extern unsigned long __strncpy_from_user(char *to, const char __user *from, unsigned long count); |
| 361 | extern unsigned long __strnlen_user(const char __user *s, long n); | 396 | extern unsigned long __strnlen_user(const char __user *s, long n); |
| 362 | 397 | ||
