前文说到了uboot的lowlevel_init都干了些什么,也就是经过了这项初期的低级启动,使得我们能在串口监视器上看见机器打印出的第一句话“OK”。当lowlevel_init结束后,uboot去做了另一件事情,那就是栈的再次设置。
第一次栈设置发生在lowlevel_init之前,在SRAM中设置的栈,现在要做的是将栈移动到DDR中。(ARM 是满减栈)
_TEXT_PHY_BASE=0X33E00000
TEXT意为代码段、PHY为物理、BASE为基地址,整句话的意思就是归定代码段的物理内存基地址的起始位置。
因为满减栈的结构,这样就不会让1栈踩到uboot。DDR已经初始化,所以将栈移过去,SRAM的空间有限,不能溢出。
uboot的启动过程其实就是一个往复的过程,但每一次的往复都实现了向上的过程,就如同刚刚说到栈二次初始化,从SRAM搬到了DDR中,这是一个螺旋向上的一个过程。
再次潘多当前地址是否重定位,这次决定是否进行uboot的relocate。冷启动uboot第一部分(8k~16k),后面的大部队还在SD卡的某个扇区中存储着。
结束判断开始进行重定位加载。
重定位:1.确定SD卡通道,值在0xD0037488自生成,若为SD0,则值为0xEB000000,SD2为0xEB200000。
2.早在start.S前部分就确定了MMCSD启动,在重定位开始时跳转到mmcsd_boot中执行重定位。
3.正真的重定位开始于movi_bl2_copy(这是一个c函数)。
copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT, CFG_PHY_UBOOT_BASE, 0)
函数解释:2表示通道2;MOVI_BL2_POS:Uboot的第二部分在SD卡的开始扇区,这个扇区必须烧录位置相同;MOVI_BL2_BLKCNT:Uboot长度占用的扇区数;CFG_PHY_UBOOT_BASE:重定位将Uboot复制到DDR的位置(0x33E00000)。
解释一些经常用到的名词:
什么是虚拟地址映射,将物理地址映射为虚拟地址,通过MMC在软件与硬件之间的一个层次,硬件与软件之间相互不知道对方的真实地址(通过映射可访问,挂载更多的硬件)。
MMC:memory management unit内存管理单元,实现内存的管理(读/写/权限)与地址映射。MMU在CP15协处理器中,映射将通过映射表实现。
地址映射的作用:访问控制。在编程时出现的segmentation fault段错误,实际就是MMC实现的,因为访问了不应该或者不能权限不同的内存地址(MMC可管理内存块的只读/写/可读/写/不可访问权限)。
cache:cache的指令规则也要依靠MMU实现(快速去访问,去读取指令等)。
enable_mmu:操作mrc读mcr写。Cx(0~15)寄存器:cp15的寄存器。
C3:域访问控制器(Domain)有效位为32位,分16给区,2个位可表示4种级别。
TTB(translation table base)转换表基地址,转换表分为两个部分:表索引(虚拟内存)和表项(物理地址)。
通过索引得到真实地址,映射中(内存映射和管理以块为单位、看MMU与设置)。此处理器(S5PV210)支持三种大小:细表1k、4k粗表、1M段、无映射。
真正的转换表由若干的转换表单元构成,每个单元负责1个内存块大小,一共负责0-4G(也就是32位的上限)空间。
转换表放在内存中,放置时要求起始地址在内存中xx位对齐,转换表将TTB放在C2中,MMU工作自会去查表(mmu_table)。
设置完成后enable MMU:c1的bit0设置为1。
转换表可理解为一个数组,每一个元素就是一个表项,下标是索引。ARM段映射最小为4096个单元,最少要4096个数组元素进行填充,填充过程中将多个元素进行批量循环处理,以减少工作量。
.macro FL_SECTION_ENTRY base,ap,dc,cd
.word (\base<<20)|(\ap<<10)\(\d<<5)|(1<<4)|(\c<<3)|(\b<<2)(1<<1)
.endm
\base<<20。其中<<10位为1k;<<20位位1M;意为以1M对齐基地址。
\ap<<10。访问控制位。
FL_SECTION_ENTRY。建立一个表项。
将这个宏循环0x100次可以负责256M空间的单元映射。
0x00000000 base=0
0x00100000 base=1
...... ......
0x10000000 base=100
并按照需求分配空间到0x1000(4G)内存空间完成分配。
结果虚拟地址映射可以等于物理地址,可可以将某一物理地址分配在不相同的虚拟地址。如果uboot只将虚拟地址的0xc0000000后的256M映射到了DMC0的0x30000000的256M。所以uboot链接地址分配到0xc3e00000映射在物理地址的0x33e00000。
再次设置栈:
此次设置栈还在DDR中,但这次设置将会使位置合理(安全,不浪费,紧凑) 。
满减栈:可用栈为2M~200K-0x1000的大小。
uboot第一阶段的最后一步:start_armboot。
ldr pc, _start_arnboot
将第二段uboot要干的事情进行远跳转。远跳转:与加载和运行(当前的)地址无关,只与链接地址有关。所以实现了从SRAM到DDR的跳转,uboot的第二阶段将会在DDR中进行。
就此跳转到DDR中,代表下一步的代码将会在DDR进行处理,也就标志着uboot的第一阶段的完成(BL1),下一章将会开始于start_armboot部分的讲解(BL2)敬请期待。