前面大致浏览了一下,uboot的流程。从感性上面理解了uboot的启动加载过程。接下来就是解惑内核的启动流程了。
同样的手上有mini2440开发板,因此,就对linux-2.6.32.2进行解析。
因为uboot使用的是uImage,所以,直接使用如下的命令,查看整个编译流程.
make uImage -n
得到部分输出如下:
...<省略部分>
echo ' LD vmlinux'; arm-linux-ld -EL -p --no-undefined -X --build-id -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/mach-s3c2410/built-in.o arch/arm/mach-s3c2400/built-in.o arch/arm/mach-s3c2412/built-in.o arch/arm/mach-s3c2440/built-in.o arch/arm/mach-s3c2442/built-in.o arch/arm/mach-s3c2443/built-in.o arch/arm/plat-s3c24xx/built-in.o arch/arm/plat-s3c/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o firmware/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o
...<省略部分>
set -e; echo ' LD arch/arm/boot/compressed/vmlinux'; arm-linux-ld -EL --defsym zreladdr=0x30008000 --defsym params_phys=0x30000100 -p --no-undefined -X /home/wanbiao/workspace/bin/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a -T arch/arm/boot/compressed/vmlinux.lds arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o -o arch/arm/boot/compressed/vmlinux ; echo 'cmd_arch/arm/boot/compressed/vmlinux := arm-linux-ld -EL --defsym zreladdr=0x30008000 --defsym params_phys=0x30000100 -p --no-undefined -X /home/wanbiao/workspace/bin/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a -T arch/arm/boot/compressed/vmlinux.lds arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o -o arch/arm/boot/compressed/vmlinux ' > arch/arm/boot/compressed/.vmlinux.cmd
:
echo ' Kernel:wanbiao arch/arm/boot/zImage is ready'
set -e; echo ' UIMAGE arch/arm/boot/uImage'; /bin/bash /home/wanbiao/workspace/linux-2.6.32.2-mini2440-20110413/linux-2.6.32.2/scripts/mkuboot.sh -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n 'Linux-2.6.32.2' -d arch/arm/boot/zImage arch/arm/boot/uImage; echo 'cmd_arch/arm/boot/uImage := /bin/bash /home/wanbiao/workspace/linux-2.6.32.2-mini2440-20110413/linux-2.6.32.2/scripts/mkuboot.sh -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n '\''Linux-2.6.32.2'\'' -d arch/arm/boot/zImage arch/arm/boot/uImage' > arch/arm/boot/.uImage.cmd
echo ' Image arch/arm/boot/uImage is ready'
从上面的输出可以看到。uImage依赖zImage,他们生成的命令如下:
/bin/bash /home/wanbiao/workspace/linux-2.6.32.2-mini2440-20110413/linux-2.6.32.2/scripts/mkuboot.sh
-A arm
-O linux
-T kernel
-C none
-a 0x30008000
-e 0x30008000
-n 'Linux-2.6.32.2'
-d arch/arm/boot/zImage
arch/arm/boot/uImage
而zImage依赖于compressed/vmlinux。他们的依赖和生成指令在./arch/arm/boot/Makefile中,如下:
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
如果发生改变,则调用objcopy命令,使用依赖,生成目标,该命令使用的选项定义在OBJCOPYFLAGS中,文件位于:./arch/arm/Makefile,值如下:
OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S
接下来就是compressed/vmlinux的生成,同样在./arch/arm/boot/Makefile中定义了compressed/vmlinux生成的依赖和指令,如下:
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
原来,依赖Image,然后直接调用同名目录下的makefile文件,即可生成compressed/vmlinux
看看,compressed/vmlinux的生成命令是什么,可以直接在make uImage -n的输出中,直接找到,如下:
arm-linux-ld -EL
--defsym zreladdr=0x30008000
--defsym params_phys=0x30000100
-p
--no-undefined
-X /home/wanbiao/workspace/bin/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/
bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a
-T arch/arm/boot/compressed/vmlinux.lds
arch/arm/boot/compressed/head.o
arch/arm/boot/compressed/piggy.o
arch/arm/boot/compressed/misc.o
-o arch/arm/boot/compressed/vmlinux
很显然,Image并没有直接作为输入文件,那么Image就有可能是作为间接输入文件了。查看上面的输入文件,发现了piggy.o的生成,如下:
arm-linux-gcc
-Wp,-MD,arch/arm/boot/compressed/.piggy.o.d
-nostdinc
-isystem /home/wanbiao/workspace/bin/arm-linux-gcc-4.3.2/usr/local/arm/
4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/include
-Iinclude
-I/home/wanbiao/workspace/linux-2.6.32.2-mini2440-20110413/linux-2.6.32.
2/arch/arm/include
-include include/linux/autoconf.h
-D__KERNEL__
-mlittle-endian
-Iarch/arm/mach-s3c2410/include
-Iarch/arm/mach-s3c2400/include
-Iarch/arm/mach-s3c2412/include
-Iarch/arm/mach-s3c2440/include
-Iarch/arm/mach-s3c2442/include
-Iarch/arm/mach-s3c2443/include
-Iarch/arm/plat-s3c24xx/include
-Iarch/arm/plat-s3c/include
-D__ASSEMBLY__
-mabi=aapcs-linux
-mno-thumb-interwork
-funwind-tables
-D__LINUX_ARM_ARCH__=4
-march=armv4t
-mtune=arm9tdmi
-include asm/unified.h
-msoft-float
-gdwarf-2
-Wa,-march=all
-c
-o arch/arm/boot/compressed/piggy.o
arch/arm/boot/compressed/piggy.S
由piggy.S直接生成,而该文件,包含了一个二进制文件,如下:
.section .piggydata,#alloc
.globl input_data
input_data:
.incbin "arch/arm/boot/compressed/piggy.gz"
.globl input_data_end
input_data_end:
piggy.gz的生成在./arch/arm/boot/compressed/Makefile中
$(obj)/piggy.gz: $(obj)/../Image FORCE
$(call if_changed,gzip)
哦,现在终于看到了,Image是如何一步步编程compressed/vmlinux了。大致经过如下
Image,先调用gzip生成piggy.gz,该文件又被piggy.S直接包含,最后piggy.S编译生成piggy.o
piggy.o最终编译生成compressed/vmlinux.
接下来,继续看,Image是如何生成的了。它生成的依赖和指令在./arch/arm/boot/Makefile中,如下:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
原来是依赖于vmlinux呀。终于到了我们熟悉的文件了。而vmlinux的生成,直接参考make uImage -n的输出即可得出,如下:
arm-linux-ld
-EL
-p
--no-undefined
-X
--build-id
-o vmlinux
-T arch/arm/kernel/vmlinux.lds
arch/arm/kernel/head.o arch/arm/kernel/init_task.o
init/built-in.o
--start-group
usr/built-in.o
arch/arm/kernel/built-in.o
arch/arm/mm/built-in.o
arch/arm/common/built-in.o
arch/arm/mach-s3c2410/built-in.o
arch/arm/mach-s3c2400/built-in.o
arch/arm/mach-s3c2412/built-in.o
arch/arm/mach-s3c2440/built-in.o
arch/arm/mach-s3c2442/built-in.o
arch/arm/mach-s3c2443/built-in.o
arch/arm/plat-s3c24xx/built-in.o
arch/arm/plat-s3c/built-in.o
kernel/built-in.o
mm/built-in.o
fs/built-in.o
ipc/built-in.o
security/built-in.o
crypto/built-in.o
block/built-in.o
arch/arm/lib/lib.a
lib/lib.a
arch/arm/lib/built-in.o
lib/built-in.o
drivers/built-in.o
sound/built-in.o
firmware/built-in.o
net/built-in.o
--end-group
.tmp_kallsyms2.o
从上面可以看到,vmlinux由各个模块连接而成,到此,基本可以告一段落了,到这里的所有其他依赖都是我们熟悉的编译环节了。
总结一下uImage的生成过程。
1.各个子模块,生成相应的中间文件
2.第一个步骤的中间文件,链接生成vmlinux
3.vmlinux通过objcopy生成Image
4.Image通过gzip生成piggy.gz
5.piggy.gz被包含到piggy.S中,并被编译成piggy.o
6.piggy.o和其他一些必要的文件,一起连接生成compressed/vmlinux
7.compressed/vmlinux通过objcopy生成zImage
8.zImag通过mkuboot.sh生成uImage
上面通过make,大致了解了uImage的生成流程,那么接下来就是定位整个vmlinux的执行起始点了。
查看arch/arm/kernel/vmlinux.lds ,如下:
OUTPUT_ARCH(arm)
ENTRY(stext)
因此,入口点在stext中,查看整个源码程序,发现arch/arm/kernel/head.S文件中,如下:
.section ".text.head", "ax"
ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ 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 __vet_atags
bl __create_page_tables
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(__enable_mmu) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( mov pc, r12 )
ENDPROC(stext)
第一条指令,进入svc模式,并且关闭中断
第二条指令,用于获取处理器ID,详细的介绍可以参考arm的官方文档:https://developer.arm.com/documentation/ddi0344/k/debug/management-registers/processor-id-registers
第三条指令,跳转到__lookup_processor_type.
__lookup_processor_type,根据r9中的处理器id,找到对应的proc_info_list结构体,并将其地址保存在r5寄存中。整个代码如下:
__lookup_processor_type:
adr r3, 3f
ldmia r3, {r5 - r7}
add r3, r3, #8
sub r3, r3, r7 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
1: ldmia r5, {r3, r4} @ value, mask
and r4, r4, r9 @ mask wanted bits
teq r3, r4
beq 2f
add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
cmp r5, r6
blo 1b
mov r5, #0 @ unknown processor
2: mov pc, lr
ENDPROC(__lookup_processor_type)
大致逻辑描述如下:
先列出,即将处理的数据结构如下:
.align 2
3: .long __proc_info_begin
.long __proc_info_end
4: .long .
.long __arch_info_begin
.long __arch_info_end
接下来为了查看__proc_info_begin处的内容是什么,查找arch/arm/kernel/vmlinux.lds得如下内容:
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
即.proc.info.init段的内容。查找整个源码,可得位于./arch/arm/mm/proc-arm920.S如下内容:
.section ".proc.info.init", #alloc, #execinstr
.type __arm920_proc_info,#object
__arm920_proc_info:
.long 0x41009200
.long 0xff00fff0
.long PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
.long PMD_TYPE_SECT | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __arm920_setup
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
.long cpu_arm920_name
.long arm920_processor_functions
.long v4wbi_tlb_fns
.long v4wb_user_fns
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
.long arm920_cache_fns
#else
.long v4wt_cache_fns
#endif
.size __arm920_proc_info, . - __arm920_proc_info
接下来回到arch/arm/kernel/head.S文件中,继续分析,再次粘贴原文如下:
.section ".text.head", "ax"
ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ 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 __vet_atags
bl __create_page_tables
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(__enable_mmu) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( mov pc, r12 )
ENDPROC(stext)
第四条指令,将r5的内容,保存在r10中。
第五条指令,如果r5是0,那么mov指令之后,状态寄存器的Z位会被置位。即该指令判断r5是否为0,如果是,则跳到__error_p
第六条指令,跳转到__lookup_machine_type中,进行machine_desc结构体的查找。查找工作和第三条指令的分析,一模一样,此处不再展开
第七条指令,第八条指令,也跟上面的分析一样,不在展开
第九条指令,跳转到__vet_atags,它的作用,是验证r2中的指针是否有效。该指针指向了tag数组。
验证的简略逻辑如下,先贴上源码:
__vet_atags:
tst r2, #0x3 @ aligned?
bne 1f
ldr r5, [r2, #0] @ is first tag ATAG_CORE?
cmp r5, #ATAG_CORE_SIZE
cmpne r5, #ATAG_CORE_SIZE_EMPTY
bne 1f
ldr r5, [r2, #4]
ldr r6, =ATAG_CORE
cmp r5, r6
bne 1f
mov pc, lr @ atag pointer is ok
1: mov r2, #0
mov pc, lr
ENDPROC(__vet_atags)
第十条指令,跳转到__create_page_tables中,它的功能是为内核设置必要的page table。
依照惯例,先将原码贴上,它位于:/arch/arm/kernel/head.S中
__create_page_tables:
pgtbl r4 @ page table address
/*
* Clear the 16K level 1 swapper page table
*/
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
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
/*
* Create identity mapping for first MB of kernel to
* cater for the MMU enable. This identity mapping
* will be removed by paging_init(). We use our current program
* counter to determine corresponding section base address.
*/
mov r6, pc
mov r6, r6, lsr #20 @ start of kernel section
orr r3, r7, r6, lsl #20 @ flags + kernel base
str r3, [r4, r6, lsl #2] @ identity mapping
/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
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
1: cmp r0, r6
add r3, r3, #1 << 20
strls r3, [r0], #4
bls 1b
#ifdef CONFIG_XIP_KERNEL
/*
* Map some ram to cover our .data and .bss areas.
*/
orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
.if (KERNEL_RAM_PADDR & 0x00f00000)
orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
.endif
add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 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 << 20
strls r3, [r0], #4
bls 1b
#endif
/*
* Then map first 1MB of ram in case it contains our boot params.
*/
add r0, r4, #PAGE_OFFSET >> 18
orr r6, r7, #(PHYS_OFFSET & 0xff000000)
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0]
#ifdef CONFIG_DEBUG_LL
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
/*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
ldr r3, [r8, #MACHINFO_PGOFFIO]
add r0, r4, r3
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
cmp r3, #0x0800 @ limit to 512MB
movhi r3, #0x0800
add r6, r0, r3
ldr r3, [r8, #MACHINFO_PHYSIO]
orr r3, r3, r7
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r6
bne 1b
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
/*
* If we're using the NetWinder or CATS, we also need to map
* in the 16550-type serial port for the debug messages
*/
add r0, r4, #0xff000000 >> 18
orr r3, r7, #0x7c000000
str r3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC
/*
* Map in screen at 0x02000000 & SCREEN2_BASE
* Similar reasons here - for debug. This is
* only for Acorn RiscPC architectures.
*/
add r0, r4, #0x02000000 >> 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)
大致逻辑如下:
PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_BIT4 | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
即,接下来将proc_info_list中的mmu标志,存放在r7中
将当前的pc地址,从高位往低位数12位,作为内核的基址
将内核基址和mmu标志按位与,作为当前pc物理地址和虚拟地址的映射表的值,保存于r3中
将r3中的数据,放入mem[r4+r6*4]中。即将物理地址和虚拟地址一一对应。所以称为一致性映射
接下来就是进行内核的直接映射了。首先计算内核的末尾地址,然后从头到尾进行映射
首先,将r3中的数据,再次放入内核开始对应的页表,即r0所指的位置
然后,保存内核末尾地址在r6中
接着,r0+4,指向下一个页表项,同时将内核末尾表示的段,保存在r6中
比较r0对应的段和r6对应的段是否相同。
如果不同,将r3中的数据,修改一下,即段基址加一,放入r3中
然后继续放入r0对应的地址中。直到r0和r6相等。
接下来就是boot参数的一个映射。该映射实在没有什么可写的
剩下的宏的条件判断,一律跳过,不再展开
使用lr寄存器,回到最开始的地方。
照例,将原文再次粘贴一下:
.section ".text.head", "ax"
ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ 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 __vet_atags
bl __create_page_tables
/*
* The following calls CPU specific code in a position independent
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above. On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(__enable_mmu) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( mov pc, r12 )
ENDPROC(stext)
第十一条指令,加载__switch_data地址在r13中。在后面使能mmu之后,会跳转到该地址中
第十二条指令,将__enable_mmu地址保存在lr中,而不是pc当前地址。
第十三条指令,根据前面的分析,已经知道,该指令,是跳转到proc_info_list的PROCINFO_INITFUNC的偏移处,即,__cpu_flush。查找./arch/arm/mm/proc-arm920.S文件,即跳转到__arm920_setup处
代码如下:
.type __arm920_setup, #function
__arm920_setup:
mov r0, #0
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
#ifdef CONFIG_MMU
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
#endif
adr r5, arm920_crval
ldmia r5, {r5, r6}
mrc p15, 0, r0, c1, c0 @ get control register v4
bic r0, r0, r5
orr r0, r0, r6
mov pc, lr
上面的代码,很简单,使指令和数据缓存无效,使指令和数据TLB无效。以及往协处理器中写入数据。
接下来,跳转到lr中。lr保存的是__enable_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)
在使能MMU之前,最后的设置.他们包括页表指针,和domain访问寄存器。最后跳到__turn_mmu_on
__turn_mmu_on代码如下:
__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, r13
mov pc, r3
ENDPROC(__turn_mmu_on)
__turn_mmu_on的功能。就是真正的使能MMU功能,一旦完成,整个内存空间就发生了改变。
在最后,pc跳转到r13寄存器中的地址。在第十一条指令中,r13保存的是__switch_data的地址
接下来就是进入,__switch_data中,代码位于:./arch/arm/kernel/head-common.S中,如下:
.align 2
.type __switch_data, %object
__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处执行,代码如下:
__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
ARM( ldmia r3, {r4, r5, r6, r7, sp})
THUMB( ldmia r3, {r4, r5, r6, r7} )
THUMB( ldr sp, [r3, #16] )
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
str r2, [r6] @ Save atags pointer
bic r4, r0, #CR_A @ Clear 'A' bit
stmia r7, {r0, r4} @ Save control register values
b start_kernel
ENDPROC(__mmap_switched)
查找整个源码,可见start_kernel在./init/main.c中。
start_kernel即为用c语言写的,内核的入口了。下篇笔记见
总结一下,整个启动过程,虽然已经看完,但是还是有好多处细节,没办法吃透。不过好在不需要在这上面工作,没有吃透,并不影响,对整体流程的把控和理解。
如果后面有时间,再把不清楚的,挨个补上。