微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM linux解析之压缩内核zImage的启动过程

ARM linux解析之压缩内核zImage的启动过程

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

r3)

THUMB(moveqpc, r12) @ call cache function

add r12, r12,#PROC_ENTRY_SIZE

b1b

首先看一下proc_types是什么,定义如下:

proc_types:

......

.word0xf0@ new CPU Id

.word0xf0

W(b)__armv7_mmu_cache_on

W(b)__armv7_mmu_cache_off

W(b)__armv7_mmu_cache_flush

.......

.word0@ unrecognised type

.word0

movpc, lr

THUMB(nop)

movpc, lr

THUMB(nop)

movpc, lr

THUMB(nop)

可以看到这是一个以proc_types为起始地址的表,上面我列出了第一个表项,和最后一个表项,如果查表不成功,则走最后一个表项返回。它实现的功能就是存两个数据,三条跳转指令,我们可以第一条是它的值,第二条是它的mask值,三条跳转分别是:cache_on,cache_off,cache_flush。

我想从ARMv4指令向下都是有CP15协处理器的吧,故:CONFIG_CPU_CP15是定义的,那下面我们来分析指令吧。

mrcp15, 0, r9, c0, c0@ get processor ID

这个意思是取得ARM处理器的ID,这个又要看《ARM Architecture Reference Manual》了,这里我找了arm1176jzfs的架构手册,也是我用的ARM所用的架构。里面的解释如下:

这里我们主要关心Architecture这项,我们的ARM这个值是: 0x410FB767,说明用的是r0p7的release。

好了读取了这个值存入r9寄存器,然后使用算法(real ^ match) & mask,程序中:

( r9 ^r1)&r2,这里r1存是是表中的第一个CPU的ID值,r2是mask值,对于我们的ARM,结果如下:

0x410FB767 ^ 0xf0 = 0x4100B767

0x4100B767 & 0xf0 = 0

故match上了,这个时候就会如下:

ARM(addeqpc, r12,r3) @ call cache function

我们知道r3的值是0x8,那么r12表项的基址加上0x8就正好是表中的第一条跳转指令:

W(b)__armv7_mmu_cache_on

明白了,为何r3要等于0x8了吧,如果要调用cache_off,那么只要把r3设为0xC就可以了。精妙吧。行接着往下看__armv7_mmu_cache_on,如下:

__armv7_mmu_cache_on:

movr12, lr

#ifdef CONFIG_MMU

mrcp15, 0, r11, c0, c1, 4@ read ID_MMFR0

tstr11, #0xf@VMSA见注:

blne__setup_mmu

注:VMSA (Virtual Memory System Architecture),其实就是虚拟内存,通俗地地说就是否支持MMU。

首先是保存lr寄存器到r12中,因为我们马上就要调用__setup_mmu了,最后返回也只要用r12就可以了。然后再查看cp15的c7,c10,4看是否支持VMSA,具体的见注解。我们在这里我们的ARM肯定是支持的,所以就要建立页表,准备打开MMU,从而可以使能cache。

好了下面,就是跳到__setup_mmu进行建产页表的过程,代码如下:

__setup_mmu:sub r3, r4, #16384@ Page directory size

bicr3, r3, #0xff@ Align the pointer

bicr3, r3, #0x3f00

movr0, r3

movr9, r0, lsr #18

movr9, r9, lsl #18@ start of RAM

add r10, r9, #0x10@ a reasonable RAM size

movr1, #0x12

orr r1, r1, #3 < 10

add r2, r3, #16384

1:cmpr1, r9@ if virt > start of RAM

#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH

orrhsr1, r1, #0x08@ set cacheable

#else

orrhsr1, r1, #0x0c@ set cacheable, bufferable

#endif

cmpr1, r10@ if virt > end of RAM

bichsr1, r1, #0x0c@ clear cacheable, bufferable

strr1, [r0], #4@ 1:1 mapping

add r1, r1, #1048576

teq r0, r2

bne 1b

关于MMU的知识又有好多啊,同样可以参看《ARM Architecture Reference Manual》,还可以看《ARM体系架构与编程》关于MMU的部分,我这里只简单介绍一下我们这里用到MMU。这里只使用到了MMU的段映,故我只介绍与此相关的部分。

对于段页的大小ARM中为1M大小,对于32位的ARM,可寻址空间为4G=4096M,故每一个页表项表示1M空间的话,需要4096个页表项,也就是4K大小,而每一个页表项的大小是4字节,这就是说我们进行段映射的话,需要16K的大小存储段页表。

下面来看一下段页表的格式,如下:

图.1段页表项的具体内容

可以知道对于进行mmu段映射这种方式,一共有4K个这样的页表项,点大小16K字节。在这里我们的16k页表放哪呢?看程序第一句:

__setup_mmu:sub r3, r4, #16384@ Page directory size

我们知道r4存内核解压后的基址,那么这句就是把页表放在解压后的内核地址的前面16K空间如下图所示:

图.2 linux内核地址空间

(里面地址是用的是以我用的ARM为例的)

好了,再回到MMU,从MMU_PAGE_BASE (0x24)建立好页表后,ARM的cpu如何知道呢?这个就是要用到CP15的C2寄存器了,页表基址就是存在这里面的,其中[31:14]为内存中页表的基址,[13:0]应为0如下图:

图.3 CP15的C2寄存器中的页表项基址格式

所以我们初始化完段页表后,就要把页表基址MMU_PAGE_BASE

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

网站地图

Top