| 
									
										
										
										
											2011-06-07 11:49:55 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * User address space access functions. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  For licencing details see kernel-base/COPYING | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/highmem.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-06 14:32:32 -07:00
										 |  |  | #include <asm/word-at-a-time.h>
 | 
					
						
							| 
									
										
										
										
											2012-04-20 15:41:36 -07:00
										 |  |  | #include <linux/sched.h>
 | 
					
						
							| 
									
										
										
										
											2012-04-06 14:32:32 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-07 11:49:55 +02: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-06-11 15:44:26 +02:00
										 |  |  | 	if (__range_not_ok(from, n, TASK_SIZE)) | 
					
						
							| 
									
										
										
										
											2012-04-20 15:41:36 -07:00
										 |  |  | 		return len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-07 11:49:55 +02: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); |