• Tiny4412 Linux 内核启动流程


     Linux内核的启动分为压缩内核和非压缩内核两种,这里我们以压缩内核为例。压缩内核运行时,将运行一段解压缩程序,得到真正的内核镜像,然后跳转到内核镜像运行。此时,Linux进入非压缩内核入口,在非压缩内核入口中,完成各种初始化操作后跳转到C语言入口处运行。主要流程如下所示。

    1.解压缩内核镜像

    解压缩程序通常在arch/arm/boot/compressed/目录中

    ├── atags_to_fdt.c
    ├── big-endian.S
    ├── decompress.c
    ├── head.S
    ├── head-sa1100.S
    ├── head-shark.S
    ├── head-sharpsl.S
    ├── head-shmobile.S
    ├── head-vt8500.S
    ├── head-xscale.S
    ├── libfdt_env.h
    ├── ll_char_wr.S
    ├── Makefile
    ├── misc.c
    ├── mmcif-sh7372.c
    ├── ofw-shark.c
    ├── piggy.gzip.S
    ├── piggy.lzma.S
    ├── piggy.lzo.S
    ├── piggy.xzkern.S
    ├── sdhi-sh7372.c
    ├── sdhi-shmobile.c
    ├── sdhi-shmobile.h
    ├── string.c
    └── vmlinux.lds.in

    它们经过编译后,生成的内容独立于真正的Linux内核,这部分内容的功能就是初始化环境,解压缩和运行真正的Linux内核。在压缩内核启动时,首先进入arch/arm/boot/compressed目录中的compressed目录中的head.S文件。

    start:
                    .type   start,#function
                    .rept   7
                    mov     r0, r0
                    .endr
       ARM(         mov     r0, r0          )    
       ARM(         b       1f              )    
     THUMB(         adr     r12, BSYM(1f)   )
     THUMB(         bx      r12             )    
                    .word   0x016f2818              @ Magic numbers to help the loader
                    .word   start                   @ absolute load/run zImage address
                    .word   _edata                  @ zImage end address
     THUMB(         .thumb                  )    
    1:              mov     r7, r1                  @ save architecture ID
                    mov     r8, r2                  @ save atags pointer  

    start是head.S的程序的开始,在此之前都是一些宏定义。在1标号处保存由bootloader传递过来的参数

    #ifndef __ARM_ARCH_2__
                    /*
                     * 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
                   @测试是否为usr模式
                    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:
                    mrs     r2, cpsr                @ turn off interrupts to
                    orr     r2, r2, #0xc0           @ prevent angel from running
                    msr     cpsr_c, r2
    #else
                    teqp    pc, #0x0c000003         @ turn off interrupts
    #endif

    如果内核从angel运行进入的运行模式将是usr mode,这时需要进入svc mode ,并禁止所有FIQ和IRQ中断。这些只有在进入时处于用户模式的时候才会执行。正常情况下,将运行not_angel标号处关闭中断的代码。然后对内核代码进行重定向(telocate)--(这部分代码还没看懂-_-!!!),重定向完成之后会跳转到not_relocated标号处运行。

    not_relocated:  mov     r0, #0     
    1:              str     r0, [r2], #4            @ clear bss
                    str     r0, [r2], #4
                    str     r0, [r2], #4
                    str     r0, [r2], #4
                    cmp     r2, r3
                    blo     1b
    /*
     * The C runtime environment should now be setup sufficiently.
     * Set up some pointers, and start decompressing.
     *   r4  = kernel execution address
     *   r7  = architecture ID
     *   r8  = atags pointer
     */
                    mov     r0, r4
                    mov     r1, sp                  @ malloc space above stack
                    add     r2, sp, #0x10000        @ 64k max
                    mov     r3, r7
                    bl      decompress_kernel
                    bl      cache_clean_flush
                    bl      cache_off
                    mov     r0, #0                  @ must be zero
                    mov     r1, r7                  @ restore architecture number
                    mov     r2, r8                  @ restore atags pointer
     ARM(           mov     pc, r4  )               @ call kernel

    重定向完成之后,首先清bss段,这时所有初始化C语言运行环境都要做的,然后调用decompress_kernel解压内核,之后跳转到非压缩内核启动阶段。

    2.汇编阶段启动流程

    对于tiny4412而言,内核的链接脚本为arch/arm/kernel/vmlinux.lds,它是由arch/arm/kernel/vmlinux.lds.S生成的。在链接脚本中,我们可以找到内核的入口

    OUTPUT_ARCH(arm) 
    ENTRY(stext)
    jiffies = jiffies_64;
    SECTIONS
    {

    可以看出内核的入口为stext,它在 linux/arch/arm/kernel/head.S 中被定义。

        .arm
        __HEAD
    ENTRY(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:            )
      @设定为SVC模式,关闭IRQ、FIQ
        setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
                            @ and irqs disabled
        @检查CPU ID 是否匹配
        mrc    p15, 0, r9, c0, c0        @ get processor id 
        bl    __lookup_processor_type        @ r5=procinfo r9=cpuid
        movs    r10, r5                @ invalid processor (r5=0)?
     THUMB( it    eq )        @ force fixup-able long branch encoding
        beq    __error_p            @ yes, error 'p'
    #ifdef CONFIG_ARM_LPAE
        mrc    p15, 0, r3, c0, c1, 4        @ read ID_MMFR0
        and    r3, r3, #0xf            @ extract VMSA support
        cmp    r3, #5                @ long-descriptor translation table format?
     THUMB( it    lo )                @ force fixup-able long branch encoding
        blo    __error_p            @ only classic page table format
    #endif
    #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
    #else
        ldr    r8, =PHYS_OFFSET        @ always constant in this case
    #endif
        /*
         * r1 = machine no, r2 = atags or dtb,
         * r8 = phys_offset, r9 = cpuid, r10 = procinfo
         */
        @检查bootloader传入的参数列表atags的合法性
        bl    __vet_atags
    #ifdef CONFIG_SMP_ON_UP
        bl    __fixup_smp
    #endif
    #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
        bl    __fixup_pv_table
    #endif
      @创建初始页表
        bl    __create_page_tables
        /*
         * The following calls CPU specific code in a position independent
         * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
         * xxx_proc_info structure selected by __lookup_processor_type
         * above.  On return, the CPU will be ready for the MMU to be
         * turned on, and r0 will hold the CPU control register value.
         */
        @建立C语言环境(代码重定位、清bss段)
        ldr    r13, =__mmap_switched        @ address to jump to after
                            @ mmu has been enabled
        adr    lr, BSYM(1f)            @ return (PIC) address
        mov    r8, r4                @ set TTBR1 to swapper_pg_dir
     ARM(    add    pc, r10, #PROCINFO_INITFUNC    )
     THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )
     THUMB(    mov    pc, r12                )
     @开启MMU
    1:    b    __enable_mmu
    ENDPROC(stext)

    __mmap_switched定义在arch/arm/kernel/head-common.S中

    __mmap_switched:
            adr     r3, __mmap_switched_data
            ldmia   r3!, {r4, r5, r6, r7}
            cmp     r4, r5                          @ Copy data segment if needed
    1:      cmpne   r5, r6
            ldrne   fp, [r4], #4
            strne   fp, [r5], #4
            bne     1b
            mov     fp, #0                          @ Clear BSS (and zero fp)
    1:      cmp     r6, r7
            strcc   fp, [r6],#4
            bcc     1b
     ARM(   ldmia   r3, {r4, r5, r6, r7, sp})
     THUMB( ldmia   r3, {r4, r5, r6, r7}    )
     THUMB( ldr     sp, [r3, #16]           )
            str     r9, [r4]                        @ Save processor ID
            str     r1, [r5]                        @ Save machine type
            str     r2, [r6]                        @ Save atags pointer
            bic     r4, r0, #CR_A                   @ Clear 'A' bit
            stmia   r7, {r0, r4}                    @ Save control register values
            b       start_kernel
    汇编阶段代码主要完成了以下工作
    ①设置处理器为SVC模式并关闭中断
    ②调用__lookup_processor_type查找处理器信息结构体proc_info
    ③调用__enable_mmu打开MMU
    ④调用__create_page_tables创建初始页表
    ⑤调用__mmap_switched初始化C语言运行环境,最红跳转到C语言阶段入口函数start_kernel

    3.C语言阶段启动流程

    内核启动流程,知识储备还不完善,以后更新 -_- ......
     
    参考文章:
    http://blog.csdn.net/zqixiao_09/article/details/50821995
  • 相关阅读:
    关于在MyEclipse中页面中文乱码的问题
    如何用Navicat for MySQL 将mysql中的数据库导出,导入。
    淘宝链接池的配置
    c3p0配置
    人生规划
    spring问题: Unable to validate using XSD: Your JAXP provider
    List数组和Set集合
    Tomcat6内存不足问题及解决方法
    清华校长送给毕业生的五句话
    个人图文理解类的封装
  • 原文地址:https://www.cnblogs.com/CoderTian/p/6035214.html
Copyright © 2020-2023  润新知