从源头开始分析启动。
问题
寻找linux的编译脚本Makefile
在kernel\kernel-3.4.39\arch\arm\boot\compressed下的Makefile。
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) FORCE
@$(check_for_multiple_zreladdr)
$(call if_changed,ld)
@$(check_for_bad_syms)
这里可以看出我们的目标vmlinux,是通过vmlinux.lds这个链接脚本生成的,$(obj)是当前目录的意思,那么
vmlinux.lds(linux/arch/arm/kernel/vmlinux.lds
OUTPUT_ARCH(arm)
ENTRY(stext)
入口为stext,寻找stext在
换一种思路,在有
的前提下使用
指令反编译源文件后有
也可以得到入口处的印证(反编译文件我稍后会上传)
THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM.
THUMB( bx r9 ) @ If this is a Thumb-2 kernel,
THUMB( .thumb ) @ switch to Thumb now.
THUMB(1: )
这段代码很明显没有在反编译内体现,原因是:
通过CONFIG_THUMB2_KERNEL宏选择是arm指令的内核还是THUMB的内核,这里没有选择CONFIG_THUMB2_KERNEL故没有体现,要开启必须GCC版本大于4.0。
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
按照官方注释说设置使能SVC模式和关闭中断
mrc p15, 0, r9, c0, c0 @ get processor id
从arm协处理器里面读到CPU ID存储到r9,这里的CPU主要是指arm架构相关的CPU型号,比如ARM9,ARM11等等
bl __lookup_processor_type @ r5=procinfo r9=cupid
通过从__lookup_processor_type_data中获取处理器信息位置,通过轮询查找该内核是否支持该本处理器。其中r9是通过processor id
movs r10, r5 @ invalid processor (r5=0)?
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p'
如果是错误,就会跳转到__error_p 内部是nop循环
#ifndef CONFIG_XIP_KERNEL
adr r3, 2f
ldmia r3, {r4, r8}
sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
add r8, r8, r4 @ PHYS_OFFSET
计算偏移量 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
bl __vet_atags
检查bootloader传入的参数列表atags的合法性其地址由Uboot传送
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
bl __fixup_pv_table
#endif
bl __create_page_tables
建立一个临时的page table(将来这个table会被抛弃,重新建立)
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
R13 堆栈,R14 返回指针,R15 程序计数器
__mmap_switched:
adr r3, __switch_data + 4
ldm.w (r6, r7, r8), [r3]+
ldw sp, [r3]
mov fp, #0 @ Clear BSS (and zero fp)
203: csub.a r6, r7
bea 204f
stw.w fp, [r6]+,#4
b 203b
204:
andn r1, r0, #CR_A @ Clear 'A' bit
stm (r0, r1), [r8]+ @ Save control register values
b start_kernel
ENDPROC(__mmap_switched)
记住这里使能mmu后会跳转到这里然后执行b start_kernel
adr lr, BSYM(1f) @ return (PIC) address
mov r8, r4 @ set TTBR1 to swapper_pg_dir
ARM( add pc, r10, #PROCINFO_INITFUNC )
1: b __enable_mmu
/*
* Enable the MMU. This completely changes the structure of the visible
* memory space. You will not be able to trace execution through this.
* If you have an enquiry about this, *please* check the linux-arm-kernel
* mailing list archives BEFORE sending another post to the list.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags or dtb pointer
* r9 = processor ID
* r13 = *virtual* address to jump to upon completion
*
* other registers depend on the function called upon completion
*/
.align 5
.pushsection .idmap.text, "ax"
ENTRY(__turn_mmu_on)
mov r0, r0
instr_sync
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
instr_sync
mov r3, r3
mov r3, r13
mov pc, r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)
这里开启MMU之后跳转到刚才的__mmap_switched
初始化完C语言环境后,跳转到start_kernel
从反编译看:
c086f54c
c086f54c: e92d40f0 push {r4, r5, r6, r7, lr}
c086f550: e24dd01c sub sp, sp, #28
c086f554: eb000902 bl c0871964
c086f558: eb002add bl c087a0d4
c086f55c: f10c0080 cpsid i
c086f560: e30f5040 movw r5, #61504 ; 0xf040
c086f564: e3a04001 mov r4, #1
c086f568: e34c508b movt r5, #49291 ; 0xc08b
c086f56c: e5c54004 strb r4, [r5, #4]
c086f570: eb002960 bl c0879af8
c086f574: ebe800e6 bl c026f914
c086f578: e1a01004 mov r1, r4
c086f57c: e1a06000 mov r6, r0
c086f580: ebdf525e bl c0043f00
start_kernel大概我们能看到执行了那些功能,通过这些功能我们定位到start_kernel的位置。\kernel\kernel-3.4.39\init\main.c
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
完全符合反汇编的流程
到start_kernel之前的大概流程就是这样,下一篇我们详细讲解start_kernel之前都做了什么进行详细分析。