ARM linux的启动部分源代码简略分析
,开始设置mmu,但首先要填充一个临时的内核页表,映射4m的内存,这在初始化过程中是足够了:
bl __create_page_tables
这个例程设置初始页表,这里只设置最起码的数量,只要能使内核运行即可,r8 = machinfo,r9 = cpuid,r10 = procinfo,在r4寄存器中返回物理页表地址。
__create_page_tables例程在文件arch/arm/kernel/head.S中定义:
__create_page_tables:
pgtbl r4 @ page table address
// pgtbl是一个宏,本文件的前面部分有定义:
//.macro pgtbl, rd
//ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
//.endm
// KERNEL_RAM_PADDR在本文件的前面有定义,为(PHYS_OFFSET + TEXT_OFFSET)
// PHYS_OFFSET在arch/arm/mach-s3c2410/include/mach/memory.h定义,
// 为UL(0x30000000)
// 而TEXT_OFFSET在arch/arm/Makefile中定义,为内核镜像在内存中到内存
// 开始位置的偏移(字节),为$(textofs-y)
// textofs-y也在文件arch/arm/Makefile中定义,
// 为textofs-y := 0x00008000
// r4 = 30004000为临时页表的起始地址
// 首先即是初始化16K的页表,高12位虚拟地址为页表索引,所以为
// 4K*4 = 16K,大页表,每一个页表项,映射1MB虚拟地址。
// 这个地方还来了个循环展开,以优化性能。
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
// PROCINFO_MM_MMUFLAGS在arch/arm/kernel/asm-offsets.c文件中定义,
// 为DEFINE(PROCINFO_MM_MMUFLAGS,
// offsetof(struct proc_info_list, __cpu_mm_mmu_flags));
// R10寄存器保存的指针指向是我们前面找到的proc_info_list结构嘛。
// 为内核的第一个MB创建一致的映射,以为打开MMU做准备,这个映射将会被
// paging_init()移除,这里使用程序计数器来获得相应的段的基地址。
// 这个地方是直接映射。
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
// 接下来为内核的直接映射区设置页表。KERNEL_START在文件的前面定义,
// 为KERNEL_RAM_VADDR,即内核的虚拟地址。
// 而KERNEL_RAM_VADDR在文件的前面定义,则为(PAGE_OFFSET + TEXT_OFFSET)
// 映射完整的内核代码段,初始化数据段。
// PAGE_OFFSET为内核镜像开始的虚拟地址,在
// arch/arm/include/asm/memory.h中定义。在配置内核时选定具体值,默认
// 为0xC0000000。
// 因为最高12位的值是页表中的偏移地址,而第三高的四位必然为0,
// 每个页表项为4字节,右移20位之后,还得再左移两位回来,所以,这里只// 是左移18位。
// R3寄存器在经过了上面的操作之后,实际上是变成了指向内核镜像代码段
// 的指针(物理地址),在这个地方,再一次为内核镜像的第一个MB做了映射。
// R6随后指向了内核镜像的尾部。R0为页表项指针。
// 这里以1MB为单位来映射内核镜像。
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
// 为了使用启动参数,将物理内存的第一MB映射到内核虚拟地址空间的
// 第一个MB,r4存放的是页表的地址。这里的PAGE_OFFSET的虚拟地址
// 比上面的KERNEL_START要小0x8000
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]
// 上面的这个步骤显得似乎有些多余。
// 总结一下,这个建立临时页表的过程:
// 1、为内核镜像的第一个MB建立直接映射
// 2、为内核镜像完整的建立从虚拟地址到物理地址的映射
// 3、为物理内存的第一个MB建立到内核的虚拟地址空间的第一个MB的映射。
// OK,内核的临时页表建立完毕。整个初始化临时页表的过程都没有修改R8,
// R9和R10。
mov pc, lr
ENDPROC(__create_page_tables)
回到stext:
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
这个地方实际上是在r13中保存了另一个例程的地址。后面的分析中,遇到执行到这个例程的情况时会有详细说明。
接着看stext:
adr lr, BSYM(__enable_mmu) @ return (PIC) address
BSYM()是一个宏,在文件arch/
ARMlinux启 相关文章:
- ARM Linux 更新启动画面(11-21)
- ARM Linux启动分析headarmv.S内幕(11-09)
- arm linux 启动流程(11-09)
- ARM Linux 的启动过程(11-09)
- arm linux 启动流程之 进入内核(11-09)
- ARM Linux启动代码分析(11-09)