Xvisor ARM32 启动分析
阅读原文时间:2023年07月15日阅读:1

Linux内核历史悠久,特性丰富,但是代码量庞大,各个子系统交叉繁琐。对于想要将操作系统内核各个特性研究一遍的人,有时候也只好"望Linux兴叹"。Xvisor是一个较新的Type-1 Hypervisor,其官方地址在:http://xhypervisor.org/。Xvisor代码逻辑清晰,编码规范,虽然其也借鉴了Linux内核的一些特性,但它有很多地方都是完全重构,因为其目的是要实现一个Hypervisor,而不是一个通用的操作系统(尽管很多操作系统内核的基本元素都是必要的)。从这一个角度看,Xvisor可以是系统软件开发者的一个很好的研究项目,因为他不仅包含了操作系统内核的课题,还包含了虚拟化这一现代系统软件开发者的必修课。最主要的是,Xvisor还很新,许多地方都需要进一步丰富,这给了想要上手玩一玩的系统软件开发者许多Contribute的机会。最近笔者就业余把Xvisor拿来玩,做一些移植和分析的事情,期间会做一些记录,准备分享在本博客上。本文就从其启动部分开始分析。由于仅仅是个人业余玩玩,不保证内容的完全准确和清晰。

根目录下有一个主Makefile。其中我们可以看到建立了许多规则。下面的规则建立起来的执行顺序要求先调用compile_cpp命令生成build_dir下面的linker.ld文件。

312$(build_dir)/vmm.bin: $(build_dir)/vmm.elf


313&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_objcopy,$@,$<)


314


315$(build_dir)/vmm.elf: $(build_dir)/linker.ld $(all-y) $(build_dir)/system.o


316&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_ld,$@,$(build_dir)/linker.ld,$(all-y) $(build_dir)/system.o)


317


318$(build_dir)/system.map: $(build_dir)/vmm_tmp.elf


319&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_nm,$@,$<)


320


321$(build_dir)/vmm_tmp.elf: $(build_dir)/linker.ld $(all-y) $(build_dir)/system_tmp1.o


322&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_ld,$@,$(build_dir)/linker.ld,$(all-y) $(build_dir)/system_tmp1.o)


323


324$(build_dir)/system_tmp1.map: $(build_dir)/vmm_tmp1.elf


325&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_nm,$@,$<)


326


327$(build_dir)/vmm_tmp1.elf: $(build_dir)/linker.ld $(all-y)


328&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_ld,$@,$(build_dir)/linker.ld,$(all-y))


329


330$(build_dir)/linker.ld: $(cpu_dir)/linker.ld


331&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_cpp,$@,$<)

而这里的compile_cpp命令如下:

compile_cpp = $(V)mkdir -p `dirname $(1)`; \

echo " (cpp) $(subst $(build_dir)/,,$(1))"; \

$(cpp) $(cppflags) $(2) | grep -v "\#" > $(1)

对于ATM32而言,这个规则实际是要从【/XVisor/arch/arm/cpu/arm32/linker.ld】这个文件生成build_dir下面的linker.ld。在用cpp进行预处理的过程中,cppflags会包含cpu-cppflags:

cppflags+=$(cpu-cppflags)

cppflags+=$(board-cppflags)

cppflags+=$(libs-cppflags-y)

这样,在【/XVisor/arch/arm/cpu/arm32/objects.mk】中的cpu-cppflags就会被包含进来,如下:

41cpu-cppflags+=-DCPU_TEXT_START=0xFF000000

因此【/XVisor/arch/arm/cpu/arm32/linker.ld】的下面一段:

24OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")


25OUTPUT_ARCH("arm")


26ENTRY(_start_vect)


27


28SECTIONS


29{


30&nbsp;&nbsp;&nbsp;&nbsp;. = CPU_TEXT_START;


31


32&nbsp;&nbsp;&nbsp;&nbsp;. = ALIGN(0x100000); /* Need this to create proper sections */


33&nbsp;&nbsp;&nbsp;&nbsp;


34&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_code_start = .);


35


36&nbsp;&nbsp;&nbsp;&nbsp;.text :


37 &nbsp;&nbsp;&nbsp;&nbsp;{


38&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_text_start = .);


39&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(.entry)


40&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(.text)


41&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. = ALIGN(4);


42&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_text_end = .);


43&nbsp;&nbsp;&nbsp;&nbsp;}


44


45&nbsp;&nbsp;&nbsp;&nbsp;.data :


46&nbsp;&nbsp;&nbsp;&nbsp;{


47&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_data_start = .);


48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(.data)


49&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. = ALIGN(4);


50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_data_end = .);


51&nbsp;&nbsp;&nbsp;&nbsp;}


52


53&nbsp;&nbsp;&nbsp;&nbsp;.rodata :


54&nbsp;&nbsp;&nbsp;&nbsp;{


55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_rodata_start = .);


56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(.rodata .rodata.*)


57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. = ALIGN(4);


58&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_rodata_end = .);


59&nbsp;&nbsp;&nbsp;&nbsp;}


60


61&nbsp;&nbsp;&nbsp;&nbsp;.percpu :


62&nbsp;&nbsp;&nbsp;&nbsp;{


63&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_percpu_start = .);


64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(.percpu)


65&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. = ALIGN(4);


66&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_percpu_end = .);


67&nbsp;&nbsp;&nbsp;&nbsp;}

就会被定为在0xFF000000这个地方。这里正好可以稍微先分析链接脚本。显然,这个连接器脚本直接使用ENTRY(_start_vect)将_start_vect这个标号作为最终映像的入口地址。这个_start_vect定为于【/XVisor/arch/arm/cpu/arm32/cpu_entry.S】这个文件。

431&nbsp;&nbsp;&nbsp;&nbsp;/*


432&nbsp;&nbsp;&nbsp;&nbsp; * Exception vector start.


433&nbsp;&nbsp;&nbsp;&nbsp; */

434&nbsp;&nbsp;&nbsp;&nbsp;.section .entry, "ax", %progbits


435&nbsp;&nbsp;&nbsp;&nbsp;.globl _start_vect


436_start_vect:


437&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __reset


438&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __undefined_instruction


439&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __software_interrupt


440&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __prefetch_abort


441&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __data_abort


442&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __not_used


443&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __irq


444&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;pc, __fiq


445__reset:


446&nbsp;&nbsp;&nbsp;&nbsp;.word _reset


