• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/arm/lib/
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 <linux/gfp.h>
20#include <asm/current.h>
21#include <asm/page.h>
22
23static int
24pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
25{
26	unsigned long addr = (unsigned long)_addr;
27	pgd_t *pgd;
28	pmd_t *pmd;
29	pte_t *pte;
30	spinlock_t *ptl;
31
32	pgd = pgd_offset(current->mm, addr);
33	if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd)))
34		return 0;
35
36	pmd = pmd_offset(pgd, addr);
37	if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd)))
38		return 0;
39
40	pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl);
41	if (unlikely(!pte_present(*pte) || !pte_young(*pte) ||
42	    !pte_write(*pte) || !pte_dirty(*pte))) {
43		pte_unmap_unlock(pte, ptl);
44		return 0;
45	}
46
47	*ptep = pte;
48	*ptlp = ptl;
49
50	return 1;
51}
52
53static unsigned long noinline
54__copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
55{
56	int atomic;
57
58	if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
59		memcpy((void *)to, from, n);
60		return 0;
61	}
62
63	/* the mmap semaphore is taken only if not in an atomic context */
64	atomic = in_atomic();
65
66	if (!atomic)
67		down_read(&current->mm->mmap_sem);
68	while (n) {
69		pte_t *pte;
70		spinlock_t *ptl;
71		int tocopy;
72
73		while (!pin_page_for_write(to, &pte, &ptl)) {
74			if (!atomic)
75				up_read(&current->mm->mmap_sem);
76			if (__put_user(0, (char __user *)to))
77				goto out;
78			if (!atomic)
79				down_read(&current->mm->mmap_sem);
80		}
81
82		tocopy = (~(unsigned long)to & ~PAGE_MASK) + 1;
83		if (tocopy > n)
84			tocopy = n;
85
86		memcpy((void *)to, from, tocopy);
87		to += tocopy;
88		from += tocopy;
89		n -= tocopy;
90
91		pte_unmap_unlock(pte, ptl);
92	}
93	if (!atomic)
94		up_read(&current->mm->mmap_sem);
95
96out:
97	return n;
98}
99
100unsigned long
101__copy_to_user(void __user *to, const void *from, unsigned long n)
102{
103	/*
104	 * This test is stubbed out of the main function above to keep
105	 * the overhead for small copies low by avoiding a large
106	 * register dump on the stack just to reload them right away.
107	 * With frame pointer disabled, tail call optimization kicks in
108	 * as well making this test almost invisible.
109	 */
110	if (n < 64)
111		return __copy_to_user_std(to, from, n);
112	return __copy_to_user_memcpy(to, from, n);
113}
114
115static unsigned long noinline
116__clear_user_memset(void __user *addr, unsigned long n)
117{
118	if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
119		memset((void *)addr, 0, n);
120		return 0;
121	}
122
123	down_read(&current->mm->mmap_sem);
124	while (n) {
125		pte_t *pte;
126		spinlock_t *ptl;
127		int tocopy;
128
129		while (!pin_page_for_write(addr, &pte, &ptl)) {
130			up_read(&current->mm->mmap_sem);
131			if (__put_user(0, (char __user *)addr))
132				goto out;
133			down_read(&current->mm->mmap_sem);
134		}
135
136		tocopy = (~(unsigned long)addr & ~PAGE_MASK) + 1;
137		if (tocopy > n)
138			tocopy = n;
139
140		memset((void *)addr, 0, tocopy);
141		addr += tocopy;
142		n -= tocopy;
143
144		pte_unmap_unlock(pte, ptl);
145	}
146	up_read(&current->mm->mmap_sem);
147
148out:
149	return n;
150}
151
152unsigned long __clear_user(void __user *addr, unsigned long n)
153{
154	/* See rational for this in __copy_to_user() above. */
155	if (n < 64)
156		return __clear_user_std(addr, n);
157	return __clear_user_memset(addr, n);
158}
159