基于Arm 的linux 的启动分析

基于 Arm linux 的启动分析

目录:

Makefile 的分析 ... 2

1.1 启动方案 ... 2

1.2 zImage 代码结构 ... 2

1.2.1 顶层vmlinux 的生成过程 ... 2

1.2.2  zImage 的生成 ... 6

二zImage 的启动过程 ... 12

2.1 compressed/vmlinux.lds 文件的分析 ... 12

2.2 compressed/head.s 文件的分析 ... 12

2.3inux/arch/arm/kernel/head.S 文件的分析 ... 18

2.4 arch/arm/kernel/common.s 文件的分析 ... 19

团队成员贡献度 ... 21

 

 

注:目录均设有超级链接,可以按住CTRL 并单击鼠标进行跟踪链接。


Makefile 的分析

 

1.1 启动方案

/arch/arm 文件夹下的Makefile 文件第215 行得到语句

define archhelp

  echo  '* zImage        - Compressed kernel image (arch/$(ARCH)/boot/zImage)'

  echo  '  Image         - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'

  echo  '* xipImage      - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'

  echo  '  bootpImage    - Combined zImage and initial RAM disk'

由此可以知道基于ARM 的启动方案有ImagezImagexipImagebootpImage 四种。

 

1.2 zImage 代码结构

zImage 为例分析zImage 型启动方案的代码结构:

arm 文件夹下的makefile 中默认目标all169

# Default target when executing plain make

ifeq ($(CONFIG_XIP_KERNEL),y)

all: xipImage

else

all: zImage

endif

 

其中zImage197 行作为目标生成:

zImage Image xipImage bootpImage uImage: vmlinux

    $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

zImage 等目标的生成依赖于vmlinux 。而vmlinux 是在顶层目录下的Makefile 文件中生成的。

 

1.2.1 顶层vmlinux 的生成过程

linux-2.6.17 顶层目录下找到并打开Makefile 文件。在文件的464 行找到缺省目标"allvmlinux" 语句.

根据关键字vmlinux 找到是由文件中的703

vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE

    $(call if_changed_rule,vmlinux__)

    $(Q)rm -f .old_version

语句生成。可以看出vmlinux 依赖于vmlinux-ldsvmlinux-initvmlinux-mainkallsysm.o 变量。

这些变量在顶层Makefile567 行定义

vmlinux-init := $(head-y) $(init-y)

vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds

 

首先查看 vmlinux.lds 文件 arm/kernel/vmlinux.lds

25:   . = PAGE_OFFSET + TEXT_OFFSET;

TEXT_OFFSET arm/Makefile 中定义:

128 行:TEXT_OFFSET := $(textofs-y)

82 行:textofs-y   := 0x00008000 这是内核启动的虚拟地址

141 行:export  TEXT_OFFSET GZFLAGS MMUEXT TEXT_OFFSET 导出供vmlinux.lds 使用

这样就有 . = PAGE_OFFSET + 0x00008000

 

再看vmlinux-main 变量

其中的head-yarch/arm/Makefile81 行定义

head-y     := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

变量init-y 在顶层Makefile427 行定义

init-y     := init/

而后在532 行修改

init-y     := $(patsubst %/, %/built-in.o, $(init-y))

这里的patsubst 是实现匹配替换的,在这里将init/'/' 替换为'/built-in.o'

所以变量init-y 应为 

init-y          := init/built-in.o

 

因此

vmlinux-init := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o init/built-in.o

 

同理看vmlinux-main 的定义

vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

core-y 定义在顶层Makefile431

core-y     := usr/

而后在521 行追加

core-y     += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

533 行修改

core-y     := $(patsubst %/, %/built-in.o, $(core-y))

所以core-y 应为

core-y     :=usr/kernel/ mm/ fs/ ipc/ security/ crypto/ block/built-in.o

 

libs-y 定义在顶层Makefile430

libs-y     := lib/

而后在536 行修改