447__undefined_instruction:


448&nbsp;&nbsp;&nbsp;&nbsp;.word _undef_inst


449__software_interrupt:


450&nbsp;&nbsp;&nbsp;&nbsp;.word _soft_irq


451__prefetch_abort:


452&nbsp;&nbsp;&nbsp;&nbsp;.word _prefetch_abort


453__data_abort:


454&nbsp;&nbsp;&nbsp;&nbsp;.word _data_abort


455__not_used:


456&nbsp;&nbsp;&nbsp;&nbsp;.word _not_used


457__irq:


458&nbsp;&nbsp;&nbsp;&nbsp;.word _irq


459__fiq:


460&nbsp;&nbsp;&nbsp;&nbsp;.word _fiq


461&nbsp;&nbsp;&nbsp;&nbsp;.global _end_vect


462_end_vect:


463&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;.


464


465&nbsp;&nbsp;&nbsp;&nbsp;/*


466&nbsp;&nbsp;&nbsp;&nbsp; * Exception stacks.


467&nbsp;&nbsp;&nbsp;&nbsp; */

468__svc_stack_end:


469&nbsp;&nbsp;&nbsp;&nbsp;.word _svc_stack_end


470__und_stack_end:


471&nbsp;&nbsp;&nbsp;&nbsp;.word _und_stack_end


472__abt_stack_end:


473&nbsp;&nbsp;&nbsp;&nbsp;.word _abt_stack_end


474__irq_stack_end:


475&nbsp;&nbsp;&nbsp;&nbsp;.word _irq_stack_end


476__fiq_stack_end:


477&nbsp;&nbsp;&nbsp;&nbsp;.word _fiq_stack_end


478


479&nbsp;&nbsp;&nbsp;&nbsp;/*


480&nbsp;&nbsp;&nbsp;&nbsp; * Reset exception handler.


481&nbsp;&nbsp;&nbsp;&nbsp; * Reset hardware state before starting Xvisor.


482&nbsp;&nbsp;&nbsp;&nbsp; */

483&nbsp;&nbsp;&nbsp;&nbsp;.globl _reset


484_reset:


因此,我们可能会认为,当从bootloader跳转到映像执行时会先执行这里的_reset这个标号处的代码。然而,实际上并不是这样的。因为【/XVisor/arch/arm/cpu/arm32/cpu_entry.S】文件本身作为一个编译单元,会生成cpu_entry.o,该对象文件会被整体放置到一起,而_reset这个标号处于这个cpu_entry.S文件中,因此cpu_entry.o会被放置在.text段中的开始处。


SECTIONS


