基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析
- static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
- {
- if (access_ok(VERIFY_READ, from, n))
- n = __copy_from_user(to, from, n);
- else /* security hole - plug it */
- memset(to, 0, n);
- return n;
- }
- static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
- {
- if (access_ok(VERIFY_WRITE, to, n))
- n = __copy_to_user(to, from, n);
- return n;
- }
- /*
- * 带有MMU的构架应该覆盖这两个函数
- */
- #ifndef __copy_from_user
- static inline __must_check long __copy_from_user(void *to,
- const void __user * from, unsigned long n)
- {
- if (__builtin_constant_p(n)) {
- switch(n) {
- case 1:
- *(u8 *)to = *(u8 __force *)from;
- return 0;
- case 2:
- *(u16 *)to = *(u16 __force *)from;
- return 0;
- case 4:
- *(u32 *)to = *(u32 __force *)from;
- return 0;
- #ifdef CONFIG_64BIT
- case 8:
- *(u64 *)to = *(u64 __force *)from;
- return 0;
- #endif
- default:
- break;
- }
- }
- memcpy(to, (const void __force *)from, n);
- return 0;
- }
- #endif
- #ifndef __copy_to_user
- static inline __must_check long __copy_to_user(void __user *to,
- const void *from, unsigned long n)
- {
- if (__builtin_constant_p(n)) {
- switch(n) {
- case 1:
- *(u8 __force *)to = *(u8 *)from;
- return 0;
- case 2:
- *(u16 __force *)to = *(u16 *)from;
- return 0;
- case 4:
- *(u32 __force *)to = *(u32 *)from;
- return 0;
- #ifdef CONFIG_64BIT
- case 8:
- *(u64 __force *)to = *(u64 *)from;
- return 0;
- #endif
- default:
- break;
- }
- }
- memcpy((void __force *)to, from, n);
- return 0;
- }
- #endif
点击(此处)折叠或打开
- GCC的内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数值是常数,函数返回 1,否则返回 0。
- #ifndef __HAVE_ARCH_MEMCPY
- /**
- * memcpy - Copy one area of memory to another
- * @dest: Where to copy to
- * @src: Where to copy from
- * @count: The size of the area.
- *
- * You should not use this function to access IO space, use memcpy_toio()
- * or memcpy_fromio() instead.
- */
- void *memcpy(void *dest, const void *src, size_t count)
- {
- char *tmp = dest;
- const char *s = src;
- while (count--)
- *tmp++ = *s++;
- return dest;
- }
- EXPORT_SYMBOL(memcpy);
- #endif
- /* We use 33-bit arithmetic here... */
- #define __range_ok(addr,size) ({ \
- unsigned long flag, roksum; \
- __chk_user_ptr(addr); \
- __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \
- : "=&r" (flag), "=&r" (roksum) \
- : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \
- : "cc"); \
- flag; })
- ......
- #define access_ok(type,addr,size) (__range_ok(addr,size) == 0)
- ......
这个就比较麻烦了,涉及到了C语言中内联汇编,如果还不熟悉的朋友可以看看《ARM GCC 内嵌汇编手册》,我也不是很熟。
现在我们来仔细分析__range_ok这个宏:
(1)unsigned long flag, roksum;\\定义两个变量
- flag:保存结果的变量:非零代表地址无效,零代表地址可以访问。初始存放非零值(current_thread_info()->addr_limit),也就是当前进程的地址上限值。
- roksum:保存要访问的地址范围末端,用于和当前进程地址空间限制数据做比较
(2)__chk_user_ptr(addr);\\定义是一个空函数
但是这个函数涉及到__CHECKER__宏的判断,__CHECKER__宏在通过Sparse(Semantic Parser for C)工具对内核代码进行检查时会定义的。在使用make C=1或C=2时便会调用该工具,这个工具可以检查在代码中声明了sparse所能检查到的相关属性的内核函数和变量。
如果定义了__CHECKER__,在网上的资料中这样解释的:__chk_user_ptr和__chk_io_ptr在这里只声明函数,没有函数体,目的就是在编译过程中Sparse能够捕捉到编译错误,检查参数的类型。
如果没有定义__CHECKER__,这就是一个空函数。
(3)接下来的汇编,我适当地翻译如下:
adds %1, %2, %3
roksum = addr + size 这个操作影响状态位(目的是影响是进位标志C)
以下的两个指令都带有条件CC,也就是当C=0的时候才执行。
如果上面的加法指令进位了(C=1),则以下的指令都不执行,flag就为初始值current_thread_info()->addr_limit(非零值),并返回。
如果没有进位(C=0),就执行下面的指令
sbcccs %1, %1, %0
roksum = roksum - flag,也就是(addr + size)- (current_thread_info()->addr_limit),操作影响符号位。
如果(addr + size)>=(current_thread_info()->addr_limit),则C=1
如果(addr + size)<(current_thread_info()->addr_limit),则C=0
(4)flag;
返回flag值
综上所诉:__range_ok宏其实等价于:
如果(addr + size)>=(current_thread_info()->addr_limit),返回非零值
如果(addr + size)<(current_thread_info()->addr_limit),返回零
而access_ok就是检验将要操作的用户空间的地址范围是否在当前进程的用户地址空间限制中。这个宏的功能很简单,完全可以用C实现,不是必须使用汇编。个人理解:由于这两个函数使用频繁,就使用汇编来实现部分功能来增加效率。
从这里再次可以认识到,copy_from_user与copy_to_user的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。
其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。
最新文章
- Java-set集合
- ASPxCallback控件
- WordPress A Forms插件HTML注入漏洞和跨站请求伪造漏洞
- 使用WiX Toolset创建.NET程序发布Bootstrapper(安装策略管理)(二)——自定义安装
- kindeditor-网页文字编辑
- PHP关于foreach使用引用变量的坑
- icon大小
- OpenGL 图形管道(graphics pipeline)过程
- C++第一天学习
- U盘为什么还有剩余空间,但却提示说空间不够
- 排座椅 2008 NOIP 普及组 第二题
- Evensgn 捡树枝
- java多线程(8)---阻塞队列
- MVP之高级MVP架构封装
- 数据挖掘---Pandas的学习
- Systick时钟定时
- org.springframework.beans.TypeMismatchException: Failed to convert property value of type &#39;null&#39; to required type &#39;double&#39; for property &#39;band&#39;; nested exception is org.springframework.core.convert.Con
- des加密解密JAVA与.NET互通实例
- cf276E 两棵线段树分别维护dfs序和bfs序,好题回头再做
- alpha通道与混合技术
热门文章
- [计算机网络-传输层] 无连接传输:UDP
- ADO.NET中DataSet、DataTable、DataRow的数据复制方法
- Lucene笔记二
- 洛谷4643:【模板】动态dp——题解
- 2017 ccpc哈尔滨 A题 Palindrome
- C++中static用法
- 洛谷 P2747 [USACO5.4]周游加拿大Canada Tour 解题报告
- AOJ.176 两数组最短距离 (乱搞题)
- Tomcat启动时报org.springframework.web.context.ContextLoaderListener错误解决方案
- java使用POI操作XWPFDocument中的XWPFParagraph(段落)对象的属性略解