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:
- static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
- {
- if (access_ok(VERIFY_READ, from, n))
- n = __arch_copy_from_user(to, from, n);
- else /* security hole - plug it */
- memzero(to, n);
- return n;
- }
先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表示从用户空间想内核空间拷贝数据的字节数。如果成功执行拷贝操作,则返回0,否则返回还没有完成拷贝的字节数。
这个函数从结构上来分析,其实都可以分为两个部分:- 首先检查用户空间的地址指针是否有效;
- 调用__arch_copy_from_user函数。
1.1.access_ok
access_ok用来对用户空间的地址指针from作某种有效性检验,这个宏和体系结构相关,在arm平台上为(linux/include/asm-arm/uaccess.h):
- #define __range_ok(addr,size) ({ \
- unsigned long flag, 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; })
- #define access_ok(type,addr,size) (__range_ok(addr,size) == 0)
(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):
- #ifdef __CHECKER__
- ...
- extern void __chk_user_ptr(void __user *);
- extern void __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 这个操作影响状态位(目的是影响是进位标志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),返回零
从这里再次可以认识到,copy_from_user的使用是结合进程上下文的,因为他们要访问“user”的内存空间,这个“user”必须是某个特定的进程。通过上面的源码就知道,其中使用了current_thread_info()来检查空间是否可以访问。如果在驱动中使用这两个函数,必须是在实现系统调用的函数中使用,不可在实现中断处理的函数中使用。如果在中断上下文中使用了,那代码就很可能操作了根本不相关的进程地址空间。其次由于操作的页面可能被换出,这两个函数可能会休眠,所以同样不可在中断上下文中使用。
1.2.__arch_copy_from_user
- #define USER(x...) \
- 9999: x; \
- .section __ex_table,"a"; \
- .align 3; \
- .long 9999b,9001f; \
- .previous
- .long 9999b,9001f;
- /* Prototype: unsigned long __arch_copy_from_user(void *to,const void *from,unsigned long n);
- * Purpose : copy a block from user memory to kernel memory
- * Params : to - kernel memory
- * : from - user memory
- * : n - number of bytes to copy
- * Returns : Number of bytes NOT copied.
- */
- .cfu_dest_not_aligned:
- rsb ip, ip, #4
- cmp ip, #2
- USER( ldrbt r3, [r1], #1) @ May fault
- strb r3, [r0], #1
- USER( ldrgebt r3, [r1], #1) @ May fault
- strgeb r3, [r0], #1
- USER( ldrgtbt r3, [r1], #1) @ May fault
- strgtb r3, [r0], #1
- sub r2, r2, ip
- b .cfu_dest_aligned
- ENTRY(__arch_copy_from_user)
- stmfd sp!, {r0, r2, r4 - r7, lr}
- cmp r2, #4
- blt .cfu_not_enough
- PLD( pld [r1, #0] )
- PLD( pld [r0, #0] )
- ands ip, r0, #3
- bne .cfu_dest_not_aligned
- .cfu_dest_aligned:
- ands ip, r1, #3
- bne .cfu_src_not_aligned
- /*
- * Seeing as there has to be at least 8 bytes to copy, we can
- * copy one word, and force a user-mode page fault...
- */
- .cfu_0fupi: subs r2, r2, #4
- addmi ip, r2, #4
- bmi .cfu_0nowords
- USER( ldrt r3, [r1], #4)
- str r3, [r0], #4
- mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
- rsb ip, ip, #0
- movs ip, ip, lsr #32 - PAGE_SHIFT
- beq .cfu_0fupi
- /*
- * ip = max no. of bytes to copy before needing another "strt" insn
- */
- cmp r2, ip
- movlt ip, r2
- sub r2, r2, ip
- subs ip, ip, #32
- blt .cfu_0rem8lp
- PLD( pld [r1, #28] )
- PLD( pld [r0, #28] )
- PLD( subs ip, 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: ldmia r1!, {r3 - r6} @ Shouldnt fault
- stmia r0!, {r3 - r6}
- ldmia r1!, {r3 - r6} @ Shouldnt fault
- subs ip, ip, #32
- stmia r0!, {r3 - r6}
- bpl .cfu_0cpy8lp
- PLD( cmn ip, #64 )
- PLD( bge .cfu_0cpynopld )
- PLD( add ip, ip, #64 )
- .cfu_0rem8lp: cmn ip, #16
- ldmgeia r1!, {r3 - r6} @ Shouldnt fault
- stmgeia r0!, {r3 - r6}
- tst ip, #8
- ldmneia r1!, {r3 - r4} @ Shouldnt fault
- stmneia r0!, {r3 - r4}
- tst ip, #4
- ldrnet r3, [r1], #4 @ Shouldnt fault
- strne r3, [r0], #4
- ands ip, ip, #3
- beq .cfu_0fupi
- .cfu_0nowords: teq ip, #0
- beq .cfu_finished
- .cfu_nowords: cmp ip, #2
- USER( ldrbt r3, [r1], #1) @ May fault
- strb r3, [r0], #1
- USER( ldrgebt r3, [r1], #1) @ May fault
- strgeb r3, [r0], #1
- USER( ldrgtbt r3, [r1], #1) @ May fault
- strgtb r3, [r0], #1
- b .cfu_finished
- .cfu_not_enough:
- movs ip, r2
- bne .cfu_nowords
- .cfu_finished: mov r0, #0
- add sp, sp, #8
- LOADREGS(fd,sp!,{r4 - r7, pc})
- .cfu_src_not_aligned:
- bic r1, r1, #3
- USER( ldrt r7, [r1], #4) @ May fault
- cmp ip, #2
- bgt .cfu_3fupi
- beq .cfu_2fupi
- .cfu_1fupi: subs r2, r2, #4
- addmi ip, r2, #4
- bmi .cfu_1nowords
- mov r3, r7, pull #8
- USER( ldrt r7, [r1], #4) @ May fault
- orr r3, r3, r7, push #24
- str r3, [r0], #4
- mov ip, r1, lsl #32 - PAGE_SHIFT
- rsb ip, ip, #0
- movs ip, ip, lsr #32 - PAGE_SHIFT
- beq .cfu_1fupi
- cmp r2, ip
- movlt ip, r2
- sub r2, r2, ip
- subs ip, ip, #16
- blt .cfu_1rem8lp
- PLD( pld [r1, #12] )
- PLD( pld [r0, #12] )
- PLD( subs ip, 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: mov r3, r7, pull #8
- ldmia r1!, {r4 - r7} @ Shouldnt fault
- subs ip, ip, #16
- orr r3, r3, r4, push #24
- mov r4, r4, pull #8
- orr r4, r4, r5, push #24
- mov r5, r5, pull #8
- orr r5, r5, r6, push #24
- mov r6, r6, pull #8
- orr r6, r6, r7, push #24
- stmia r0!, {r3 - r6}
- bpl .cfu_1cpy8lp
- PLD( cmn ip, #32 )
- PLD( bge .cfu_1cpynopld )
- PLD( add ip, ip, #32 )
- .cfu_1rem8lp: tst ip, #8
- movne r3, r7, pull #8
- ldmneia r1!, {r4, r7} @ Shouldnt fault
- orrne r3, r3, r4, push #24
- movne r4, r4, pull #8
- orrne r4, r4, r7, push #24
- stmneia r0!, {r3 - r4}
- tst ip, #4
- movne r3, r7, pull #8
- USER( ldrnet r7, [r1], #4) @ May fault
- orrne r3, r3, r7, push #24
- strne r3, [r0], #4
- ands ip, ip, #3
- beq .cfu_1fupi
- .cfu_1nowords: mov r3, r7, get_byte_1
- teq ip, #0
- beq .cfu_finished
- cmp ip, #2
- strb r3, [r0], #1
- movge r3, r7, get_byte_2
- strgeb r3, [r0], #1
- movgt r3, r7, get_byte_3
- strgtb r3, [r0], #1
- b .cfu_finished
- .cfu_2fupi: subs r2, r2, #4
- addmi ip, r2, #4
- bmi .cfu_2nowords
- mov r3, r7, pull #16
- USER( ldrt r7, [r1], #4) @ May fault
- orr r3, r3, r7, push #16
- str r3, [r0], #4
- mov ip, r1, lsl #32 - PAGE_SHIFT
- rsb ip, ip, #0
- movs ip, ip, lsr #32 - PAGE_SHIFT
- beq .cfu_2fupi
- cmp r2, ip
- movlt ip, r2
- sub r2, r2, ip
- subs ip, ip, #16
- blt .cfu_2rem8lp
- PLD( pld [r1, #12] )
- PLD( pld [r0, #12] )
- PLD( subs ip, 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: mov r3, r7, pull #16
- ldmia r1!, {r4 - r7} @ Shouldnt fault
- subs ip, ip, #16
- orr r3, r3, r4, push #16
- mov r4, r4, pull #16
- orr r4, r4, r5, push #16
- mov r5, r5, pull #16
- orr r5, r5, r6, push #16
- mov r6, r6, pull #16
- orr r6, r6, r7, push #16
- stmia r0!, {r3 - r6}
- bpl .cfu_2cpy8lp
- PLD( cmn ip, #32 )
- PLD( bge .cfu_2cpynopld )
- PLD( add ip, ip, #32 )
- .cfu_2rem8lp: tst ip, #8
- movne r3, r7, pull #16
- ldmneia r1!, {r4, r7} @ Shouldnt fault
- orrne r3, r3, r4, push #16
- movne r4, r4, pull #16
- orrne r4, r4, r7, push #16
- stmneia r0!, {r3 - r4}
- tst ip, #4
- movne r3, r7, pull #16
- USER( ldrnet r7, [r1], #4) @ May fault
- orrne r3, r3, r7, push #16
- strne r3, [r0], #4
- ands ip, ip, #3
- beq .cfu_2fupi
- .cfu_2nowords: mov r3, r7, get_byte_2
- teq ip, #0
- beq .cfu_finished
- cmp ip, #2
- strb r3, [r0], #1
- movge r3, r7, get_byte_3
- strgeb r3, [r0], #1
- USER( ldrgtbt r3, [r1], #0) @ May fault
- strgtb r3, [r0], #1
- b .cfu_finished
- .cfu_3fupi: subs r2, r2, #4
- addmi ip, r2, #4
- bmi .cfu_3nowords
- mov r3, r7, pull #24
- USER( ldrt r7, [r1], #4) @ May fault
- orr r3, r3, r7, push #8
- str r3, [r0], #4
- mov ip, r1, lsl #32 - PAGE_SHIFT
- rsb ip, ip, #0
- movs ip, ip, lsr #32 - PAGE_SHIFT
- beq .cfu_3fupi
- cmp r2, ip
- movlt ip, r2
- sub r2, r2, ip
- subs ip, ip, #16
- blt .cfu_3rem8lp
- PLD( pld [r1, #12] )
- PLD( pld [r0, #12] )
- PLD( subs ip, 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: mov r3, r7, pull #24
- ldmia r1!, {r4 - r7} @ Shouldnt fault
- orr r3, r3, r4, push #8
- mov r4, r4, pull #24
- orr r4, r4, r5, push #8
- mov r5, r5, pull #24
- orr r5, r5, r6, push #8
- mov r6, r6, pull #24
- orr r6, r6, r7, push #8
- stmia r0!, {r3 - r6}
- subs ip, ip, #16
- bpl .cfu_3cpy8lp
- PLD( cmn ip, #32 )
- PLD( bge .cfu_3cpynopld )
- PLD( add ip, ip, #32 )
- .cfu_3rem8lp: tst ip, #8
- movne r3, r7, pull #24
- ldmneia r1!, {r4, r7} @ Shouldnt fault
- orrne r3, r3, r4, push #8
- movne r4, r4, pull #24
- orrne r4, r4, r7, push #8
- stmneia r0!, {r3 - r4}
- tst ip, #4
- movne r3, r7, pull #24
- USER( ldrnet r7, [r1], #4) @ May fault
- orrne r3, r3, r7, push #8
- strne r3, [r0], #4
- ands ip, ip, #3
- beq .cfu_3fupi
- .cfu_3nowords: mov r3, r7, get_byte_3
- teq ip, #0
- beq .cfu_finished
- cmp ip, #2
- strb r3, [r0], #1
- USER( ldrgebt r3, [r1], #1) @ May fault
- strgeb r3, [r0], #1
- USER( ldrgtbt r3, [r1], #1) @ May fault
- strgtb r3, [r0], #1
- b .cfu_finished
- .section .fixup,"ax"
- .align 0
- /*
- * We took an exception. r0 contains a pointer to
- * the byte not copied.
- */
- 9001: ldr r2, [sp], #4 @ void *to
- sub r2, r0, r2 @ bytes copied
- ldr r1, [sp], #4 @ unsigned long count
- subs r4, r1, r2 @ bytes left to copy
- movne r1, r4
- blne __memzero
- mov r0, r4
- LOADREGS(fd,sp!, {r4 - r7, pc})
- .previous
上一篇:linux内核中的copy_to_user和copy_from_user(二)
下一篇:linux内核中的get_user和put_user