diff options
Diffstat (limited to 'arch/arm/lib')
-rw-r--r-- | arch/arm/lib/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/lib/uaccess_with_memcpy.c | 139 |
2 files changed, 142 insertions, 0 deletions
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 866f84a586ff..030ba7219f48 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile | |||
@@ -29,6 +29,9 @@ else | |||
29 | endif | 29 | endif |
30 | endif | 30 | endif |
31 | 31 | ||
32 | # using lib_ here won't override already available weak symbols | ||
33 | obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o | ||
34 | |||
32 | lib-$(CONFIG_MMU) += $(mmu-y) | 35 | lib-$(CONFIG_MMU) += $(mmu-y) |
33 | 36 | ||
34 | ifeq ($(CONFIG_CPU_32v3),y) | 37 | ifeq ($(CONFIG_CPU_32v3),y) |
diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c new file mode 100644 index 000000000000..bf987b4a2571 --- /dev/null +++ b/arch/arm/lib/uaccess_with_memcpy.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/lib/uaccess_with_memcpy.c | ||
3 | * | ||
4 | * Written by: Lennert Buytenhek and Nicolas Pitre | ||
5 | * Copyright (C) 2009 Marvell Semiconductor | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/ctype.h> | ||
14 | #include <linux/uaccess.h> | ||
15 | #include <linux/rwsem.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/hardirq.h> /* for in_atomic() */ | ||
19 | #include <asm/current.h> | ||
20 | #include <asm/page.h> | ||
21 | |||
22 | static int | ||
23 | pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) | ||
24 | { | ||
25 | unsigned long addr = (unsigned long)_addr; | ||
26 | pgd_t *pgd; | ||
27 | pmd_t *pmd; | ||
28 | pte_t *pte; | ||
29 | spinlock_t *ptl; | ||
30 | |||
31 | pgd = pgd_offset(current->mm, addr); | ||
32 | if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd))) | ||
33 | return 0; | ||
34 | |||
35 | pmd = pmd_offset(pgd, addr); | ||
36 | if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd))) | ||
37 | return 0; | ||
38 | |||
39 | pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); | ||
40 | if (unlikely(!pte_present(*pte) || !pte_young(*pte) || | ||
41 | !pte_write(*pte) || !pte_dirty(*pte))) { | ||
42 | pte_unmap_unlock(pte, ptl); | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | *ptep = pte; | ||
47 | *ptlp = ptl; | ||
48 | |||
49 | return 1; | ||
50 | } | ||
51 | |||
52 | unsigned long | ||
53 | __copy_to_user(void __user *to, const void *from, unsigned long n) | ||
54 | { | ||
55 | int atomic; | ||
56 | |||
57 | if (n < 1024) | ||
58 | return __copy_to_user_std(to, from, n); | ||
59 | |||
60 | if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { | ||
61 | memcpy((void *)to, from, n); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* the mmap semaphore is taken only if not in an atomic context */ | ||
66 | atomic = in_atomic(); | ||
67 | |||
68 | if (!atomic) | ||
69 | down_read(¤t->mm->mmap_sem); | ||
70 | while (n) { | ||
71 | pte_t *pte; | ||
72 | spinlock_t *ptl; | ||
73 | int tocopy; | ||
74 | |||
75 | while (!pin_page_for_write(to, &pte, &ptl)) { | ||
76 | if (!atomic) | ||
77 | up_read(¤t->mm->mmap_sem); | ||
78 | if (__put_user(0, (char __user *)to)) | ||
79 | goto out; | ||
80 | if (!atomic) | ||
81 | down_read(¤t->mm->mmap_sem); | ||
82 | } | ||
83 | |||
84 | tocopy = (~(unsigned long)to & ~PAGE_MASK) + 1; | ||
85 | if (tocopy > n) | ||
86 | tocopy = n; | ||
87 | |||
88 | memcpy((void *)to, from, tocopy); | ||
89 | to += tocopy; | ||
90 | from += tocopy; | ||
91 | n -= tocopy; | ||
92 | |||
93 | pte_unmap_unlock(pte, ptl); | ||
94 | } | ||
95 | if (!atomic) | ||
96 | up_read(¤t->mm->mmap_sem); | ||
97 | |||
98 | out: | ||
99 | return n; | ||
100 | } | ||
101 | |||
102 | unsigned long __clear_user(void __user *addr, unsigned long n) | ||
103 | { | ||
104 | if (n < 256) | ||
105 | return __clear_user_std(addr, n); | ||
106 | |||
107 | if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { | ||
108 | memset((void *)addr, 0, n); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | down_read(¤t->mm->mmap_sem); | ||
113 | while (n) { | ||
114 | pte_t *pte; | ||
115 | spinlock_t *ptl; | ||
116 | int tocopy; | ||
117 | |||
118 | while (!pin_page_for_write(addr, &pte, &ptl)) { | ||
119 | up_read(¤t->mm->mmap_sem); | ||
120 | if (__put_user(0, (char __user *)addr)) | ||
121 | goto out; | ||
122 | down_read(¤t->mm->mmap_sem); | ||
123 | } | ||
124 | |||
125 | tocopy = (~(unsigned long)addr & ~PAGE_MASK) + 1; | ||
126 | if (tocopy > n) | ||
127 | tocopy = n; | ||
128 | |||
129 | memset((void *)addr, 0, tocopy); | ||
130 | addr += tocopy; | ||
131 | n -= tocopy; | ||
132 | |||
133 | pte_unmap_unlock(pte, ptl); | ||
134 | } | ||
135 | up_read(¤t->mm->mmap_sem); | ||
136 | |||
137 | out: | ||
138 | return n; | ||
139 | } | ||