libs-y     := $(patsubst %/, %/lib.a, $(libs-y))

所以libs-y 应为

libs-y     := lib/lib.a

 

drivers-y 定义在顶层Makefile428

drivers-y  := drivers/ sound/

而后在534 行修改

drivers-y  := $(patsubst %/, %/built-in.o, $(drivers-y))

所以drivers-y 应为

drivers-y  := drivers/ sound/ built-in.o

 

net-y 变量定义在顶层Makefile429

net-y      := net/

而后在第535 行修改

net-y      := $(patsubst %/, %/built-in.o, $(net-y))

所以net-y 应为

net-y      := net-y/ built-in.o

 

因此

vmlinux-main := usr/kernel/ mm/ fs/ ipc/ security/ crypto/ block/built-in.o

       lib/lib.a

       drivers/ sound/ built-in.o

       net-y/ built-in.o

这些依赖文件的生成规则和依赖如下:

$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;

此处规则为空规则,而依赖文件$(vmlinux-dirs) 的生成如下:

$(vmlinux-dirs): prepare scripts

    $(Q)$(MAKE) $(build)=$@

至此vmlinux 的依赖文件分析完毕。

 

然后看vmlinux 的生成规则

$(Q)$(MAKE) -f $(srctree)/Makefile headers_check 是进行头文件的相关检测。下一条命令

       $(call if_changed_rule,vmlinux__)

变量if_changed_rule 没在顶层Makefile 中定义,所以在文件中向上查找include ,第一条语句在266 行找到:

include  $(srctree)/scripts/Kbuild.include

在这个文件中定义了大量的函数和变量,供顶层makefile 和其他makefile 文件使用。

在文件/linuv-2.6.27/scripts/Kbuild.include 文件中145 行找到if_changed_rule 变量。描述如下:

# Usage: $(call if_changed_rule,foo)

# will check if $(cmd_foo) changed, or any of the prequisites changed,

# and if so will execute $(rule_foo)

if_changed_rule = $(if $(strip $(filter-out $(PHONY),$?)            /

           $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ),/

           @set -e; /

           $(rule_$(1)))

此处的$(1)=vmlinux__$(rule_$(1)) 语句得到rule_vmlinux__ ,因此$(call if_changed_rule,vmlinux__) 语句是通过call 函数调用执行的rule_vmlinux__, 在顶层Makefile601

define rule_vmlinux__

    :

    $(if $(CONFIG_KALLSYMS),,+$(call cmd,vmlinux_version))

 

    $(call cmd,vmlinux__)

    $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd

 

    $(Q)$(if $($(quiet)cmd_sysmap),                 /

      echo '  $($(quiet)cmd_sysmap) System.map' &&) /

    $(cmd_sysmap) $@ System.map;                    /

    if [ $$? -ne 0 ]; then                          /

       rm -f $@;                               /

       /bin/false;                             /

    fi;

    $(verify_kallsyms)

endef

这里主要还是调用 cmd_vmlinux__ 定义在顶层文件 575

      cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ /

      -T $(vmlinux-lds) $(vmlinux-init)                          /

      --start-group $(vmlinux-main) --end-group                  /

      $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) FORCE ,$^)

) FORCE ,$^)

通过这个命令将变量vmlinux-initvmlinux-main 指定的目标链接成vmlinux 文件。链接脚本由vmlinux-lds 指定。在顶层 Makefile 570 行定义:

vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds

 

至此vmlinux 生成分析完毕。

 

1.2.2   zImage 的生成

zImage 生成语句重写如下

zImage Image xipImage bootpImage uImage: vmlinux

    $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

然后看zImage 的生成规则:

$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

 

命令行的build 变量在arm/makefile 中没有定义,然而此文件包含于顶层makefile ,而顶层makefile 中首先包含的是scripts/Kbuild.include ,所以在Kbuild.include

查找build :在Kbuild.include88 行定义:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

 

boot 变量在arm/makefile172 行定义:

