Uboot27之start.S的MMU操作

#if defined(CONFIG_ENABLE_MMU)

enable_mmu:

    /* enable domain access */

    ldr    r5, =0x0000ffff

    mcr    p15, 0, r5, c3, c0, 0        @load domain access register

使能域访问(cp15的c3寄存器)

1)cp15协处理器内部有c0到c15共16个寄存器,这些寄存器每一个都有自己的作用。我们通过mrc和mcr指令来访问这些寄存器。所谓的操作cp协处理器其实就是操作cp15的这些寄存器。

2)c3寄存器在mmu中的作用是控制域访问。域访问是和MMU的访问控制有关的。

    /* Set the TTB register */

    ldr    r0, _mmu_table_base

#ifdef CONFIG_ENABLE_MMU
_mmu_table_base:
.word mmu_table
#endif

-----------------------lowlever_init.s中的代码讲解-----------------------

#ifdef CONFIG_ENABLE_MMU

我们定义了

    #ifdef CONFIG_MCP_SINGLE
我们也定义了

/*

* MMU Table for SMDKC110

* 0x0000_0000 -- 0xBFFF_FFFF => Not Allowed

* 0xB000_0000 -- 0xB7FF_FFFF => A:0xB000_0000 -- 0xB7FF_FFFF

* 0xC000_0000 -- 0xC7FF_FFFF => A:0x3000_0000 -- 0x37FF_FFFF

* 0xC800_0000 -- 0xDFFF_FFFF => Not Allowed

* 0xE000_0000 -- 0xFFFF_FFFF => A:0xE000_0000 -- 0XFFFF_FFFF

*/

    /* form a first-level section entry */

.macro FL_SECTION_ENTRY base,ap,d,c,b

    .word (\base << 20) | (\ap << 10) | \

     (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)

.endm

/*这里开始设置虚拟映射转换表,这里使用的是段式映射模式,即以段(1MB)为单位进行映射*/

/*因此表中的一个单元只能管1MB,整个4G内存需要创建4096个表单元,所以后面采用了循环的方法来创建*/

/*查表的方法是:例如虚拟地址的高12位是x,则去查第x个表单元,该表单元的高12位就是物理地址的高12位,其实这一部分只要知道一些大概的原理即可,不必深究,等用到了再去看*/

/* form a first-level section entry */

.macro FL_SECTION_ENTRY base,ap,d,c,b /*.macro指令是汇编中宏定义的意思,此带参宏将FL_SECTION_ENTRY base,ap,d,c,b定义成一个word大小的特定值*/

.word (\base << 20) | (\ap << 10) | \ /*这个特定值就是转换表的填充量,其中,参数base是映射出来的段地址的基地址,从第20位开始。*/

(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)/*20位的大小正好是1MB,由此可知转换表中未保留前20位,映射出来的地址之间间隔为1MB*/

/*故这里采用的是段式映射;继续来分析参数,ap是访问控制位,从第10位开始。dcb都是一些权限位*/

.endm /*.endm,即结束宏定义*/

定义一个函数。类似C中的宏定义

MMU初始化过程中有一步是将页表基址(CFG_PHY_UBOOT_BASE + mmu_table)存入TTBR0中,在lowlevel_init.S中可以看到对页表的初始化。

关于页表:ARMv7的MMU进行地址映射时涉及到两种页表,一级页表(first level page table)和二级页表(coarse page table)。

关于映射方式:映射方式有两种,段映射和页映射。段映射只用到一级页表,页映射用到一级页表和二级页表。

关于映射粒度:段映射的映射粒度有两种,1M section和16M supersection;页映射的映射粒度也有两种,4K small page和64K large page。

这些信息可以从页表的入口描述符中获得。

定义一个宏FL_SECTION_ENTRY用来设置页表入口描述符,base即物理基址,ap即access permission,d即domain,c即cacheable,b即bufferable。

.section .mmudata, "a"

    .align 14

定义一个名为mmudata的段,段属性为"a",allowable,该段16K对齐。从u-boot.lds中可以看到,u-boot的各个段在内存中的分布依次为:.text,.rodata,.data,.got,.u_boot_cmd,.mmudata,.bss。

为什么页表是16K对齐呢?

