Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103253
  • 博文数量: 30
  • 博客积分: 536
  • 博客等级: 中士
  • 技术积分: 224
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-29 09:23
文章分类
文章存档

2012年(16)

2011年(14)

我的朋友

分类: LINUX

2012-01-01 23:57:34

2009年01月07日 星期三 17:38

ARM920T的MMU与Cache

宋劲杉

原文来源——

————————————————————————————————————————————

目录

        虚拟地址和物理地址的概念
    
虚拟内存管理
    
ARM920T的CP15协处理器
    
MMU
     Cache

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

    
参考资料 索引

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

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

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

表 3. 寄存器的初始值

    接下来的步骤是:

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

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

    3 启用MMU和Cache

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

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

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

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

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

表 4. 需要建立的映射项

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

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

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

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

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

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

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

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

    相关索引

    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详细解释了这种情况,按该手册的建议应该采用等价映射的方法解决这个问题。

阅读(1118) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~