2011-06-07 17:49:55 +08:00
|
|
|
/*
|
|
|
|
* User address space access functions.
|
|
|
|
*
|
|
|
|
* For licencing details see kernel-base/COPYING
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
2012-04-07 05:32:32 +08:00
|
|
|
#include <asm/word-at-a-time.h>
|
2012-04-21 06:41:36 +08:00
|
|
|
#include <linux/sched.h>
|
2012-04-07 05:32:32 +08:00
|
|
|
|
2011-06-07 17:49:55 +08:00
|
|
|
/*
|
|
|
|
* best effort, GUP based copy_from_user() that is NMI-safe
|
|
|
|
*/
|
|
|
|
unsigned long
|
|
|
|
copy_from_user_nmi(void *to, const void __user *from, unsigned long n)
|
|
|
|
{
|
|
|
|
unsigned long offset, addr = (unsigned long)from;
|
|
|
|
unsigned long size, len = 0;
|
|
|
|
struct page *page;
|
|
|
|
void *map;
|
|
|
|
int ret;
|
|
|
|
|
2012-04-21 06:41:36 +08:00
|
|
|
if (__range_not_ok(from, n, TASK_SIZE) == 0)
|
|
|
|
return len;
|
|
|
|
|
2011-06-07 17:49:55 +08:00
|
|
|
do {
|
|
|
|
ret = __get_user_pages_fast(addr, 1, 0, &page);
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
offset = addr & (PAGE_SIZE - 1);
|
|
|
|
size = min(PAGE_SIZE - offset, n - len);
|
|
|
|
|
|
|
|
map = kmap_atomic(page);
|
|
|
|
memcpy(to, map+offset, size);
|
|
|
|
kunmap_atomic(map);
|
|
|
|
put_page(page);
|
|
|
|
|
|
|
|
len += size;
|
|
|
|
to += size;
|
|
|
|
addr += size;
|
|
|
|
|
|
|
|
} while (len < n);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(copy_from_user_nmi);
|