Linux架构/开发板相关的引导过程(head.S)

    分析内核顶层Makefile,以及arch/arm/Makefile可知,从u-boot跳到内核后,第一个启动的文件是arch/arm/kernel/head.S。接下来就围绕这个文件来分析Linux架构/开发板相关的引导过程。

/******************************以下仅分析重要部分********************************/

#include "head-common.S"    //下面会调用到这个文件里面的函数

......

/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code.  The requirements
* are:
MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.

*
*
This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).

*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
* ........
*/
.section ".text.head", "ax"         //".text.head"段,在vmlinux.lds链接文件中定义
ENTRY(stext)
       msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE //确保进入管理模式并禁止中断
       mrc p15, 0, r9, c0, c0               // r9 = CPU ID
       bl  __lookup_processor_type  // r5=procinfo r9=cpuid
       movs r10, r5                            //如果不支持该CPU则r5=0
       beq __error_p                         //如果r5=0,则打印错误信息
       bl __lookup_machine_type    // r5=machinfo
       movs r8, r5                             // 如果不支持该开发板r5=0
       beq __error_a                        //如果r5=0,则打印错误信息
       bl __vet_atags
       bl __create_page_tables

       .......
       ldr r13, __switch_data  @ address to jump to after  mmu has been enabled
       adr lr, __enable_mmu  @ return (PIC) address
       add pc, r10, #PROCINFO_INITFUNC
ENDPROC(stext)

.......

架构/开发板相关的引导过程就是由以上蓝色部分的函数来完成的,都是汇编子程序,下面逐个分析。

__lookup_processor_type子程序在arch/arm/kernel/head-common.S文件中定义

/*
 * r9 = cpuid
 * Returns:
 * r3, r4, r6 corrupted
 * r5 = proc_info pointer in physical address space
 * r9 = cpuid (preserved)
 */

__lookup_processor_type:
      adr r3, 3f             //将下面3标号的物理地址存到r3寄存器

/* r5 = __proc_info_begin,r6 = __proc_info_end 的虚拟地址,r7 = 3标号的虚拟地址 */
      ldmda r3, {r5 - r7}
      sub r3, r3, r7    // r3 = r3 - r7,物理地址与虚拟地址之差
      add r5, r5, r3    // r5 = __ proc_info_begin 对应的物理地址
      add r6, r6, r3    // r6 = __proc_info_end 对应的物理地址
1: ldmia r5, {r3, r4} // r3 = cpu_val,r4 = cpu_mask 这两个成员在proc_info_list中定义
      and r4, r4, r9    // r4 = r4&r9 = cpu_mask & 最开始从CP15传入的cpu id
      teq r3, r4
      beq 2f              //相等则返回
      add r5, r5, #PROC_INFO_SZ  //下一个pro_info_list ,sizeof(proc_info_list)
      cmp r5, r6       //判断是否已经比较完所有的proc_info_list
      blo 1b             //否则继续比较
      mov r5, #0      //比较完后还是没有匹配的proc_info_list结构,r5 = 0
2: mov pc, lr         // 返回
ENDPROC(__lookup_processor_type)

.......

/*
 * Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for
 * more information about the __proc_info and __arch_info structures.
 */
 .long __proc_info_begin    //proc_info_list结构体的开始地址(虚拟地址)
 .long __proc_info_end      //proc_info_list结构体的结束地址(虚拟地址)
3: .long .                             // 3标号在内存中的的物理地址
 .long __arch_info_begin  
 .long __arch_info_end   

 

proc_info_list结构体在arch/arm/include/asm中定义

struct proc_info_list {
     unsigned int  cpu_val;
     unsigned int  cpu_mask;

     unsigned long  __cpu_mm_mmu_flags; /* used by head.S */
     unsigned long  __cpu_io_mmu_flags; /* used by head.S */
     unsigned long  __cpu_flush;  /* used by head.S */
     const char  *arch_name;
     const char  *elf_name;
     unsigned int  elf_hwcap;
     const char  *cpu_name;
     struct processor *proc;
     struct cpu_tlb_fns *tlb;
     struct cpu_user_fns *user;
     struct cpu_cache_fns *cache;
};

对于S3C2440开发板的匹配,它的CPU ID是0x41129200,可查询协处理器的CP15获知。

而在arch/arm/mm/proc_arm920.S中定义的__arm920_proc_info结构如下

__arm920_proc_info:
   
.long 0x41009200
    .long 0xff00fff0

    ........

即r3 = cpu_val = 0x41009200,r4 = cpu_mask = 0xff00ff00,刚好匹配。

 

__lookup_machine_type子程序也在arch/arm/kernel/head-common.S文件中定义

/*

 * ....

 *  r1 = machine architecture number
 * Returns:
 *  r3, r4, r6 corrupted
 *  r5 = mach_info pointer in physical address space
 */
__lookup_machine_type:
     adr r3, 3b                  //获得3标号的物理地址

/* r4 = __arch_info_begin,r6 = __arch_info_end 的虚拟地址,r7 = 3标号的虚拟地址 */

     ldmia r3, {r4, r5, r6}  
     sub r3, r3, r4    // r3 = r3 - r4  物理地址与虚拟地址之差
     add r5, r5, r3    // r5 = __arch_info_begin 的物理地址
     add r6, r6, r3    // r6 = __arch_info_end 的物理地址
