From: NIIBE Y. <gn...@m1...> - 2002-03-25 10:01:31
|
With (microscopic) optimization of the functionality of __addr_ok (which is used to access user space memory from kernel), kernel runs slightly faster. Besides, I've re-wrote __clear_user function which has been left as not-so-good implementation for three years (it's not so important as its use is so limited). 2002-03-25 NIIBE Yutaka <gn...@m1...> * include/asm-sh/uaccess.h (__clear_user): Make it external function. (Was: inline function). * arch/sh/mm/Makefile: Added the entry for __clear_user.o. * arch/sh/mm/__clear_user.S: File name change. As it's also used by SH-3, renamed from __clear_user_page-sh4.S, (__clear_user): New function. 2002-03-24 NIIBE Yutaka <gn...@m1...> * include/asm-sh/system.h (mm_segment_t): Move the definition to... * include/asm-sh/uaccess.h: ... here. And change it to have boolean is_user_space. (KERNEL_DS, USER_DS): New value domain: 0 or 1. (get_fs): Simplified. (set_fs): Optimized by asm. (__get_user_check): New implementation. (__get_user_1, __get_user_2, __get_user_4): New macros. (strnlen_user): Use __access_ok function. (strlen_user): Implemented as inline function. 2002-03-23 NIIBE Yutaka <gn...@m1...> * include/asm-sh/uaccess.h (__range_ok): Removed. (__access_ok, access_ok): New implementation. * include/asm-sh/thread_info.h (TIF_USERSPACE): Change to 31, so that expression can be simple (was: 18). Index: arch/sh/mm/Makefile =================================================================== RCS file: /cvsroot/linuxsh/linux/arch/sh/mm/Makefile,v retrieving revision 1.1.1.1 diff -u -3 -p -r1.1.1.1 Makefile --- arch/sh/mm/Makefile 15 Oct 2001 20:44:53 -0000 1.1.1.1 +++ arch/sh/mm/Makefile 25 Mar 2002 09:51:56 -0000 @@ -10,8 +10,8 @@ O_TARGET := mm.o obj-y := init.o fault.o extable.o clear_page.o copy_page.o -obj-$(CONFIG_CPU_SH3) += cache-sh3.o -obj-$(CONFIG_CPU_SH4) += cache-sh4.o __clear_user_page-sh4.o __copy_user_page-sh4.o ioremap.o +obj-$(CONFIG_CPU_SH3) += cache-sh3.o __clear_user.o +obj-$(CONFIG_CPU_SH4) += cache-sh4.o __clear_user.o __copy_user_page-sh4.o ioremap.o USE_STANDARD_AS_RULE := true Index: arch/sh/mm/__clear_user.S =================================================================== RCS file: arch/sh/mm/__clear_user.S diff -N arch/sh/mm/__clear_user.S --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ arch/sh/mm/__clear_user.S 25 Mar 2002 09:51:56 -0000 @@ -0,0 +1,138 @@ +/* $Id: __clear_user_page.S,v 1.1.1.1 2001/10/15 20:44:53 mrbrown Exp $ + * + * __clear_user_page implementation of SuperH + * + * Copyright (C) 2001, 2002 Niibe Yutaka & Kaz Kojima + * + */ + +#if defined(__SH4__) +/* + * __clear_user_page + * @to: P1 address (with same color) + * @orig_to: P1 address + * + * void __clear_user_page(void *to, void *orig_to) + */ + +/* + * r0 --- scratch + * r4 --- to + * r5 --- orig_to + * r6 --- to + 4096 + */ +#include <linux/linkage.h> +ENTRY(__clear_user_page) + mov.w .L4096,r0 + mov r4,r6 + add r0,r6 + mov #0,r0 + ! +1: ocbi @r5 + add #32,r5 + movca.l r0,@r4 + mov r4,r1 + add #32,r4 + mov.l r0,@-r4 + mov.l r0,@-r4 + mov.l r0,@-r4 + mov.l r0,@-r4 + mov.l r0,@-r4 + mov.l r0,@-r4 + mov.l r0,@-r4 + add #28,r4 + cmp/eq r6,r4 + bf/s 1b + ocbwb @r1 + ! + rts + nop +.L4096: .word 4096 +#endif + +ENTRY(__clear_user) + ! + mov #0, r0 + mov #0xe0, r1 ! 0xffffffe0 + ! + ! r4..r4&~32 -------- not aligned [ Area 0 ] + ! r4&~32..(r4+r5)&~32 -------- aligned [ Area 1 ] + ! (r4+r5)&~32..r4+r5 -------- not aligned [ Area 2 ] + ! + ! Clear area 0 + mov r4, r2 + and r1, r2 + cmp/eq r4, r2 + bt/s area1 + mov r4, r3 + sub r2, r3 + mov r4, r2 + ! +l0: dt r3 +0: mov.b r0, @r2 + bf/s l0 + add #1, r2 + ! + mov r4, r3 + add r5, r3 + and r1, r3 + ! + ! Clear area 1 +area1: +#if defined(__SH4__) +1: movca.l r0, @r2 +#else +1: mov.l r0, @r2 +#endif + add #4, r2 +2: mov.l r0, @r2 + add #4, r2 +3: mov.l r0, @r2 + add #4, r2 +4: mov.l r0, @r2 + add #4, r2 +5: mov.l r0, @r2 + add #4, r2 +6: mov.l r0, @r2 + add #4, r2 +7: mov.l r0, @r2 + add #4, r2 +8: mov.l r0, @r2 + add #4, r2 + cmp/hi r2, r3 + bt/s 1b + nop + ! + ! Clear area 2 + add r5, r4 + cmp/eq r4, r2 + bt/s done + sub r2, r4 +l2: dt r4 +9: mov.b r0, @r2 + bf/s l2 + add #1, r2 + ! +done: rts + nop ! return 0 as normal return + + ! return the number of bytes remained +bad_clear_user: + mov r4, r0 + mov r5, r0 + rts + sub r2, r0 + +.section __ex_table,"a" + .align 2 + .long 0b, bad_clear_user + .long 1b, bad_clear_user + .long 2b, bad_clear_user + .long 3b, bad_clear_user + .long 4b, bad_clear_user + .long 5b, bad_clear_user + .long 6b, bad_clear_user + .long 7b, bad_clear_user + .long 8b, bad_clear_user + .long 9b, bad_clear_user +.previous Index: arch/sh/mm/__clear_user_page-sh4.S =================================================================== RCS file: arch/sh/mm/__clear_user_page-sh4.S diff -N arch/sh/mm/__clear_user_page-sh4.S --- arch/sh/mm/__clear_user_page-sh4.S 15 Oct 2001 20:44:53 -0000 1.1.1.1 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,49 +0,0 @@ -/* $Id: __clear_user_page-sh4.S,v 1.1.1.1 2001/10/15 20:44:53 mrbrown Exp $ - * - * __clear_user_page implementation of SuperH - * - * Copyright (C) 2001 Niibe Yutaka & Kaz Kojima - * - */ - -/* - * __clear_user_page - * @to: P1 address (with same color) - * @orig_to: P1 address - * - * void __clear_user_page(void *to, void *orig_to) - */ - -/* - * r0 --- scratch - * r4 --- to - * r5 --- orig_to - * r6 --- to + 4096 - */ -#include <linux/linkage.h> -ENTRY(__clear_user_page) - mov r4,r6 - mov.w .L4096,r0 - add r0,r6 - mov #0,r0 - ! -1: ocbi @r5 - add #32,r5 - movca.l r0,@r4 - mov r4,r1 - add #32,r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - mov.l r0,@-r4 - add #28,r4 - cmp/eq r6,r4 - bf/s 1b - ocbwb @r1 - ! - rts - nop -.L4096: .word 4096 Index: include/asm-sh/system.h =================================================================== RCS file: /cvsroot/linuxsh/linux/include/asm-sh/system.h,v retrieving revision 1.3 diff -u -3 -p -r1.3 system.h --- include/asm-sh/system.h 22 Mar 2002 12:57:10 -0000 1.3 +++ include/asm-sh/system.h 25 Mar 2002 09:51:56 -0000 @@ -12,10 +12,6 @@ * switch_to() should switch tasks to task nr n, first */ -typedef struct { - unsigned long seg; -} mm_segment_t; - #ifdef CONFIG_SMP #error no SMP SuperH #else Index: include/asm-sh/thread_info.h =================================================================== RCS file: /cvsroot/linuxsh/linux/include/asm-sh/thread_info.h,v retrieving revision 1.3 diff -u -3 -p -r1.3 thread_info.h --- include/asm-sh/thread_info.h 22 Mar 2002 12:57:10 -0000 1.3 +++ include/asm-sh/thread_info.h 25 Mar 2002 09:51:56 -0000 @@ -94,7 +94,7 @@ static inline struct thread_info *curren #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ #define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ #define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ -#define TIF_USERSPACE 18 /* true if FS sets userspace */ +#define TIF_USERSPACE 31 /* true if FS sets userspace */ #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) Index: include/asm-sh/uaccess.h =================================================================== RCS file: /cvsroot/linuxsh/linux/include/asm-sh/uaccess.h,v retrieving revision 1.4 diff -u -3 -p -r1.4 uaccess.h --- include/asm-sh/uaccess.h 6 Mar 2002 23:04:52 -0000 1.4 +++ include/asm-sh/uaccess.h 25 Mar 2002 09:51:56 -0000 @@ -2,7 +2,7 @@ * * User space memory access functions * - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999, 2002 Niibe Yutaka * * Based on: * MIPS implementation version 1.15 by @@ -15,9 +15,21 @@ #include <linux/errno.h> #include <linux/sched.h> +/* + * NOTE: Macro/functions in this file depends on threads_info.h implementation. + * Assumes: + * TI_FLAGS == 8 + * TIF_USERSPACE == 31 + * USER_ADDR_LIMIT == 0x80000000 + */ + #define VERIFY_READ 0 #define VERIFY_WRITE 1 +typedef struct { + unsigned int is_user_space; +} mm_segment_t; + /* * The fs value determines whether argument validity checking should be * performed or not. If get_fs() == USER_DS, checking is performed, with @@ -28,50 +40,83 @@ #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) -#define KERN_ADDR_LIMIT 0xFFFFFFFF #define USER_ADDR_LIMIT 0x80000000 -#define KERNEL_DS MAKE_MM_SEG(KERN_ADDR_LIMIT) -#define USER_DS MAKE_MM_SEG(USER_ADDR_LIMIT) +#define KERNEL_DS MAKE_MM_SEG(0) +#define USER_DS MAKE_MM_SEG(1) #define get_ds() (KERNEL_DS) static inline mm_segment_t get_fs(void) { - if (test_thread_flag(TIF_USERSPACE)) - return USER_DS; - else - return KERNEL_DS; + return MAKE_MM_SEG(test_thread_flag(TIF_USERSPACE)); } static inline void set_fs(mm_segment_t s) { - if (s.seg == USER_ADDR_LIMIT) + unsigned long ti, flag; + __asm__ __volatile__( + "stc r7_bank, %0\n\t" + "mov.l @(8,%0), %1\n\t" + "shal %1\n\t" + "cmp/pl %2\n\t" + "rotcr %1\n\t" + "mov.l %1, @(8,%0)" + : "=&r" (ti), "=&r" (flag) + : "r" (s.is_user_space) + : "t"); +/**** + if (s.is_user_space) set_thread_flag(TIF_USERSPACE); else clear_thread_flag(TIF_USERSPACE); +****/ } -#define segment_eq(a,b) ((a).seg == (b).seg) - -#define __addr_ok(addr) ((unsigned long)(addr) < (get_fs().seg)) +#define segment_eq(a,b) ((a).is_user_space == (b).is_user_space) /* - * Uhhuh, this needs 33-bit arithmetic. We have a carry.. + * __access_ok: Check if address with size is OK or not. * - * sum := addr + size; carry? --> flag = true; - * if (sum >= addr_limit) flag = true; + * We do three checks: + * (1) is it user space? + * (2) addr + size --> carry? + * (3) addr + size >= 0x80000000 (USER_ADDR_LIMIT) + * + * (1) (2) (3) | RESULT + * 0 0 0 | ok + * 0 0 1 | ok + * 0 1 0 | bad + * 0 1 1 | bad + * 1 0 0 | ok + * 1 0 1 | bad + * 1 1 0 | bad + * 1 1 1 | bad */ -#define __range_ok(addr,size) ({ \ - unsigned long flag,sum; \ - __asm__("clrt; addc %3, %1; movt %0; cmp/hi %4, %1; rotcl %0" \ - :"=&r" (flag), "=r" (sum) \ - :"1" (addr), "r" ((int)(size)), "r" (get_fs().seg) \ - :"t"); \ - flag; }) +static int __access_ok(unsigned long addr, unsigned long size) +{ + unsigned long flag, tmp; -#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) -#define __access_ok(addr,size) (__range_ok(addr,size) == 0) + __asm__("stc r7_bank, %0\n\t" + "mov.l @(8,%0), %0\n\t" + "clrt\n\t" + "addc %2, %1\n\t" + "and %1, %0\n\t" + "rotcl %0\n\t" + "rotcl %0\n\t" + "and #3, %0" + : "=&z" (flag), "=r" (tmp) + : "r" (addr), "1" (size) + : "t"); + + return flag == 0; +} + +static inline int access_ok(int type, const void *p, unsigned long size) +{ + unsigned long addr = (unsigned long)p; + return __access_ok(addr, size); +} static inline int verify_area(int type, const void * addr, unsigned long size) { @@ -118,20 +163,92 @@ case 4: __get_user_asm("l"); break; \ default: __get_user_unknown(); break; \ } x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) -#define __get_user_check(x,ptr,size) ({ \ -long __gu_err; \ -__typeof__(*(ptr)) __gu_val; \ -long __gu_addr; \ -__asm__("":"=r" (__gu_val)); \ -__gu_addr = (long) (ptr); \ -__asm__("":"=r" (__gu_err)); \ -if (__access_ok(__gu_addr,size)) { \ -switch (size) { \ -case 1: __get_user_asm("b"); break; \ -case 2: __get_user_asm("w"); break; \ -case 4: __get_user_asm("l"); break; \ -default: __get_user_unknown(); break; \ -} } x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) +#define __get_user_check(x,ptr,size) \ +({ __typeof__(*(ptr)) __val; long __err; \ + switch(size) { \ + case 1: __err = __get_user_1(__val, ptr); break; \ + case 2: __err = __get_user_2(__val, ptr); break; \ + case 4: __err = __get_user_4(__val, ptr); break; \ + default: __get_user_unknown(); break; \ + } \ + (x) = __val; __err; }) + +#define __get_user_1(x,ptr) ({ \ +long __gu_err; \ +__typeof__(*(ptr)) __gu_val; \ +long __gu_addr = (long) (ptr); \ +__asm__("stc r7_bank, %1\n\t" \ + "mov.l @(8,%1), %1\n\t" \ + "and %2, %1\n\t" \ + "cmp/pz %1\n\t" \ + "bt/s 1f\n\t" \ + " mov #0, %0\n\t" \ + "0:\n" \ + "mov #-14, %0\n\t" \ + "bra 2f\n\t" \ + " mov #0, %1\n" \ + "1:\n\t" \ + "mov.b @%2, %1\n\t" \ + "extu.b %1, %1\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n\t" \ + ".long 1b, 0b\n\t" \ + ".previous" \ + : "=&r" (__gu_err), "=&r" (__gu_val) \ + : "r" (__gu_addr) \ + : "t"); \ +x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) + +#define __get_user_2(x,ptr) ({ \ +long __gu_err; \ +__typeof__(*(ptr)) __gu_val; \ +long __gu_addr = (long) (ptr); \ +__asm__("stc r7_bank, %1\n\t" \ + "mov.l @(8,%1), %1\n\t" \ + "and %2, %1\n\t" \ + "cmp/pz %1\n\t" \ + "bt/s 1f\n\t" \ + " mov #0, %0\n\t" \ + "0:\n" \ + "mov #-14, %0\n\t" \ + "bra 2f\n\t" \ + " mov #0, %1\n" \ + "1:\n\t" \ + "mov.w @%2, %1\n\t" \ + "extu.w %1, %1\n" \ + "2:\n" \ + ".section __ex_table,\"a\"\n\t" \ + ".long 1b, 0b\n\t" \ + ".previous" \ + : "=&r" (__gu_err), "=&r" (__gu_val) \ + : "r" (__gu_addr) \ + : "t"); \ +x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) + +#define __get_user_4(x,ptr) ({ \ +long __gu_err; \ +__typeof__(*(ptr)) __gu_val; \ +long __gu_addr = (long) (ptr); \ +__asm__("stc r7_bank, %1\n\t" \ + "mov.l @(8,%1), %1\n\t" \ + "and %2, %1\n\t" \ + "cmp/pz %1\n\t" \ + "bt/s 1f\n\t" \ + " mov #0, %0\n\t" \ + "0:\n" \ + "mov #-14, %0\n\t" \ + "bra 2f\n\t" \ + " mov #0, %1\n" \ + "1:\n\t" \ + "mov.l @%2, %1\n\t" \ + "2:\n" \ + ".section __ex_table,\"a\"\n\t" \ + ".long 1b, 0b\n\t" \ + ".previous" \ + : "=&r" (__gu_err), "=&r" (__gu_val) \ + : "r" (__gu_addr) \ + : "t"); \ +x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) #define __get_user_asm(insn) \ ({ \ @@ -325,39 +442,11 @@ __copy_res; }) __copy_user((void *)(to), \ (void *)(from), n) -/* XXX: Not sure it works well.. - should be such that: 4byte clear and the rest. */ -static __inline__ __kernel_size_t -__clear_user(void *addr, __kernel_size_t size) -{ - unsigned long __a; - - __asm__ __volatile__( - "9:\n\t" - "dt %0\n" - "1:\n\t" - "mov.b %4, @%1\n\t" - "bf/s 9b\n\t" - " add #1, %1\n" - "2:\n" - ".section .fixup,\"ax\"\n" - "3:\n\t" - "mov.l 4f, %1\n\t" - "jmp @%1\n\t" - " nop\n" - ".balign 4\n" - "4: .long 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .balign 4\n" - " .long 1b,3b\n" - ".previous" - : "=r" (size), "=r" (__a) - : "0" (size), "1" (addr), "r" (0) - : "memory", "t"); - - return size; -} +/* + * Clear the area and return remaining number of bytes + * (on failure. Usually it's 0.) + */ +extern __kernel_size_t __clear_user(void *addr, __kernel_size_t size); #define clear_user(addr,n) ({ \ void * __cl_addr = (addr); \ @@ -413,8 +502,6 @@ if(__access_ok(__sfu_src, __sfu_count)) __sfu_res = __strncpy_from_user((unsigned long) (dest), __sfu_src, __sfu_count); \ } __sfu_res; }) -#define strlen_user(str) strnlen_user(str, ~0UL >> 1) - /* * Return the size of a string (including the ending 0!) */ @@ -453,10 +540,18 @@ static __inline__ long __strnlen_user(co static __inline__ long strnlen_user(const char *s, long n) { - if (!__addr_ok(s)) + if (!access_ok(VERIFY_READ, s, n)) return 0; else return __strnlen_user(s, n); +} + +static __inline__ long strlen_user(const char *s) +{ + if (!access_ok(VERIFY_READ, s, 0)) + return 0; + else + return __strnlen_user(s, ~0UL >> 1); } struct exception_table_entry -- |