操作MMU和Cache的内核启动代码

bootloader加载linux内核到内存并解压之后,Linux内核首先在汇编代码中读取CPU的基本信息,对CPU做一些基本设置,创建最简单的 临时页表,然后开启MMU和Cache,启用虚拟内存管理(此后CPU核发出的地址都是虚拟地址),然后跳到C代码中完成其它初始化工作,比如创建完整的 页表、初始化各种内核子系统、初始化硬件设备等。本节以Linux 2.4内核的启动代码为例,了解一下操作MMU和Cache的具体指令是怎么写的,通过实例来加深对前面内容的理解。本节的内容改编自[ARM Linux演义]。

假设目标板的RAM物理地址是从0x3000 0000开始的(也就是说,RAM芯片连接到CPU芯片上从0x3000 0000开始的bank)。经过内核的若干初始化代码之后,寄存器的内容如下:

表 3. 寄存器的初始值

R4     0x3000 4000      临时页表的起始地址(物理地址)

R5     0x3000 0000      Sdram起始物理地址

R6     0x0000 0cle      页描述符标志位

接下来的步骤是:

1 创建简单的临时页表和临时映射

2 配置与MMU和Cache相关的CP15寄存器

3 启用MMU和Cache

临时页表存放在物理内存地址0x3000 4000开始的16K(回想一下,第一级页表是16K,有4096个页描述符)。后面将会把页描述符填写成Section格式,也就是直接映射到1M的大 页面,这些都是内核初始化阶段临时用的,为了是写尽可能少的汇编代码,尽快启用MMU并跳到C代码中做剩下的初始化工作,在完整的两级页表建立之后临时页 表就没有用了。首先将16K的临时页表清零:

mov r0, r4
mov r3, #0
add r2, r0, #0x4000 @ 16k of page table
1: str r3, [r0], #4 @ Clear page table
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r2
bne 1b

下面我们将使用Section格式的页描述符来填充表项,由于是内核初始化阶段,还没有用户进程,我们只映射4M的地址空间,覆盖内核本身的代码和数据就 可以了。思考一下,为什么首先要把这16K临时页表清零,即使没用到的表项也要清零?由于Linux内核在编译时确定的代码链接地址是0xc000 8000(虚拟地址),而bootloader将内核代码加载到物理地址0x3000 8000,我们需要把物理地址从0x3000 0000开始的4M映射到虚拟地址从0xc000 0000开始的4M。

但是这里有一个问题:设置好页表之后,最终有一条指令是启用MMU的,假设该指令的PA是0x3000810c,根据我们要做的映射关系,它的VA应该是0xc000 810c,没有启用MMU之前CPU核发出的都是物理地址,从0x3000810c地址取这条指令来执行,然而该指令执行之后,CPU核发出的地址都要被MMU拦截,CPU核就必须用虚拟地址来取指令了,因此下一条指令应该从 0xc000 8110处取得,然而这时pc寄存器(也就是r15寄存器)的值并没有变,CPU核取下一条指令仍然要从0x3000 8110处取得,此时0x3000 8110已经成了非法地址了。

图. 启用MMU的那条指令导致的问题

为了解决这个问题,要求启用MMU的那条指令及其附近的指令虚拟地址跟物理地址相同,这样在启用MMU前后,附近指令的地址不会发生变化,从而实现平稳过 渡。因此需要将物理地址从0x3000 0000开始的1M再映射到虚拟地址从0x3000 0000开始的1M,也就是做一个等价映射(identity map)[5]。现在把需要建立的映射项总结如下:

以下代码建立上面所说的等价映射。

回头看一下表 3 “寄存器的初始值”,r8的值是页描述符标志位,r5的值是RAM起始物理地址0x0800 0000,由于要做的是等价映射,这里的r5既是PA同时也是VA,第一条指令将r5当作PA,r3=r8+r5=0x0800 0c1e得到完整的页描述符,比对一下看看各bit的含义。

