本文主要是介绍uboot的虚拟地址映射学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
参考资料:
1: 关于ARM地址映射的理解.
2: S5PV210 uboot中的 MMU代码分析.
3: 协处理器CP15介绍—MCR/MRC指令(6).
4.朱老师的课程uboot的2.5.10-2.5.12节
虚拟地址映射
把虚拟地址映射到物理地址。
怎么映射
两者之间的进行转换的话会有一个转换关系,这个转换关系对应的是虚拟地址映射表。
uboot是在进行完BL2的重定位之后,开始虚拟地址映射,然后后面都使用的虚拟地址了。
重定位之后进入after_copy
段
具体思路可以看注释,并且要结合用到的参考资料,知道一个个转换关系到底是怎样结合起来的。
还有,这里只是一级映射,用了段模式,算是最简单的一种映射方法。
after_copy:#if defined(CONFIG_ENABLE_MMU)
enable_mmu:/* enable domain access */ldr r5, =0x0000ffffmcr p15, 0, r5, c3, c0, 0 @load domain access register //mcr 把arm处理器的数据传输到协处理器里面 即把r5的值写到c3里面//c3是协处理器cp15的一个寄存器,控制了ARM处理器16个域的访问权限//ffff即16个1对应16个域的权限。/* Set the TTB register */ ldr r0, _mmu_table_base //获取mmu_table(转换表)的基地址ldr r1, =CFG_PHY_UBOOT_BASE //根据注释算得是0x33e000000ldr r2, =0xfff00000 //取bit[31:20]位,也就是高12位bic r0, r0, r2 //取把这个值的高12位清零orr r1, r0, r1 //把物理地址和这个经过处理的转换表进行位或运算,组合成一个段地址+条目描述符mcr p15, 0, r1, c2, c0, 0 //把这个得到的转换关系写到c2寄存器,也就是TTB 转换表里面/* Enable the MMU */ //设置完之后就启动mmu
mmu_on:mrc p15, 0, r0, c1, c0, 0orr r0, r0, #1mcr p15, 0, r0, c1, c0, 0nopnopnopnop
#endif
下面这段代码是在lowlevel_init里面,作用就是建立转换表
/* form a first-level section entry *///ap是设置权限在 cp15 c2寄存器 bit[10-11]//d是domain域 cp15 c2寄存器 bit[5-8]不管是段模式还是页模式,系统都把4GB空间分为16个域,每个域有相同的权限检查//c bit 3 b bit 2 C/B位是控制位,与本条目(描述符)所在域的Cache和Buffer有关(是否允许本域开启Cache和Buffer)//最后的1<<1设置bit1为1,其实bit[0:1]是设置模式描述符,0b10是段模式的描述符标识.macro FL_SECTION_ENTRY base,ap,d,c,b //.macro 声明一个宏 一个宏名,接受5个参数.word (\base << 20) | (\ap << 10) | \(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
.endm //.end macro 应该是完成的宏定义标志
.section .mmudata, "a" //.section.mmudata 这里是链接脚本的mmudata段,后面的"a"不懂.align 14// the following alignment creates the mmu table at address 0x4000. 这句注释感觉不对,不止是不是没改还是我理解不对.globl mmu_table
mmu_table:.set __base,0 //base是<<20位,20-31是段基址的地方,也就设置段基址位0// Access for iRAM.rept 0x100 //.rept repeat 伪指令 循环0x100次 转成十进制 256次FL_SECTION_ENTRY __base,3,0,0,0 //每一次循环,段基地址都加1 一段1MB 共256MB.set __base,__base+1.endr //.endr end repeat 构成一个for循环// Not Allowed .rept 0x200 - 0x100 //又循环256次 但是每次循环里面都置零.word 0x00000000 .endr.set __base,0x200 //设置段基址位0x200 ap权限还是3 也就是11 cb控制为控制cache和buffer开吧// should be accessed.rept 0x600 - 0x200 //循环1024次FL_SECTION_ENTRY __base,3,0,1,1.set __base,__base+1 //每次段基址+1 一段1M 1024段就1024M 1G.endr.rept 0x800 - 0x600 // 循环512次 但是都是置0.word 0x00000000.endr.set __base,0x800 //设置段基址0x800// should be accessed.rept 0xb00 - 0x800 // 0x300 256*3次 一次段+1 768MBFL_SECTION_ENTRY __base,3,0,0,0.set __base,__base+1.endr/* .rept 0xc00 - 0xb00.word 0x00000000.endr */.set __base,0xB00 //设置段基址 B00.rept 0xc00 - 0xb00 //0x100 256次 256MBFL_SECTION_ENTRY __base,3,0,0,0.set __base,__base+1.endr// 0xC000_0000映射到0x3000_0000 .set __base,0x300 //设置段基址0x300//.set __base,0x200// 256MB for SDRAM with cacheable.rept 0xD00 - 0xC00 //从0xc00开始到0xd00一共256次,一次1MB,共256MBFL_SECTION_ENTRY __base,3,0,1,1.set __base,__base+1.endr// access is not allowed.@.rept 0xD00 - 0xC80 //从c80到d00又不给用@.word 0x00000000@.endr.set __base,0xD00 //d00到 10000 循环0x300次 768次 即768M// 1:1 mapping for debugging with non-cacheable.rept 0x1000 - 0xD00FL_SECTION_ENTRY __base,3,0,0,0.set __base,__base+1.endr
抽取前面两端进行分析讲解,如图所示。
跟着代码一步步分析最终算出了的长度加起来刚刚好是4G的长度,也就是全映射了。
细心点会发现,0xC8000000到0xD0000000也是设置为不可用的。
结果就完成了转换表的建立。
回顾
DRAM有效范围:
DMC0: 0x30000000-0x3FFFFFFF
DMC1: 0x40000000-0x4FFFFFFF
结论:虚拟地址映射只是把虚拟地址的c0000000开头的256MB映射到了DMC0的30000000开头的256MB物理内存上去了。其他的虚拟地址空间根本没动,还是原样映射的。思考:为什么配置时将链接地址设置为c3e00000,因为这个地址将来会被映射到33e00000这个物理地址。
之前在主Makefile里面的TEXT_BASE设置为c3e00000,经过映射后就到了33e00000的位置。
这篇关于uboot的虚拟地址映射学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!