在上一节我们讲过:有两个寄存器TTBR0和TTBR1用来存放一级页表基址,操作系统把虚拟地址空间划分为用户空间和内核空间,0x0 -> 1<<(32-N)为用户空间,由TTBR0控制,1<<(32-N) -> 4GB为内核空间,由TTBR1控制,N的大小由TTBCR寄存器决定。

由于u-boot主要作用是硬件初始化和引导操作系统,所以没有必要对虚拟地址空间进行划分,即N=0,整个虚拟地址空间由TTBR0控制,TTBR0的格式如下:

N=0时,[31:14]存放页表基址,即一级页表的基址为([31:14]<<14),2^14为16K。

虚拟地址到物理地址的映射过程如下:

虚拟地址的[31:24]位存放一级页表的入口index,[23:0]位存放段偏移;

从TTBR(translation table base register,协处理器CP15中的一个寄存器,用于存放一级页表的基址)寄存器中获取一级页表的基址;

一级页表基址+ VA[31:24] = 该虚拟地址对应的页表描述符的入口地址;

页表描述符的[31:24]位为该虚拟地址对应的物理段基址;

物理段基址+ VA[23:0]段偏移= 物理地址

由映射图可知,一个虚拟地址可以索引2^8个一级页表入口,每个入口映射2^24大小的内存,故虚拟地址可以映射的最大物理内存为:2^8 * 2^24,即4G。

    // the following alignment creates the mmu table at address 0x4000.

    .globl mmu_table

mmu_table:

    .set __base,0

    // Access for iRAM

    .rept 0x100

.set __base,0 /*设置变量__base值为0*/

// Access for iRAM

.rept 0x100 /*.rept 0x100相当于for循环,一共循环0x100次,所以这一块代码创建了0x100256)个转换表单元*/

FL_SECTION_ENTRY __base,3,0,0,0 /*利用刚才定义的带参宏创建转换表的内容,变量__base3,0,0,0作为参数*/

.set __base,__base+1 /*相当于base=base+1*/

.endr /*.endr对应.rept,表示循环体的结束*/

Syntax: .rept count   重复

Repeat the sequence of lines between the 
.rept directive and the next .endr directive count times. 

For example, assembling

.rept 3

.long 0

.endr

is equivalent to assembling

.long 0

.long 0

.long 0

    FL_SECTION_ENTRY __base,3,0,0,0

对虚拟地址0x0-0x10000000作平行映射(flat mapping),即把虚拟地址0x0-0x10000000映射到物理地址0x0-0x10000000。

    .set __base,__base+1

.endr

 

    // Not Allowed

    .rept 0x200 - 0x100

    .word 0x00000000

    .endr

不对虚拟地址空间0x10000000-0x20000000作映射,即禁止访问虚拟地址空间0x10000000-0x20000000。

Syntax: .word expressions   分配一段内存单元,并用expressions 初始化

This directive expects zero or more expressions, of any section, separated by commas. For each expression, as emits a 16-bit number for this target.

    .set __base,0x200

// 128MB for SDRAM 0xC0000000 -> 0x50000000   这里应该是256M,需要根据自己的开发板配置进行更改

    // should be accessed

    .rept 0x600 - 0x200

    FL_SECTION_ENTRY __base,3,0,1,1

    .set __base,__base+1

    .endr

把虚拟地址空间0x20000000-0x6000000映射到物理地址空间0x20000000-0x6000000,0x20000000-0x6000000为sdram的地址空间,此时sdram有1G。

    .rept 0x800 - 0x600

    .word 0x00000000

    .endr

不对虚拟地址空间0x60000000-0x80000000作映射,即禁止访问虚拟地址空间0x60000000-0x80000000。

    .set __base,0x800

    // should be accessed

    .rept 0xb00 - 0x800

    FL_SECTION_ENTRY __base,3,0,0,0

    .set __base,__base+1

    .endr

对虚拟地址0x80000000-0B0000000作平行映射(flat mapping),即把虚拟地址0x80000000-0xB0000000映射到物理地址0x80000000-0xB0000000。

/*    .rept 0xc00 - 0xb00

    .word 0x00000000

    .endr */

 

    .set __base,0xB00

    .rept 0xc00 - 0xb00

    FL_SECTION_ENTRY __base,3,0,0,0

    .set __base,__base+1

    .endr

