#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位开始。d、c、b都是一些权限位*/

.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次,所以这一块代码创建了0x100(256)个转换表单元*/

FL_SECTION_ENTRY __base,3,0,0,0 /*利用刚才定义的带参宏创建转换表的内容,变量__base和3,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的转换才能发给下层物理层去执行。

Uboot27之start.S的MMU操作相关推荐

  1. ARM64的启动过程之(三):为打开MMU而进行的CPU初始化

    原文地址:http://www.wowotech.net/linux_kenrel/__cpu_setup.html 一.前言 上一节主要描述了为了打开MMU而进行的Translation table ...

  2. 深入了解Linux内核MMU管理机制

    MMU:存储器管理单元 *虚拟内存空间到物理存储空间的映射.在ARM中采用了页式虚拟内存管理.它把虚拟地址空间分为一个个大小固定的块,每一块称为一页,把物理内存的地址空间也分成同样大小的页.页的大小可 ...

  3. arm64的ioremap_ARM64的启动过程之(三):为打开MMU而进行的CPU初始化

    ARM64的启动过程之(三):为打开MMU而进行的CPU初始化 作者:linuxer 发布于:2015-10-21 19:32 分类:ARMv8A Arch 一.前言 上一节主要描述了为了打开MMU而 ...

  4. 内存管理单元——MMU

    一.基本概念介绍 MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时称作分页内存管理单元(英语:paged memory management unit,缩写为PM ...

  5. kernel 3.10代码分析--KVM相关--虚拟机创建\VCPU创建\虚拟机运行

    分三部分:一是KVM虚拟机创建.二是VCPU创建.三是KVM虚拟机运行 第一部分: 1.基本原理 如之前分析,kvm虚拟机通过对/dev/kvm字符设备的ioctl的System指令KVM_CREAT ...

  6. ARM64的启动过程之(二):创建启动阶段的页表

    原文地址: http://www.wowotech.net/linux_kenrel/create_page_tables.html 一.前言 本文主要描述了ARM64启动过程中,如何建立初始化阶段页 ...

  7. vmware的原理和影子页表

    vmware启动的时候同时会有一个vmware-vmx启动,二者通过pipe或者socket通信,实际上,vmware只是一个输入/显示客户端,类似X服务器,它一般在一个窗口中运行一个虚拟操作系统.真 ...

  8. arm linux系统启动流程

    ===================================================== arm linux系统启动相关文章列表: arm linux系统启动流程 http://bl ...

  9. 【重识云原生】计算第2.4节——主流虚拟化技术之KVM

    <重识云原生系列>专题索引: 第一章--不谋全局不足以谋一域 第二章计算第1节--计算虚拟化技术总述 第二章计算第2节--主流虚拟化技术之VMare ESXi 第二章计算第3节--主流虚拟 ...

最新文章

  1. apache 版本_Apache Hudi 0.5.1版本重磅发布
  2. 使用ajax进行汽车详情表的查询
  3. android二分查找法简书,【PYTHON】二分查找算法
  4. javascript 校验 非空_Javascript的表单与验证-非空验证
  5. 机器学习近年来之怪现状
  6. Audirvana for Mac(高品质音乐播放器)
  7. 在职场中,如何能够让下属认真完成工作,又不顶撞你?
  8. Codeforces Round #190 (Div. 1): E. Ciel and Gondolas(决策单调性DP+wqs二分)
  9. Sql Server函数全解一字符串函数
  10. 做了一天的程序,很困啊,明天还要赶着去上课
  11. 计算机磁盘无法创建文件夹,无法创建文件,小编告诉你无法新建文件夹怎么办...
  12. 计算机硬件技术基础教程mcs-51单片机原理及应用,mcs51单片机原理及应用
  13. 集团施工企业安全生产风险管控和隐患排查治理双重预防机制数字化建设方案
  14. Apache高并发测试工具JMeter
  15. 那些年的广告语【持续更】
  16. vue3.0+vite跑项目遇到的问题
  17. srio 门铃_如何更改SkyBell HD门铃的LED颜色
  18. python综合应用题 论语 230322
  19. 2020.08.11 【ABAP随笔】-ITS Mobile 配置
  20. 项目十大管理(六)人力资源管理

热门文章

  1. JavaScript使用MQTT
  2. STM32读取TCS3472颜色传感器读取RGB颜色和色温值和Lux
  3. krpano切换皮肤样式
  4. cmos可以修改计算机的硬件配置参数吗,(计算机CMOS设置详解1.doc
  5. java 子类转换_java 子类父类相互转换
  6. ICLR 2023 | DIFFormer: 扩散过程启发的Transformer
  7. Open*** + Frp 组建本地局域网
  8. Photoshop临摹 icon
  9. php离线bt下载,基于TP3.1的多用户离线下载
  10. 微信小程序获取当前日期