• 2. 内核解压-关中断进入svc模式


    内核解压的代码在arch/arm/boot/compressed/head.S

    1. start:
    2. .type start,#function
    3. .rept 7
    4. __nop
    5. .endr
    6. #ifndef CONFIG_THUMB2_KERNEL
    7. mov r0, r0
    8. #else
    9. AR_CLASS( sub pc, pc, #3 ) @ A/R: switch to Thumb2 mode
    10. M_CLASS( nop.w ) @ M: already in Thumb2 mode
    11. .thumb
    12. #endif
    13. W(b) 1f
    14. .word _magic_sig @ Magic numbers to help the loader
    15. .word _magic_start @ absolute load/run zImage address
    16. .word _magic_end @ zImage end address
    17. .word 0x04030201 @ endianness flag
    18. .word 0x45454545 @ another magic number to indicate
    19. .word _magic_table @ additional data table
    20. __EFI_HEADER
    21. 1:
    22. ARM_BE8( setend be ) @ go BE8 if compiled for BE8
    23. AR_CLASS( mrs r9, cpsr )

    首先将start定义成函数类型

    然后放置了7个nop指令,由于没有配置THUMB2,所以会有指令 mov r0, r0 等同nop

    也就是说内核上来就执行了8个nop指令

    注意这里的__nop是一个宏定义 arch/arm/boot/compressed/efi-header.S

    1. .macro __nop
    2. #ifdef CONFIG_EFI_STUB
    3. @ This is almost but not quite a NOP, since it does clobber the
    4. @ condition flags. But it is the best we can do for EFI, since
    5. @ PE/COFF expects the magic string "MZ" at offset 0, while the
    6. @ ARM/Linux boot protocol expects an executable instruction
    7. @ there.
    8. .inst MZ_MAGIC | (0x1310 << 16) @ tstne r0, #0x4d000
    9. #else
    10. AR_CLASS( mov r0, r0 )
    11. M_CLASS( nop.w )
    12. #endif

    平台是armv7A,所以上面就是8个 mov r0, r0指令

    这里验证一下我们的猜想,首先看一个mov的机器码

     其中cond的值如下

     这样就可以知道mov r0, r0的机器码是 1110_0001_1010_0000_0000_0000_0000_0000 = 0xE1A00000

    1. hexdump kernel/arch/arm/boot/zImage -n 64
    2. 0000000 0000 e1a0 0000 e1a0 0000 e1a0 0000 e1a0
    3. *
    4. 0000020 0005 ea00 2818 016f 0000 0000 74c0 005c
    5. 0000030 0201 0403 4545 4545 3d40 0000 9000 e10f

    对比压缩的内核镜像,可以看到确实如此,猜想没有问题

    对于下面的.word其实也是可以看出来的,比如 _magic_sig = (0x016f2818);这个是vmlinux.lds中定义的,继续分析上面的数据

    0000020 0005 ea00  <- W(b) 1f     2818 016f   <-  _magic_sig  0000 0000 <- _magic_start 74c0 005c <- _magic_end
    0000030 0201 0403  <- 0x04030201  4545 4545 <- 0x45454545  3d40 0000  <- _magic_table 9000 e10f <- mrs r9, cpsr

    cpsr寄存器如下

     其中M bit

    1. #ifdef CONFIG_ARM_VIRT_EXT
    2. bl __hyp_stub_install @ get into SVC mode, reversibly
    3. #endif

    这部分是虚拟化相关的,不了解

    1. mov r7, r1 @ save architecture ID
    2. mov r8, r2 @ save atags pointer
    3. #ifndef CONFIG_CPU_V7M
    4. /*
    5. * Booting from Angel - need to enter SVC mode and disable
    6. * FIQs/IRQs (numeric definitions from angel arm.h source).
    7. * We only do this if we were in user mode on entry.
    8. */
    9. mrs r2, cpsr @ get current mode
    10. tst r2, #3 @ not user?
    11. bne not_angel
    12. mov r0, #0x17 @ angel_SWIreason_EnterSVC
    13. ARM( swi 0x123456 ) @ angel_SWI_ARM
    14. THUMB( svc 0xab ) @ angel_SWI_THUMB
    15. not_angel:
    16. safe_svcmode_maskall r0
    17. msr spsr_cxsf, r9 @ Save the CPU boot mode in
    18. @ SPSR
    19. #endif

    首先判断是不是用户模式,只要最低2个bit不是0,那么就不是user模式。

    对于非user模式进入的则跳到not_angle,对于通过angle进来的则是user模式,用户模式直接调用swi进入svc模式。

    这里的angle百度了一下,好像是arm的调试协议,这里忽略。

    arch/arm/include/asm/assembler.h

    1. .macro safe_svcmode_maskall reg:req
    2. #if __LINUX_ARM_ARCH__ >= 6 && !defined(CONFIG_CPU_V7M)
    3. /* 读取cpsr的值 */
    4. mrs \reg , cpsr
    5. /* HYP_MODE值是0x1a
    6. * 异或之后,只有当前模式是HYP mode的时候,bit[3:0]值才会是0
    7. * 这里异或的原因其实是判断是不是在HYP mode
    8. */
    9. eor \reg, \reg, #HYP_MODE
    10. /* #define MODE_MASK 0x0000001f
    11. * bit[4:0]]
    12. */
    13. /* 判断cpsr的低5bits值
    14. * 如果是0,则Z=1,说明是HYP mode
    15. * 否则Z=0
    16. */
    17. tst \reg, #MODE_MASK
    18. /*5bits清0 */
    19. bic \reg , \reg , #MODE_MASK
    20. /* #define PSR_I_BIT 0x00000080
    21. * #define PSR_F_BIT 0x00000040
    22. * #define SVC_MODE 0x00000013
    23. * 或操作
    24. * 关闭FIQ IRQ 设置为SVC模式
    25. */
    26. orr \reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE
    27. THUMB( orr \reg , \reg , #PSR_T_BIT )
    28. /* bne跳转的条件是z==0
    29. * 也就是说明进来的时候不是用户模式
    30. * 如果z==1, 说明进来的时候是HYP mode
    31. */
    32. bne 1f
    33. /* 走到这里说明是 HYP mode*/
    34. /* 禁止异步中止 */
    35. /* asynchronous abort来自外部memory system,
    36. * 源于bus上的一些错误,例如不可恢复的ECC error
    37. */
    38. orr \reg, \reg, #PSR_A_BIT
    39. /* badr是一个宏 调用的是adr指令
    40. * adr     r0, _start  得到的是_start的当前执行位置,
    41. * 由 pc+offset 决定的 得到有效地址
    42. * ldr     r0, =_start  得到的是绝对的地址,链接时决定;*/
    43. /* 这里为什么要提前保存lr ? */
    44. badr lr, 2f
    45. /*
    46. * c - control field mask byte(xPSR[7:0])
    47. * x - extension field mask byte(xPSR[15:8])
    48. * s - status field mask byte(xPSR[23:16)
    49. * f - flags field mask byte(xPSR[31:24]).
    50. * reg的值写到spsr中
    51. */
    52. msr spsr_cxsf, \reg
    53. __MSR_ELR_HYP(14)
    54. __ERET
    55. 1: msr cpsr_c, \reg
    56. 2:
    57. #else
    58. /*
    59. * workaround for possibly broken pre-v6 hardware
    60. * (akita, Sharp Zaurus C-1000, PXA270-based)
    61. */
    62. setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, \reg
    63. #endif
    64. .endm

    这章节有个疑惑,badr lr, 2f,为什么提前保存lr(HYP mode)?

  • 相关阅读:
    在Go项目中二次封装Kafka客户端功能
    jenkins部署
    答辩提纲的写作内容指导
    XML 编辑器:功能、选择与使用技巧
    Spring依赖注入与控制反转
    6 寻找比目标字母大的最小字母
    StreamSets解析MySQL Binlog写入Kafka
    MXNet学习笔记
    设置windos电脑开机自动启动chrome浏览器,并且打开指定网页
    【网络教程】如何解决Docker删除镜像和容器后磁盘空间未释放的问题
  • 原文地址:https://blog.csdn.net/ldl617/article/details/126932676