设为首页 收藏本站
查看: 656|回复: 0

[经验分享] * Linux内核源码分析--内核启动之(2)Image内核启动(汇编部分)(Linux-3.0 ARMv7)

[复制链接]

尚未签到

发表于 2015-12-11 09:26:07 | 显示全部楼层 |阅读模式
  在完成了zImage自解压之后,就跳转到了解压后的内核(也就是vmlinux的bin版本Image),具体的入口可以在arch/arm/kernel/vmlinux.lds.S(最终的链接脚本是通过这个文件产生的)中获得:


  • ......
  • SECTIONS
  • {
  • #ifdef CONFIG_XIP_KERNEL
  • . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
  • #else
  • . = PAGE_OFFSET + TEXT_OFFSET;
  • #endif

  • .init : {/* Init code and data*/
  • _stext = .;
  • _sinittext = .;
  • ......

    这个入口在arch/arm/kernel/head.S中,这个文件就是Linux内核真正启动的地方,是初始化部分的开始,用汇编写成。他必须为后面的C代码做好准备,下面先给出程序的流程图,后面是中文注释的代码。


    这里有一些宏定义必须知道他的含义:

出现的位置

默认值

定义

KERNEL_RAM_ADDR

arch/arm/kernel/head.S

0xC0008000

内核在内存中的虚拟地址

PAGE_OFFSET

arch/arm/include/asm/memory.h

0xC0000000

内核虚拟地址空间的起始地址

TEXT_OFFSET

arch/arm/Makefile

0x00008000

内核起始位置相对于内存起始位置的偏移

PHYS_OFFSET

arch/arm/include/asm/memory.h

构架相关

物理内存的起始地址




DSC0000.jpeg   arch/arm/kernel/head.S



  • /*
  • * linux/arch/arm/kernel/head.S
  • *
  • * Copyright (C) 1994-2002 Russell King
  • * Copyright (c) 2003 ARM Limited
  • * All Rights Reserved
  • *
  • * This program is free software; you can redistribute it and/or modify
  • * it under the terms of the GNU General Public License version 2 as
  • * published by the Free Software Foundation.
  • *
  • * 所有32-bit CPU的内核启动代码
  • */
  • #include
  • #include

  • #include
  • #include
  • #include
  • #include
  • #include
  • #include
  • #include

  • #ifdef CONFIG_DEBUG_LL
  • #include
  • #endif

  • /*

  • * swapper_pg_dir 是初始页表的虚拟地址.
  •   * 我们将页表放在KERNEL_RAM_VADDR以下16K的空间中. 因此我们必须保证

  •   * KERNEL_RAM_VADDR已经被正常设置. 当前, 我们期望的是

  •   * 这个地址的最后16 bits为0x8000, 但我们或许可以放宽这项限制到

  •   * KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
  •   */
  • #define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)
  • #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
  • #error KERNEL_RAM_VADDR must start at 0xXXXX8000
  • #endif

  •     .globl    swapper_pg_dir
  •     .equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000

  • /*

  •   * TEXT_OFFSET 是内核代码(解压后)相对于RAM起始的偏移.

  •   *#TEXT_OFFSET - 0x4000就是页表相对于RAM起始的偏移.

  •   * 这个宏的作用是将phys(RAM的启示地址)加上页表的偏移,

  •   * 而得到页表的起始物理地址

  •   */
  •     .macro    pgtbl, rd, phys
  •     add    \rd, \phys, #TEXT_OFFSET - 0x4000
  •     .endm

  • #ifdef CONFIG_XIP_KERNEL
  • #define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
  • #define KERNEL_END    _edata_loc
  • #else
  • #define KERNEL_START    KERNEL_RAM_VADDR

  • #define KERNEL_END    _end
  • #endif

  • /*

  •   * 内核启动入口点.

  •   * ---------------------------

  •   *

  •   * 这个入口正常情况下是在解压完成后被调用的.

  •   * 调用条件: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,

  •   * r1 = machine nr, r2 = atags or dtb pointer.

  •   * 这些条件在解压完成后会被逐一满足,然后才跳转过来。

  •   *

  •   * 这些代码大多数是位置无关的, 如果你的内核入口地址在连接时确定为

  •   * 0xc0008000, 你调用此函数的物理地址就是 __pa(0xc0008000).

  •   *

  •   * 完整的machineID列表,请参见 linux/arch/arm/tools/mach-types

  •   *

  •   * 我们尽量让代码简洁; 不在此处添加任何设备特定的代码

  •   * - 这些特定的初始化代码是boot loader的工作(或在极端情况下,

  •   * 有充分理由的情况下, 可以由zImage完成)

  •   */
  •     __HEAD
  • ENTRY(stext)
  •      setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ CPU模式设置宏

  •                                                     @ (进入svc模式并且关闭中断)

  •      mrc    p15, 0, r9, c0, c0                       @ 获取处理器id-->r9

  •      bl    __lookup_processor_type                   @ 返回r5=procinfo r9=cpuid

  •      movs    r10, r5                                 @ r10=r5,并可以检测r5=0?注意当前r10的值
  • THUMB( it    eq )            @ force fixup-able long branch encoding
  •      beq    __error_p            @ yes, error 'p'如果r5=0,则内核处理器不匹配,出错~死循环

  •     /*
  •       * 获取RAM的起始物理地址,并保存于 r8 = phys_offset
  •      * XIP内核与普通在RAM中运行的内核不同
  •      * (1)CONFIG_XIP_KERNEL
  •      *         通过运行时计算????
  •      * (2)正常RAM中运行的内核

  •       *         通过编译时确定(PLAT_PHYS_OFFSET 一般在arch/arm/mach-xxx/include/mach/memory.h定义)
  •      *
  •      */
  • #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, =PLAT_PHYS_OFFSET
  • #endif

  •     /*
  •      * r1 = machine no, r2 = atags or dtb,
  •      * r8 = phys_offset, r9 = cpuid, r10 = procinfo
  •      */
  •     bl    __vet_atags            @ 判断r2(内核启动参数)指针的有效性
  • #ifdef CONFIG_SMP_ON_UP
  •     bl    __fixup_smp            @ ???如果运行SMP内核在单处理器系统中启动,做适当调整
  • #endif
  • #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
  •     bl    __fixup_pv_table    @ ????根据内核在内存中的位置修正物理地址与虚拟地址的转换机制
  • #endif
  •      bl    __create_page_tables    @ 初始化页表!

  •      /*

  •       * 以下使用位置无关的方法调用的是CPU特定代码。

  •       * 详情请见arch/arm/mm/proc-*.S

  •       * r10 = xxx_proc_info 结构体的基地址(在上面__lookup_processor_type函数中选中的)

  •       * 返回时, CPU 已经为 MMU 的启动做好了准备,

  •       * 且 r0 保存着CPU控制寄存器的值.

  •       */
  •      ldr    r13, =__mmap_switched                @ 在MMU启动之后跳入的第一个虚拟地址

  •      adr    lr, BSYM(1f)                        @ 设置返回的地址(PIC)

  •      mov    r8, r4                                @ 将swapper_pg_dir的物理地址放入r8,

  •                                              @ 以备__enable_mmu中将其放入TTBR1

  •   ARM(    add    pc, r10, #PROCINFO_INITFUNC    )    @ 跳入构架相关的初始化处理器函数(例如A8的是__v7_setup)
  • THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )    @主要目的只配置CP15(包括缓存配置)
  • THUMB(    mov    pc, r12                )
  • 1:    b    __enable_mmu                        @ 启动MMU
  • ENDPROC(stext)
  •     .ltorg
  • #ifndef CONFIG_XIP_KERNEL
  • 2:    .long    .
  •     .long    PAGE_OFFSET
  • #endif

  • /*

  •   * 创建初始化页表. 我们只创建最基本的页表,

  •   * 以满足内核运行的需要,

  •   * 这通常意味着仅映射内核代码本身.

  •   *

  •   * r8 = phys_offset, r9 = cpuid, r10 = procinfo

  •   *

  •   * 返回:

  •   * r0, r3, r5-r7 被篡改

  •   * r4 = 页表物理地址

  •   */
  • __create_page_tables:
  •      pgtbl    r4, r8                @ 现在r4 = 页表的起始物理地址

  •      /*

  •       * 清零16K的一级初始页表区

  •       * 这些页表在内核自解压时被设置过

  •       * (此时MMU已关闭)

  •       */
  •     mov    r0, r4
  •     mov    r3, #0
  •     add    r6, r0, #0x4000
  • 1:    str    r3, [r0], #4
  •     str    r3, [r0], #4
  •     str    r3, [r0], #4
  •     str    r3, [r0], #4
  •     teq    r0, r6
  •     bne    1b

  •      /*

  •       * 获取节描述符的默认配置(除节基址外的其他配置)

  •       * 这个数据依构架而不同,数据是用汇编文件配置的:

  •       * arch/arm/mm/proc-xxx.S

  •       * (此时MMU已关闭)

  •       */
  •     ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ 获取mm_mmuflags(节描述符默认配置),保存于r7

  •      /*

  •       * 创建特定映射,以满足__enable_mmu的需求。

  •       * 此特定映射将被paging_init()删除。

  •       *

  •       * 其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表

  •       * 以保证在启用mmu时代码的正确执行--1:1映射(物理地址=虚拟地址)

  •       */
  •     adr    r0, __enable_mmu_loc
  •     ldmia    r0, {r3, r5, r6}
  •     sub    r0, r0, r3            @ 获取编译时确定的虚拟地址到当前物理地址的偏移
  •     add    r5, r5, r0            @ __enable_mmu的当前物理地址
  •     add    r6, r6, r0            @ __enable_mmu_end的当前物理地址
  •     mov    r5, r5, lsr #20        @ __enable_mmu的节基址
  •     mov    r6, r6, lsr #20        @ __enable_mmu_end的节基址

  • 1:    orr    r3, r7, r5, lsl #20        @ 生成节描述符:flags + 节基址
  •     str    r3, [r4, r5, lsl #2]    @ 设置节描述符,1:1映射(物理地址=虚拟地址)
  •     teq    r5, r6                    @ 完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)
  •     addne    r5, r5, #1            @ r5 = 下一节的基址
  •     bne    1b

  •      /*

  •       * 现在创建内核的逻辑映射区页表(节映射)

  •       * 创建范围:KERNEL_START---KERNEL_END

  •       * KERNEL_START:内核最终运行的虚拟地址

  •       * KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)

  •       */
  •     mov    r3, pc                @ 获取当前物理地址
  •     mov    r3, r3, lsr #20        @ r3 = 当前物理地址的节基址
  •     orr    r3, r7, r3, lsl #20    @ r3 为当前物理地址的节描述符
  •      /*

  •       * 下面是为了确定页表项的入口地址

  •       * 其实页表入口项的偏移就反应了对应的虚拟地址的高位

  •       *

  •       * 由于ARM指令集的8bit位图问题,只能分两次得到

  •       * KERNEL_START:内核最终运行的虚拟地址

  •       *

  •       */
  •     add    r0, r4, #(KERNEL_START & 0xff000000) >> 18
  •     str    r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
  •     ldr    r6, =(KERNEL_END - 1)
  •     add    r0, r0, #4
  •     add    r6, r4, r6, lsr #18    @ r6 = 内核逻辑映射结束的节基址
  • 1:    cmp    r0, r6
  •     add    r3, r3, #1 18
  •     str    r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
  •     ldr    r6, =(_end - 1)
  •     add    r0, r0, #4
  •     add    r6, r4, r6, lsr #18
  • 1:    cmp    r0, r6
  •     add    r3, r3, #1 18
  •     orr    r3, r7, #0x02000000
  •     str    r3, [r0]
  •     add    r0, r4, #0xd8000000 >> 18
  •     str    r3, [r0]
  • #endif
  • #endif
  •      mov    pc, lr        @页表创建结束,返回
  • ENDPROC(__create_page_tables)
  •     .ltorg
  •     .align
  • __enable_mmu_loc:
  •     .long    .
  •     .long    __enable_mmu
  •     .long    __enable_mmu_end

  • #if defined(CONFIG_SMP)
  •     __CPUINIT
  • ENTRY(secondary_startup)
  •     /*
  •      * Common entry point for secondary CPUs.
  •      *
  •      * Ensure that we're in SVC mode, and IRQs are disabled. Lookup
  •      * the processor type - there is no need to check the machine type
  •      * as it has already been validated by the primary processor.
  •      */
  •     setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
  •     mrc    p15, 0, r9, c0, c0        @ get processor id
  •     bl    __lookup_processor_type
  •     movs    r10, r5                @ invalid processor?
  •     moveq    r0, #'p'            @ yes, error 'p'
  • THUMB( it    eq )        @ force fixup-able long branch encoding
  •     beq    __error_p

  •     /*
  •      * Use the page tables supplied from  __cpu_up.
  •      */
  •     adr    r4, __secondary_data
  •     ldmia    r4, {r5, r7, r12}        @ address to jump to after
  •     sub    lr, r4, r5            @ mmu has been enabled
  •     ldr    r4, [r7, lr]            @ get secondary_data.pgdir
  •     add    r7, r7, #4
  •     ldr    r8, [r7, lr]            @ get secondary_data.swapper_pg_dir
  •     adr    lr, BSYM(__enable_mmu)        @ return address
  •     mov    r13, r12            @ __secondary_switched address
  • ARM(    add    pc, r10, #PROCINFO_INITFUNC    ) @ initialise processor
  •                           @ (return control reg)
  • THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )
  • THUMB(    mov    pc, r12                )
  • ENDPROC(secondary_startup)

  •     /*
  •      * r6 = &secondary_data
  •      */
  • ENTRY(__secondary_switched)
  •     ldr    sp, [r7, #4]            @ get secondary_data.stack
  •     mov    fp, #0
  •     b    secondary_start_kernel
  • ENDPROC(__secondary_switched)

  •     .align

  •     .type    __secondary_data, %object
  • __secondary_data:
  •     .long    .
  •     .long    secondary_data
  •     .long    __secondary_switched
  • #endif /* defined(CONFIG_SMP) */



  • /*

  •   * 在最后启动MMU前,设置一些常用位 Essentially

  •   * 其实,这里只是加载了页表指针和域访问控制数据寄存器

  •   *

  •   *

  •   * r0 = cp#15 control register

  •   * r1 = machine ID

  •   * r2 = atags or dtb pointer

  •   * r4 = page table pointer

  •   * r9 = processor ID

  •   * r13 = 最后要跳入的虚拟地址

  •   */
  • __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        @ 载入域访问控制数据到DACR
  •     mcr    p15, 0, r4, c2, c0, 0        @ 载入页表基址到TTBR0
  •     b    __turn_mmu_on                @ 开启MMU
  • ENDPROC(__enable_mmu)

  • /*

  •   * 使能 MMU. 这完全改变了可见的内存地址空间结构。

  •   * 您将无法通过这里跟踪执行。

  •   * 如果你已对此进行探究, **在向邮件列表发送另一个新帖之前,

  •   * 检查linux-arm-kernel的邮件列表归档

  •   *

  •   * r0 = cp#15 control register

  •   * r1 = machine ID

  •   * r2 = atags or dtb pointer

  •   * r9 = processor ID

  •   * r13 = 最后要跳入的*虚拟*地址

  •   *

  •   * 其他寄存器依赖上面的调用函数

  •   */
  •     .align    5
  • __turn_mmu_on:
  •     mov    r0, r0
  •     mcr    p15, 0, r0, c1, c0, 0        @ 设置cp#15控制寄存器(启用MMU)
  •     mrc    p15, 0, r3, c0, c0, 0        @ read id reg
  •     mov    r3, r3
  •     mov    r3, r13                        @ r3中装入最后要跳入的*虚拟*地址
  •     mov    pc, r3                        @ 跳转到__mmap_switched
  • __enable_mmu_end:
  • ENDPROC(__turn_mmu_on)


  • #ifdef CONFIG_SMP_ON_UP
  •     __INIT
  • __fixup_smp:
  •     and    r3, r9, #0x000f0000    @ architecture version
  •     teq    r3, #0x000f0000        @ CPU ID supported?
  •     bne    __fixup_smp_on_up    @ no, assume UP

  •     bic    r3, r9, #0x00ff0000
  •     bic    r3, r3, #0x0000000f    @ mask 0xff00fff0
  •     mov    r4, #0x41000000
  •     orr    r4, r4, #0x0000b000
  •     orr    r4, r4, #0x00000020    @ val 0x4100b020
  •     teq    r3, r4            @ ARM 11MPCore?
  •     moveq    pc, lr            @ yes, assume SMP

  •     mrc    p15, 0, r0, c0, c0, 5    @ read MPIDR
  •     and    r0, r0, #0xc0000000    @ multiprocessing extensions and
  •     teq    r0, #0x80000000        @ not part of a uniprocessor system?
  •     moveq    pc, lr            @ yes, assume SMP

  • __fixup_smp_on_up:
  •     adr    r0, 1f
  •     ldmia    r0, {r3 - r5}
  •     sub    r3, r0, r3
  •     add    r4, r4, r3
  •     add    r5, r5, r3
  •     b    __do_fixup_smp_on_up
  • ENDPROC(__fixup_smp)

  •     .align
  • 1:    .word    .
  •     .word    __smpalt_begin
  •     .word    __smpalt_end

  •     .pushsection .data
  •     .globl    smp_on_up
  • smp_on_up:
  •     ALT_SMP(.long    1)
  •     ALT_UP(.long    0)
  •     .popsection
  • #endif

  •     .text
  • __do_fixup_smp_on_up:
  •     cmp    r4, r5
  •     movhs    pc, lr
  •     ldmia     {r0, r6}
  • ARM(    str    r6, [r0, r3]    )
  • THUMB(    add    r0, r0, r3    )
  • #ifdef __ARMEB__
  • THUMB(    mov    r6, r6, ror #16    )    @ Convert word order for big-endian.
  • #endif
  • THUMB(    strh    r6, [r0], #2    )    @ For Thumb-2, store as two halfwords
  • THUMB(    mov    r6, r6, lsr #16    )    @ to be robust against misaligned r3.
  • THUMB(    strh    r6, [r0]    )
  •     b    __do_fixup_smp_on_up
  • ENDPROC(__do_fixup_smp_on_up)

  • ENTRY(fixup_smp)
  •     stmfd     {r4 - r6, lr}
  •     mov    r4, r0
  •     add    r5, r0, r1
  •     mov    r3, #0
  •     bl    __do_fixup_smp_on_up
  •     ldmfd     {r4 - r6, pc}
  • ENDPROC(fixup_smp)

  • #ifdef CONFIG_ARM_PATCH_PHYS_VIRT

  • /* __fixup_pv_table - patch the stub instructions with the delta between
  • * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and
  • * can be expressed by an immediate shifter operand. The stub instruction
  • * has a form of '(add|sub) rd, rn, #imm'.
  • */
  •     __HEAD
  • __fixup_pv_table:
  •     adr    r0, 1f
  •     ldmia    r0, {r3-r5, r7}
  •     sub    r3, r0, r3    @ PHYS_OFFSET - PAGE_OFFSET
  •     add    r4, r4, r3    @ adjust table start address
  •     add    r5, r5, r3    @ adjust table end address
  •     add    r7, r7, r3    @ adjust __pv_phys_offset address
  •     str    r8, [r7]    @ save computed PHYS_OFFSET to __pv_phys_offset
  • #ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  •     mov    r6, r3, lsr #24    @ constant for add/sub instructions
  •     teq    r3, r6, lsl #24 @ must be 16MiB aligned
  • #else
  •     mov    r6, r3, lsr #16    @ constant for add/sub instructions
  •     teq    r3, r6, lsl #16    @ must be 64kiB aligned
  • #endif
  • THUMB(    it    ne        @ cross section branch )
  •     bne    __error
  •     str    r6, [r7, #4]    @ save to __pv_offset
  •     b    __fixup_a_pv_table
  • ENDPROC(__fixup_pv_table)

  •     .align
  • 1:    .long    .
  •     .long    __pv_table_begin
  •     .long    __pv_table_end
  • 2:    .long    __pv_phys_offset

  •     .text
  • __fixup_a_pv_table:
  • #ifdef CONFIG_THUMB2_KERNEL
  • #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  •     lsls    r0, r6, #24
  •     lsr    r6, #8
  •     beq    1f
  •     clz    r7, r0
  •     lsr    r0, #24
  •     lsl    r0, r7
  •     bic    r0, 0x0080
  •     lsrs    r7, #1
  •     orrcs   r0, #0x0080
  •     orr    r0, r0, r7, lsl #12
  • #endif
  • 1:    lsls    r6, #24
  •     beq    4f
  •     clz    r7, r6
  •     lsr    r6, #24
  •     lsl    r6, r7
  •     bic    r6, #0x0080
  •     lsrs    r7, #1
  •     orrcs    r6, #0x0080
  •     orr    r6, r6, r7, lsl #12
  •     orr    r6, #0x4000
  •     b    4f
  • 2:    @ at this point the C flag is always clear
  •     add     r7, r3
  • #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  •     ldrh    ip, [r7]
  •     tst    ip, 0x0400    @ the i bit tells us LS or MS byte
  •     beq    3f
  •     cmp    r0, #0        @ set C flag, and ...
  •     biceq    ip, 0x0400    @ immediate zero value has a special encoding
  •     streqh    ip, [r7]    @ that requires the i bit cleared
  • #endif
  • 3:    ldrh    ip, [r7, #2]
  •     and    ip, 0x8f00
  •     orrcc    ip, r6    @ mask in offset bits 31-24
  •     orrcs    ip, r0    @ mask in offset bits 23-16
  •     strh    ip, [r7, #2]
  • 4:    cmp    r4, r5
  •     ldrcc    r7, [r4], #4    @ use branch for delay slot
  •     bcc    2b
  •     bx    lr
  • #else
  • #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT
  •     and    r0, r6, #255    @ offset bits 23-16
  •     mov    r6, r6, lsr #8    @ offset bits 31-24
  • #else
  •     mov    r0, #0        @ just in case...
  • #endif
  •     b    3f
  • 2:    ldr    ip, [r7, r3]
  •     bic    ip, ip, #0x000000ff
  •     tst    ip, #0x400    @ rotate shift tells us LS or MS byte
  •     orrne    ip, ip, r6    @ mask in offset bits 31-24
  •     orreq    ip, ip, r0    @ mask in offset bits 23-16
  •     str    ip, [r7, r3]
  • 3:    cmp    r4, r5
  •     ldrcc    r7, [r4], #4    @ use branch for delay slot
  •     bcc    2b
  •     mov    pc, lr
  • #endif
  • ENDPROC(__fixup_a_pv_table)

  • ENTRY(fixup_pv_table)
  •     stmfd     {r4 - r7, lr}
  •     ldr    r2, 2f            @ get address of __pv_phys_offset
  •     mov    r3, #0            @ no offset
  •     mov    r4, r0            @ r0 = table start
  •     add    r5, r0, r1        @ r1 = table size
  •     ldr    r6, [r2, #4]        @ get __pv_offset
  •     bl    __fixup_a_pv_table
  •     ldmfd     {r4 - r7, pc}
  • ENDPROC(fixup_pv_table)

  •     .align
  • 2:    .long    __pv_phys_offset

  •     .data
  •     .globl    __pv_phys_offset
  •     .type    __pv_phys_offset, %object
  • __pv_phys_offset:
  •     .long    0
  •     .size    __pv_phys_offset, . - __pv_phys_offset
  • __pv_offset:
  •     .long    0
  • #endif

  • #include "head-common.S"

arch/arm/kernel/head-common.S


  • /*
  • * linux/arch/arm/kernel/head-common.S
  • *
  • * Copyright (C) 1994-2002 Russell King
  • * Copyright (c) 2003 ARM Limited
  • * All Rights Reserved
  • *
  • * This program is free software; you can redistribute it and/or modify
  • * it under the terms of the GNU General Public License version 2 as
  • * published by the Free Software Foundation.
  • *
  • */

  • #define ATAG_CORE 0x54410001
  • #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
  • #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)

  • #ifdef CONFIG_CPU_BIG_ENDIAN
  • #define OF_DT_MAGIC 0xd00dfeed
  • #else
  • #define OF_DT_MAGIC 0xedfe0dd0 /* 0xd00dfeed in big-endian */
  • #endif

  • /*
  • * 异常处理. 一些我们无法处理的错误.
  • * 我们应当告诉用户(这些错误信息),但因为我们甚至无法保证是在正确的架构上运行,
  • * 所以我们什么都不做(死循环)。
  • *
  • * 如果 CONFIG_DEBUG_LL 被设置,我们试图打印出错误信息,
  • * 并希望这可以对我们有帮助 (例如这对bootloader没有提供适当的处理器ID
  • * 是有帮助的).
  • */
  •     __HEAD

  • /* 确定r2(内核启动参数)指针的有效性。  The heuristic 要求

  •   * 是4Byte对齐的、在物理内存的头16K中,且以ATAG_CORE标记开头。

  •   * 如果选择了CONFIG_OF_FLATTREE,dtb指针也是可以接受的.

  •   *

  •   * 在这个函数的未来版本中 可能会对物理地址的要求更为宽松,

  •   * 且如果有必要的话,可能可以移动ATAGS数据块.

  •   *

  •   * 返回:

  •   * r2 可能是有效的 atags 指针, 有效的 dtb 指针,或者0

  •   * r5, r6 被篡改

  •   */
  • __vet_atags:
  •     tst    r2, #0x3            @ 是否4Byte对齐?
  •     bne    1f                    @ 不是则认为指针无效,返回

  •     ldr    r5, [r2, #0]        @获取r2指向的前4Byte,用于下面测试
  • #ifdef CONFIG_OF_FLATTREE
  •     ldr    r6, =OF_DT_MAGIC        @ is it a DTB?
  •     cmp    r5, r6
  •     beq    2f
  • #endif

  •     /* 内核启动参数块的规范是:
  •      * (wait for updata)
  •      */
  •     cmp    r5, #ATAG_CORE_SIZE        @ 第一个tag是ATAG_CORE吗?测试的是tag_header中的size
  •                                 @ 如果为ATAG_CORE,那么必为ATAG_CORE_SIZE
  •     cmpne    r5, #ATAG_CORE_SIZE_EMPTY    @ 如果第一个tag的tag_header中的size为ATAG_CORE_SIZE_EMPTY
  •                                         @ 说明此处也有atags
  •     bne    1f
  •     ldr    r5, [r2, #4]            @ 第一个tag_header的tag(魔数)
  •     ldr    r6, =ATAG_CORE            @ 获取ATAG_CORE的魔数
  •     cmp    r5, r6                    @ 判断第一个tag是否为ATAG_CORE
  •     bne    1f                        @ 不是则认为指针无效,返回

  • 2:    mov    pc, lr                @ atag/dtb 指针有效

  • 1:    mov    r2, #0
  •     mov    pc, lr
  • ENDPROC(__vet_atags)

  • /*

  •   * 以下的代码段是在MMU开启的状态下执行的,

  •   * 而且使用的是绝对地址; 这不是位置无关代码.

  •   *

  •   * r0 = cp#15 控制寄存器值

  •   * r1 = machine ID

  •   * r2 = atags/dtb pointer

  •   * r9 = processor ID

  •   */
  •     __INIT
  • __mmap_switched:
  •     adr    r3, __mmap_switched_data

  •     ldmia     {r4, r5, r6, r7}
  •     cmp    r4, r5                @ 如果有必要,拷贝数据段。
  •                             @ 对比__data_loc和_sdata
  •                             @ __data_loc是数据段在内核代码映像中的存储位置
  •                             @ _sdata是数据段的链接位置(在内存中的位置)
  •                             @ 如果是XIP技术的内核,这两个数据肯定不同
  • 1:    cmpne    r5, r6            @ 检测数据是否拷贝完成
  •     ldrne    fp, [r4], #4
  •     strne    fp, [r5], #4
  •     bne    1b

  •     mov    fp, #0                @ 清零 BSS 段(and zero fp)
  • 1:    cmp    r6, r7                @ 检测是否完成
  •     strcc    fp, [r6],#4
  •     bcc    1b

  •      /* 这里将需要的数据从寄存器中转移到全局变量中,

  •       * 因为最后会跳入C代码,寄存器会被使用。

  •       */
  • ARM(    ldmia    r3, {r4, r5, r6, r7, sp})
  • THUMB(    ldmia    r3, {r4, r5, r6, r7}    )
  • THUMB(    ldr    sp, [r3, #16]        )
  •      str    r9, [r4]            @ 保存 processor ID到全局变量processor_id

  •      str    r1, [r5]            @ 保存 machine type到全局变量__machine_arch_type

  •      str    r2, [r6]            @ 保存 atags指针到全局变量__atags_pointer

  •      bic    r4, r0, #CR_A            @ 清除cp15 控制寄存器值的 'A' bit(禁用对齐错误检查)

  •      stmia    r7, {r0, r4}            @ 保存控制寄存器值到全局变量cr_alignment(在arch/arm/kernel/entry-armv.S)
  •      b    start_kernel        @ 跳入C代码(init/main.c)
  • ENDPROC(__mmap_switched)

  •     .align    2
  •     .type    __mmap_switched_data, %object
  • __mmap_switched_data:
  •     .long    __data_loc            @ r4
  •     .long    _sdata                @ 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
  •     .size    __mmap_switched_data, . - __mmap_switched_data

  • /*
  • * 这里提供一个 C-API 版本的 __lookup_processor_type
  • */
  • ENTRY(lookup_processor_type)
  •     stmfd     {r4 - r6, r9, lr}
  •     mov    r9, r0
  •     bl    __lookup_processor_type
  •     mov    r0, r5
  •     ldmfd     {r4 - r6, r9, pc}
  • ENDPROC(lookup_processor_type)

  • /*

  •   * 读取处理器ID寄存器 (CP#15, CR0), 并且查找编译时确定的处理器

  •   * 支持列表. 注意:我们不能对__proc_info使用绝对地址,

  •   * 因为我们还没有重新初始化页表(MMU已关闭,之前是解压时使用的1:1映射)。

  •   * (我们不在正确的地址空间:内核是按虚拟地址(0xc00008000)编译的,

  •   * 而现在我们运行在MMU关闭的情况下)

  •   * 我们必须计算偏移量。

  •   *

  •   *    r9 = cpuid

  •   * Returns:

  •   *    r3, r4, r6 被篡改

  •   *    r5 = proc_info 指针(物理地址空间)

  •   *    r9 = cpuid (保留)

  •   */
  •     __CPUINIT
  • __lookup_processor_type:
  •     adr    r3, __lookup_processor_type_data        @获取运行时的地址数据
  •     ldmia    r3, {r4 - r6}    @获取编译时确定的地址数据(虚拟地址)
  •     sub    r3, r3, r4            @ 获取地址偏移 virt&phys(r3)
  •     add    r5, r5, r3            @ 将虚拟地址空间转换为物理地址空间
  •     add    r6, r6, r3            @ r5=__proc_info_begin r6=__proc_info_end
  • 1:    ldmia    r5, {r3, r4}    @ 获取proc_info_list结构体中的value, mask
  •     and    r4, r4, r9            @ 利用掩码处理从CP15获取的处理器ID
  •     teq    r3, r4                @ 对比编译时确定的处理器ID
  •     beq    2f                    @ 若处理器ID匹配,返回
  •     add    r5, r5, #PROC_INFO_SZ        @ 利用sizeof(proc_info_list)跳入下一个处理器ID的匹配
  •     cmp    r5, r6                @ 是否已经处理完proc_info_list数据
  •     blo    1b                    @ 如果还有proc_info_list数据,再次检查匹配
  •     mov    r5, #0                @ 否则,编译的内核与此处理器不匹配,r5 = #0
  • 2:    mov    pc, lr
  • ENDPROC(__lookup_processor_type)

  • /*

  •   * 参见 中关于 __proc_info 结构体的信息.

  •   */
  •     .align    2
  •     .type    __lookup_processor_type_data, %object
  • __lookup_processor_type_data:
  •     .long    .
  •     .long    __proc_info_begin
  •     .long    __proc_info_end
  •     .size    __lookup_processor_type_data, . - __lookup_processor_type_data

  • /*

  •   * 处理器ID不匹配时的入口

  •   * 如果启用了调试信息,会从consol打印提示信息

  •   * 之后会进入__error的死循环

  •   */
  • __error_p:
  • #ifdef CONFIG_DEBUG_LL
  •     adr    r0, str_p1
  •     bl    printascii
  •     mov    r0, r9
  •     bl    printhex8
  •     adr    r0, str_p2
  •     bl    printascii
  •     b    __error
  • str_p1:    .asciz    "\nError: unrecognized/unsupported processor variant (0x"
  • str_p2:    .asciz    ").\n"
  •     .align
  • #endif
  • ENDPROC(__error_p)

  • /*

  •   * 出错时的死循环入口

  •   */
  • __error:
  • #ifdef CONFIG_ARCH_RPC
  • /*
  • * 出错时屏幕变红 - RiscPC only.
  • */
  •     mov    r0, #0x02000000
  •     mov    r3, #0x11
  •     orr    r3, r3, r3, lsl #8
  •     orr    r3, r3, r3, lsl #16
  •     str    r3, [r0], #4
  •     str    r3, [r0], #4
  •     str    r3, [r0], #4
  •     str    r3, [r0], #4
  • #endif
  • 1:    mov    r0, r0
  •     b    1b
  • ENDPROC(__error)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-149511-1-1.html 上篇帖子: linux kernel map 下篇帖子: 基于MCP2515的Linux CAN总线驱动程序设计(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表