linux启动流程之汇编阶段(一)

从源头开始分析启动。

问题

  1. linux如何编译的?
  2. 从哪里启动?

 

寻找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在

linux启动流程之汇编阶段(一)_第1张图片

 

换一种思路,在有

linux启动流程之汇编阶段(一)_第2张图片

的前提下使用

指令反编译源文件后有

linux启动流程之汇编阶段(一)_第3张图片

也可以得到入口处的印证(反编译文件我稍后会上传)

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:                )

这段代码很明显没有在反编译内体现,原因是:

linux启动流程之汇编阶段(一)_第4张图片

通过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之前都做了什么进行详细分析。

 

 

 

你可能感兴趣的:(linux驱动开发)