基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析

上传人:痛*** 文档编号:162879601 上传时间:2022-10-20 格式:DOC 页数:11 大小:76.55KB
收藏 版权申诉 举报 下载
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析_第1页
第1页 / 共11页
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析_第2页
第2页 / 共11页
基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析_第3页
第3页 / 共11页
资源描述:

《基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析》由会员分享,可在线阅读,更多相关《基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析(11页珍藏版)》请在装配图网上搜索。

1、基于ARM 构架(带MMU)的copy_from_user与copy_to_user详细分析在学习Linux内核驱动的时候,一开始就会碰到copy_from_user和copy_to_user这两个常用的函数。这两个函数在内核使用的非常频繁,负责将数据从用户空间拷贝到内核空间以及将数据从内核空间拷贝到用户空间。在4年半前初学Linux内核驱动程序的时候,我只是知道这个怎么用,并没有很深入的分析这两个函数。这次研究内核模块挂载的时候,又碰到了它们。决定还是认真跟踪一下函数。首先这两个函数的原型在arch/arm/include/asm/uaccess.h文件中:1 static inline u

2、nsigned long _must_check copy_from_user(void *to, const void _user *from, unsigned long n)2 3 if (access_ok(VERIFY_READ, from, n)4 n = _copy_from_user(to, from, n);5 else /* security hole - plug it */6 memset(to, 0, n);7 return n;8 910 static inline unsigned long _must_check copy_to_user(void _user

3、*to, const void *from, unsigned long n)11 12 if (access_ok(VERIFY_WRITE, to, n)13 n = _copy_to_user(to, from, n);14 return n;15 这两个函数从结构上来分析,其实都可以分为两个部分:1、首先检查用户空间的地址指针是否有效(难点)2、调用_copy_from_user和_copy_to_user函数在这个分析中,我们先易后难。首先看看具体数据拷贝功能的_copy_from_user和_copy_to_user函数对于ARM构架,没有单独实现这两个函数,所以他们的代码位于in

4、clude/asm-generic/uaccess.h16 /*17 * 带有MMU的构架应该覆盖这两个函数18 */19 #ifndef _copy_from_user20 static inline _must_check long _copy_from_user(void *to,21 const void _user * from, unsigned long n)22 23 if (_builtin_constant_p(n) 24 switch(n) 25 case 1:26 *(u8 *)to = *(u8 _force *)from;27 return 0;28 case 2:

5、29 *(u16 *)to = *(u16 _force *)from;30 return 0;31 case 4:32 *(u32 *)to = *(u32 _force *)from;33 return 0;34 #ifdef CONFIG_64BIT35 case 8:36 *(u64 *)to = *(u64 _force *)from;37 return 0;38 #endif39 default:40 break;41 42 4344 memcpy(to, (const void _force *)from, n);45 return 0;46 47 #endif4849 #ifn

6、def _copy_to_user50 static inline _must_check long _copy_to_user(void _user *to,51 const void *from, unsigned long n)52 53 if (_builtin_constant_p(n) 54 switch(n) 55 case 1:56 *(u8 _force *)to = *(u8 *)from;57 return 0;58 case 2:59 *(u16 _force *)to = *(u16 *)from;60 return 0;61 case 4:62 *(u32 _for

7、ce *)to = *(u32 *)from;63 return 0;64 #ifdef CONFIG_64BIT65 case 8:66 *(u64 _force *)to = *(u64 *)from;67 return 0;68 #endif69 default:70 break;71 72 7374 memcpy(void _force *)to, from, n);75 return 0;76 77 #endif点击(此处)折叠或打开78 GCC的内建函数 _builtin_constant_p 用于判断一个值是否为编译时常数,如果参数值是常数,函数返回 1,否则返回 0。 从这两个

8、函数中可以看出其实结构是一样的,首先看看n是不是常数,如果是并为1、2、4、8(64bit)则直接就用一个赋值语句拷贝数据。如果不是常数或n过大,则使用memcpy函数。而这个memcpy函数位于lib/string.c:79 #ifndef _HAVE_ARCH_MEMCPY80 /*81 * memcpy - Copy one area of memory to another82 * dest: Where to copy to83 * src: Where to copy from84 * count: The size of the area.85 *86 * You should

9、not use this function to access IO space, use memcpy_toio()87 * or memcpy_fromio() instead.88 */89 void *memcpy(void *dest, const void *src, size_t count)90 91 char *tmp = dest;92 const char *s = src;9394 while (count-)95 *tmp+ = *s+;96 return dest;97 98 EXPORT_SYMBOL(memcpy);99 #endif这个函数其实就是一个简单的利

10、用循环来数据拷贝,非常简单。好了如何拷贝数据我们已经了解了,现在我们来看看前面的用户空间指针检测函数access_ok,这其实是一个宏定义,位于arch/arm/include/asm/uaccess.h文件中:100 /* We use 33-bit arithmetic here. */101 #define _range_ok(addr,size) ( 102 unsigned long flag, roksum; 103 _chk_user_ptr(addr); 104 _asm_(adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0 105

11、 : =&r (flag), =&r (roksum) 106 : r (addr), Ir (size), 0 (current_thread_info()-addr_limit) 107 : cc); 108 flag; )109 .110 #define access_ok(type,addr,size) (_range_ok(addr,size) = 0)111 .这个就比较麻烦了,涉及到了C语言中内联汇编,如果还不熟悉的朋友可以看看ARM GCC 内嵌汇编手册,我也不是很熟。现在我们来仔细分析_range_ok这个宏:(1)unsigned long flag, roksum;定义两

12、个变量112 flag:保存结果的变量:非零代表地址无效,零代表地址可以访问。初始存放非零值(current_thread_info()-addr_limit),也就是当前进程的地址上限值。113 roksum:保存要访问的地址范围末端,用于和当前进程地址空间限制数据做比较(2)_chk_user_ptr(addr);定义是一个空函数 但是这个函数涉及到_CHECKER_宏的判断,_CHECKER_宏在通过Sparse(Semantic Parser for C)工具对内核代码进行检查时会定义的。在使用make C=1或C=2时便会调用该工具,这个工具可以检查在代码中声明了sparse所能检查

13、到的相关属性的内核函数和变量。 如果定义了_CHECKER_,在网上的资料中这样解释的:_chk_user_ptr和_chk_io_ptr在这里只声明函数,没有函数体,目的就是在编译过程中Sparse能够捕捉到编译错误,检查参数的类型。 如果没有定义_CHECKER_,这就是一个空函数。(3)接下来的汇编,我适当地翻译如下: adds %1, %2, %3roksum = addr + size 这个操作影响状态位(目的是影响是进位标志C)以下的两个指令都带有条件CC,也就是当C=0的时候才执行。如果上面的加法指令进位了(C=1),则以下的指令都不执行,flag就为初始值current_thr

14、ead_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)addr_limit),则C=0当C=0的时候执行以下指令,否则跳过(flag非零)。 movcc %0, #0 flag = 0,给flag赋值0 (

15、4)flag; 返回flag值综上所诉:_range_ok宏其实等价于:如果(addr + size)=(current_thread_info()-addr_limit),返回非零值如果(addr + size)addr_limit),返回零而access_ok就是检验将要操作的用户空间的地址范围是否在当前进程的用户地址空间限制中。这个宏的功能很简单,完全可以用C实现,不是必须使用汇编。个人理解:由于这两个函数使用频繁,就使用汇编来实现部分功能来增加效率。 从这里再次可以认识到,copy_from_user与copy_to_user的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。 其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!