linux內(nèi)核中的copy_to_user和copy_from_user(一)
CPU architecture:ARM920T
本文引用地址:http://m.butianyuan.cn/article/201611/319995.htmAuthor:ce123(http://blog.csdn.net/ce123)
1.copy_from_user
在學(xué)習(xí)Linux內(nèi)核驅(qū)動的時候,經(jīng)常會碰到copy_from_user和copy_to_user這兩個函數(shù),設(shè)備驅(qū)動程序中的ioctl函數(shù)就經(jīng)常會用到。這兩個函數(shù)負(fù)責(zé)在用戶空間和內(nèi)核空間傳遞數(shù)據(jù)。首先看看它們的定義(linux/include/asm-arm/uaccess.h),先看copy_from_user:
- 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;
- }
先看函數(shù)的三個參數(shù):*to是內(nèi)核空間的指針,*from是用戶空間指針,n表示從用戶空間想內(nèi)核空間拷貝數(shù)據(jù)的字節(jié)數(shù)。如果成功執(zhí)行拷貝操作,則返回0,否則返回還沒有完成拷貝的字節(jié)數(shù)。
這個函數(shù)從結(jié)構(gòu)上來分析,其實都可以分為兩個部分:- 首先檢查用戶空間的地址指針是否有效;
- 調(diào)用__arch_copy_from_user函數(shù)。
1.1.access_ok
access_ok用來對用戶空間的地址指針from作某種有效性檢驗,這個宏和體系結(jié)構(gòu)相關(guān),在arm平臺上為(linux/include/asm-arm/uaccess.h):
- #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)
(1)unsigned long flag, sum;\定義兩個變量
- flag:保存結(jié)果的變量:非零代表地址無效,零代表地址可以訪問。初始存放非零值(current_thread_info()->addr_limit),也就是當(dāng)前進(jìn)程的地址上限值。
- sum:保存要訪問的地址范圍末端,用于和當(dāng)前進(jìn)程地址空間限制數(shù)據(jù)做比較。
這個函數(shù)涉及到__CHECKER__宏的判斷,__CHECKER__宏在通過Sparse(Semantic Parser for C)工具對內(nèi)核代碼進(jìn)行檢查時會定義的。在使用make C=1或C=2時便會調(diào)用該工具,這個工具可以檢查在代碼中聲明了sparse所能檢查到的相關(guān)屬性的內(nèi)核函數(shù)和變量。
- 如果定義了__CHECKER__,__chk_user_ptr和__chk_io_ptr在這里只聲明函數(shù),沒有函數(shù)體,目的就是在編譯過程中Sparse能夠捕捉到編譯錯誤,檢查參數(shù)的類型。
- 如果沒有定義__CHECKER__,這就是一個空函數(shù)。
請看具體的定義(linux/compiler.h):
- #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
adds %1, %2, %3
sum = addr + size 這個操作影響狀態(tài)位(目的是影響是進(jìn)位標(biāo)志C),以下的兩個指令都帶有條件CC,也就是當(dāng)C=0的時候才執(zhí)行。
如果上面的加法指令進(jìn)位了(C=1),則以下的指令都不執(zhí)行,flag就為初始值current_thread_info()->addr_limit(非0),并返回。
如果沒有進(jìn)位(C=0),就執(zhí)行下面的指令:
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
當(dāng)C=0的時候執(zhí)行以下指令,否則跳過(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),返回零
從這里再次可以認(rèn)識到,copy_from_user的使用是結(jié)合進(jìn)程上下文的,因為他們要訪問“user”的內(nèi)存空間,這個“user”必須是某個特定的進(jìn)程。通過上面的源碼就知道,其中使用了current_thread_info()來檢查空間是否可以訪問。如果在驅(qū)動中使用這兩個函數(shù),必須是在實現(xiàn)系統(tǒng)調(diào)用的函數(shù)中使用,不可在實現(xiàn)中斷處理的函數(shù)中使用。如果在中斷上下文中使用了,那代碼就很可能操作了根本不相關(guān)的進(jìn)程地址空間。其次由于操作的頁面可能被換出,這兩個函數(shù)可能會休眠,所以同樣不可在中斷上下文中使用。
1.2.__arch_copy_from_user
- #defineUSER(x...)
- 9999:x;
- .section__ex_table,"a";
- .align3;
- .long9999b,9001f;
- .previous
- .long9999b,9001f;
- /*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
評論