对虚拟地址0xB0000000-0xC0000000作平行映射(flat mapping),即把虚拟地址0xB0000000-0xC0000000映射到物理地址0xB0000000-0xC0000000。

 

    // 0xC000_0000鏄犲皠鍒?x2000_0000

    .set __base,0x300

    //.set __base,0x200

    // 256MB for SDRAM with cacheable

    .rept 0xD00 - 0xC00

    FL_SECTION_ENTRY __base,3,0,1,1

    .set __base,__base+1

    .endr

把虚拟地址空间0xC0000000-0xD000000映射到物理地址空间0xC0000000-0xD000000,0xC0000000-0xD000000为sdram的地址空间,此时sdram有1G。

 

    // access is not allowed.

    @.rept 0xD00 - 0xC80

    @.word 0x00000000

    @.endr

 

    .set __base,0xD00

    // 1:1 mapping for debugging with non-cacheable

    .rept 0x1000 - 0xD00

    FL_SECTION_ENTRY __base,3,0,0,0

    .set __base,__base+1

    .endr    

宏观上理解转换表:整个转换表可以看作是一个int类型的数组,数组中的一个元素就是一个表索引和表项的单元。数组中的元素值就是表项,这个元素的数组下标就是表索引。

ARM的段式映射中长度为1MB,因此一个映射单元只能管1MB内存,那我们整个4G范围内需要4G/1MB=4096个映射单元,也就是说这个数组的元素个数是4096.实际上我们做的时候并没有依次单个处理这4096个单元,而是把4096个分成几部分,然后每部分用for循环做相同的处理。VA是虚拟地址,PA是物理地址。

VA                    PA            length

0-10000000            0-10000000                    256MB

10000000-20000000        0                        256MB

20000000-60000000        20000000-60000000        1GB        512-1.5G

60000000-80000000        0                512MB        1.5G-2G

80000000-b0000000        80000000-b0000000        768MB        2G-2.75G

b0000000-c0000000        b0000000-c0000000        256MB        2.75G-3G

c0000000-d0000000        30000000-40000000        256MB        3G-3.25G

d-完                d-完                768MB        3.25G-4G

 

DRAM有效范围:

DMC0:     0x30000000-0x3FFFFFFF

DMC1:    0x40000000-0x4FFFFFFF

 

结论:虚拟地址映射只是把虚拟地址的c0000000开头的256MB映射到了DMC0的30000000开头的256MB物理内存上去了。其他的虚拟地址空间根本没动,还是原样映射的。

 

思考:为什么配置时将链接地址设置为c3e00000,因为这个地址将来会被映射到33e00000这个物理地址。

 

-----------------------lowlever_init.s中的代码讲解结束-----------------------

 

    ldr    r1, =CFG_PHY_UBOOT_BASE

    ldr    r2, =0xfff00000

    bic    r0, r0, r2

    orr    r1, r0, r1

    mcr    p15, 0, r1, c2, c0, 0

设置TTB(cp15的c2寄存器)

1)TTB就是translation table base,转换表基地址。首先要明白什么是TT(translation table转换表),TTB其实就是转换表的基地址。

2)转换表是建立一套虚拟地址映射的关键。转换表分2部分,表索引和表项。表索引对应虚拟地址,表项对应物理地址。一对表索引和表项构成一个转换表单元,能够对一个内存块进行虚拟地址转换。(映射中基本规定中规定了内存映射和管理是以块为单位的,至于块有多大,要看你的MMU的支持和你自己的选择。在ARM中支持3种块大小,细表1KB、粗表4KB、段1MB)。真正的转换表就是由若干个转换表单元构成的,每个单元负责1个内存块,总体的转换表负责整个内存空间(0-4G)的映射。

3)整个建立虚拟地址映射的主要工作就是建立这张转换表

4)转换表放置在内存中的,放置时要求起始地址在内存中要xx位对齐。转换表不需要软件去干涉使用,而是将基地址TTB设置到cp15的c2寄存器中,然后MMU工作时会自动去查转换表。

    /* Enable the MMU */

mmu_on:

    mrc    p15, 0, r0, c1, c0, 0

    orr    r0, r0, #1

    mcr    p15, 0, r0, c1, c0, 0

    nop

    nop

    nop

    nop

#endif

cp15的c1寄存器的bit0控制MMU的开关。只要将这一个bit置1即可开启MMU。开启MMU之后上层软件层的地址就必须经过TT的转换才能发给下层物理层去执行。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值