![这里写图片描述](https://img-blog.csdn.net/20180724212840235?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0ODg4MDM2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

图 22. 等价映射的页描述符

add r3, r8, r5 @ mmuflags + start of RAM
add r0, r4, r5, lsr #18
str r3, [r0] @ identity mapping

该描述符所描述的Section属于第0个Domain,AP位是11,可读可写,C、B位都是1,允许Cache,并且Cache是Write Back方式的。第二条指令,将虚拟地址r5右移18位(对照图 14 “Translation Table Walk的详细过程”看一下为什么是右移18位),加到页表基地址上,得到该描述符在页表中的地址,结果保存在r0中。第三条指令,将第一条指令计算出的 页描述符的值r3保存在第二条指令计算出的r0地址处,这样就填写好了页表项。

下面映射物理地址从0x3000 0000开始的4M到虚拟地址0xc000 0000,其中TEXTADDR是Linux内核在编译时确定的代码链接地址0xc000 8000,PAGE_OFFSET定义为0xc000 0000。请读者自己分析以下代码。

add r0, r4, #(TEXTADDR & 0xfff00000) >> 18 @ start of kernel 注:r0 = r4+ 0x3000 = 3000 4000 + 3000 = 3000 7000 str r3, [r0], #4 @ PAGE_OFFSET + 0MB 注:3000 7000地址的内容为3000 0c1e add r3, r3, #1 << 20 注:r3=3010 0c1e str r3, [r0], #4 @ PAGE_OFFSET + 1MB 注:3000 7004地址的内容为3010 0c1 e add r3, r3, #1 << 20 注:r3=0820 0c1e str r3, [r0], #4 @ PAGE_OFFSET + 2MB 注:3000 7008地址的内容为3020 0c1e add r3, r3, #1 << 20 注:r3=3030 0c1e str r3, [r0], #4 @ PAGE_OFFSET + 3MB 注:3000 700c地址的内容为3030 0c1e

设置好了页表,接下来设置与MMU和Cache相关的CP15寄存器:

mov r0, #0
mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
mcr p15, 0, r0, c7, c10, 4@ drain write buffer on v4
mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
mcr p15, 0, r4, c2, c0 @ load page table pointer
mov r0, #0x1f @ Domains 0, 1 = client
mcr p15, 0, r0, c3, c0 @ load domain access register
mrc p15, 0, r0, c1, c0 @ get control register v4

@ VI ZFRS BLDP WCAM
bic r0, r0, #0x0e00
bic r0, r0, #0x0002
bic r0, r0, #0x000c
bic r0, r0, #0x1000 @ …0 000. …. 000.

orr r0, r0, #0x0031
orr r0, r0, #0x2100 @ ..1. …1 ..11 …1
#ifdef CONFIG_CPU_ARM920_D_CACHE_ON
orr r0, r0, #0x0004 @ …. …. …. .1..
#endif
#ifdef CONFIG_CPU_ARM920_I_CACHE_ON
orr r0, r0, #0x1000 @ …1 …. …. ….
#endif

这一段有很多协处理器指令,请读者对照[S3C2410用户手册]和代码中的注释查看各指令的含义。大体上来说做了以下事情:首先禁用指令和数据 Cache,等待Write Buffer写回内存,然后用r4寄存器的值设置CP15的TTB寄存器,然后设置Domain权限位,我们先前填写的页描述符都属于第0个 Domain,Domain寄存器中第0个Domain的权限位设置为11,表示访问不必检查AP位。接下来读出CP15的控制寄存器的值来修改,准备启 用MMU,根据内核配置决定是否启用数据和指令Cache,修改之后一并写回控制寄存器,使设置生效:

mcr p15, 0, r0, c1, c0

参考资料

[S3C2410用户手册] User’s Manual S3C2410A - 200MHz & 266MHz 32-Bit RISC Microprocessor. 1.0. 版权 © 2004 Samsung Electronics.

[ARM参考手册] ARM Architecture Reference Manual. 版权 © 1996-2000 ARM Limited.

[ARM Linux演义] 网上到处转载的帖子: ARM Linux演义. 作者已不可考,据说是r58452网友.

索引

C

Cache,高速缓存, 虚拟内存管理

Cache Hit, ARM920T的CP15协处理器

Cache Line, ARM920T的CP15协处理器

Cache Miss, ARM920T的CP15协处理器

Cache Thrash,Cache抖动, Cache

Direct Mapped Cache,直接映射Cache, Cache

Fully Associative Cache,全相联Cache, Cache

n-way Set Associative Cache,n路组相联Cache, Cache

M

MMU,Memory Management Unit,内存管理单元, 虚拟地址和物理地址的概念

P

Page Frame,页框,物理页面, 虚拟地址和物理地址的概念

Page Table,页表, ARM920T的CP15协处理器

Page,页, 虚拟地址和物理地址的概念
   (参见 Page Frame,页框)

Paging,换页, 虚拟内存管理
    Page in,换入, 虚拟内存管理
    Page out,换出, 虚拟内存管理

PA,Physical Address,物理地址, 虚拟地址和物理地址的概念
    (参见 VA,Virtual Address,虚拟地址)

S

Swap Device,交换设备, 虚拟内存管理

T

Tag, Cache

TLB,Translation Lookaside Buffer, ARM920T的CP15协处理器

Translation Table Walk, ARM920T的CP15协处理器

V

VA,Virtual Address,虚拟地址, 虚拟地址和物理地址的概念
    (参见 PA,Physical Address,物理地址)

Virtual Memory Management,虚拟内存管理, 虚拟内存管理

W

Write Back, Cache(参见 Write Through)

Write Through, Cache(参见 Write Back)

1 对于32位的CPU,从CPU核这边看地址线是32条(图中只是示意性地画了4条地址线),可寻址空间是4GB,但是通常嵌入式处理器的CPU外部地址引 脚不会有这么多条地址线,因为引脚是芯片上十分有限而宝贵的资源,而且也不太可能用到4GB这么大的物理内存。另一方面,在启用MMU的情况下VA地址空 间和PA地址空间是完全独立的,PA地址空间既可以小于也可以大于VA地址空间,例如有些32位的服务器可以配置大于4GB的物理内存。

2 这里说Cache是用VA来索引数据的,只是针对一些嵌入式处理器,实际上大多数PC和服务器的CPU都有两级Cache,靠近CPU核的一级缓存是以VA来索引数据的,而靠近物理内存的二级缓存则是以PA来索引数据的。

3 如果读者看了[S3C2410用户手册]可能会注意到有MVA(Modified Virtual Address)这个概念,这属于ARM的快速上下文切换(Fast Context Switch)机制,适用于一些小型的RTOS,每个进程的地址空间不超过32M,Linux并没有利用快速上下文切换机制,因此在我们的讨论当中,VA 和MVA不加区分,认为是相同的。

4 我们说Cache的大小是16K,是指Cache能缓存16K的内存数据,其实Cache还需要保存相应的Tag和其它标志位,其存储容量应该是大于 16K的。另外,Cache的构造和内存不同,不必以字节为单位来存储,例如VA[31:5]这个Tag有27个bit,既不是3个字节也不是4个字节。

    5 事实上,以上解释并不完全正确,这里还有一个更复杂的细节,启用MMU的指令在执行时,后面两条指令已经预取到CPU流水线里了,如果利用那两条指令跳转 到0xc000 8110不就行了?但是流水线是靠不住的,跳转和异常都会清空流水线,[ARM参考手册]的Chapter A2详细解释了这种情况,按该手册的建议应该采用等价映射的方法解决这个问题。

ARM920T MMU and Cache相关推荐

  1. ARM处理器之MMU和Cache

    ARM处理器之MMU和Cache ARM920T的MMU和Cache都集成在CP15协处理器中,MMU和Cache的联系非常密切,以下是CP15协处理器的寄存器列表(摘自[S3C2410用户手册]), ...

  2. [国嵌笔记][036][关闭MMU和CACHE]

    关闭MMU和CACHE 1.Cache是一种容量小,但存取速度非常快的存储器,它保存最近用到的存储器中数据的拷贝.按功能分为ICache(指令Cache)和DCache(数据Cache) 2.虚拟地址 ...

  3. (亚嵌)ARM920T的MMU与Cache之MMU

    MMU 我们已经简单了解了一下查页表的过程,实际上ARM920T支持多种尺寸规格的页表,图 9 "Translation Table Walk"所示的只是其中一种情况.下图示意了所 ...

  4. MMU和cache学习

    1.      MMU MMU:memory management unit,称为内存管理单元,或者是存储器管理单元,MMU是硬件设备,它被保存在主存(main memory)的两级也表控制,并且是由 ...

  5. ARM协处理器CP15(设置MMU,cache等)学习

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 作者:w ...

  6. ARM架构中MMU/TLB/Cache的一些概念和寄存器

    ★★★ 个人博客导读首页-点击此处 ★★★ 相关文章 1.ARMV8-aarch64的MMU学习笔记 2.aarch64的TCR寄存器介绍 文章目录 1.MMU/Cache相关的一些基本概念 (1). ...

  7. 思考:通过MMU/TLB/Cache对安全内存攻击的可能性

    快速链接: .

  8. [mmu/cache]-ARMV8-aarch64的虚拟内存(mmu/tlb/cache)介绍-概念扫盲

    快速链接: .

  9. ARM920T内存管理单元MMU

    作为程序员已经有4-5个年头了,发现学的知识杂且乱,很多学习过的东西,有时也会忘记.索性开始整理,由于是电子专业出身,于是想把之前玩过的2440开发板,重新再玩一遍.顺便对各个知识点进行较全面的总结. ...

  10. cache与MMU与总线仲裁

    为了以合理的价格,设计容量和速度满足计算机系统的需求,计算机体系结构设计者设计出了存储器的层次结构. "Cache-主存"和"主存-辅存"是最常见的两种层次结构 ...

最新文章

  1. Java开发环境的搭建以及使用eclipse创建项目
  2. 近世代数--极大理想--I是R的极大理想↔R/I是域
  3. python函数必背知识点_必背函数——python学习第四次总结
  4. 再窥--单链表和顺序存储
  5. 卡方 python_卡方优度检测 (Python 实现) --基于jupyter
  6. POJ2255Tree Recovery
  7. 解决QQ未启用状态,QQ留言图标未启用
  8. python解释器在语法上不支持什么编程_python解释器和编辑器的区别 - CSDN
  9. 共享一个从字符串转 Lambda 表达式的类(2)
  10. JavaWeb知识点
  11. 新闻发布系统——网站发布
  12. springboot报错:Use of @OneToMany or @ManyToMany targeting an unmapped class:
  13. LimeSDR 中文教程 (九)
  14. 简易的安卓天气app(四)——搜索城市、完善页面
  15. 3dMax 导出材质球
  16. 【Windows安装RabbitMQ详细教程】
  17. 主数据管理方法论之主数据全生命周期管理
  18. for in 中的index
  19. WEB学习路线2020完整版+附视频教程
  20. 推荐系统组队学习之概述

热门文章

  1. -XX:SurvivorRatio 命令解释
  2. linux修改dns教程,修改Centos的DNS地址
  3. html树状图在线画板,树状思维导图怎样绘制
  4. transductive inference(转导推理,直推式学习)
  5. VLAN-TAG超经典解释
  6. 域控服务器的ip地址,域控更改ip地址问题
  7. 图文详解,浪涌保护器(SPD)的参数解读与选用
  8. 组合数性质--二项式系数之和等于2^n的证明
  9. 5.13 利用图层的矢量蒙版打造浪漫情调 [原创Ps教程]
  10. 【元胞自动机】基于元胞自动机实现单边教室人群疏散含Matlab源码