Kernel version:2.6.14
CPU architecture:ARM920T
Author:ce123(http://blog.csdn.net/ce123)
1.copy_from_user
在学习Linux内核驱动的时分,常常会碰到copy_from_user和copy_to_user这两个函数,设备驱动程序中的ioctl函数就常常会用到。这两个函数担任在用户空间和内核空间传递数据。首要看看它们的界说(linux/include/asm-arm/uaccess.h),先看copy_from_user:
print?
- staticinlineunsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongn)
- {
- if(access_ok(VERIFY_READ,from,n))
- n=__arch_copy_from_user(to,from,n);
- else/*securityhole-plugit*/
- memzero(to,n);
- returnn;
- }
先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表明从用户空间想内核空间复制数据的字节数。假如成功履行复制操作,则回来0,不然回来还没有完结复制的字节数。
这个函数从结构上来剖析,其实都能够分为两个部分:
- 首要查看用户空间的地址指针是否有用;
- 调用__arch_copy_from_user函数。
1.1.access_ok
access_ok用来对用户空间的地址指针from作某种有用性查验,这个宏和体系结构相关,在arm渠道上为(linux/include/asm-arm/uaccess.h):
print?
- #define__range_ok(addr,size)({\
- unsignedlongflag,sum;\
- __chk_user_ptr(addr);\
- __asm__(“adds%1,%2,%3;sbcccs%1,%1,%0;movcc%0,#0″\
- :”=&r”(flag),”=&r”(sum)\
- :”r”(addr),”Ir”(size),”0″(current_thread_info()->addr_limit)\
- :”cc”);\
- flag;})
- #defineaccess_ok(type,addr,size)(__range_ok(addr,size)==0)
能够看到access_ok中第一个参数type并没有用到,__range_ok的效果在于判别addr+size之后是否还在进程的用户空间规模之内。下面咱们详细看一下。这段代码涉及到GCC内联汇编,不明白的朋友能够先看看这篇博客(http://blog.csdn.net/ce123/article/details/8209702)。
(1)unsigned long flag, sum;\\界说两个变量
- flag:保存成果的变量:非零代表地址无效,零代表地址能够拜访。初始寄存非零值(current_thread_info()->addr_limit),也便是当时进程的地址上限值。
- sum:保存要拜访的地址规模结尾,用于和当时进程地址空间约束数据做比较。
这个函数涉及到__CHECKER__宏的判别,__CHECKER__宏在经过Sparse(Semantic Parser for C)东西对内核代码进行查看时会界说的。在运用make C=1或C=2时便会调用该东西,这个东西能够查看在代码中声明晰sparse所能查看到的相关特点的内核函数和变量。
- 假如界说了__CHECKER__,__chk_user_ptr和__chk_io_ptr在这儿只声明函数,没有函数体,意图便是在编译过程中Sparse能够捕捉到编译过错,查看参数的类型。
- 假如没有界说__CHECKER__,这便是一个空函数。
请看详细的界说(linux/compiler.h):
print?
- #ifdef__CHECKER__
- …
- externvoid__chk_user_ptr(void__user*);
- externvoid__chk_io_ptr(void__iomem*);
- #else
- …
- #define__chk_user_ptr(x)(void)0
- #define__chk_io_ptr(x)(void)0
- …
- #endif
(3)接下来是汇编:
adds %1, %2, %3
sum = addr + size 这个操作影响状况位(意图是影响是进位标志C),以下的两个指令都带有条件CC,也便是当C=0的时分才履行。
假如上面的加法指令进位了(C=1),则以下的指令都不履行,flag就为初始值current_thread_info()->addr_limit(非0),并回来。
假如没有进位(C=0),就履行下面的指令:
sbcccs %1, %1, %0
sum = sum – flag – 1,也便是(addr + size) – (current_thread_info()->addr_limit) – 1,操作影响符号位。
假如(addr + size) >= (current_thread_info()->addr_limit) – 1,则C=1
假如(addr + size) < (current_thread_info()->addr_limit) – 1,则C=0
当C=0的时分履行以下指令,不然越过(flag非零)。
movcc %0, #0
flag = 0,给flag赋值0。
综上所述:__range_ok宏其实等价于:
- 假如(addr + size) >= (current_thread_info()->addr_limit) – 1,回来非零值
- 假如(addr + size) < (current_thread_info()->addr_limit),回来零
而access_ok便是查验即将操作的用户空间的地址规模是否在当时进程的用户地址空间约束中。这个宏的功用很简单,彻底能够用C完成,不是有必要运用汇编。但于这两个函数运用频频,就运用汇编来完成部分功用来添加功率。
从这儿再次能够认识到,copy_from_user的运用是结合进程上下文的,由于他们要拜访“user”的内存空间,这个“user”有必要是某个特定的进程。经过上面的源码就知道,其间运用了current_thread_info()来查看空间是否能够拜访。假如在驱动中运用这两个函数,有必要是在完成体系调用的函数中运用,不可在完成中止处理的函数中运用。假如在中止上下文中运用了,那代码就很或许操作了底子不相关的进程地址空间。其次由于操作的页面或许被换出,这两个函数或许会休眠,所以相同不可在中止上下文中运用。
1.2.__arch_copy_from_user
print?
- #defineUSER(x…)\
- 9999:x;\
- .section__ex_table,”a”;\
- .align3;\
- .long9999b,9001f;\
- .previous
print?
- .long9999b,9001f;
其间9999b对应标号9999处的指令,9001f是9001处的指令,是9999b处指令的修正指令。这样,当标号9999处发生缺页反常时,体系将调用do_page_fault提交物理页面,然后跳到9001持续履行。
下面咱们深化剖析__arch_copy_from_user函数的完成,该函数是用汇编完成的,界说在linux/arch/arm/lib/uaccess.S文件中。
print?
- /*Prototype:unsignedlong__arch_copy_from_user(void*to,constvoid*from,unsignedlongn);
- *Purpose:copyablockfromusermemorytokernelmemory
- *Params:to-kernelmemory
- *:from-usermemory
- *:n-numberofbytestocopy
- *Returns:NumberofbytesNOTcopied.
- */
- .cfu_dest_not_aligned:
- rsbip,ip,#4
- cmpip,#2
- USER(ldrbtr3,[r1],#1)@Mayfault
- strbr3,[r0],#1
- USER(ldrgebtr3,[r1],#1)@Mayfault
- strgebr3,[r0],#1
- USER(ldrgtbtr3,[r1],#1)@Mayfault
- strgtbr3,[r0],#1
- subr2,r2,ip
- b.cfu_dest_aligned
- ENTRY(__arch_copy_from_user)
- stmfdsp!,{r0,r2,r4-r7,lr}
- cmpr2,#4
- blt.cfu_not_enough
- PLD(pld[r1,#0])
- PLD(pld[r0,#0])
- andsip,r0,#3
- bne.cfu_dest_not_aligned
- .cfu_dest_aligned:
- andsip,r1,#3
- bne.cfu_src_not_aligned
- /*
- *Seeingastherehastobeatleast8bytestocopy,wecan
- *copyoneword,andforceauser-modepagefault…
- */
- .cfu_0fupi:subsr2,r2,#4
- addmiip,r2,#4
- bmi.cfu_0nowords
- USER(ldrtr3,[r1],#4)
- strr3,[r0],#4
- movip,r1,lsl#32-PAGE_SHIFT@Oneachpage,useald/st??tinstruction
- rsbip,ip,#0
- movsip,ip,lsr#32-PAGE_SHIFT
- beq.cfu_0fupi
- /*
- *ip=maxno.ofbytestocopybeforeneedinganother”strt”insn
- */
- cmpr2,ip
- movltip,r2
- subr2,r2,ip
- subsip,ip,#32
- blt.cfu_0rem8lp
- PLD(pld[r1,#28])
- PLD(pld[r0,#28])
- PLD(subsip,ip,#64)
- PLD(blt.cfu_0cpynopld)
- PLD(pld[r1,#60])
- PLD(pld[r0,#60])
- .cfu_0cpy8lp:
- PLD(pld[r1,#92])
- PLD(pld[r0,#92])
- .cfu_0cpynopld:ldmiar1!,{r3-r6}@Shouldntfault
- stmiar0!,{r3-r6}
- ldmiar1!,{r3-r6}@Shouldntfault
- subsip,ip,#32
- stmiar0!,{r3-r6}
- bpl.cfu_0cpy8lp
- PLD(cmnip,#64)
- PLD(bge.cfu_0cpynopld)
- PLD(addip,ip,#64)
- .cfu_0rem8lp:cmnip,#16
- ldmgeiar1!,{r3-r6}@Shouldntfault
- stmgeiar0!,{r3-r6}
- tstip,#8
- ldmneiar1!,{r3-r4}@Shouldntfault
- stmneiar0!,{r3-r4}
- tstip,#4
- ldrnetr3,[r1],#4@Shouldntfault
- strner3,[r0],#4
- andsip,ip,#3
- beq.cfu_0fupi
- .cfu_0nowords:teqip,#0
- beq.cfu_finished
- .cfu_nowords:cmpip,#2
- USER(ldrbtr3,[r1],#1)@Mayfault
- strbr3,[r0],#1
- USER(ldrgebtr3,[r1],#1)@Mayfault
- strgebr3,[r0],#1
- USER(ldrgtbtr3,[r1],#1)@Mayfault
- strgtbr3,[r0],#1
- b.cfu_finished
- .cfu_not_enough:
- movsip,r2
- bne.cfu_nowords
- .cfu_finished:movr0,#0
- addsp,sp,#8
- LOADREGS(fd,sp!,{r4-r7,pc})
- .cfu_src_not_aligned:
- bicr1,r1,#3
- USER(ldrtr7,[r1],#4)@Mayfault
- cmpip,#2
- bgt.cfu_3fupi
- beq.cfu_2fupi
- .cfu_1fupi:subsr2,r2,#4
- addmiip,r2,#4
- bmi.cfu_1nowords
- movr3,r7,pull#8
- USER(ldrtr7,[r1],#4)@Mayfault
- orrr3,r3,r7,push#24
- strr3,[r0],#4
- movip,r1,lsl#32-PAGE_SHIFT
- rsbip,ip,#0
- movsip,ip,lsr#32-PAGE_SHIFT
- beq.cfu_1fupi
- cmpr2,ip
- movltip,r2
- subr2,r2,ip
- subsip,ip,#16
- blt.cfu_1rem8lp
- PLD(pld[r1,#12])
- PLD(pld[r0,#12])
- PLD(subsip,ip,#32)
- PLD(blt.cfu_1cpynopld)
- PLD(pld[r1,#28])
- PLD(pld[r0,#28])
- .cfu_1cpy8lp:
- PLD(pld[r1,#44])
- PLD(pld[r0,#44])
- .cfu_1cpynopld:movr3,r7,pull#8
- ldmiar1!,{r4-r7}@Shouldntfault
- subsip,ip,#16
- orrr3,r3,r4,push#24
- movr4,r4,pull#8
- orrr4,r4,r5,push#24
- movr5,r5,pull#8
- orrr5,r5,r6,push#24
- movr6,r6,pull#8
- orrr6,r6,r7,push#24
- stmiar0!,{r3-r6}
- bpl.cfu_1cpy8lp
- PLD(cmnip,#32)
- PLD(bge.cfu_1cpynopld)
- PLD(addip,ip,#32)
- .cfu_1rem8lp:tstip,#8
- movner3,r7,pull#8
- ldmneiar1!,{r4,r7}@Shouldntfault
- orrner3,r3,r4,push#24
- movner4,r4,pull#8
- orrner4,r4,r7,push#24
- stmneiar0!,{r3-r4}
- tstip,#4
- movner3,r7,pull#8
- USER(ldrnetr7,[r1],#4)@Mayfault
- orrner3,r3,r7,push#24
- strner3,[r0],#4
- andsip,ip,#3
- beq.cfu_1fupi
- .cfu_1nowords:movr3,r7,get_byte_1
- teqip,#0
- beq.cfu_finished
- cmpip,#2
- strbr3,[r0],#1
- movger3,r7,get_byte_2
- strgebr3,[r0],#1
- movgtr3,r7,get_byte_3
- strgtbr3,[r0],#1
- b.cfu_finished
- .cfu_2fupi:subsr2,r2,#4
- addmiip,r2,#4
- bmi.cfu_2nowords
- movr3,r7,pull#16
- USER(ldrtr7,[r1],#4)@Mayfault
- orrr3,r3,r7,push#16
- strr3,[r0],#4
- movip,r1,lsl#32-PAGE_SHIFT
- rsbip,ip,#0
- movsip,ip,lsr#32-PAGE_SHIFT
- beq.cfu_2fupi
- cmpr2,ip
- movltip,r2
- subr2,r2,ip
- subsip,ip,#16
- blt.cfu_2rem8lp
- PLD(pld[r1,#12])
- PLD(pld[r0,#12])
- PLD(subsip,ip,#32)
- PLD(blt.cfu_2cpynopld)
- PLD(pld[r1,#28])
- PLD(pld[r0,#28])
- .cfu_2cpy8lp:
- PLD(pld[r1,#44])
- PLD(pld[r0,#44])
- .cfu_2cpynopld:movr3,r7,pull#16
- ldmiar1!,{r4-r7}@Shouldntfault
- subsip,ip,#16
- orrr3,r3,r4,push#16
- movr4,r4,pull#16
- orrr4,r4,r5,push#16
- movr5,r5,pull#16
- orrr5,r5,r6,push#16
- movr6,r6,pull#16
- orrr6,r6,r7,push#16
- stmiar0!,{r3-r6}
- bpl.cfu_2cpy8lp
- PLD(cmnip,#32)
- PLD(bge.cfu_2cpynopld)
- PLD(addip,ip,#32)
- .cfu_2rem8lp:tstip,#8
- movner3,r7,pull#16
- ldmneiar1!,{r4,r7}@Shouldntfault
- orrner3,r3,r4,push#16
- movner4,r4,pull#16
- orrner4,r4,r7,push#16
- stmneiar0!,{r3-r4}
- tstip,#4
- movner3,r7,pull#16
- USER(ldrnetr7,[r1],#4)@Mayfault
- orrner3,r3,r7,push#16
- strner3,[r0],#4
- andsip,ip,#3
- beq.cfu_2fupi
- .cfu_2nowords:movr3,r7,get_byte_2
- teqip,#0
- beq.cfu_finished
- cmpip,#2
- strbr3,[r0],#1
- movger3,r7,get_byte_3
- strgebr3,[r0],#1
- USER(ldrgtbtr3,[r1],#0)@Mayfault
- strgtbr3,[r0],#1
- b.cfu_finished
- .cfu_3fupi:subsr2,r2,#4
- addmiip,r2,#4
- bmi.cfu_3nowords
- movr3,r7,pull#24
- USER(ldrtr7,[r1],#4)@Mayfault
- orrr3,r3,r7,push#8
- strr3,[r0],#4
- movip,r1,lsl#32-PAGE_SHIFT
- rsbip,ip,#0
- movsip,ip,lsr#32-PAGE_SHIFT
- beq.cfu_3fupi
- cmpr2,ip
- movltip,r2
- subr2,r2,ip
- subsip,ip,#16
- blt.cfu_3rem8lp
- PLD(pld[r1,#12])
- PLD(pld[r0,#12])
- PLD(subsip,ip,#32)
- PLD(blt.cfu_3cpynopld)
- PLD(pld[r1,#28])
- PLD(pld[r0,#28])
- .cfu_3cpy8lp:
- PLD(pld[r1,#44])
- PLD(pld[r0,#44])
- .cfu_3cpynopld:movr3,r7,pull#24
- ldmiar1!,{r4-r7}@Shouldntfault
- orrr3,r3,r4,push#8
- movr4,r4,pull#24
- orrr4,r4,r5,push#8
- movr5,r5,pull#24
- orrr5,r5,r6,push#8
- movr6,r6,pull#24
- orrr6,r6,r7,push#8
- stmiar0!,{r3-r6}
- subsip,ip,#16
- bpl.cfu_3cpy8lp
- PLD(cmnip,#32)
- PLD(bge.cfu_3cpynopld)
- PLD(addip,ip,#32)
- .cfu_3rem8lp:tstip,#8
- movner3,r7,pull#24
- ldmneiar1!,{r4,r7}@Shouldntfault
- orrner3,r3,r4,push#8
- movner4,r4,pull#24
- orrner4,r4,r7,push#8
- stmneiar0!,{r3-r4}
- tstip,#4
- movner3,r7,pull#24
- USER(ldrnetr7,[r1],#4)@Mayfault
- orrner3,r3,r7,push#8
- strner3,[r0],#4
- andsip,ip,#3
- beq.cfu_3fupi
- .cfu_3nowords:movr3,r7,get_byte_3
- teqip,#0
- beq.cfu_finished
- cmpip,#2
- strbr3,[r0],#1
- USER(ldrgebtr3,[r1],#1)@Mayfault
- strgebr3,[r0],#1
- USER(ldrgtbtr3,[r1],#1)@Mayfault
- strgtbr3,[r0],#1
- b.cfu_finished
- .section.fixup,”ax”
- .align0
- /*
- *Wetookanexception.r0containsapointerto
- *thebytenotcopied.
- */
- 9001:ldrr2,[sp],#4@void*to
- subr2,r0,r2@bytescopied
- ldrr1,[sp],#4@unsignedlongcount
- subsr4,r1,r2@byteslefttocopy
- movner1,r4
- blne__memzero
- movr0,r4
- LOADREGS(fd,sp!,{r4-r7,pc})
- .previous
咱们将在另一篇博文中详细剖析该函数。