Linux 启动流程之自解压(二)
2 Head.s .start段程序入口start
Start是zImage在启动时,最开始执行的代码。也就是说.start段被链接到代码的最开头处。
.section ".start", #alloc, #execinstr
//创建.start段,将以下代码放入该段空间中
/*
* sort out different calling conventions
*/
.align
/*
* Always enter in ARM state for CPUs that support the ARM ISA.
* As of today (2014) that's exactly the members of the A and R
* classes.
*/
AR_CLASS( .arm )
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 )
#ifdef CONFIG_ARM_VIRT_EXT
bl __hyp_stub_install @ get into SVC mode, reversibly
#endif
mov r7, r1 @ save architecture ID,保存arch_id到r7
mov r8, r2 @ save atags pointer,保存启动参数到r8
#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 @使CPU进去SVC模式
msr spsr_cxsf, r9 @ Save the CPU boot mode in
@ SPSR
#endif
3 Head.s .text段
3.1 内核加压地址的确定
//TEXT_OFFSET是代码相对于物理内存的偏移,通常选为32k=0x8000。
//这个是有原因的,主要适用于存放Cache的一级页表。
//先看CONFIG_AUTO_ZRELADDR这个宏所含的内容,
//它的意思是如果你不知道ZRELADDR地址要定在内存什么地方,那么这段代码就可以帮你。
//看到0xf8000000了吧,那么后面有多少个0呢?
//答案是27个,那么2的27次方就是128M,这就明白了,
//只要你把解压程序放在你最后解压完成后的内核空间的128M之内的偏移的话,
//就可以自动设定好解压后内核要运行的地址ZRELADDR。
//如果你没有定义的话,那么,就会去取zreladdr作为最后解压的内核运行地。
//那么这个zreladdr是从哪里来的呢?
//答案是在:arch/arm/boot/compressed/Makefile中定义的
#ifdef CONFIG_AUTO_ZRELADDR
/*
* Find the start of physical memory. As we are executing
* without the MMU on, we are in the physical address space.
* We just need to get rid of any offset by aligning the
* address.
*
* This alignment is a balance between the requirements of
* different platforms - we have chosen 128MB to allow
* platforms which align the start of their physical memory
* to 128MB to use this feature, while allowing the zImage
* to be placed within the first 128MB of memory on other
* platforms. Increasing the alignment means we place
* stricter alignment requirements on the start of physical
* memory, but relaxing it means that we break people who
* are already placing their zImage in (eg) the top 64MB
* of this range.
*/
mov r4, pc
and r4, r4, #0xf8000000
/* Determine final kernel image address. */
add r4, r4, #TEXT_OFFSET
#else
ldr r4, =zreladdr
#endif
3.2 Zreladdr的来源
# Supply ZRELADDR to the decompressor via a linker symbol.
ifneq ($(CONFIG_AUTO_ZRELADDR),y)
LDFLAGS_vmlinux += --defsym zreladdr=$(ZRELADDR)
Endif
ZRELADDR的定义如下:
ifneq ($(MACHINE),)
include $(srctree)/$(MACHINE)/Makefile.boot
endif
# Note: the following conditions must always be true:
# ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
# PARAMS_PHYS must be within 4MB of ZRELADDR
# INITRD_PHYS must be in RAM
ZRELADDR := $(zreladdr-y)
PARAMS_PHYS:= $(params_phys-y)
INITRD_PHYS:= $(initrd_phys-y)
而里面的几个参数是在每个arch/arm/Mach-xxx/ Makefile.boot里面定义的,内容如下:
zreladdr-y := 0x20008000
params_phys-y := 0x20000100
initrd_phys-y := 0x21000000
这下知道了,绕了一大圈,终于知道r4存的是什么了,就是最后内核解压的起址,也是最后解压后的内核的运行地址,记住,这个地址很重要。
Linux基本参数,如下表: