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

ARM Linux启动代码分析

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

别看这个定义这么长,其实需要关注的代码并不多。

220行,pgtbl是一个宏,定义如下:

47     .macro    pgtbl, rd48     ldr    \rd, =(KERNEL_RAM_PADDR - 0x4)49     .endm

就是将KERNEL_RAM_PADDR - 0x4的值赋给r4,现在关键是KERNEL_RAM_PADDR的定义:

#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)

其中PHYS_OFFSET就是SDRAM的起始地址,对于s3c6410,它的值为0x50,TEXT_OFFSET在arch/arm/Makefile中定义:

00 TEXT_OFFSET := $(textofs-y)00240 export    TEXT_OFFSET GZFLAGS MMUEXT

而textofs-y的定义为:

00118 textofs-y    := 0x08

因此KERNEL_RAM_PADDR的值就为0x58,而r4的值就为0x54。

225行,r0 = r4。

226行,r3 = 0。

227行,r6 = r0 + 0x4,即0x58。

228到233行,将0x54开始到0x58这段内存清零。

235行,别忘了r10存的是struct proc_info_list变量的起始地址。这里将其__cpu_mm_mmu_flags成员的值赋给r7。

在分析下面的代码之前,先了解点预备知识。我们知道MMU的主要作用是将虚拟地址转换为物理地址,但是虚拟地址与物理地址的转换关系需要我们预先设置好(就是设置页表项),而转换的过程需要通过页表来完成。对于ARM来说,映射大体分为段映射和二级映射,段映射只需要一级页表,段映射的大小为1MB,二级映射需要两级页表。下面分析的代码都只用到段映射,因此只介绍段映射。

如图1所示(以ARM9为例),根据上面的分析可知,寄存器r4里存放的是一级页表的基地址,当启动MMU后,CPU发出的是虚拟地址(正确来说是修正后的虚拟地址,即MVA),然后MMU利用该地址的最高12位(MVA[31:20])做为索引值,以一级页表基地址作为起始地址索引对应的页表项,当索引到相应的页表项后,根据页表项的内容找到对应的大小为1MB的起始物理地址,然后利用MVA的低20位(MVA[19:0])索引确切的物理地址(精确到1个字节)。

图1 段映射

具体过程如图2所示,关键看图中的虚线部分,由于页表项的大小为4字节,因此最低两位为0,也即4字节对齐,根据虚线里的值就可以找到相应页表项的起始地址。从图中也可以知道页表基地址是16KB对齐的(最低14位为0)。

图2 获取一级描述符

有了上面的基础知识后就可以继续分析代码了。

243行,r6 = pc,保存当前PC的值。

244行,r6 = r6 >> 20。

245行,r3 = r7 (r6 < 20)。此时,r3的值就是一个页表项的内容,也即段描述符。从这就可以知道244行的作用是清零r6的低20位。

246行,mem[r4 + r6 < 2] = r3,刚好与图2中的虚线部分对应。将r3的值存到页表相应的位置里,这样就完成了一个页表项的构建,也即完成了内核前1MB的映射。因为这里直接使用物理地址作为索引,所以虚拟地址与物理地址是直接映射关系,比如说虚拟地址0x58对应的物理地址也是0x58。后面会看到,这样做是为了开启MMU之后不用考虑太多的事情。

252行,r0 = r4 + (KERNEL_START & 0xff) >> 18,KERNEL_START的定义如下:

55 #define KERNEL_START   KERNEL_RAM_VADDR

而KERNEL_RAM_VADDR的定义为:

29 #define KERNEL_RAM_VADDR  (PAGE_OFFSET + TEXT_OFFSET)

PAGE_OFFSET的值板子对应的config文件里定义,这里为0xC0,因此KERNEL_START = 0xC0 + 0x08。

253行,mem[r0 + (KERNEL_START & 0x00f00) >> 18] = r3和r0 = r0 + (KERNEL_START & 0x00f00) >> 18。其实252行253行的意思就是mem[r4 + (0xC8 & 0xfff00) >> 18] = r3,即将内核的前1MB映射到以0xC8为起始的虚拟内存处。

254行,r6 = KERNEL_END – 1,KERNEL_END的定义为:

56 #define KERNEL_END _end

而_end在arch/arm/kernel/vmlinux.lds.S中定义,表示的是内核Image的结束链接地址。

255行,r0 = r0 + 4,即下一个页表项的起始地址。

256行,r6 = r4 + r6 >> 18。

257行,比较r0,r6的值,并根据结果影响标志位。

258行,r3 = r3 + 1 < 20,即将r3的值加1MB。

259行,如果257行r0 <= r6的值就执行次句,mem[r0] = r3,r0 = r0 + 4。

260行,如果257行r0 <= r6的值就执行此句,跳转到257行。

257到260行的作用就是将整个内核Image映射到以0xC8为起始地址的虚拟地址处,如图3所示。

图3 内核Image映射到虚拟地址

162行,XIP大概就是说在Flash里执行内核,而不必把内核拷贝到内存里再执行,具体没了解过,在此略过,直接到284行。

284行,r0 = r4 + PAGE_OFFSET >> 18。

285行,r6 = r7 ( PHYS_OFFSET & 0xff)。

289行,mem[r0] = r6,即将物理内存的前1MB映射到0xC0,因为这1MB里存放有bootloader传过来的启动参数,从

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

网站地图

Top