微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > linux内核中的copy_to_user和copy_from_user(一)

linux内核中的copy_to_user和copy_from_user(一)

时间:11-22 来源:互联网 点击:

1.2.__arch_copy_from_user

在深入讲解之前,我们先想一个问题:为什么要使用copy_from_user函数???理论上,内核空间可以直接使用用户空间传过来的指针,即使要做数据拷贝的动作,也可以直接使用memcpy,事实上,在没有MMU的体系架构上,copy_form_user最终的实现就是利用了memcpy。但对于大多数有MMU的平台,情况就有了一些变化:用户空间传过来的指针是在虚拟地址空间上的,它指向的虚拟地址空间很可能还没有真正映射到实际的物理页面上。但这又能怎样呢?缺页导致的异常会透明的被内核予以修复(为缺页的地址空间提交新的物理页面),访问到缺页的指令会继续运行仿佛什么都没有发生一样。但这只是用户空间缺页异常的行为,在内核空间这样却因一场必须被显示的修复,这是由内核提供的缺页异常处理函数的设计模式决定的,其背后的思想后:在内核态中,如果程序试图访问一个尚未提交物理页面的用户空间地址,内核必须对此保持警惕而不能像用户空间那样毫无察觉。

如果内核访问一个尚未被提交物理页面的空间,将产生缺页异常,内核会调用do_page_fault,因为异常发生在内核空间,do_page_fault将调用search_exception_tables在“ __ex_table”中查找异常指令的修复指令,在__arch_copy_from_user函数中经常使用USER宏,这个宏中了定义了“__ex_table”section。

linux/include/asm-arm/assembler.h

[plain]view plaincopyprint?

  1. #defineUSER(x...)\
  2. 9999:x;\
  3. .section__ex_table,"a";\
  4. .align3;\
  5. .long9999b,9001f;\
  6. .previous

该定义中有如下数据;

[plain]view plaincopyprint?

  1. .long9999b,9001f;

其中9999b对应标号9999处的指令,9001f是9001处的指令,是9999b处指令的修复指令。这样,当标号9999处发生缺页异常时,系统将调用do_page_fault提交物理页面,然后跳到9001继续执行。

如果在驱动程序中不使用copy_from_user而用memcpy来代替,对于上述的情形会产生什么结果呢?当标号9999出发生缺页异常时,系统在“__ex_table”section总将找不到修复地址,因为memcpy没有像copy_from_user那样定义一个“__ex_table”section,此时do_page_fault将通过no_context函数产生Oops。极有可能会看到类似如下信息:

Unable to handle kernel NULL pointer dereference at virtual address 00000fe0

所有为了确保设备驱动程序的安全,应该使用copy_from_user函数而不是memcpy。

下面我们深入分析__arch_copy_from_user函数的实现,该函数是用汇编实现的,定义在linux/arch/arm/lib/uaccess.S文件中。

[plain]view plaincopyprint?

  1. /*Prototype:unsignedlong__arch_copy_from_user(void*to,constvoid*from,unsignedlongn);
  2. *Purpose:copyablockfromusermemorytokernelmemory
  3. *Params:to-kernelmemory
  4. *:from-usermemory
  5. *:n-numberofbytestocopy
  6. *Returns:NumberofbytesNOTcopied.
  7. */
  8. .cfu_dest_not_aligned:
  9. rsbip,ip,#4
  10. cmpip,#2
  11. USER(ldrbtr3,[r1],#1)@Mayfault
  12. strbr3,[r0],#1
  13. USER(ldrgebtr3,[r1],#1)@Mayfault
  14. strgebr3,[r0],#1
  15. USER(ldrgtbtr3,[r1],#1)@Mayfault
  16. strgtbr3,[r0],#1
  17. subr2,r2,ip
  18. b.cfu_dest_aligned
  19. ENTRY(__arch_copy_from_user)
  20. stmfdsp!,{r0,r2,r4-r7,lr}
  21. cmpr2,#4
  22. blt.cfu_not_enough
  23. PLD(pld[r1,#0])
  24. PLD(pld[r0,#0])
  25. andsip,r0,#3
  26. bne.cfu_dest_not_aligned
  27. .cfu_dest_aligned:
  28. andsip,r1,#3
  29. bne.cfu_src_not_aligned
  30. /*
  31. *Seeingastherehastobeatleast8bytestocopy,wecan
  32. *copyoneword,andforceauser-modepagefault...
  33. */
  34. .cfu_0fupi:subsr2,r2,#4
  35. addmiip,r2,#4
  36. bmi.cfu_0nowords
  37. USER(ldrtr3,[r1],#4)
  38. strr3,[r0],#4
  39. movip,r1,lsl#32-PAGE_SHIFT@Oneachpage,useald/st??tinstruction
  40. rsbip,ip,#0
  41. movsip,ip,lsr#32-PAGE_SHIFT
  42. beq.cfu_0fupi
  43. /*
  44. *ip=maxno.ofbytestocopybeforeneedinganother"strt"insn
  45. */
  46. cmpr2,ip
  47. movltip,r2
  48. subr2,r2,ip
  49. subsip,ip,#32
  50. blt.cfu_0rem8lp
  51. PLD(pld[r1,#28])
  52. PLD(pld[r0,#28])
  53. PLD(subsip,ip,#64)
  54. PLD(blt.cfu_0cpynopld)
  55. PLD(pld[r1,#60])
  56. PLD(pld[r0,#60])
  57. .cfu_0cpy8lp:
  58. PLD(pld[r1,#92])
  59. PLD(pld[r0,#92])
  60. .cfu_0cpynopld:ldmiar1!,{r3-r6}@Shouldntfault
  61. stmiar0!,{r3-r6}
  62. ldmiar1!,{r3-r6}@Shouldntfault
  63. subsip,ip,#32
  64. stmiar0!,{r3-r6}
  65. bpl.cfu_0cpy8lp
  66. PLD(cmnip,#64)
  67. PLD(bge.cfu_0cpynopld)
  68. PLD(addip,ip,#64)
  69. .cfu_0rem8lp:cmnip,#16
  70. ldmgeiar1!,{r3-r6}@Shouldntfault
  71. stmgeiar0!,{r3-r6}
  72. tstip,#8
  73. ldmneiar1!,{r3-r4}@Shouldntfault
  74. stmneiar0!,{r3-r4}
  75. tstip,

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top