|
@@ -22,101 +22,73 @@
|
|
|
#include <asm/mmu.h>
|
|
|
#include <asm/page.h>
|
|
|
#include <asm/pgtable.h>
|
|
|
-#include <asm/segment.h>
|
|
|
#include <linux/string.h>
|
|
|
|
|
|
#define VERIFY_READ 0
|
|
|
#define VERIFY_WRITE 1
|
|
|
|
|
|
-#define __clear_user(addr, n) (memset((void *)(addr), 0, (n)), 0)
|
|
|
-
|
|
|
-#ifndef CONFIG_MMU
|
|
|
-
|
|
|
-extern int ___range_ok(unsigned long addr, unsigned long size);
|
|
|
-
|
|
|
-#define __range_ok(addr, size) \
|
|
|
- ___range_ok((unsigned long)(addr), (unsigned long)(size))
|
|
|
-
|
|
|
-#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0)
|
|
|
-#define __access_ok(add, size) (__range_ok((addr), (size)) == 0)
|
|
|
-
|
|
|
-/* Undefined function to trigger linker error */
|
|
|
-extern int bad_user_access_length(void);
|
|
|
-
|
|
|
-/* FIXME this is function for optimalization -> memcpy */
|
|
|
-#define __get_user(var, ptr) \
|
|
|
-({ \
|
|
|
- int __gu_err = 0; \
|
|
|
- switch (sizeof(*(ptr))) { \
|
|
|
- case 1: \
|
|
|
- case 2: \
|
|
|
- case 4: \
|
|
|
- (var) = *(ptr); \
|
|
|
- break; \
|
|
|
- case 8: \
|
|
|
- memcpy((void *) &(var), (ptr), 8); \
|
|
|
- break; \
|
|
|
- default: \
|
|
|
- (var) = 0; \
|
|
|
- __gu_err = __get_user_bad(); \
|
|
|
- break; \
|
|
|
- } \
|
|
|
- __gu_err; \
|
|
|
-})
|
|
|
+/*
|
|
|
+ * On Microblaze the fs value is actually the top of the corresponding
|
|
|
+ * address space.
|
|
|
+ *
|
|
|
+ * The fs value determines whether argument validity checking should be
|
|
|
+ * performed or not. If get_fs() == USER_DS, checking is performed, with
|
|
|
+ * get_fs() == KERNEL_DS, checking is bypassed.
|
|
|
+ *
|
|
|
+ * For historical reasons, these macros are grossly misnamed.
|
|
|
+ *
|
|
|
+ * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal.
|
|
|
+ */
|
|
|
+# define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
|
|
|
|
|
|
-#define __get_user_bad() (bad_user_access_length(), (-EFAULT))
|
|
|
+# ifndef CONFIG_MMU
|
|
|
+# define KERNEL_DS MAKE_MM_SEG(0)
|
|
|
+# define USER_DS KERNEL_DS
|
|
|
+# else
|
|
|
+# define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF)
|
|
|
+# define USER_DS MAKE_MM_SEG(TASK_SIZE - 1)
|
|
|
+# endif
|
|
|
|
|
|
-/* FIXME is not there defined __pu_val */
|
|
|
-#define __put_user(var, ptr) \
|
|
|
-({ \
|
|
|
- int __pu_err = 0; \
|
|
|
- switch (sizeof(*(ptr))) { \
|
|
|
- case 1: \
|
|
|
- case 2: \
|
|
|
- case 4: \
|
|
|
- *(ptr) = (var); \
|
|
|
- break; \
|
|
|
- case 8: { \
|
|
|
- typeof(*(ptr)) __pu_val = (var); \
|
|
|
- memcpy(ptr, &__pu_val, sizeof(__pu_val)); \
|
|
|
- } \
|
|
|
- break; \
|
|
|
- default: \
|
|
|
- __pu_err = __put_user_bad(); \
|
|
|
- break; \
|
|
|
- } \
|
|
|
- __pu_err; \
|
|
|
-})
|
|
|
+# define get_ds() (KERNEL_DS)
|
|
|
+# define get_fs() (current_thread_info()->addr_limit)
|
|
|
+# define set_fs(val) (current_thread_info()->addr_limit = (val))
|
|
|
|
|
|
-#define __put_user_bad() (bad_user_access_length(), (-EFAULT))
|
|
|
+# define segment_eq(a, b) ((a).seg == (b).seg)
|
|
|
|
|
|
-#define put_user(x, ptr) __put_user((x), (ptr))
|
|
|
-#define get_user(x, ptr) __get_user((x), (ptr))
|
|
|
+/*
|
|
|
+ * The exception table consists of pairs of addresses: the first is the
|
|
|
+ * address of an instruction that is allowed to fault, and the second is
|
|
|
+ * the address at which the program should continue. No registers are
|
|
|
+ * modified, so it is entirely up to the continuation code to figure out
|
|
|
+ * what to do.
|
|
|
+ *
|
|
|
+ * All the routines below use bits of fixup code that are out of line
|
|
|
+ * with the main instruction path. This means when everything is well,
|
|
|
+ * we don't even have to jump over them. Further, they do not intrude
|
|
|
+ * on our cache or tlb entries.
|
|
|
+ */
|
|
|
+struct exception_table_entry {
|
|
|
+ unsigned long insn, fixup;
|
|
|
+};
|
|
|
|
|
|
-#define copy_to_user(to, from, n) (memcpy((to), (from), (n)), 0)
|
|
|
-#define copy_from_user(to, from, n) (memcpy((to), (from), (n)), 0)
|
|
|
+/* Returns 0 if exception not found and fixup otherwise. */
|
|
|
+extern unsigned long search_exception_table(unsigned long);
|
|
|
|
|
|
-#define __copy_to_user(to, from, n) (copy_to_user((to), (from), (n)))
|
|
|
-#define __copy_from_user(to, from, n) (copy_from_user((to), (from), (n)))
|
|
|
-#define __copy_to_user_inatomic(to, from, n) \
|
|
|
- (__copy_to_user((to), (from), (n)))
|
|
|
-#define __copy_from_user_inatomic(to, from, n) \
|
|
|
- (__copy_from_user((to), (from), (n)))
|
|
|
+#ifndef CONFIG_MMU
|
|
|
|
|
|
-static inline unsigned long clear_user(void *addr, unsigned long size)
|
|
|
+/* Check against bounds of physical memory */
|
|
|
+static inline int ___range_ok(unsigned long addr, unsigned long size)
|
|
|
{
|
|
|
- if (access_ok(VERIFY_WRITE, addr, size))
|
|
|
- size = __clear_user(addr, size);
|
|
|
- return size;
|
|
|
+ return ((addr < memory_start) ||
|
|
|
+ ((addr + size) > memory_end));
|
|
|
}
|
|
|
|
|
|
-/* Returns 0 if exception not found and fixup otherwise. */
|
|
|
-extern unsigned long search_exception_table(unsigned long);
|
|
|
+#define __range_ok(addr, size) \
|
|
|
+ ___range_ok((unsigned long)(addr), (unsigned long)(size))
|
|
|
|
|
|
-extern long strncpy_from_user(char *dst, const char *src, long count);
|
|
|
-extern long strnlen_user(const char *src, long count);
|
|
|
+#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0)
|
|
|
|
|
|
-#else /* CONFIG_MMU */
|
|
|
+#else
|
|
|
|
|
|
/*
|
|
|
* Address is valid if:
|
|
@@ -129,24 +101,88 @@ extern long strnlen_user(const char *src, long count);
|
|
|
/* || printk("access_ok failed for %s at 0x%08lx (size %d), seg 0x%08x\n",
|
|
|
type?"WRITE":"READ",addr,size,get_fs().seg)) */
|
|
|
|
|
|
-/*
|
|
|
- * All the __XXX versions macros/functions below do not perform
|
|
|
- * access checking. It is assumed that the necessary checks have been
|
|
|
- * already performed before the finction (macro) is called.
|
|
|
- */
|
|
|
+#endif
|
|
|
|
|
|
-#define get_user(x, ptr) \
|
|
|
-({ \
|
|
|
- access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) \
|
|
|
- ? __get_user((x), (ptr)) : -EFAULT; \
|
|
|
-})
|
|
|
+#ifdef CONFIG_MMU
|
|
|
+# define __FIXUP_SECTION ".section .fixup,\"ax\"\n"
|
|
|
+# define __EX_TABLE_SECTION ".section __ex_table,\"a\"\n"
|
|
|
+#else
|
|
|
+# define __FIXUP_SECTION ".section .discard,\"ax\"\n"
|
|
|
+# define __EX_TABLE_SECTION ".section .discard,\"a\"\n"
|
|
|
+#endif
|
|
|
|
|
|
-#define put_user(x, ptr) \
|
|
|
-({ \
|
|
|
- access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) \
|
|
|
- ? __put_user((x), (ptr)) : -EFAULT; \
|
|
|
+extern unsigned long __copy_tofrom_user(void __user *to,
|
|
|
+ const void __user *from, unsigned long size);
|
|
|
+
|
|
|
+/* Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. */
|
|
|
+static inline unsigned long __must_check __clear_user(void __user *to,
|
|
|
+ unsigned long n)
|
|
|
+{
|
|
|
+ /* normal memset with two words to __ex_table */
|
|
|
+ __asm__ __volatile__ ( \
|
|
|
+ "1: sb r0, %2, r0;" \
|
|
|
+ " addik %0, %0, -1;" \
|
|
|
+ " bneid %0, 1b;" \
|
|
|
+ " addik %2, %2, 1;" \
|
|
|
+ "2: " \
|
|
|
+ __EX_TABLE_SECTION \
|
|
|
+ ".word 1b,2b;" \
|
|
|
+ ".previous;" \
|
|
|
+ : "=r"(n) \
|
|
|
+ : "0"(n), "r"(to)
|
|
|
+ );
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+static inline unsigned long __must_check clear_user(void __user *to,
|
|
|
+ unsigned long n)
|
|
|
+{
|
|
|
+ might_sleep();
|
|
|
+ if (unlikely(!access_ok(VERIFY_WRITE, to, n)))
|
|
|
+ return n;
|
|
|
+
|
|
|
+ return __clear_user(to, n);
|
|
|
+}
|
|
|
+
|
|
|
+/* put_user and get_user macros */
|
|
|
+extern long __user_bad(void);
|
|
|
+
|
|
|
+#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \
|
|
|
+({ \
|
|
|
+ __asm__ __volatile__ ( \
|
|
|
+ "1:" insn " %1, %2, r0;" \
|
|
|
+ " addk %0, r0, r0;" \
|
|
|
+ "2: " \
|
|
|
+ __FIXUP_SECTION \
|
|
|
+ "3: brid 2b;" \
|
|
|
+ " addik %0, r0, %3;" \
|
|
|
+ ".previous;" \
|
|
|
+ __EX_TABLE_SECTION \
|
|
|
+ ".word 1b,3b;" \
|
|
|
+ ".previous;" \
|
|
|
+ : "=&r"(__gu_err), "=r"(__gu_val) \
|
|
|
+ : "r"(__gu_ptr), "i"(-EFAULT) \
|
|
|
+ ); \
|
|
|
})
|
|
|
|
|
|
+/**
|
|
|
+ * get_user: - Get a simple variable from user space.
|
|
|
+ * @x: Variable to store result.
|
|
|
+ * @ptr: Source address, in user space.
|
|
|
+ *
|
|
|
+ * Context: User context only. This function may sleep.
|
|
|
+ *
|
|
|
+ * This macro copies a single simple variable from user space to kernel
|
|
|
+ * space. It supports simple types like char and int, but not larger
|
|
|
+ * data types like structures or arrays.
|
|
|
+ *
|
|
|
+ * @ptr must have pointer-to-simple-variable type, and the result of
|
|
|
+ * dereferencing @ptr must be assignable to @x without a cast.
|
|
|
+ *
|
|
|
+ * Returns zero on success, or -EFAULT on error.
|
|
|
+ * On error, the variable @x is set to zero.
|
|
|
+ */
|
|
|
+
|
|
|
#define __get_user(x, ptr) \
|
|
|
({ \
|
|
|
unsigned long __gu_val; \
|
|
@@ -163,30 +199,74 @@ extern long strnlen_user(const char *src, long count);
|
|
|
__get_user_asm("lw", (ptr), __gu_val, __gu_err); \
|
|
|
break; \
|
|
|
default: \
|
|
|
- __gu_val = 0; __gu_err = -EINVAL; \
|
|
|
+ /* __gu_val = 0; __gu_err = -EINVAL;*/ __gu_err = __user_bad();\
|
|
|
} \
|
|
|
x = (__typeof__(*(ptr))) __gu_val; \
|
|
|
__gu_err; \
|
|
|
})
|
|
|
|
|
|
-#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \
|
|
|
+
|
|
|
+#define get_user(x, ptr) \
|
|
|
({ \
|
|
|
- __asm__ __volatile__ ( \
|
|
|
- "1:" insn " %1, %2, r0; \
|
|
|
- addk %0, r0, r0; \
|
|
|
- 2: \
|
|
|
- .section .fixup,\"ax\"; \
|
|
|
- 3: brid 2b; \
|
|
|
- addik %0, r0, %3; \
|
|
|
- .previous; \
|
|
|
- .section __ex_table,\"a\"; \
|
|
|
- .word 1b,3b; \
|
|
|
- .previous;" \
|
|
|
- : "=r"(__gu_err), "=r"(__gu_val) \
|
|
|
- : "r"(__gu_ptr), "i"(-EFAULT) \
|
|
|
- ); \
|
|
|
+ access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) \
|
|
|
+ ? __get_user((x), (ptr)) : -EFAULT; \
|
|
|
+})
|
|
|
+
|
|
|
+#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \
|
|
|
+({ \
|
|
|
+ __asm__ __volatile__ ( \
|
|
|
+ "1:" insn " %1, %2, r0;" \
|
|
|
+ " addk %0, r0, r0;" \
|
|
|
+ "2: " \
|
|
|
+ __FIXUP_SECTION \
|
|
|
+ "3: brid 2b;" \
|
|
|
+ " addik %0, r0, %3;" \
|
|
|
+ ".previous;" \
|
|
|
+ __EX_TABLE_SECTION \
|
|
|
+ ".word 1b,3b;" \
|
|
|
+ ".previous;" \
|
|
|
+ : "=&r"(__gu_err) \
|
|
|
+ : "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT) \
|
|
|
+ ); \
|
|
|
})
|
|
|
|
|
|
+#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err) \
|
|
|
+({ \
|
|
|
+ __asm__ __volatile__ (" lwi %0, %1, 0;" \
|
|
|
+ "1: swi %0, %2, 0;" \
|
|
|
+ " lwi %0, %1, 4;" \
|
|
|
+ "2: swi %0, %2, 4;" \
|
|
|
+ " addk %0, r0, r0;" \
|
|
|
+ "3: " \
|
|
|
+ __FIXUP_SECTION \
|
|
|
+ "4: brid 3b;" \
|
|
|
+ " addik %0, r0, %3;" \
|
|
|
+ ".previous;" \
|
|
|
+ __EX_TABLE_SECTION \
|
|
|
+ ".word 1b,4b,2b,4b;" \
|
|
|
+ ".previous;" \
|
|
|
+ : "=&r"(__gu_err) \
|
|
|
+ : "r"(&__gu_val), "r"(__gu_ptr), "i"(-EFAULT) \
|
|
|
+ ); \
|
|
|
+})
|
|
|
+
|
|
|
+/**
|
|
|
+ * put_user: - Write a simple value into user space.
|
|
|
+ * @x: Value to copy to user space.
|
|
|
+ * @ptr: Destination address, in user space.
|
|
|
+ *
|
|
|
+ * Context: User context only. This function may sleep.
|
|
|
+ *
|
|
|
+ * This macro copies a single simple value from kernel space to user
|
|
|
+ * space. It supports simple types like char and int, but not larger
|
|
|
+ * data types like structures or arrays.
|
|
|
+ *
|
|
|
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
|
|
|
+ * to the result of dereferencing @ptr.
|
|
|
+ *
|
|
|
+ * Returns zero on success, or -EFAULT on error.
|
|
|
+ */
|
|
|
+
|
|
|
#define __put_user(x, ptr) \
|
|
|
({ \
|
|
|
__typeof__(*(ptr)) volatile __gu_val = (x); \
|
|
@@ -195,7 +275,7 @@ extern long strnlen_user(const char *src, long count);
|
|
|
case 1: \
|
|
|
__put_user_asm("sb", (ptr), __gu_val, __gu_err); \
|
|
|
break; \
|
|
|
- case 2: \
|
|
|
+ case 2: \
|
|
|
__put_user_asm("sh", (ptr), __gu_val, __gu_err); \
|
|
|
break; \
|
|
|
case 4: \
|
|
@@ -205,121 +285,82 @@ extern long strnlen_user(const char *src, long count);
|
|
|
__put_user_asm_8((ptr), __gu_val, __gu_err); \
|
|
|
break; \
|
|
|
default: \
|
|
|
- __gu_err = -EINVAL; \
|
|
|
+ /*__gu_err = -EINVAL;*/ __gu_err = __user_bad(); \
|
|
|
} \
|
|
|
__gu_err; \
|
|
|
})
|
|
|
|
|
|
-#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err) \
|
|
|
-({ \
|
|
|
-__asm__ __volatile__ (" lwi %0, %1, 0; \
|
|
|
- 1: swi %0, %2, 0; \
|
|
|
- lwi %0, %1, 4; \
|
|
|
- 2: swi %0, %2, 4; \
|
|
|
- addk %0,r0,r0; \
|
|
|
- 3: \
|
|
|
- .section .fixup,\"ax\"; \
|
|
|
- 4: brid 3b; \
|
|
|
- addik %0, r0, %3; \
|
|
|
- .previous; \
|
|
|
- .section __ex_table,\"a\"; \
|
|
|
- .word 1b,4b,2b,4b; \
|
|
|
- .previous;" \
|
|
|
- : "=&r"(__gu_err) \
|
|
|
- : "r"(&__gu_val), \
|
|
|
- "r"(__gu_ptr), "i"(-EFAULT) \
|
|
|
- ); \
|
|
|
-})
|
|
|
+#ifndef CONFIG_MMU
|
|
|
|
|
|
-#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \
|
|
|
-({ \
|
|
|
- __asm__ __volatile__ ( \
|
|
|
- "1:" insn " %1, %2, r0; \
|
|
|
- addk %0, r0, r0; \
|
|
|
- 2: \
|
|
|
- .section .fixup,\"ax\"; \
|
|
|
- 3: brid 2b; \
|
|
|
- addik %0, r0, %3; \
|
|
|
- .previous; \
|
|
|
- .section __ex_table,\"a\"; \
|
|
|
- .word 1b,3b; \
|
|
|
- .previous;" \
|
|
|
- : "=r"(__gu_err) \
|
|
|
- : "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT) \
|
|
|
- ); \
|
|
|
-})
|
|
|
+#define put_user(x, ptr) __put_user((x), (ptr))
|
|
|
|
|
|
-/*
|
|
|
- * Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail.
|
|
|
- */
|
|
|
-static inline int clear_user(char *to, int size)
|
|
|
-{
|
|
|
- if (size && access_ok(VERIFY_WRITE, to, size)) {
|
|
|
- __asm__ __volatile__ (" \
|
|
|
- 1: \
|
|
|
- sb r0, %2, r0; \
|
|
|
- addik %0, %0, -1; \
|
|
|
- bneid %0, 1b; \
|
|
|
- addik %2, %2, 1; \
|
|
|
- 2: \
|
|
|
- .section __ex_table,\"a\"; \
|
|
|
- .word 1b,2b; \
|
|
|
- .section .text;" \
|
|
|
- : "=r"(size) \
|
|
|
- : "0"(size), "r"(to)
|
|
|
- );
|
|
|
- }
|
|
|
- return size;
|
|
|
-}
|
|
|
+#else /* CONFIG_MMU */
|
|
|
|
|
|
-#define __copy_from_user(to, from, n) copy_from_user((to), (from), (n))
|
|
|
+#define put_user(x, ptr) \
|
|
|
+({ \
|
|
|
+ access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) \
|
|
|
+ ? __put_user((x), (ptr)) : -EFAULT; \
|
|
|
+})
|
|
|
+#endif /* CONFIG_MMU */
|
|
|
+
|
|
|
+/* copy_to_from_user */
|
|
|
+#define __copy_from_user(to, from, n) \
|
|
|
+ __copy_tofrom_user((__force void __user *)(to), \
|
|
|
+ (void __user *)(from), (n))
|
|
|
#define __copy_from_user_inatomic(to, from, n) \
|
|
|
copy_from_user((to), (from), (n))
|
|
|
|
|
|
-#define copy_to_user(to, from, n) \
|
|
|
- (access_ok(VERIFY_WRITE, (to), (n)) ? \
|
|
|
- __copy_tofrom_user((void __user *)(to), \
|
|
|
- (__force const void __user *)(from), (n)) \
|
|
|
- : -EFAULT)
|
|
|
+static inline long copy_from_user(void *to,
|
|
|
+ const void __user *from, unsigned long n)
|
|
|
+{
|
|
|
+ might_sleep();
|
|
|
+ if (access_ok(VERIFY_READ, from, n))
|
|
|
+ return __copy_from_user(to, from, n);
|
|
|
+ return n;
|
|
|
+}
|
|
|
|
|
|
-#define __copy_to_user(to, from, n) copy_to_user((to), (from), (n))
|
|
|
+#define __copy_to_user(to, from, n) \
|
|
|
+ __copy_tofrom_user((void __user *)(to), \
|
|
|
+ (__force const void __user *)(from), (n))
|
|
|
#define __copy_to_user_inatomic(to, from, n) copy_to_user((to), (from), (n))
|
|
|
|
|
|
-#define copy_from_user(to, from, n) \
|
|
|
- (access_ok(VERIFY_READ, (from), (n)) ? \
|
|
|
- __copy_tofrom_user((__force void __user *)(to), \
|
|
|
- (void __user *)(from), (n)) \
|
|
|
- : -EFAULT)
|
|
|
+static inline long copy_to_user(void __user *to,
|
|
|
+ const void *from, unsigned long n)
|
|
|
+{
|
|
|
+ might_sleep();
|
|
|
+ if (access_ok(VERIFY_WRITE, to, n))
|
|
|
+ return __copy_to_user(to, from, n);
|
|
|
+ return n;
|
|
|
+}
|
|
|
|
|
|
+/*
|
|
|
+ * Copy a null terminated string from userspace.
|
|
|
+ */
|
|
|
extern int __strncpy_user(char *to, const char __user *from, int len);
|
|
|
-extern int __strnlen_user(const char __user *sstr, int len);
|
|
|
|
|
|
-#define strncpy_from_user(to, from, len) \
|
|
|
- (access_ok(VERIFY_READ, from, 1) ? \
|
|
|
- __strncpy_user(to, from, len) : -EFAULT)
|
|
|
-#define strnlen_user(str, len) \
|
|
|
- (access_ok(VERIFY_READ, str, 1) ? __strnlen_user(str, len) : 0)
|
|
|
+#define __strncpy_from_user __strncpy_user
|
|
|
|
|
|
-#endif /* CONFIG_MMU */
|
|
|
-
|
|
|
-extern unsigned long __copy_tofrom_user(void __user *to,
|
|
|
- const void __user *from, unsigned long size);
|
|
|
+static inline long
|
|
|
+strncpy_from_user(char *dst, const char __user *src, long count)
|
|
|
+{
|
|
|
+ if (!access_ok(VERIFY_READ, src, 1))
|
|
|
+ return -EFAULT;
|
|
|
+ return __strncpy_from_user(dst, src, count);
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
- * The exception table consists of pairs of addresses: the first is the
|
|
|
- * address of an instruction that is allowed to fault, and the second is
|
|
|
- * the address at which the program should continue. No registers are
|
|
|
- * modified, so it is entirely up to the continuation code to figure out
|
|
|
- * what to do.
|
|
|
+ * Return the size of a string (including the ending 0)
|
|
|
*
|
|
|
- * All the routines below use bits of fixup code that are out of line
|
|
|
- * with the main instruction path. This means when everything is well,
|
|
|
- * we don't even have to jump over them. Further, they do not intrude
|
|
|
- * on our cache or tlb entries.
|
|
|
+ * Return 0 on exception, a value greater than N if too long
|
|
|
*/
|
|
|
-struct exception_table_entry {
|
|
|
- unsigned long insn, fixup;
|
|
|
-};
|
|
|
+extern int __strnlen_user(const char __user *sstr, int len);
|
|
|
+
|
|
|
+static inline long strnlen_user(const char __user *src, long n)
|
|
|
+{
|
|
|
+ if (!access_ok(VERIFY_READ, src, 1))
|
|
|
+ return 0;
|
|
|
+ return __strnlen_user(src, n);
|
|
|
+}
|
|
|
|
|
|
#endif /* __ASSEMBLY__ */
|
|
|
#endif /* __KERNEL__ */
|