{


 . = 0xFF000000;


 . = ALIGN(0x100000);


 PROVIDE(_code_start = .);


 .text :


  {


  PROVIDE(_text_start = .);


  *(.entry)


  *(.text)


  . = ALIGN(4);


  PROVIDE(_text_end = .);


 }


这里的*(.entry)也指定名为.entry的代码段应该在.text段开始处。也就是说,这个名为.entry的代码段才是第一个要执行的代码(这也符合了我们的预期)。


38&nbsp;&nbsp;&nbsp;&nbsp;.section .entry, "ax", %progbits


39&nbsp;&nbsp;&nbsp;&nbsp;.globl _start


40&nbsp;&nbsp;&nbsp;&nbsp;.globl _start_secondary


41_start:


在Makefile规定的编译顺序中,第一步会编译并链接vmm_tmp1.elf这个目标:


$(build_dir)/vmm_tmp1.elf: $(build_dir)/linker.ld $(all-y)


&nbsp;&nbsp;&nbsp;&nbsp;$(call compile_ld,$@,$(build_dir)/linker.ld,$(all-y))

第二步会对vmm_tmp1.elf进行进一步处理,提取出来这个映像中的所有符号:

$(build_dir)/system_tmp1.map: $(build_dir)/vmm_tmp1.elf

$(call compile_nm,$@,$<)

这里的compile_nm命令如下:

compile_nm = $(V)mkdir -p `dirname $(1)`; \

echo " (nm) $(subst $(build_dir)/,,$(1))"; \

$(nm) -n $(2) | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $(1)

这样,vmm_tmp1.elf中的所有符号被使用nm -n vmm_tmp1.elf提取出来,进一步使用grep处理,提取出全局的符号(非static的符号,包括全局函数和全局变量),生成system_tmp1.map。注意到在【/XVisor/tools/rules.mk】中有如下规则:

68$(build_dir)/%.S: $(build_dir)/%.map $(build_dir)/tools/kallsyms/kallsyms


69&nbsp;&nbsp;&nbsp;&nbsp;$(V)mkdir -p `dirname $@`


70&nbsp;&nbsp;&nbsp;&nbsp;$(if $(V), @echo " (kallsyms)  $(subst $(build_dir)/,,$@)")


71&nbsp;&nbsp;&nbsp;&nbsp;$(V)$(build_dir)/tools/kallsyms/kallsyms --all-symbols < $< > $@

因此,为了满足下面的编译目标,这个system_tmp1.map就会被上面这条规则处理,也就是使用kallsyms工具将system_tmp1.map转换成一个system_tmp1.S文件。

$(build_dir)/vmm_tmp.elf: $(build_dir)/linker.ld $(all-y) $(build_dir)/system_tmp1.o

$(call compile_ld,$@,$(build_dir)/linker.ld,$(all-y) $(build_dir)/system_tmp1.o)

这个规则将再次进行链接,这次链接加入了一个所有全局符号信息组成的system_tmp1.o,从而可以对这些符号按照名字和地址进行处理。在vmm_tmp.elf中,会增加下面几个全局变量:

  • kallsyms_addresses
  • kallsyms_num_syms
  • kallsyms_names
  • kallsyms_markers
  • kallsyms_token_table
  • kallsyms_token_index

kallsyms工具对这些符号和地址进行了压缩,以减小映像尺寸。更进一步,为了将这些符号也包括进入kallsyms库的分析中,下面的规则对vmm_tmp.elf进行了再次类似的处理:

$(build_dir)/vmm.elf: $(build_dir)/linker.ld $(all-y) $(build_dir)/system.o

$(call compile_ld,$@,$(build_dir)/linker.ld,$(all-y) $(build_dir)/system.o)

$(build_dir)/system.map: $(build_dir)/vmm_tmp.elf

$(call compile_nm,$@,$<)

这样,vmm.elf中的符号就包含了上面这些新加入的符号的地址和名称,从而在【/XVisor/libs/include/libs/kallsyms.h】中的对这些符号的弱引用就是实际的引用了。

32/*


33 * Tell the compiler that the count isn't in the small data section if the arch


34 * has one (eg: FRV).


35 */

36extern
                    const
                    unsigned
                    long kallsyms_num_syms


37    __attribute__ ((weak, section(".rodata")));


38extern
                    const
                    unsigned
                    long kallsyms_addresses[] __attribute__ ((weak));


39extern
                    const
                    unsigned
                    char kallsyms_names[] __attribute__ ((weak));


40extern
                    const
                    unsigned
                    char kallsyms_token_table[] __attribute__ ((weak));


41extern
                    const
                    unsigned
                    short kallsyms_token_index[] __attribute__ ((weak));


42extern
                    const
                    unsigned
                    long kallsyms_markers[] __attribute__ ((weak));

下面接着分析【/XVisor/arch/arm/cpu/arm32/cpu_entry.S】这个文件。

28#include

29

30    /*

31     * _start: Primary CPU startup code

32     * _start_secondary: Secondary CPU startup code

33     *

34     * Note: Xvisor could be loaded any where in memory by boot loaders.

35     * The _start ensures that Xvisor executes from intended base address

36     * provided at compile time.

37     */

38    .section .entry, "ax", %progbits

39    .globl _start

40    .globl _start_secondary

41_start:

42    /* r4 -> load start

43     * r5 -> load end

44     * r6 -> execution start

45     * r7 -> execution end

46     * r10 -> core# in case of SMP

47     */

48    add    r4, pc, #-0x8

49    ldr    r6, __exec_start

50    ldr    r7, __exec_end

51    sub    r3, r7, r6

52    add    r5, r4, r3

正如注释所言,Xvisor可以被加载到任何内存位置。这里标号_start会被定位于在链接器脚本中指定的起始地址,并且由PROVIDE(_code_start = .);指定。

28SECTIONS


29{


30&nbsp;&nbsp;&nbsp;&nbsp;. = CPU_TEXT_START;


31


32&nbsp;&nbsp;&nbsp;&nbsp;. = ALIGN(0x100000); /* Need this to create proper sections */


33&nbsp;&nbsp;&nbsp;&nbsp;


34&nbsp;&nbsp;&nbsp;&nbsp;PROVIDE(_code_start = .);

这里为了防止CPU_TEXT_START被指定为一个没有对齐到1MB的地址,还专门用ALIGN(0x100000)对映像起始位置进行了对齐。尽管如此,映像也可能被bootloader随意加载到某处。因此,在代码一开始,就用一条"add    r4, pc, #-0x8"指令把实际的加载地址(bootloader加载映像的地址)存储于r4中,这条指令相当于"ADR R4, _code_start"。这里的__exec_start实际上是存放_code_start这个值的地方,这个_code_start符号是连接器脚本提供的符号,用于表示在链接过程中最后对齐之后的实际代码其实地址(因此可能跟编译时指定的CPU_TEXT_START不一样,如果CPU_TEXT_START没有1MB对齐的话)。

310__exec_start:


311&nbsp;&nbsp;&nbsp;&nbsp;.word _code_start


312__exec_end:


313&nbsp;&nbsp;&nbsp;&nbsp;.word _code_end

如果代码真的被加载到了一个没有对齐的地方,启动代码也会尝试将代码自动转移到一个对齐的地方。

155align_1m_boundary:


156&nbsp;&nbsp;&nbsp;&nbsp;/* Relocate code if load start is not 1MB aligned */

157&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r4


158&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, lsr #20

159&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, lsl #20

160&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;r0, r4


161&nbsp;&nbsp;&nbsp;&nbsp;/* Skip relocation if already aligned */

162&nbsp;&nbsp;&nbsp;&nbsp;beq&nbsp;&nbsp;&nbsp;&nbsp;_start_mmu_init


163


164&nbsp;&nbsp;&nbsp;&nbsp;/* Relocate copy function at end after load end address */

165&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r0, __copy_start


166&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r1, __copy_end


167&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r2, r1, r0&nbsp;&nbsp;&nbsp;&nbsp; /* r2 -> __copy size */

168&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r6


169&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r4&nbsp;&nbsp;&nbsp;&nbsp; /* r0 -> load_address of copy_start */

170&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, r5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* r1 -> load end */

171&nbsp;&nbsp;&nbsp;&nbsp;bl&nbsp;&nbsp;&nbsp;&nbsp;_copy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* copy the _copy function after the code */

172


173&nbsp;&nbsp;&nbsp;&nbsp;/* Use newly relocated copy function to relocate entire code


174&nbsp;&nbsp;&nbsp;&nbsp; * to 1MB boundary


175&nbsp;&nbsp;&nbsp;&nbsp; */

176&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* r0 -> load start */

177&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, r4


178&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, r1, lsr #20

179&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, r1, lsl #20&nbsp;&nbsp;&nbsp;&nbsp; /* r1 -> load start aligned to 1MB boundary */

180&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r2, r5, r4&nbsp;&nbsp;&nbsp;&nbsp; /* r2 -> code size */

181&nbsp;&nbsp;&nbsp;&nbsp;bl&nbsp;&nbsp;&nbsp;&nbsp;_start_nextpc1


182_start_nextpc1:


183&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;lr, lr, #16&nbsp;&nbsp;&nbsp;&nbsp; /* Adjust return address (lr) for jump to */

184&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;lr, lr, r4&nbsp;&nbsp;&nbsp;&nbsp; /* relocated address on return from _copy */

185&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;lr, lr, r1


186&nbsp;&nbsp;&nbsp;&nbsp;bx&nbsp;&nbsp;&nbsp;&nbsp;r5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* call _copy */

187&nbsp;&nbsp;&nbsp;&nbsp;/* Update load start and load end */

188&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r4


189&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, lsr #20

190&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, lsl #20
                            /* r0 -> load_start aligned to 1MB boundary */

191&nbsp;&nbsp;&nbsp;&nbsp;subs&nbsp;&nbsp;&nbsp;&nbsp;r1, r4, r0&nbsp;&nbsp;&nbsp;&nbsp; /* r1 -> offset between load start and aligned address */

192&nbsp;&nbsp;&nbsp;&nbsp;subs&nbsp;&nbsp;&nbsp;&nbsp;r4, r4, r1&nbsp;&nbsp;&nbsp;&nbsp; /* r4 -> new load start */

193&nbsp;&nbsp;&nbsp;&nbsp;subs&nbsp;&nbsp;&nbsp;&nbsp;r5, r5, r1&nbsp;&nbsp;&nbsp;&nbsp; /* r5 -> new load end */

194&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r0, __load_start


195&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r6


196&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r4


197&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;r4, [r0]


198&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r0, __load_end


199&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r6


200&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r4


201&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;r5, [r0]


这段代码无须更多解释。

如果对齐了的话,则会接着执行MMU初始化。

203_start_mmu_init:


204&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r0, __tmpl1_mem


205&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r6


206&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r4


207&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r1, __tmpl1_mem_addr


208&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r1, r1, r6


209&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r1, r1, r4


210&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;r0, [r1]


211&nbsp;&nbsp;&nbsp;&nbsp;/* r0 -> default Level1 Table base address */

212&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, #1

213&nbsp;&nbsp;&nbsp;&nbsp;lsl&nbsp;&nbsp;&nbsp;&nbsp;r1, #14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* r1 -> 16K */

214&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r2, #0


215&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r3, r0

记住在cpu_entry.S文件开始的注释说了的如下寄存器在这段代码中的作用:

  • r4 -> load start
  • r5 -> load end
  • r6 -> execution start
  • r7 -> execution end
  • r10 -> core# in case of SMP

这里,__tmpl1_mem是存放tmpl1_mem变量值的地方:

326__tmpl1_mem_addr:


327&nbsp;&nbsp;&nbsp;&nbsp;.word __tmpl1_mem


328__tmpl1_mem:


329&nbsp;&nbsp;&nbsp;&nbsp;.word tmpl1_mem

而tmpl1_mem是在文件【/XVisor/arch/arm/cpu/arm32/cpu_mmu.c】中的一个数组变量。

52u8 __attribute__((aligned(TTBL_L1TBL_SIZE))) tmpl1_mem[TTBL_L1TBL_SIZE];


53u8 __attribute__((aligned(TTBL_L1TBL_SIZE))) defl1_mem[TTBL_L1TBL_SIZE];

为了方便理解,我们用一次实际的编译结果来说明。在一次编译之后,通过分析前面说过的system.map文件,得知:

  • ff0002b8     t     __tmpl1_mem_addr
  • ff0002bc     t     __tmpl1_mem
  • ff098000     B     defl1_mem
  • ff09c000     B     tmpl1_mem

因此,前面start_mmu_init的开始处,实际上是从__tmpl1_mem地址处获得tmpl1_mem变量的地址将tmpl1_mem变量的地址加载到r0,并且减去r6再加上r4,实际上是为了得到这个tmpl1_mem变量加载后的实际内存地址存于r0。而__tmpl1_mem_addr实际上是保存了__tmpl1_mem变量加载后的实际地址存于r1。接着的"str    r0, [r1]"实际上是将tmpl1_mem变量的地址再次存于__tmpl1_mem变量加载后的实际地址处。接着执行下面的代码:

217&nbsp;&nbsp;&nbsp;&nbsp;/* Clear default Level1 Table */

218_start_mmu_tmpl1_clear:


219&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;r2, [r3]


220&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r3, r3, #4

221&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r1, r1, #4

222&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;r1, #0


223&nbsp;&nbsp;&nbsp;&nbsp;bgt&nbsp;&nbsp;&nbsp;&nbsp;_start_mmu_tmpl1_clear

这里的意思很明白,就是要清零tmpl1_mem变量(数组),大小为(1<<14),即为16KB。接着:

224


225&nbsp;&nbsp;&nbsp;&nbsp;/* Create entries in default Level1 Table


226&nbsp;&nbsp;&nbsp;&nbsp; * for execution & load addresses


227&nbsp;&nbsp;&nbsp;&nbsp; */

228&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r3, __mmu_section_attr


229&nbsp;&nbsp;&nbsp;&nbsp;/* r1 -> load entry start address */

230&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, r4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* r4 -> load start address */

231&nbsp;&nbsp;&nbsp;&nbsp;lsr&nbsp;&nbsp;&nbsp;&nbsp;r1, #18 &nbsp;&nbsp;&nbsp;&nbsp;/* r1 >> 20, r1 << 2 */

232&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r1, r0, r1


233&nbsp;&nbsp;&nbsp;&nbsp;/* Setup load entry */

234&nbsp;&nbsp;&nbsp;&nbsp;orr&nbsp;&nbsp;&nbsp;&nbsp;r2, r3, r4


235&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;r2, [r1]


236&nbsp;&nbsp;&nbsp;&nbsp;/* r1 -> execute entry start address */

237&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r1, r6


238&nbsp;&nbsp;&nbsp;&nbsp;lsr&nbsp;&nbsp;&nbsp;&nbsp;r1, #18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* r1 >> 20, r1 << 2 */

239&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r1, r0, r1


240&nbsp;&nbsp;&nbsp;&nbsp;/* r2 -> execute entry end address */

241&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;r2, r7, r6


242&nbsp;&nbsp;&nbsp;&nbsp;lsr&nbsp;&nbsp;&nbsp;&nbsp;r2, #18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* r2 >> 20, r2 << 2 */

243&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r2, r1, r2


244&nbsp;&nbsp;&nbsp;&nbsp;/* r5 -> temporary value */

245&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r5, #0


246_start_mmu_tmpl1_set:


247&nbsp;&nbsp;&nbsp;&nbsp;orr&nbsp;&nbsp;&nbsp;&nbsp;r5, r3, r4


248&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;r5, [r1]


249&nbsp;&nbsp;&nbsp;&nbsp;lsr&nbsp;&nbsp;&nbsp;&nbsp;r4, #20

250&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r4, r4, #1

251&nbsp;&nbsp;&nbsp;&nbsp;lsl&nbsp;&nbsp;&nbsp;&nbsp;r4, #20

252&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;r1, r1, #4

253&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;r1, r2


254&nbsp;&nbsp;&nbsp;&nbsp;blt&nbsp;&nbsp;&nbsp;&nbsp;_start_mmu_tmpl1_set

我们看到:

  • r3被加载为MMU的section的属性值

    300__mmu_section_attr:

    301    .word SECTION_ATTR

    280#if (ARM_ARCH_VERSION < 6)

    281#define
    TTBL_L1TBL_TTE_ARCH_ATTR    TTBL_L1TBL_TTE_REQ_MASK

    282#else

    283#define
    TTBL_L1TBL_TTE_ARCH_ATTR    (TTBL_L1TBL_TTE_C_MASK | </p>

    284                    TTBL_L1TBL_TTE_B_MASK)

    285#endif

    286

    287#define
    SECTION_ATTR     ((TTBL_AP_SRW_U << TTBL_L1TBL_TTE_AP_SHIFT) | </p>

    288            (TTBL_L1TBL_TTE_DOM_RESERVED << TTBL_L1TBL_TTE_DOM_SHIFT) | </p>

    289            TTBL_L1TBL_TTE_ARCH_ATTR | </p>

    290            TTBL_L1TBL_TTE_TYPE_SECTION)

  • r1被设置为load start address (即r4)对应的Section在tmpl1_mem这个Table中的索引,加上tmpl1_mem变量的地址就是得到对应的entry地址。然后"orr    r2, r3, r4"实际上是把这个1MB对齐的load start address加上内存属性,接着使用一个"str    r2, [r1]"就设置了这个entry;也就是说,这里就映射了load start address开始的一个1MB为一个Section。参见下面的Translation Table的格式。这里是实现的V->P按照1:1映射的。

  • 然后对从execute entry start address到execute entry end address为止的执行空间,分别得到对应的在tmpl1_mem变量中的entry范围(分别存在于r1和r2中),将r5初始化为0,然后以r5依次设置这个执行空间对应的Translation Table中的所有entry,每个entry设置后r5和r4都分别增加1MB,从而映射了这个执行空间。这样完成的是将执行空间V映射到加载地址空间P,并不是1:1映射。例如,执行空间CPU_TEXT_START = 0xFF000000,但是加载空间可以是内存的任意1MB对齐的地址,例如0x108000000。

  • 也就是说,要使用加载地址访问,那么可以使用load start address开始的1MB,超过就不行;但是如果映像很大,超过1MB大小,那么要访问这些代码空间的代码和数据,则必须使用执行空间的地址。

接着,执行下面的代码来将Translation Table的基地址设置到TTBR0中。

256&nbsp;&nbsp;&nbsp;&nbsp;/*


257&nbsp;&nbsp;&nbsp;&nbsp; * Secondary CPU startup code


258&nbsp;&nbsp;&nbsp;&nbsp; *


259&nbsp;&nbsp;&nbsp;&nbsp; * Note: From this point primary CPU startup is same as secondary CPU


260&nbsp;&nbsp;&nbsp;&nbsp; */

261_start_secondary:


262&nbsp;&nbsp;&nbsp;&nbsp;/* Setup Translation Table Base Register 0 */

263&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r0, __tmpl1_mem


264&nbsp;&nbsp;&nbsp;&nbsp;mcr&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c2, c0, 0

再接着,更新Domain Access Control Register,将TTBL_L1TBL_TTE_DOM_RESERVED这个0号domain的访问权限设置为Client模式,即要求按照Translation Table的entry指定的属性字段来判断是否允许访问。

265


266&nbsp;&nbsp;&nbsp;&nbsp;/* Setup Domain Control Register */

267&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r1, __dacr_mmu_val


268&nbsp;&nbsp;&nbsp;&nbsp;mcr&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r1, c3, c0, 0


302__dacr_mmu_val:


303&nbsp;&nbsp;&nbsp;&nbsp;.word (TTBL_DOM_CLIENT << (2 * TTBL_L1TBL_TTE_DOM_RESERVED))

在【/XVisor/arch/arm/cpu/arm32/include/cpu_defines.h】定义了TTBL_L1TBL_TTE_DOM_RESERVED:

282#define
                    TTBL_L1TBL_TTE_DOM_RESERVED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x0

283#define
                    TTBL_L1TBL_TTE_DOM_VCPU_SUPER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x1

284#define
                    TTBL_L1TBL_TTE_DOM_VCPU_SUPER_RW_USER_R&nbsp;&nbsp;&nbsp;&nbsp;0x2

285#define
                    TTBL_L1TBL_TTE_DOM_VCPU_USER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x3

在初始化页表并将其基地址写入TTBR0之后,需要进一步初始化系统控制寄存器从而使能MMU。

270&nbsp;&nbsp;&nbsp;&nbsp;/* Setup System Control Register */

271&nbsp;&nbsp;&nbsp;&nbsp;bl&nbsp;&nbsp;&nbsp;&nbsp;proc_setup


272&nbsp;&nbsp;&nbsp;&nbsp;mcr&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c1, c0, 0

这里调用了一个函数proc_setup,该函数实现在【/XVisor/arch/arm/cpu/arm32/cpu_proc_v7.S】中。

95/*


96 *&nbsp;&nbsp;&nbsp;&nbsp;Boot-time processor setup


97 *


98 *&nbsp;&nbsp;&nbsp;&nbsp;Initialise TLB, Caches, and MMU state ready to switch the MMU


99 *&nbsp;&nbsp;&nbsp;&nbsp;on.  Return in r0 the new CP15 C1 control register setting.


100 *


101 *&nbsp;&nbsp;&nbsp;&nbsp;This should be able to cover all ARMv7 cores.


102 *


103 *&nbsp;&nbsp;&nbsp;&nbsp;It is assumed that:


104 *&nbsp;&nbsp;&nbsp;&nbsp;- cache type register is implemented


105 *


106 *&nbsp;&nbsp;&nbsp;&nbsp;Note: We blindly use all registers because this will be


107 *&nbsp;&nbsp;&nbsp;&nbsp;called at boot-time when there is not stack


108 */

109&nbsp;&nbsp;&nbsp;&nbsp;.globl proc_setup


110proc_setup:

从函数的注释看,这个函数式要做一些跟MMU和Cache相关的系统级初始化,并且返回一个值用来在调用函数proc_setup后初始化System Control Register。下面我们一步步来看都做了些啥?

111#ifdef CONFIG_SMP


112#if
                    defined(CONFIG_CPU_CORTEX_A9)


113&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r10, #(1 << 0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ TLB ops broadcasting


114#elif defined(CONFIG_CPU_CORTEX_A15)


115&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r10, #0


116#endif


117&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c1, c0, 1

118#if 0


119&nbsp;&nbsp;&nbsp;&nbsp;ALT_UP(mov&nbsp;&nbsp;&nbsp;&nbsp;r0, #(1 << 6))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ fake it for UP


120#endif


121&nbsp;&nbsp;&nbsp;&nbsp;tst&nbsp;&nbsp;&nbsp;&nbsp;r0, #(1 << 6)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ SMP/nAMP mode enabled?


122&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, #(1 << 6)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ Enable SMP/nAMP mode


123&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ Enable CPU-specific SMP bits


124&nbsp;&nbsp;&nbsp;&nbsp;mcreq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c1, c0, 1

125#endif

这里首先是读取一个实现特定的Auxiliary Control Register使能SMP模式,并且使能了Cache and TLB maintenance broadcast这种保持一致性的广播操作。ARMv7的TRM文档中对这个寄存器描述如下:

那么,对于我们的i.MX6,属于Cortex A9,因此应该看Cortex A9的TRM文档里是怎么描述的:

接下来是读取芯片的版本信息,并更具版本信息来应用一些特定于某些版本的Errata修复。

126&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c0, c0, 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read main ID register


127&nbsp;&nbsp;&nbsp;&nbsp;and&nbsp;&nbsp;&nbsp;&nbsp;r10, r0, #0xff000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ ARM?


128&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r10, #0x41000000

129&nbsp;&nbsp;&nbsp;&nbsp;bne&nbsp;&nbsp;&nbsp;&nbsp;3f

130&nbsp;&nbsp;&nbsp;&nbsp;and&nbsp;&nbsp;&nbsp;&nbsp;r5, r0, #0x00f00000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ variant


131&nbsp;&nbsp;&nbsp;&nbsp;and&nbsp;&nbsp;&nbsp;&nbsp;r6, r0, #0x0000000f&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ revision


132&nbsp;&nbsp;&nbsp;&nbsp;orr&nbsp;&nbsp;&nbsp;&nbsp;r6, r6, r5, lsr #20-4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ combine variant and revision


133&nbsp;&nbsp;&nbsp;&nbsp;ubfx&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, #4, #12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ primary part number


134


135&nbsp;&nbsp;&nbsp;&nbsp;/* Cortex-A8 Errata */

136&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r10, =0x00000c08&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ Cortex-A8 primary part number


137&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r0, r10


138&nbsp;&nbsp;&nbsp;&nbsp;bne&nbsp;&nbsp;&nbsp;&nbsp;2f

139#ifdef CONFIG_ARM_ERRATA_430973


140&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r5, #0x00100000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ only present in r1p*


141&nbsp;&nbsp;&nbsp;&nbsp;mrceq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c1, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read aux control register


142&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #(1 << 6)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set IBE to 1

143&nbsp;&nbsp;&nbsp;&nbsp;mcreq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c1, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write aux control register


144#endif


145#ifdef CONFIG_ARM_ERRATA_458693


146&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ only present in r2p0


147&nbsp;&nbsp;&nbsp;&nbsp;mrceq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c1, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read aux control register


148&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #(1 << 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set L1NEON to 1

149&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #(1 << 9)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set PLDNOP to 1

150&nbsp;&nbsp;&nbsp;&nbsp;mcreq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c1, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write aux control register


151#endif


152#ifdef CONFIG_ARM_ERRATA_460075


153&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ only present in r2p0


154&nbsp;&nbsp;&nbsp;&nbsp;mrceq&nbsp;&nbsp;&nbsp;&nbsp;p15, 1, r10, c9, c0, 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read L2 cache aux ctrl register


155&nbsp;&nbsp;&nbsp;&nbsp;tsteq&nbsp;&nbsp;&nbsp;&nbsp;r10, #1 << 22

156&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #(1 << 22)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set the Write Allocate disable bit


157&nbsp;&nbsp;&nbsp;&nbsp;mcreq&nbsp;&nbsp;&nbsp;&nbsp;p15, 1, r10, c9, c0, 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write the L2 cache aux ctrl register


158#endif


159&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;3f

160


161&nbsp;&nbsp;&nbsp;&nbsp;/* Cortex-A9 Errata */

1622:&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;r10, =0x00000c09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ Cortex-A9 primary part number


163&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r0, r10


164&nbsp;&nbsp;&nbsp;&nbsp;bne&nbsp;&nbsp;&nbsp;&nbsp;3f

165#ifdef CONFIG_ARM_ERRATA_742230


166&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ only present up to r2p2


167&nbsp;&nbsp;&nbsp;&nbsp;mrcle&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read diagnostic register


168&nbsp;&nbsp;&nbsp;&nbsp;orrle&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #1 << 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set bit #4

169&nbsp;&nbsp;&nbsp;&nbsp;mcrle&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write diagnostic register


170#endif


171#ifdef CONFIG_ARM_ERRATA_742231


172&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ present in r2p0


173&nbsp;&nbsp;&nbsp;&nbsp;teqne&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x21&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ present in r2p1


174&nbsp;&nbsp;&nbsp;&nbsp;teqne&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ present in r2p2


175&nbsp;&nbsp;&nbsp;&nbsp;mrceq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read diagnostic register


176&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #1 << 12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set bit #12

177&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #1 << 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set bit #22

178&nbsp;&nbsp;&nbsp;&nbsp;mcreq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write diagnostic register


179#endif


180#ifdef CONFIG_ARM_ERRATA_743622


181&nbsp;&nbsp;&nbsp;&nbsp;teq&nbsp;&nbsp;&nbsp;&nbsp;r5, #0x00200000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ only present in r2p*


182&nbsp;&nbsp;&nbsp;&nbsp;mrceq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read diagnostic register


183&nbsp;&nbsp;&nbsp;&nbsp;orreq&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #1 << 6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set bit #6

184&nbsp;&nbsp;&nbsp;&nbsp;mcreq&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write diagnostic register


185#endif


186#if
                    defined(CONFIG_ARM_ERRATA_751472) && defined(CONFIG_SMP)


187&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;&nbsp;&nbsp;r6, #0x30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ present prior to r3p0


188&nbsp;&nbsp;&nbsp;&nbsp;mrclt&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read diagnostic register


189&nbsp;&nbsp;&nbsp;&nbsp;orrlt&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #1 << 11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set bit #11

190&nbsp;&nbsp;&nbsp;&nbsp;mcrlt&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c15, c0, 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ write diagnostic register


1911:


192#endif

我们假设i.MX6不存在这几个Errata,因此就会执行到下面的代码,执行一个Instruction cache invalidate all操作:

1943:&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r10, #0


195&nbsp;&nbsp;&nbsp;&nbsp;mcr&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c7, c5, 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ I+BTB cache invalidate


196&nbsp;&nbsp;&nbsp;&nbsp;dsb

在Invalidate所有的指令Cache之后,执行下面的代码:

198&nbsp;&nbsp;&nbsp;&nbsp;adr&nbsp;&nbsp;&nbsp;&nbsp;r5, v7_crval


199&nbsp;&nbsp;&nbsp;&nbsp;ldmia&nbsp;&nbsp;&nbsp;&nbsp;r5, {r5, r6}


200#ifdef CONFIG_SWP_EMULATE


201&nbsp;&nbsp;&nbsp;&nbsp;orr     r5, r5, #(1 << 10)              @ set SW bit in "clear"

202&nbsp;&nbsp;&nbsp;&nbsp;bic     r6, r6, #(1 << 10)              @ clear it in "mmuset"

203#endif


204   &nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c1, c0, 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ read control register


205&nbsp;&nbsp;&nbsp;&nbsp;bic&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ clear bits them


206&nbsp;&nbsp;&nbsp;&nbsp;orr&nbsp;&nbsp;&nbsp;&nbsp;r0, r0, r6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ set them


207&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;pc, lr


208


209&nbsp;&nbsp;&nbsp;&nbsp;/*   AT


210&nbsp;&nbsp;&nbsp;&nbsp; *  TFR   EV X F   I D LR    S


211&nbsp;&nbsp;&nbsp;&nbsp; * .EEE ..EE PUI. .T.T 4RVI ZWRS BLDP WCAM


212&nbsp;&nbsp;&nbsp;&nbsp; * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced


213&nbsp;&nbsp;&nbsp;&nbsp; *    0    0 110       0001 1100 .111 1101 < we want


214&nbsp;&nbsp;&nbsp;&nbsp; */

215&nbsp;&nbsp;&nbsp;&nbsp;.align&nbsp;&nbsp;&nbsp;&nbsp;2

216&nbsp;&nbsp;&nbsp;&nbsp;.type&nbsp;&nbsp;&nbsp;&nbsp;v7_crval, #object


217v7_crval:


218&nbsp;&nbsp;&nbsp;&nbsp;.word&nbsp;&nbsp;&nbsp;&nbsp;0x0120c302
                            /* clear */

219&nbsp;&nbsp;&nbsp;&nbsp;.word&nbsp;&nbsp;&nbsp;&nbsp;0x00c01c7d
                            /* mmuset */

这里是要将System Control Register寄存器读取到r0中,并且使用r5里的值作为掩码清除掉一些字段,然后用r6中的值设置上一些字段。我们看了这个系统控制寄存器的格式之后就知道具体清除或者设置了哪些字段。

因此,使用0x0120c302这个值清掉的字段包括:

  • VE,Interrupt Vectors Enable,从而Use the FIQ and IRQ vectors from the vector table, see the V bit entry。
  • FI,Fast interrupts configuration enable,从而All performance features enabled。
  • RR,Round Robin select,从而Cache的replacement strategy就是Normal replacement strategy, for example, random replacement。
  • Alignment check enable,从而就是Alignment fault checking disabled。

类似,使用0x00c01c7d这个值设置的指端包括:

  • U, In ARMv7 this bit is RAO/SBOP,就是说这个bit是Read-As-One, Should-Be-One-or-Preserved on writes的,所以设置为1是必然的。
  • I, Instruction cache enable,这样就使能了指令Cache。
  • Z,Branch prediction enable,这样就使能了分支预取功能。
  • CP15BEN,CP15 barrier enable,这样就使能了CP15 DMB, DSB, and ISB barrier操作。
  • M, MMU enable,这样就使能了MMU,即PL1&0 stage 1 MMU enabled。
  • SW,SWP and SWPB enable,从而SWP and SWPB指令是使能的。

注意上面的操作中没有碰那个TRE字段,应该是采用了从bootloader加载过来的值。根据前面分析的Section页表属性:

280#if (__ARM_ARCH_VERSION__ < 6)


281#define
                    TTBL_L1TBL_TTE_ARCH_ATTR&nbsp;&nbsp;&nbsp;&nbsp;TTBL_L1TBL_TTE_REQ_MASK


282#else


283#define
                    TTBL_L1TBL_TTE_ARCH_ATTR&nbsp;&nbsp;&nbsp;&nbsp;(TTBL_L1TBL_TTE_C_MASK | \


284&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TTBL_L1TBL_TTE_B_MASK)


285#endif


287#define
                    SECTION_ATTR &nbsp;&nbsp;&nbsp;&nbsp;((TTBL_AP_SRW_U << TTBL_L1TBL_TTE_AP_SHIFT) | \


288&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(TTBL_L1TBL_TTE_DOM_RESERVED << TTBL_L1TBL_TTE_DOM_SHIFT) | \


289&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TTBL_L1TBL_TTE_ARCH_ATTR | \


290&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TTBL_L1TBL_TTE_TYPE_SECTION)

我们注意到这里的Section属性设置了下面的字段:
  • NS, Non-secure bit,设置为0。

  • AP, Access Permissions bits,即设置为TTBL_AP_SRW_U 0x1,即在PL1/PL2下可读可写。

  • Domain,设置为默认的0号domain。

  • C,设置为1,参照下表(同时TEX[2:0]=000)。

  • B,设置为1,参照下表(同时TEX[2:0]=000)。

    • TEX[2:0]=000,C=1,B=1,也就是说,将内存设置为Outer and Inner Write-Back, no Write-Allocate,并且是Normal内存,其Page Shareable属性由S位控制,但是这里S位被设置为0,也就是说不可共享。

当代码返回proc_setp的调用者后,r0寄存器中存放着用来设置系统控制寄存器的值,因此直接写入该寄存器,就实现了MMU的使能。

270&nbsp;&nbsp;&nbsp;&nbsp;/* Setup System Control Register */

271&nbsp;&nbsp;&nbsp;&nbsp;bl&nbsp;&nbsp;&nbsp;&nbsp;proc_setup


272&nbsp;&nbsp;&nbsp;&nbsp;mcr&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r0, c1, c0, 0

到此为止,MMU已经在发挥作用了。我们可以看出,对于MMU的配置,目前主要有两个内存区间:

  • 加载地址处的1:1映射区间,这样可以使得按照加载地址读写还可以继续可用。

  • 执行地址处的非1:1映射区间,这样就可用通过一个对PC的直接加载而跳转到"执行地址处"。

    274    /* Jump to reset code */

    275    ldr    pc, __reset

    445__reset:

    446    .word _reset

    479    /*

    480     * Reset exception handler.

    481     * Reset hardware state before starting Xvisor.

    482     */

    483    .globl _reset

    484_reset:

    485    /* Clear a register for temporary usage */

    486    mov    r8, #0

    487    /* Disable IRQ & FIQ */

    488    mrs    r8, cpsr_all

    489    orr    r8, r8, #(CPSR_IRQ_DISABLED | CPSR_FIQ_DISABLED)

    490    msr    cpsr_cxsf, r8

这样,代码转而进入_reset,接着执行,但是现在已经在"执行内存空间"中了。进来的第一步就是禁止中断(IRQ和FIQ),这样以后的代码就可以一线执行下去,直到后面使能中断。

491&nbsp;&nbsp;&nbsp;&nbsp;/* Set Supervisor Mode Stack */

492&nbsp;&nbsp;&nbsp;&nbsp;CMODE&nbsp;&nbsp;&nbsp;&nbsp;r8, CPSR_MODE_SUPERVISOR


493&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;sp, __svc_stack_end


494#ifdef CONFIG_SMP


495&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c0, c0, 5

496&nbsp;&nbsp;&nbsp;&nbsp;ands&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #0xFF

497&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r9, #CONFIG_IRQ_STACK_SIZE


498&nbsp;&nbsp;&nbsp;&nbsp;mul&nbsp;&nbsp;&nbsp;&nbsp;r9, r9, r10


499&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;sp, sp, r9


500#endif


501&nbsp;&nbsp;&nbsp;&nbsp;/* Set Undefined Mode Stack */

502&nbsp;&nbsp;&nbsp;&nbsp;CMODE&nbsp;&nbsp;&nbsp;&nbsp;r8, CPSR_MODE_UNDEFINED


503&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;sp, __und_stack_end


504#ifdef CONFIG_SMP


505&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c0, c0, 5

506&nbsp;&nbsp;&nbsp;&nbsp;ands&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #0xFF

507&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r9, #0x100

508&nbsp;&nbsp;&nbsp;&nbsp;mul&nbsp;&nbsp;&nbsp;&nbsp;r9, r9, r10


509&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;sp, sp, r9


510#endif


511&nbsp;&nbsp;&nbsp;&nbsp;/* Set Abort Mode Stack */

512&nbsp;&nbsp;&nbsp;&nbsp;CMODE&nbsp;&nbsp;&nbsp;&nbsp;r8, CPSR_MODE_ABORT


513&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;sp, __abt_stack_end


514#ifdef CONFIG_SMP


515&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c0, c0, 5

516&nbsp;&nbsp;&nbsp;&nbsp;ands&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #0xFF

517&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r9, #0x100

518&nbsp;&nbsp;&nbsp;&nbsp;mul&nbsp;&nbsp;&nbsp;&nbsp;r9, r9, r10


519&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;sp, sp, r9


520#endif


521&nbsp;&nbsp;&nbsp;&nbsp;/* Set IRQ Mode Stack */

522&nbsp;&nbsp;&nbsp;&nbsp;CMODE&nbsp;&nbsp;&nbsp;&nbsp;r8, CPSR_MODE_IRQ


523&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;sp, __irq_stack_end


524#ifdef CONFIG_SMP


525&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c0, c0, 5

526&nbsp;&nbsp;&nbsp;&nbsp;ands&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #0xFF

527&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r9, #0x100

528&nbsp;&nbsp;&nbsp;&nbsp;mul&nbsp;&nbsp;&nbsp;&nbsp;r9, r9, r10


529&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;sp, sp, r9


530#endif


531&nbsp;&nbsp;&nbsp;&nbsp;/* Set FIQ Mode Stack */

532&nbsp;&nbsp;&nbsp;&nbsp;CMODE&nbsp;&nbsp;&nbsp;&nbsp;r8, CPSR_MODE_FIQ


533&nbsp;&nbsp;&nbsp;&nbsp;ldr&nbsp;&nbsp;&nbsp;&nbsp;sp, __fiq_stack_end


534#ifdef CONFIG_SMP


535&nbsp;&nbsp;&nbsp;&nbsp;mrc&nbsp;&nbsp;&nbsp;&nbsp;p15, 0, r10, c0, c0, 5

536&nbsp;&nbsp;&nbsp;&nbsp;ands&nbsp;&nbsp;&nbsp;&nbsp;r10, r10, #0xFF

537&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;r9, #0x100

538&nbsp;&nbsp;&nbsp;&nbsp;mul&nbsp;&nbsp;&nbsp;&nbsp;r9, r9, r10


539&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;sp, sp, r9


540#endif

这一段代码其实很简单,就是切换到不同的处理器模式来设置在该模式下的堆栈指针,因此无需多说。要注意的是,对于SMP的情形,这段代码是BSP和AP都会走过的,因此对于AP而言,需要相应地调整堆栈指针。接下来,代码切换回到Supervisor Mode,并且跳转到C代码cpu_init处执行。cpu_init()会进而调用vmm_init(),进而在初始化其他子系统,必然中断和调度等,因此不会再返回。

541&nbsp;&nbsp;&nbsp;&nbsp;/* Set to Supervisor Mode */

542&nbsp;&nbsp;&nbsp;&nbsp;CMODE&nbsp;&nbsp;&nbsp;&nbsp;r8, CPSR_MODE_SUPERVISOR


543&nbsp;&nbsp;&nbsp;&nbsp;/* Call CPU init function */

544&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;cpu_init


545&nbsp;&nbsp;&nbsp;&nbsp;/* We should never reach here */

546&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;&nbsp;&nbsp;&nbsp;.

对于初始化的基本流程就分析到这里。下面是这个周末移植到i.MX6Q上的结果(基本的串口,时钟,中断,调度已经可用,但是还没有调试SMP从核的启动,也还没有调试Guest的初始化部分,因此输出中还有一些错误消息可以看到,不过这个是下一步的事情了!)。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章