boot := arch/arm/boot

 

这个规则的命令最终会进入scripts 目录,执行Makefile.build 文件,并传递参数obj=scripts/arch/arm/boot.

 

MACHINE arm/makefile135 行定义

ifneq ($(machine-y),)

MACHINE  := arch/arm/mach-$(machine-y)/

else

MACHINE  :=

endif

 

machine-y( 假设为s3c2410) 由语句

  machine-$(CONFIG_ARCH_S3C2410)    := s3c2410

决定为machine-s3c2410

所以zImage 为:

zImage: vmlinux

    $(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj = arch/arm/boot /

    MACHINE = arch/arm/mach-s3c2410.

 

然后看scripts/Makefile.build 文件(由上一句将obj = arch/arm/boot 赋值)

5     src := $(obj)

# The filename Kbuild has precedence over Makefile

17    kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile)

即此处为

kbuild-dir := arch/arm/boot

include arch/arm/boot/Kbuild,arch/arm/boot/Makefile

 

打开boot/Makefile:

17 行:

include $(srctree)/$(MACHINE)/Makefile.boot

Makefile.boot 中得到

   zreladdr-y := 0x30008000

注释行有:ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)

这个是zImage 的运行地址。

 

49 行:

$(obj)/Image: vmlinux FORCE

    $(call if_changed,objcopy)

 

$(obj)/compressed/vmlinux: $(obj)/Image FORCE

    $(Q)$(MAKE) $(build)=$(obj)/compressed $@

 

$(obj)/zImage:    $(obj)/compressed/vmlinux FORCE

    $(call if_changed,objcopy)

49 行的vmlinux 是在顶层生成的,在前面已经得到。

50    $(call if_changed,objcopy) 即调用callif_changedscripts/Kbuild.include 文件中125 行被执行)来执行cmd_objcopy