1: ldr r3, [r5, #MACHINFO_TYPE]   // r5 是machine_desc结构体的地址
     teq r3, r1       //判断从r1传入的机器ID是否匹配
     beq 2f           //匹配则返回
     add r5, r5, #SIZEOF_MACHINE_DESC  //否则,指向下一个 machine_desc
     cmp r5, r6    //是否已经比较完所有的machine_desc结构
     blo 1b          //没有则继续比较
     mov r5, #0   //比较完毕之后都没有找到匹配的,r5 = 0
2: mov pc, lr     //返回
ENDPROC(__lookup_machine_type)

 

machine_desc结构在 arch/arm/include/asm/mach/arch.h中定义

struct machine_desc {
 /*
  * Note! The first four elements are used
  * by assembler code in head.S, head-common.S
  */
     unsigned int  nr;  /* architecture number */
     unsigned int  phys_io; /* start of physical io */
     unsigned int  io_pg_offst; /* byte offset for io
       * page tabe entry */

     const char  *name;  /* architecture name */
     unsigned long  boot_params; /* tagged list  */

     unsigned int  video_start; /* start of video RAM */
     unsigned int  video_end; /* end of video RAM */

     unsigned int  reserve_lp0 :1; /* never has lp0 */
     unsigned int  reserve_lp1 :1; /* never has lp1 */
     unsigned int  reserve_lp2 :1; /* never has lp2 */
     unsigned int  soft_reboot :1; /* soft reboot  */
     void   (*fixup)(struct machine_desc *,
     struct tag *, char **,
     struct meminfo *);
     void   (*map_io)(void);/* IO mapping function */
     void   (*init_irq)(void);
     struct sys_timer *timer;  /* system tick timer */
     void   (*init_machine)(void);
};

#define MACHINE_START(_type,_name)   \
static const struct machine_desc __mach_desc_##_type \
 __used       \
 __attribute__((__section__(".arch.info.init"))) = { \
 .nr  = MACH_TYPE_##_type,  \
 .name  = _name,

#define MACHINE_END    \
};

 

MACHINE_START(S3C2440, "SMDK2440")
    .phys_io = S3C2410_PA_UART,
    .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    .boot_params = S3C2410_SDRAM_PA + 0x100,

    .init_irq = s3c24xx_init_irq,
    .map_io  = smdk2440_map_io,
    .init_machine = smdk2440_machine_init,
    .timer  = &s3c24xx_timer,
MACHINE_END

 

由上面二个定义得到S3C2440 的machine_desc结构体 

static const struct machine_desc __mach_desc_S3C2440
 __used       
 __attribute__((__section__(".arch.info.init"))) =


    
.nr  = MACH_TYPE_S3C2440,  
    .name  = "SMDK2440",

    .phys_io = S3C2410_PA_UART,
    .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    .boot_params = S3C2410_SDRAM_PA + 0x100,

    .init_irq = s3c24xx_init_irq,
    .map_io  = smdk2440_map_io,
    .init_machine = smdk2440_machine_init,
    .timer  = &s3c24xx_timer,

};

到此,__lookup_processor_type、__lookup_machine_type函数如果都匹配成功,则将继续执行

__vet_atags、(处理u-boot传入的ATAGS)

__create_page_tables创建一级页表,在arch/arm/kernel/head.S原文定义

__switch_data在arch/arm/kernel/head_common.S 定义

__switch_data:
    .long __mmap_switched
    .long __data_loc        @ r4
    .long _data                @ r5
    .long __bss_start      @ r6
    .long _end                 @ r7
    .long processor_id     @ r4
    .long __machine_arch_type  @ r5
    .long __atags_pointer            @ r6
    .long cr_alignment                  @ r7
    .long init_thread_union + THREAD_START_SP @ sp

......

__mmap_switched:
    adr r3, __switch_data + 4

    ldmia r3!, {r4, r5, r6, r7}
    cmp r4, r5      //复制数据段
1: cmpne r5, r6
    ldrne fp, [r4], #4
    strne fp, [r5], #4
    bne 1b

    mov fp, #0    //清除BSS段
1: cmp r6, r7
    strcc fp, [r6],#4
    bcc 1b

    ldmia r3, {r4, r5, r6, r7, sp}
    str r9, [r4]   //保存CPU ID到 r9 寄存器
    str r1, [r5]   //保存机器ID到 r1 寄存器
    str r2, [r6]   // 保存ATAGS指针到 r2 寄存器
    bic r4, r0, #CR_A   @ Clear 'A' bit
    stmia r7, {r0, r4}   @ Save control register values
    b start_kernel         //跳到第二阶段的C函数去执行,在init/main.c中定义
ENDPROC(__mmap_switched)

 

__enable_mmu  使能MMU

__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
 orr r0, r0, #CR_A
#else
 bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
 bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
 bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
 bic r0, r0, #CR_I
#endif
 mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
        domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
        domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
        domain_val(DOMAIN_IO, DOMAIN_CLIENT))
   mcr p15, 0, r5, c3, c0, 0  @ load domain access register
   mcr p15, 0, r4, c2, c0, 0  @ load page table pointer
   b __turn_mmu_on
ENDPROC(__enable_mmu)

 

 .align 5
__turn_mmu_on:
    mov r0, r0
    mcr p15, 0, r0, c1, c0, 0  @ write control reg
    mrc p15, 0, r3, c0, c0, 0  @ read id reg
    mov r3, r3
    mov r3, r3
    mov pc, r13
ENDPROC(__turn_mmu_on)

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值