内核解压的代码在arch/arm/boot/compressed/head.S
- start:
- .type start,#function
- .rept 7
- __nop
- .endr
- #ifndef CONFIG_THUMB2_KERNEL
- mov r0, r0
- #else
- AR_CLASS( sub pc, pc, #3 ) @ A/R: switch to Thumb2 mode
- M_CLASS( nop.w ) @ M: already in Thumb2 mode
- .thumb
- #endif
- W(b) 1f
-
- .word _magic_sig @ Magic numbers to help the loader
- .word _magic_start @ absolute load/run zImage address
- .word _magic_end @ zImage end address
- .word 0x04030201 @ endianness flag
- .word 0x45454545 @ another magic number to indicate
- .word _magic_table @ additional data table
-
- __EFI_HEADER
- 1:
- ARM_BE8( setend be ) @ go BE8 if compiled for BE8
- AR_CLASS( mrs r9, cpsr )
首先将start定义成函数类型
然后放置了7个nop指令,由于没有配置THUMB2,所以会有指令 mov r0, r0 等同nop
也就是说内核上来就执行了8个nop指令
注意这里的__nop是一个宏定义 arch/arm/boot/compressed/efi-header.S
- .macro __nop
- #ifdef CONFIG_EFI_STUB
- @ This is almost but not quite a NOP, since it does clobber the
- @ condition flags. But it is the best we can do for EFI, since
- @ PE/COFF expects the magic string "MZ" at offset 0, while the
- @ ARM/Linux boot protocol expects an executable instruction
- @ there.
- .inst MZ_MAGIC | (0x1310 << 16) @ tstne r0, #0x4d000
- #else
- AR_CLASS( mov r0, r0 )
- M_CLASS( nop.w )
- #endif
平台是armv7A,所以上面就是8个 mov r0, r0指令
这里验证一下我们的猜想,首先看一个mov的机器码
其中cond的值如下
这样就可以知道mov r0, r0的机器码是 1110_0001_1010_0000_0000_0000_0000_0000 = 0xE1A00000
- hexdump kernel/arch/arm/boot/zImage -n 64
- 0000000 0000 e1a0 0000 e1a0 0000 e1a0 0000 e1a0
- *
- 0000020 0005 ea00 2818 016f 0000 0000 74c0 005c
- 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
- #ifdef CONFIG_ARM_VIRT_EXT
- bl __hyp_stub_install @ get into SVC mode, reversibly
- #endif
这部分是虚拟化相关的,不了解
- mov r7, r1 @ save architecture ID
- mov r8, r2 @ save atags pointer
-
- #ifndef CONFIG_CPU_V7M
- /*
- * Booting from Angel - need to enter SVC mode and disable
- * FIQs/IRQs (numeric definitions from angel arm.h source).
- * We only do this if we were in user mode on entry.
- */
- mrs r2, cpsr @ get current mode
- tst r2, #3 @ not user?
- bne not_angel
- mov r0, #0x17 @ angel_SWIreason_EnterSVC
- ARM( swi 0x123456 ) @ angel_SWI_ARM
- THUMB( svc 0xab ) @ angel_SWI_THUMB
- not_angel:
- safe_svcmode_maskall r0
- msr spsr_cxsf, r9 @ Save the CPU boot mode in
- @ SPSR
- #endif
首先判断是不是用户模式,只要最低2个bit不是0,那么就不是user模式。
对于非user模式进入的则跳到not_angle,对于通过angle进来的则是user模式,用户模式直接调用swi进入svc模式。
这里的angle百度了一下,好像是arm的调试协议,这里忽略。
arch/arm/include/asm/assembler.h
- .macro safe_svcmode_maskall reg:req
- #if __LINUX_ARM_ARCH__ >= 6 && !defined(CONFIG_CPU_V7M)
- /* 读取cpsr的值 */
- mrs \reg , cpsr
-
- /* HYP_MODE值是0x1a
- * 异或之后,只有当前模式是HYP mode的时候,bit[3:0]值才会是0
- * 这里异或的原因其实是判断是不是在HYP mode
- */
- eor \reg, \reg, #HYP_MODE
-
- /* #define MODE_MASK 0x0000001f
- * bit[4:0]]
- */
- /* 判断cpsr的低5bits值
- * 如果是0,则Z=1,说明是HYP mode
- * 否则Z=0
- */
- tst \reg, #MODE_MASK
-
- /* 低5bits清0 */
- bic \reg , \reg , #MODE_MASK
-
- /* #define PSR_I_BIT 0x00000080
- * #define PSR_F_BIT 0x00000040
- * #define SVC_MODE 0x00000013
- * 或操作
- * 关闭FIQ IRQ 设置为SVC模式
- */
- orr \reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE
-
- THUMB( orr \reg , \reg , #PSR_T_BIT )
- /* bne跳转的条件是z==0
- * 也就是说明进来的时候不是用户模式
- * 如果z==1, 说明进来的时候是HYP mode
- */
- bne 1f
- /* 走到这里说明是 HYP mode*/
- /* 禁止异步中止 */
- /* asynchronous abort来自外部memory system,
- * 源于bus上的一些错误,例如不可恢复的ECC error
- */
- orr \reg, \reg, #PSR_A_BIT
-
- /* badr是一个宏 调用的是adr指令
- * adr r0, _start 得到的是_start的当前执行位置,
- * 由 pc+offset 决定的 得到有效地址
- * ldr r0, =_start 得到的是绝对的地址,链接时决定;*/
- /* 这里为什么要提前保存lr ? */
- badr lr, 2f
-
- /*
- * c - control field mask byte(xPSR[7:0])
- * x - extension field mask byte(xPSR[15:8])
- * s - status field mask byte(xPSR[23:16)
- * f - flags field mask byte(xPSR[31:24]).
- * reg的值写到spsr中
- */
- msr spsr_cxsf, \reg
- __MSR_ELR_HYP(14)
- __ERET
- 1: msr cpsr_c, \reg
- 2:
- #else
- /*
- * workaround for possibly broken pre-v6 hardware
- * (akita, Sharp Zaurus C-1000, PXA270-based)
- */
- setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, \reg
- #endif
- .endm
这章节有个疑惑,badr lr, 2f,为什么提前保存lr(HYP mode)?