|
@@ -113,3 +113,161 @@ extern int __get_user_4(void *);
|
|
|
__asm__ __volatile__ ( \
|
|
|
__asmeq("%0", "r0") __asmeq("%1", "r2") \
|
|
|
__asmeq("%3", "r1") \
|
|
|
+ "bl __get_user_" #__s \
|
|
|
+ : "=&r" (__e), "=r" (__r2) \
|
|
|
+ : "0" (__p), "r" (__l) \
|
|
|
+ : __GUP_CLOBBER_##__s)
|
|
|
+
|
|
|
+#define __get_user_check(x,p) \
|
|
|
+ ({ \
|
|
|
+ unsigned long __limit = current_thread_info()->addr_limit - 1; \
|
|
|
+ register const typeof(*(p)) __user *__p asm("r0") = (p);\
|
|
|
+ register unsigned long __r2 asm("r2"); \
|
|
|
+ register unsigned long __l asm("r1") = __limit; \
|
|
|
+ register int __e asm("r0"); \
|
|
|
+ switch (sizeof(*(__p))) { \
|
|
|
+ case 1: \
|
|
|
+ __get_user_x(__r2, __p, __e, __l, 1); \
|
|
|
+ break; \
|
|
|
+ case 2: \
|
|
|
+ __get_user_x(__r2, __p, __e, __l, 2); \
|
|
|
+ break; \
|
|
|
+ case 4: \
|
|
|
+ __get_user_x(__r2, __p, __e, __l, 4); \
|
|
|
+ break; \
|
|
|
+ default: __e = __get_user_bad(); break; \
|
|
|
+ } \
|
|
|
+ x = (typeof(*(p))) __r2; \
|
|
|
+ __e; \
|
|
|
+ })
|
|
|
+
|
|
|
+#define get_user(x,p) \
|
|
|
+ ({ \
|
|
|
+ might_fault(); \
|
|
|
+ __get_user_check(x,p); \
|
|
|
+ })
|
|
|
+
|
|
|
+extern int __put_user_1(void *, unsigned int);
|
|
|
+extern int __put_user_2(void *, unsigned int);
|
|
|
+extern int __put_user_4(void *, unsigned int);
|
|
|
+extern int __put_user_8(void *, unsigned long long);
|
|
|
+
|
|
|
+#define __put_user_x(__r2,__p,__e,__l,__s) \
|
|
|
+ __asm__ __volatile__ ( \
|
|
|
+ __asmeq("%0", "r0") __asmeq("%2", "r2") \
|
|
|
+ __asmeq("%3", "r1") \
|
|
|
+ "bl __put_user_" #__s \
|
|
|
+ : "=&r" (__e) \
|
|
|
+ : "0" (__p), "r" (__r2), "r" (__l) \
|
|
|
+ : "ip", "lr", "cc")
|
|
|
+
|
|
|
+#define __put_user_check(x,p) \
|
|
|
+ ({ \
|
|
|
+ unsigned long __limit = current_thread_info()->addr_limit - 1; \
|
|
|
+ register const typeof(*(p)) __r2 asm("r2") = (x); \
|
|
|
+ register const typeof(*(p)) __user *__p asm("r0") = (p);\
|
|
|
+ register unsigned long __l asm("r1") = __limit; \
|
|
|
+ register int __e asm("r0"); \
|
|
|
+ switch (sizeof(*(__p))) { \
|
|
|
+ case 1: \
|
|
|
+ __put_user_x(__r2, __p, __e, __l, 1); \
|
|
|
+ break; \
|
|
|
+ case 2: \
|
|
|
+ __put_user_x(__r2, __p, __e, __l, 2); \
|
|
|
+ break; \
|
|
|
+ case 4: \
|
|
|
+ __put_user_x(__r2, __p, __e, __l, 4); \
|
|
|
+ break; \
|
|
|
+ case 8: \
|
|
|
+ __put_user_x(__r2, __p, __e, __l, 8); \
|
|
|
+ break; \
|
|
|
+ default: __e = __put_user_bad(); break; \
|
|
|
+ } \
|
|
|
+ __e; \
|
|
|
+ })
|
|
|
+
|
|
|
+#define put_user(x,p) \
|
|
|
+ ({ \
|
|
|
+ might_fault(); \
|
|
|
+ __put_user_check(x,p); \
|
|
|
+ })
|
|
|
+
|
|
|
+#else /* CONFIG_MMU */
|
|
|
+
|
|
|
+/*
|
|
|
+ * uClinux has only one addr space, so has simplified address limits.
|
|
|
+ */
|
|
|
+#define USER_DS KERNEL_DS
|
|
|
+
|
|
|
+#define segment_eq(a,b) (1)
|
|
|
+#define __addr_ok(addr) ((void)(addr),1)
|
|
|
+#define __range_ok(addr,size) ((void)(addr),0)
|
|
|
+#define get_fs() (KERNEL_DS)
|
|
|
+
|
|
|
+static inline void set_fs(mm_segment_t fs)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+#define get_user(x,p) __get_user(x,p)
|
|
|
+#define put_user(x,p) __put_user(x,p)
|
|
|
+
|
|
|
+#endif /* CONFIG_MMU */
|
|
|
+
|
|
|
+#define access_ok(type,addr,size) (__range_ok(addr,size) == 0)
|
|
|
+
|
|
|
+#define user_addr_max() \
|
|
|
+ (segment_eq(get_fs(), USER_DS) ? TASK_SIZE : ~0UL)
|
|
|
+
|
|
|
+/*
|
|
|
+ * The "__xxx" versions of the user access functions do not verify the
|
|
|
+ * address space - it must have been done previously with a separate
|
|
|
+ * "access_ok()" call.
|
|
|
+ *
|
|
|
+ * The "xxx_error" versions set the third argument to EFAULT if an
|
|
|
+ * error occurs, and leave it unchanged on success. Note that these
|
|
|
+ * versions are void (ie, don't return a value as such).
|
|
|
+ */
|
|
|
+#define __get_user(x,ptr) \
|
|
|
+({ \
|
|
|
+ long __gu_err = 0; \
|
|
|
+ __get_user_err((x),(ptr),__gu_err); \
|
|
|
+ __gu_err; \
|
|
|
+})
|
|
|
+
|
|
|
+#define __get_user_error(x,ptr,err) \
|
|
|
+({ \
|
|
|
+ __get_user_err((x),(ptr),err); \
|
|
|
+ (void) 0; \
|
|
|
+})
|
|
|
+
|
|
|
+#define __get_user_err(x,ptr,err) \
|
|
|
+do { \
|
|
|
+ unsigned long __gu_addr = (unsigned long)(ptr); \
|
|
|
+ unsigned long __gu_val; \
|
|
|
+ __chk_user_ptr(ptr); \
|
|
|
+ might_fault(); \
|
|
|
+ switch (sizeof(*(ptr))) { \
|
|
|
+ case 1: __get_user_asm_byte(__gu_val,__gu_addr,err); break; \
|
|
|
+ case 2: __get_user_asm_half(__gu_val,__gu_addr,err); break; \
|
|
|
+ case 4: __get_user_asm_word(__gu_val,__gu_addr,err); break; \
|
|
|
+ default: (__gu_val) = __get_user_bad(); \
|
|
|
+ } \
|
|
|
+ (x) = (__typeof__(*(ptr)))__gu_val; \
|
|
|
+} while (0)
|
|
|
+
|
|
|
+#define __get_user_asm_byte(x,addr,err) \
|
|
|
+ __asm__ __volatile__( \
|
|
|
+ "1: " TUSER(ldrb) " %1,[%2],#0\n" \
|
|
|
+ "2:\n" \
|
|
|
+ " .pushsection .fixup,\"ax\"\n" \
|
|
|
+ " .align 2\n" \
|
|
|
+ "3: mov %0, %3\n" \
|
|
|
+ " mov %1, #0\n" \
|
|
|
+ " b 2b\n" \
|
|
|
+ " .popsection\n" \
|
|
|
+ " .pushsection __ex_table,\"a\"\n" \
|
|
|
+ " .align 3\n" \
|
|
|
+ " .long 1b, 3b\n" \
|
|
|
+ " .popsection" \
|
|
|
+ : "+r" (err), "=&r" (x) \
|
|
|
+ : "r" (addr), "i" (-EFAULT) \
|