微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM linux的启动部分源代码简略分析

ARM linux的启动部分源代码简略分析

时间:11-09 来源:互联网 点击:

,开始设置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/

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top