|
@@ -206,3 +206,196 @@ extern void __get_user_unknown (void);
|
|
|
|
|
|
extern void __put_user_unknown (void);
|
|
|
|
|
|
+/*
|
|
|
+ * Evaluating arguments X, PTR, SIZE, and SEGMENT may involve subroutine-calls, which
|
|
|
+ * could clobber r8 (among others). Thus, be careful not to evaluate them while using r8.
|
|
|
+ */
|
|
|
+#define __do_put_user(check, x, ptr, size, segment) \
|
|
|
+({ \
|
|
|
+ __typeof__ (x) __pu_x = (x); \
|
|
|
+ __typeof__ (*(ptr)) __user *__pu_ptr = (ptr); \
|
|
|
+ __typeof__ (size) __pu_size = (size); \
|
|
|
+ long __pu_err = -EFAULT; \
|
|
|
+ \
|
|
|
+ if (!check || __access_ok(__pu_ptr, __pu_size, segment)) \
|
|
|
+ switch (__pu_size) { \
|
|
|
+ case 1: __put_user_size(__pu_x, __pu_ptr, 1, __pu_err); break; \
|
|
|
+ case 2: __put_user_size(__pu_x, __pu_ptr, 2, __pu_err); break; \
|
|
|
+ case 4: __put_user_size(__pu_x, __pu_ptr, 4, __pu_err); break; \
|
|
|
+ case 8: __put_user_size(__pu_x, __pu_ptr, 8, __pu_err); break; \
|
|
|
+ default: __put_user_unknown(); break; \
|
|
|
+ } \
|
|
|
+ __pu_err; \
|
|
|
+})
|
|
|
+
|
|
|
+#define __put_user_nocheck(x, ptr, size) __do_put_user(0, x, ptr, size, KERNEL_DS)
|
|
|
+#define __put_user_check(x, ptr, size, segment) __do_put_user(1, x, ptr, size, segment)
|
|
|
+
|
|
|
+/*
|
|
|
+ * Complex access routines
|
|
|
+ */
|
|
|
+extern unsigned long __must_check __copy_user (void __user *to, const void __user *from,
|
|
|
+ unsigned long count);
|
|
|
+
|
|
|
+static inline unsigned long
|
|
|
+__copy_to_user (void __user *to, const void *from, unsigned long count)
|
|
|
+{
|
|
|
+ return __copy_user(to, (__force void __user *) from, count);
|
|
|
+}
|
|
|
+
|
|
|
+static inline unsigned long
|
|
|
+__copy_from_user (void *to, const void __user *from, unsigned long count)
|
|
|
+{
|
|
|
+ return __copy_user((__force void __user *) to, from, count);
|
|
|
+}
|
|
|
+
|
|
|
+#define __copy_to_user_inatomic __copy_to_user
|
|
|
+#define __copy_from_user_inatomic __copy_from_user
|
|
|
+#define copy_to_user(to, from, n) \
|
|
|
+({ \
|
|
|
+ void __user *__cu_to = (to); \
|
|
|
+ const void *__cu_from = (from); \
|
|
|
+ long __cu_len = (n); \
|
|
|
+ \
|
|
|
+ if (__access_ok(__cu_to, __cu_len, get_fs())) \
|
|
|
+ __cu_len = __copy_user(__cu_to, (__force void __user *) __cu_from, __cu_len); \
|
|
|
+ __cu_len; \
|
|
|
+})
|
|
|
+
|
|
|
+#define copy_from_user(to, from, n) \
|
|
|
+({ \
|
|
|
+ void *__cu_to = (to); \
|
|
|
+ const void __user *__cu_from = (from); \
|
|
|
+ long __cu_len = (n); \
|
|
|
+ \
|
|
|
+ __chk_user_ptr(__cu_from); \
|
|
|
+ if (__access_ok(__cu_from, __cu_len, get_fs())) \
|
|
|
+ __cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len); \
|
|
|
+ __cu_len; \
|
|
|
+})
|
|
|
+
|
|
|
+#define __copy_in_user(to, from, size) __copy_user((to), (from), (size))
|
|
|
+
|
|
|
+static inline unsigned long
|
|
|
+copy_in_user (void __user *to, const void __user *from, unsigned long n)
|
|
|
+{
|
|
|
+ if (likely(access_ok(VERIFY_READ, from, n) && access_ok(VERIFY_WRITE, to, n)))
|
|
|
+ n = __copy_user(to, from, n);
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+extern unsigned long __do_clear_user (void __user *, unsigned long);
|
|
|
+
|
|
|
+#define __clear_user(to, n) __do_clear_user(to, n)
|
|
|
+
|
|
|
+#define clear_user(to, n) \
|
|
|
+({ \
|
|
|
+ unsigned long __cu_len = (n); \
|
|
|
+ if (__access_ok(to, __cu_len, get_fs())) \
|
|
|
+ __cu_len = __do_clear_user(to, __cu_len); \
|
|
|
+ __cu_len; \
|
|
|
+})
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Returns: -EFAULT if exception before terminator, N if the entire buffer filled, else
|
|
|
+ * strlen.
|
|
|
+ */
|
|
|
+extern long __must_check __strncpy_from_user (char *to, const char __user *from, long to_len);
|
|
|
+
|
|
|
+#define strncpy_from_user(to, from, n) \
|
|
|
+({ \
|
|
|
+ const char __user * __sfu_from = (from); \
|
|
|
+ long __sfu_ret = -EFAULT; \
|
|
|
+ if (__access_ok(__sfu_from, 0, get_fs())) \
|
|
|
+ __sfu_ret = __strncpy_from_user((to), __sfu_from, (n)); \
|
|
|
+ __sfu_ret; \
|
|
|
+})
|
|
|
+
|
|
|
+/* Returns: 0 if bad, string length+1 (memory size) of string if ok */
|
|
|
+extern unsigned long __strlen_user (const char __user *);
|
|
|
+
|
|
|
+#define strlen_user(str) \
|
|
|
+({ \
|
|
|
+ const char __user *__su_str = (str); \
|
|
|
+ unsigned long __su_ret = 0; \
|
|
|
+ if (__access_ok(__su_str, 0, get_fs())) \
|
|
|
+ __su_ret = __strlen_user(__su_str); \
|
|
|
+ __su_ret; \
|
|
|
+})
|
|
|
+
|
|
|
+/*
|
|
|
+ * Returns: 0 if exception before NUL or reaching the supplied limit
|
|
|
+ * (N), a value greater than N if the limit would be exceeded, else
|
|
|
+ * strlen.
|
|
|
+ */
|
|
|
+extern unsigned long __strnlen_user (const char __user *, long);
|
|
|
+
|
|
|
+#define strnlen_user(str, len) \
|
|
|
+({ \
|
|
|
+ const char __user *__su_str = (str); \
|
|
|
+ unsigned long __su_ret = 0; \
|
|
|
+ if (__access_ok(__su_str, 0, get_fs())) \
|
|
|
+ __su_ret = __strnlen_user(__su_str, len); \
|
|
|
+ __su_ret; \
|
|
|
+})
|
|
|
+
|
|
|
+/* Generic code can't deal with the location-relative format that we use for compactness. */
|
|
|
+#define ARCH_HAS_SORT_EXTABLE
|
|
|
+#define ARCH_HAS_SEARCH_EXTABLE
|
|
|
+
|
|
|
+struct exception_table_entry {
|
|
|
+ int addr; /* location-relative address of insn this fixup is for */
|
|
|
+ int cont; /* location-relative continuation addr.; if bit 2 is set, r9 is set to 0 */
|
|
|
+};
|
|
|
+
|
|
|
+extern void ia64_handle_exception (struct pt_regs *regs, const struct exception_table_entry *e);
|
|
|
+extern const struct exception_table_entry *search_exception_tables (unsigned long addr);
|
|
|
+
|
|
|
+static inline int
|
|
|
+ia64_done_with_exception (struct pt_regs *regs)
|
|
|
+{
|
|
|
+ const struct exception_table_entry *e;
|
|
|
+ e = search_exception_tables(regs->cr_iip + ia64_psr(regs)->ri);
|
|
|
+ if (e) {
|
|
|
+ ia64_handle_exception(regs, e);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define ARCH_HAS_TRANSLATE_MEM_PTR 1
|
|
|
+static __inline__ char *
|
|
|
+xlate_dev_mem_ptr (unsigned long p)
|
|
|
+{
|
|
|
+ struct page *page;
|
|
|
+ char * ptr;
|
|
|
+
|
|
|
+ page = pfn_to_page(p >> PAGE_SHIFT);
|
|
|
+ if (PageUncached(page))
|
|
|
+ ptr = (char *)p + __IA64_UNCACHED_OFFSET;
|
|
|
+ else
|
|
|
+ ptr = __va(p);
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Convert a virtual cached kernel memory pointer to an uncached pointer
|
|
|
+ */
|
|
|
+static __inline__ char *
|
|
|
+xlate_dev_kmem_ptr (char * p)
|
|
|
+{
|
|
|
+ struct page *page;
|
|
|
+ char * ptr;
|
|
|
+
|
|
|
+ page = virt_to_page((unsigned long)p);
|
|
|
+ if (PageUncached(page))
|
|
|
+ ptr = (char *)__pa(p) + __IA64_UNCACHED_OFFSET;
|
|
|
+ else
|
|
|
+ ptr = p;
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* _ASM_IA64_UACCESS_H */
|