if_changed = $(if $(strip $(filter-out $(PHONY),$?)          /

       $(call arg-check, $(cmd_$(1)), $(cmd_$@)) ), /

 

cmd_$(1) = cmd_objcpy;

arg-check scripts/Kbuild.include112 行进行测试:

ifneq ($(KBUILD_NOCMDDEP),1)

# Check if both arguments has same arguments. Result in empty string if equal

# User may override this check using make KBUILD_NOCMDDEP=1

arg-check = $(strip $(filter-out $(1), $(2)) $(filter-out $(2), $(1)) )

endif

我们关心的是变量cmd_objcpy

 

$(call if_changed,objcopy) 规则中,将前面创建的vmlinux 文件通过二进制工具objcopy 进行处理,在scripts/Makefile.build 的第19

行包含了scripts/Makefile.lib

include scripts/Makefile.lib 

在这个makefile 文件中,有cmd_objcopy 的定义,在156 行开始定义

quiet_cmd_objcopy = OBJCOPY $@

cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $

 

顶层Makefile 中有

282   OBJCOPY        = $(CROSS_COMPILE)objcopy

CROSS_COMPILE ?=

 

OBJCOPYFLAGS arm/Makefile15 行:

OBJCOPYFLAGS  :=-O binary -R .note -R .comment -S

 

cmd_objcopy = objcopy -O binary -R .note -R .comment -S

objcopy 命令可以将一种格式的目标文件内容进行转换,并输出为另一种

格式的目标文件。

makefile 里面用-O binary 选项来生成原始的二进制文件,

即通常说的 image 文件

 

也就是说顶层Makefile 中生成的vmlinux 二进制化得到Image 文件。

 

然后是53:

$(obj)/compressed/vmlinux: $(obj)/Image FORCE

    $(Q)$(MAKE) $(build)=$(obj)/compressed $@

这里53$(obj) = arch/arm/boot;

变量build 为:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

所以执行$(build)=$(obj)/compressed 之后,

obj = arch/arm/boot/compressed;

$(obj)/compressed/vmlinux: arch/arm/boot/Image FORCE

    $(Q)$(MAKE) build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build

    obj = arch/arm/boot/compressed arch/arm/boot/compressed/vmlinux

 

build 语句令最终会进入scripts 目录,执行Makefile.build 文件,并传递参数obj = arch/arm/boot/compressed.

Makefile.build 的第5 行有:  

src := $(obj)  

这就把传递进来的值赋给了src ,所以  

src := arch/arm/boot/compressed

从第16 行开始的两行把src (arch/arm/boot/compressed) 目录下的Makefile 包含进来(如果有Kbuild 则包含Kbuild

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile)

 

到达arch/arm/boot/compressed 目录下的Makefile94 行:

$(obj)/vmlinux: $(obj)/vmlinux.lds.in $(obj)/$(HEAD) $(obj)/piggy.o/

       $(addprefix $(obj)/,$(BOJS)) FORCE

    $(call if_changed,ld)

    @:

(addprefix 函数 ( 两个参数) 将源串( 第二个参数中由空格分隔) 中的每一项添加前缀( 第一个参数).)

 

$(obj)/piggy.gz: $(obj)/../Image FORCE

    $(call if_changed,gzip)

 

$(obj)/piggy.o: $(obj)/piggy.gz FORCE

 

HEAD = head.o

OBJS = misc.o

 

106 行:

$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config

    @sed "$(SEDFLAGS)" < $< > $@

 

$(obj)/misc.o: $(obj)/misc.c include/asm/arch/uncompress.h lib/inflate.c

 

打开vmlimux.lds.in14 行:

  . = TEXT_START;

 

而在compressed/Makefile 中有语句:

66 行:  ZTEXTADDR := 0

    ZBSSADDR := ALIGN(4) 

 

ZTEXTADDR 是自解压代码的起始地址,如果从内存启动内核,设置为0 即可,如果从Rom/Flash 启动,则设置 ZTEXTADDR 为相应的值。ZRELADDR 是内核解压缩后的执行地址。

这里我们普通的启动就是得到ZTEXTADDR0 了,ZTEXTADDR 是指zImage.text 节开始的物理地址,也就是zImage 的第一条指令的物理地址.

 

70 行:

    SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/

这里把链接教本中的TEXT_START 换成了ZTEXTADDR ,我们的例子来说就是0 了,TEXT_ADDR 是链接教本中引入,或说zImage 的虚拟首址.

此时TEXT_START = 0

 

zImage 是由一个压缩后的内核piggy.o ,连接上一段初始化及解压功能的代码(head.o misc.o )组成的。

piggy.o 由内核压缩生成,head.omisc.o 用于初始化和内核解压缩。

 

即:

vmlinux-( 二进制化)->Image-->compress/vmlinux( 包含了piggy.ohead.omisc.o)-( 二进制化)->zImage

 

zImage 的启动过程

zImage 的启动过程:

zImage 的生成经历了两次大的链接过程:一次是顶层vmlinux 的生成,由arch/arm/boot/vmlinux.lds (这个lds 文件是由 arch/arm/kernel/vmlinux.lds.S 生成的)决定;另一次是arch/arm/boot/compressed/vmlinux 的生成,是由arch/arm/boot/compressed/vmlinux.lds (这个lds 文件是由arch/arm/boot/compressed/vmlinux.lds.in 生成的)决定。

所以zImage 的入口由arch/arm/boot/compressed/vmlinux.lds 来决定。

2.1 compressed/vmlinux.lds 文件的分析

打开vmlinux.lds.in

 

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

{

  . = TEXT_START; (上文分析TEXT_START = 0

  _text = .;

 

  .text : {

    _start = .;

    *(.start)

    *(.text)

 

2.2 compressed/head.s 文件的分析

首先执行的是compressed/head.s 中的start 段:

start:

       .type  start,#function

                  .rept       8 // 重复8 次下面的指令,也就是空出中断向量表的位置

                  mov r0, r0 // 就是nop 指令

       .endr

 

       b   1f

       .word  0x016f2818    @ Magic numbers to help the loader

       .word  start         @ absolute load/run zImage address

       .word  _edata        @ zImage end address

1:     mov r7, r1        @ save architecture ID

       mov r8, r2        @ save atags pointer

保存IDatags pointerr1r2 ,然后执行下面的关中断程序段。

#ifndef __ARM_ARCH_2__

       mrs r2, cpsr      @ get current mode

       tst r2, #3        @ not user?

       bne not_angel

       mov r0, #0x17     @ angel_SWIreason_EnterSVC

       swi 0x123456      @ angel_SWI_ARM

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

至此start 段结束,接下来是text 段。

首先LCO 段定义如下数据结构:

LC0:       .word  LC0        @ r1

       .word  __bss_start       @ r2

       .word  _end          @ r3

       .word  zreladdr      @ r4

       .word  _start        @ r5

       .word  _got_start    @ r6

       .word  _got_end      @ ip

       .word  user_stack+4096      @ sp

 

.text

       adr r0, LC0

       ldmia  r0, {r1, r2, r3, r4, r5, r6, ip, sp}

       subs   r0, r0, r1    @ calculate the delta offset

 

                      @ if delta is zero, we are

       beq not_relocated     @ running at the address we

                     @ were linked at.

       /*

         * We're running at a different address.  We need to fix

         * up various pointers:

         *   r5 - zImage base address

         *   r6 - GOT start

         *   ip - GOT end

         */

       add r5, r5, r0

       add r6, r6, r0

       add ip, ip, r0

 

 

206 行是BBS 清零部分:

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

结束之后到219 行执行跳转命令,执行启动cache 命令行:

       bl  cache_on

 

       mov r1, sp        @ malloc space above stack

       add r2, sp, #0x10000  @ 64k max

cache_on 321 行:

cache_on:  mov r3, #8        @ cache_on function

       b   call_cache_fn

call_cache_fn 503 行:

call_cache_fn:    adr r12, proc_types

       mrc p15, 0, r6, c0, c0   @ get processor ID

1:     ldr r1, [r12, #0]     @ get value

       ldr r2, [r12, #4]     @ get mask

       eor r1, r1, r6    @ (real ^ match)

       tst r1, r2        @       & mask

       addeq  pc, r12, r3       @ call cache function

       add r12, r12, #4*5

       b   1b

这里cache_on: r3 中存入#8, 跳到call_cache_fn, proc_types 为缓存操作表格, 一个条目有五条

* - CPU ID match

* - CPU ID mask

* - 'cache on' method instruction

* - 'cache off' method instruction

* - 'cache flush' method instruction

7

r3 的值决定了调用的是on 的还是off 的函数。

 

bl  cache_on 之后的两条指令:

       mov r1, sp        @ malloc space above stack

       add r2, sp, #0x10000  @ 64k max

建立了c 程序运行需要的缓存,并赋予64K 的栈空间。

 

这时r2 是缓存的结束地址,r4 kernel 的最后执行地址,r5 kernel 境象文件的开始地

址。检查是否地址有冲突。将r5 等于r2 ,使decompress 后的kernel 地址就在64K 的栈之后。

head.s 233 行:

       cmp r4, r2

       bhs wont_overwrite

       add r0, r4, #4096*1024   @ 4MB largest kernel size

       cmp r0, r5

       bls wont_overwrite

 

       mov r5, r2        @ decompress after malloc space

       mov r0, r5

       mov r3, r7

       bl  decompress_kernel

执行程序至:bl decompress_kernel 进入内核解压。调用文件misc.c 的函数decompress_kernel() ,解压内核于缓存结束的地方(r2 地址之后)

进入misc.c 文件:第325  

decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,

         int arch_id)

然后返回head.s

紧接bl decompress_kernel 之后的命令:

       add r0, r0, #127

       bic r0, r0, #127      @ align the kernel length

此时各寄存器值有如下变化:

r0 为解压后kernel 的大小

r4 kernel 执行时的地址

r5 为解压后kernel 的起始地址

r6 CPU 类型值(processor ID)

r7 为系统类型值(architecture ID)

  * r0     = decompressed kernel length

  * r1-r3  = unused

  * r4     = kernel execution address

  * r5     = decompressed kernel start

  * r6     = processor ID

  * r7     = architecture ID

  * r8     = atags pointer

  * r9-r14 = corrupted

  * r0     = decompressed kernel length

  * r1-r3  = unused

  * r4     = kernel execution address

  * r5     = decompressed kernel start

  * r6     = processor ID

  * r7     = architecture ID

  * r8     = atags pointer

  * r9-r14 = corrupted

  */

256 行:

       add r1, r5, r0    @ end of decompressed kernel

       adr r2, reloc_start

       ldr r3, LC1

       add r3, r2, r3

1:     ldmia  r2!, {r9 - r14}      @ copy relocation code

       stmia  r1!, {r9 - r14}

       ldmia  r2!, {r9 - r14}

       stmia  r1!, {r9 - r14}

       cmp r2, r3

       blo 1b

       bl  cache_clean_flush

       add pc, r5, r0    @ call relocation code

reloc_start 代码拷贝之kernel 之后(r5+r0 之后) ,首先清除缓存(bl cache_clean_flush ),而后执行reloc_startadd  pc, r5, r0 )。reloc_start r5 开始的kernel 重载于r4 地址处。清除cache 内容,关闭cache ,将r7 architecture ID 赋于r1 ,执行r4 开始的kernel 代码。

然后通过长跳转指令跳至decompress_kernel

276 行:

wont_overwrite:   mov r0, r4

       mov r3, r7

       bl  decompress_kernel

       b   call_kernel

执行完解压过程后,再返回到head.s 执行转移语句call_kernel ,启动内核:

482 行:

call_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

       mov pc, r4        @ call kernel

上面分析已知道 * r4     = kernel execution address

所以mov   pc, r4 语句之后,开始执行r4 开始的kernel 代码。

2.3inux/arch/arm/kernel/head.S 文件的分析

进入linux/arch/arm/kernel/head.S 文件:

文件从stext 开始:

ENTRY(stext)

    msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC @ ensure svc mode

                     @ and irqs disabled

    mrc p15, 0, r9, c0, c0       @ get processor id

    bl  __lookup_processor_type     @ r5=procinfo r9=cpuid

    movs   r10, r5              @ invalid processor (r5=0)?

    beq __error_p         @ yes, error 'p'

    bl  __lookup_machine_type       @ r5=machinfo

    movs   r8, r5            @ invalid machine (r5=0)?

    beq __error_a         @ yes, error 'a'

    bl  __create_page_tables

这里检测了cpu ID 、进程类型、机器类型以及建立并初始化页表。

然后第92 行:

    ldr r13, __switch_data

__switch_data linux/arch/arm/kernel/common.s 文件中定义的一个地址。

之后语句#if defined(CONFIG_SMP) 为多核的初始化程序,跳过直接到第148 行:

__enable_mmu: 使能mmu

167 行:   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

184 行:

__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

这里打开了mmu 并且将__switch_data 地址送入pc 中,转到linux/arch/arm/kernel/common.s 文件.

 

2.4 arch/arm/kernel/common.s 文件的分析

__switch_data: 在第15 行,而可执行程序代码在文件35 行:

__mmap_switched:

    adr r3, __switch_data + 4

 

    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

 

    ldmia  r3, {r4, r5, r6, sp}

    str r9, [r4]          @ Save processor ID

    str r1, [r5]          @ Save machine type

    bic r4, r0, #CR_A        @ Clear 'A' bit

    stmia  r6, {r0, r4}         @ Save control register values

    b   start_kernel

启动过程从这里开始跳转到 start_kernel

 

 


三 团队成员贡献度

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(基于Arm 的linux 的启动分析)