• ARM MMU原理


    本文描述基于存储器管理单元的系统结构, 包含以下内容: 

    1. 关于存储器管理单元的结构  
    2. 存储器访问的顺序  
    3. 转换过程
    4. 访问权限   
    5. 域  
    6. 异常  
    7. CP15寄存器

    3.1 关于存储器管理单元的结构
     
     
        MMU存储器系统的结构允许对存储器系统的精细控制。大部分的控制细节由存在存储器中的转换表提供。这些表的入口定义了从 1KB到 1MB的各种存储器区域的属性。这些属性包括:

    虚拟地址到物理地址映射:
     
        ARM 处理器产生的地址叫虚拟地址,MMU允许把这个虚拟地址映射到一个不同的物理地址去。这个物理地址表示了被访问的主存储器的位置。 
    它允许用很多方式管理物理存储器的位置,例如:它可以用具有潜在冲突的地址映射为不同的进程分配存储器,或允许具有不连续地址的应用把它映射到连续的地址空间。 
        注:如果使用了快速上下文切换扩展(Fast Context Switch Extension),则在本文中的虚拟地址的意思应该是修改过的虚拟地址(Modified virtual address) 

    存储器访问权限(permissions) :
     
        这些控制对存储器区域的不可访问权限、只读权限、读写权限。当访问不可访问权限的存储器时,会有一个存储器异常通知 ARM处理器。  
    允许权限的级别也受程序运行在用户状态还是特权状态影响,还受是否使用了域有关。
     
    高速缓存和缓冲位(Cachability and bufferability bits [C and B]):
     
        这些在高速缓存和缓冲一节讲系统控制协处理器的寄存器允许对系统的高级控制,如转换表的位置。他们也用来为 ARM 提供内存异常的状态信息。 

    查找整个转换表的过程叫转换表遍历。它由硬件制动进行,并需要大量的执行时间(至少一个存储器访问,通常是两个)。为了减少存储器访问的平均消耗, 转换表遍历结果被高速缓存在一个或多个叫作 Translation Lookaside Buffers(TLBs)的结构中。通常在 ARM的实现中每个内存接口有一个 TLB。 
        *有一个存储器接口的系统通常有一个唯一的 TLB     

        *指令和数据的内存接口分开的系统通常有分开的指令TLB和数据 TLB
     
        如果系统有高速缓存, 高速缓存的数量也通常是由同样的方法确定的。所以在高速缓存的系统中,每个高速缓存一个 TLB。
     
        当存储器中的转换表被改变或选中了不同的转换表(通过写 CP15的寄存器2),先前高速缓存的转换表遍历结果将不再有效。MMU 结构提供了刷新 TLB的操作。 
     
        MMU结构也允许特定的转换表遍历结果被锁定在一个TLB中,这就保证了对相关的存储器区域的访问绝不会导致转换表遍历,这也对那些把指令和数据锁定在高速缓存中的实时代码有相同的好处。

    3.2 存储器访问的顺序

        当ARM要访问存储器时,MMU先查找TLB中的虚拟地址表,如果ARM的结构支持分开的地址TLB和指令TLB,那么它用:

        * 取指令使用指令TLB  

        * 其它的所有访问类别用数据TLB

        如果 TLB 中没有虚拟地址的入口,则转换表遍历硬件从存在主存储器中的转换表中获取转换和访问权限,一旦取到,这些信息将被放在TLB中,它会放在一个没有使用的入口处或覆盖一个已有的入口。关于转换表的信息和转换表遍历的实现参见转换过程一节。

        一旦为存储器访问的 TLB的入口被拿到,这些信息将被用于:
     
        1. C(高速缓存)和B(缓冲)位被用来控制高速缓存和写缓冲,并决定是否高速缓存。(如果系统中没有高速缓存和写缓冲,则对应的位将被忽略)
     
        2. 访问权限和域位用来控制访问是否被允许。如果不允许,则 MMU将向 ARM处理器发送一个存储器异常;否则访问将被允许进行。 访问权限、域和异常几节有详细描述。

        3. 对没有高速缓存的系统(包括在没有高速缓存系统中的所有存储器访问),物理地址将被用作主存储器访问的地址。 对有高速缓存的系统,在高速缓存没有选中的情况下,物理地址将被用行取(line fetch)的地址。如果选中了高速缓存,则物理地址将被忽略。

     
        3.2.1 允许和禁止 MMU
     
        通过写系统控制协处理器的寄存器1的第 0位可以允许和禁止MMU。在复位后这位是 0,MMU 被禁止。当 MMU 被禁止时,存储器访问将被按如下处理:
     
        1. 由具体的实现确定当 MMU 被禁止时是否能够允许高速缓存和写缓冲。

           当 MMU 被禁止时不能允许高速缓存和写缓冲时,C 和 B 位不起作用。   当 MMU 被禁止时能允许高速缓存和写缓冲时:
     
           i.  访问数据时被认为没有高速缓存和写缓冲(C==0,B==0) 
          ii.  取指令时: 
     
          a)   当系统只有一个唯一的 TLB 时,认为是没有高速缓存。(C==0)
          b)   当系统只有独立的指令 TLB 时,认为是有高速缓存。(C==1) 
     
        2. 没有存储器访问权限的检查,MMU 也不产生异常信号。 
     
        3. 物理地址与虚拟地址相同(即所谓的平坦地址映射模式)。 

        在允许 MMU之前,必须在内存中建立适当的转换表,并且所有相关的 CP15寄存器要被初始化正确。 
        注:允许和禁止MMU直接改变了虚拟地址到物理地址的映射(除非转换表被设定为平坦地址映射模式)。所以很可能在允许 MMU 时所有的高速缓存需要被刷新。另外,如果允许MMU的指令的物理地址和虚拟地址不同,取指令将变得复杂化。所以,强烈建议允许 MMU 的指令具有相同的物理地址和虚拟地址。

    3.3 转换过程

    MMU支持基于节或页的存储器访问:
     
        节(Section)       构成 1MB的存储器块 
        支持 3中不同的页尺寸:
        微页(Tiny page)   构成 1KB的存储器块
        小页(Small page)  构成 4KB 的存储器块
        大页(Large page)  构成 64KB 的存储器块
     
    节和大页是支持允许只用一个TLB入口去映射大的存储器区间。小页和大页有附加的访问控制:小页分成 1KB 的子页,和大页分成16KB的子页。微页没有子页,对微页的访问控制是对整个页。

    存在主存储器内的转换表有两个级别:
     
    第一级表  存储节转换表和指向第二级表的指针。
    第二级表  存储大页和小页的转换表。一种类型的第二级表存储微页转换表。
     
        MMU把 CPU 产生的虚拟地址转换成物理地址去访问外部存储器,同时继承并检查访问权限。地址转换有四条路径。路径的选取由这个地址是被标记成节映射访问还是页映射访问确定。页映射访问可以是大、小和微页的访问。  
        然而,转换过程总是由下面所描述的那样由第一级表的获取开始。节映射的访问只需要读取第一级表,页映射的访问还需要读取第二级表。

    3.3.1 转换表基址
     
        当片上(on-chip)的 TLB 中不包含被要求的虚拟地址的入口时,转换过程被启动。转换表基址寄存器(CP15的寄存器2)保存着第一级转换表基址的物理地址。只有 bits[31:14]有效,bits[13:0]应该是零(SBZ)。所以第一级表必须在16KB的边界。  

    3.3.2 取第一级表

        转换表基址寄存器的 bits[31:14]与虚拟地址的 bits[31:20]和两个0位连接形成32为物理地址,如图3-2。这个地址选择了一个四字节的转换表入口,它是第一级描述符或是指向第二级页表的指针。

    3.3.3 第一级描述符
     
    第一级表的每个入口是一个描述它所关联的1MB虚拟地址是如何映射的描述符。见表 3-1,根据 bits[1:0]的组合,有四种可能:
        如果 bits[1:0]==0b00,所关联的地址没有被映射,试图访问他们将产生一个转换错(fault)。因为他们被硬件忽略,所以软件可以利用这样的描述符的 bits[31:2]做自己的用途。推荐为描述符继续保持正确的访问权限。  

       * 如果 bits[1:0]==0b10,这个入口是它所关联地址的节描述符。见节描述符和转换节参考中的细节。  

       * 如果 bits[0]==1,这个入口给出粗糙第二级表(bit[1]==0),或精细第二级表(bit[1]==1)。每一种类型的表描述了它所关联的 1MB存储区域的映射。粗糙第二级表较小,每个表 1KB,每个精细第二级表 4KB。然而粗糙第二级表只能映射大页和小页,精细第二级表可以映射大页、小页和微页。

    3.3.4 节描述符和转换节参考 
     
        如果第一级描述符是节描述符,那么各个字段有如下的意义:  
        Bits[1:0]  描述符类型标识(0b10 表示节描述符)
        Bits[3:2]  高速缓存和缓冲位
        Bits[4]    由具体实现定义
        Bits[8:5]  这个描述符控制的节的 16 种域之一
        Bits[9]    现在没有使用,应该为零
        Bits[11:10] 访问控制,见表 3-3
        Bits[19:12] 现在没有使用,应该为零
        Bits[31:20] 节基址,形成物理地址的高 12 位 
        图 3-3 表示了节转换的完整过程。
        注:访问权限必须在物理地址产生之前去检查,检查访问权限的顺序见访问权限一节。

    3.3.5 粗糙页表描述符 
      
        如果第一级描述符是粗糙页表描述符,那么各个字段有如下的意义: 
        Bits[1:0]  描述符类型标识(0b01 表示粗糙页表描述符)
        Bits[4:2]  由具体实现定义
        Bits[8:5]  这个描述符控制的页的 16 种域之一
        Bits[9]    现在没有使用,应该为零
        Bits[31:10] 页表基地址是一个指向第二极粗糙页表的指针,它给出第二级表访问 的基地址。而第二级粗糙页表必须在 1KB 边界对齐。 
     
        如果从第一级读取到的是二级粗糙页表描述符,那么会象图 3-4所示执行第二级描述符读取。 

    3.3.6  精细页表描述符 
        如果第一级描述符是精细页表描述符,那么各个字段有如下的意义:
        Bits[1:0]  描述符类型标识(0b11 表示精细页表描述符)
        Bits[4:2]  由具体实现定义
        Bits[8:5]  这个描述符控制的页的 16 种域之一
        Bits[11:9]  现在没有使用,应该为零
        Bits[31:10] 页表基地址是一个指向第二级精细页表的指针,它给出第二级表访问的基地址。而第二级精细页表必须在 4KB 边界对齐。 
     
        如果从第一级读取到的是二级精细页表描述符,那么会象图 3-5所示执行第二级描述符读取。 


    3.3.7 第二级描述符
     
    每个粗糙第二级表对映着以 4KB为单位的虚拟地址范围市怎么映射的,每个精细第二级表对映着以1KB为单位的虚拟地址范围市怎么映射的。那些入口是页描述符,他们能够分别描述大于 4KB或1KB的页。在这种情况下,这个描述符必须被重复足够次,以保证这个页始终使用相同的描述符,不论访问这个页中的哪个虚拟地址。
     
    对于一个第二级描述符,有四种可能,由描述符的 bits[1:0]选择。见表 3-2:
       * 如果 bits[1:0]==0b00,说关联的虚拟地址没有被映射,任何对这些虚拟地址的访问将会导致转换错(fault)。软件可以利用这样的描述符的
          bits[31:2]做自己的用途,因为他们被硬件忽略。推荐为描述符继续保持正确的访问权限。 
       * 如果 bits[1:0]==0b01,这个入口是大页描述符,描述 64KB的虚拟地址。
          见转换大页参考。
          一个大页描述符在精细第二级表中必须被重复64次,在粗糙第二级表中必须被重复 16 次以保证所有的虚拟地址都被描述。 

       * 如果 bits[1:0]== 0b10,这个入口是小页描述符,描述 4KB 的虚拟地址。
         见转换小页参考。
         一个小页描述符在精细第二级表中必须被重复 4次,以保证所有的虚拟地址都被描述。在粗糙第二级表中只有一个实例。

       * 如果 bits[1:0]== 0b11,这个入口是微页描述符,描述 1KB 的虚拟地址。
          见转换微页参考。 
         在精细第二级表中只需要一个微页描述符的实例。微页描述符不能在粗糙第二级表中出现,如果出现了,结果不可预测。

     大页描述符字段
     
    大页描述符的字段有如下意义:
     
    bits[1:0]  表示描述符的类型
    bits[3:2]  高速缓促和缓冲位
    bits[11:4]  访问权限位。这些为控制对页的访问。关于这些位的解释见表3-3。
    大页被分成 4 各子页。
    AP0  编码对第一个子页的访问权限。
    AP1  编码对第二个子页的访问权限。
    AP2  编码对第三个子页的访问权限。
    AP3  编码对第四个子页的访问权限。

    bits[15:12] 现在没有使用,应该为零。
    bits[31:16] 用来形成物理地址的对应位。
     
    小页描述符字段
     
    小页描述符的字段有如下意义:
     
    bits[1:0]  表示描述符的类型
    bits[3:2]  高速缓促和缓冲位
    bits[11:4]  访问权限位。这些为控制对页的访问。关于这些位的解释见表3-3。
    小页被分成 4 各子页。
    AP0  编码对第一个子页的访问权限。
    AP1  编码对第二个子页的访问权限。
    AP2  编码对第三个子页的访问权限。
    AP3  编码对第四个子页的访问权限。

     
    bits[31:12] 用来形成物理地址的对应位。
     
    微页描述符字段
     
    微页描述符的字段有如下意义:
     
    bits[1:0]  表示描述符的类型
    bits[3:2]  高速缓促和缓冲位
    bits[5:4]  访问权限位。这些为控制对页的访问。关于这些位的解释见表3-3关
    于微页的解释。
    bits[9:6]  现在没有使用,应该为零。
    bits[31:10] 用来形成物理地址的对应位。

    3.3.8 转换大页参考
     
     
    图 3-6 显示了在粗糙第二级表中转换一个 64KB的大页的完整顺序。在精细第二级
    表中的转换顺序页相似,只是第二级描述符的地址如精细页表描述符一节所决定。
     
     
    注:-----------------------------
    页索引的高4位和第二级表的低阶 4位重叠,在粗糙页表中大页的每个页表入口必
    须被重复 16次。在精细页表中大页的每个页表入口必须被重复 64 次。
    -----------------------------------

                                                       图 3-7 粗糙第二级表中的小页转换

    3.3.10 转换微页索引
     
     
    图 3-8 显示了在精细第二级表中转换 1KB 微页的完整过程。
     
     
    注:---------------------------
    微页不能出现在粗糙第二级表中。
    ---------------------------------

    3.4  访问权限 

        在节和页描述符中的访问权限位控制对相应的节和页的访问。访问权限由 CP15的寄存器 1 的 System(S)和 ROM(R)位修改。表 3-3描述了访问权限位和 S、R位相互作用时的意义。如果访问了没有访问权限的存储器空间,将会产生权限错(见异常一节)。

     
     

    3.5 域

        域是节、大页和小页的集合。ARM结构支持16个域。对域的访问由域访问控制寄存器的两个位字段控制。因为每个字段对访问对应的域的使能非常迅速,所以整个存储器区间能很快地交换进出虚拟存储器。这里支持 2种域访问方式:
     
        客户    域的用户(执行程序,访问数据),被形成这个域的节或页来监督访问权限。
        管理者  控制域的行为(域中的当前节和页,对域的访问),不被形成这个域的节或页来监督访问权限。

        一个程序可以是一些域的客户,也是另外一些域的管理者,同时没有对其它域的访问权限。这允许对程序访问不同存储器资源的非常灵活的存储器保护。表 3-4说明了域访问控制寄存器的位编码方式。 

    3.6 异常 

        由于存储器访问的约束而导致 ARM处理器停止执行的机制有: 
     
        MMU fault  MMU 检测到约束并统治处理器
        外部 Abort 外部存储器系统发出一个非法存储器访问信号
     
        MMU fault 和 外部 Abort 都叫异常(ABORT)。
     
        如果导致异常的存储器操作是取指令,那么当处理器去执行这个非法访问得到的指令时产生一个预取指令异常。如果导致异常的存储器操作是存取数据,那么产生一个数据异常。参见 2.6 节。 

    3.6.1 MMU fault 
     
        MMU产生 4 种类型的 fault   

        对齐错 
        转换错 

        * 域错  

        权限错
     
    存储器系统能终止 3种类型的访问  

        line fetch  

        存储器访问(没有高速缓存和没有缓冲的访问)  

        转换表访问
     
        MMU检测到的异常在任何外部存储器访问发生之前被停止。终止导致外部异常的外部访问是外部系统的职责。 

        系统控制协处理器有 2个寄存器,当外部访问被终止时更新。指令预取异常不更新这些寄存器,由于程序流程的改变,产生遗产的指令没有被执行。
     
        错误地址寄存器(FAR)和错误状态寄存器(FSR)
     
        数据异常会被 CPU 立即响应。FSR被一个 4位的错误状态(FS[3:0])和域序号更新。虚拟地址被写入FAR。如果数据访问连续地产生多个数据异常,则它们的优先级由表 3-5决定。
     
        由取指令产生的异常在指令进入指令流水线时被标记。只有当指令执行时产生预取异常。一个异常导致取指令没有被执行如果那条指令不被使用(例如跳转到别处)

        通常当进入预取指令异常服务程序时,fault所关联的地址被放在R14_abt寄存器中。由具体实现决定当预取指令异常产生时是否更新FSR和 FAR。但是预取指令异常在数据异常产生时(更新 FAR 和 FSR)和进入数据异常项量入口时绝不更新 FSR和 FAR。也就是说数据异常可以使用 FAR和FSR的值,它们不会被预取指令异常给破坏。 

       

        注:Alignment 能写 0b0001 或 0b0011 到 FSR[3:0]。 在域(Domain[3:0])中的非法值是因为 fault 发生在有效的域被装入之前。
    任何被优先级编码屏蔽的异常能够由修改原来的异常(by fixing the primary abort) 并重新执行那条指令产生。 Vector Exception 打破了 FS[0]=0标致着外部异常的模式。

    错误检测顺序(Fault-checking sequence)
     
    MMU检测访问错对节(Section)和页(Page)有些不同,见图 3-9

    Terminal exception 终端错
     
    这标志着发生一个不可挽回的错误。这在(如果至少)是具体实现决定的情况下发生。
     
    Vector exception 向量错
     
    当处理器是 32 位配置(PROG32 被激活),在 26 位模式(CPSR[4]==0),数据访问(不是取指令)通常的向量表(地址0x00-0x1F)导致一个数据异常,这叫向量错。如果向量错发生在 MMU被禁止时,由具体实现决定如何实现。详细信息见 A8-11页。
     
    Alignment fault 对齐错
     
    如果对齐错被允许,则当访问一个不在字对齐地址的数据字(虚拟地址位bits[1:0]!=0b00)或访问一个不在半字对齐地址的数据半字(虚拟地址位
    bits[0]!=0)时产生对齐错。取指令和字节访问不产生对齐错。 

    ---注-----
    如果产生对齐错,访问将被终止,而不去进行权限检查。如果对齐错发生在MMU被禁止时,由具体实现决定如何实现。
    ----------
     
    Translation fault 转换错
     
    有两类转换错:
    节  当第一级描述符被标志为无效时产生。这时候描述符的 bits[1:0]==0b00。
    页  当第二级描述符被标志为无效时产生。这时候描述符的 bits[1:0]==0b00。
     
    Domain fault 域错
     
    有两类域错:   节   页 
     

    在这两种情况下,第一级描述符中有 4位域字段用来选择16个域中的一个。域由域访问控制寄存器中的 2个位控制,这 2 个位用来检查访问权限。见表 3-4。
    如果是节,域访问的检查在第一级描述符返回时进行。
    如果是页,域访问的检查在第二级描述符返回时进行。
    如果特定的访问在域访问控制寄存器中被标识为不可访问,则回产生一个节域错或页域错。 

    Permission fault 权限错
     
    有节权限错和子页权限错。
     
    权限错与域错的检查同时进行。如果那2位的域字段返回的是客户(client)(01),权限检查如下进行: 

    节:    如果第一级描述符定义了一个节访问,那么描述符的 AP位定义了访问是否允许,见表 3-3。如果访问不允许,那将产生节权限错。 
    子页:  如果第一级描述符定义了一个页映射的访问,那么第二级描述符可以定义一 个微页,小页或大页。如果第二级描述符定义了一个微页,那它 只有一个AP 位,这个位适用于整个页。否则第二级描述符有4个AP位(AP3、AP2、AP1 和 AP0),每一个对应 1/4 页。
    小页: AP3 选择顶上的 1KB,AP0选择底下的 1KB。大页:AP3 选择顶上的16KB,AP0选择底下的 16KB。被选择的 AP位与节完全一样地解释,(见表3-3),只是产生的是子页错。 

    3.6.2 外部异常
     
    除了 MMU 错,ARM 结构还定义了一个外部异常管脚,可以用来标识访问外部存储器错误。然而不是所有的访问都能用这种方式终止,所以这个脚必须被仔细使用。以下的访问可以被外部终止并安全地重启动:  

        * 读 
        无缓冲的写   

        * 取第一级描述符  

        * 取第二级描述符  

        * 在没有高速缓存和没有缓冲的存储器区域的信号(Semaphore)
     
        行取能够在行传送的任何一个字上安全地终止。如果异常发生在处理器请求的数据上(而不是在 cache line上取的其它数据),访问被终止。任何不是立刻访问的数据传送(在 cache line 上取的其它数据),只在访问它们时产生异常。
     
        由具体实现确定 FAR寄存器是指向 cache line的起始地址还是包含产生遗产的地址。 

        缓冲的写不能被外部终止。所以系统必须配置成不向能够标识外部异常的存储器区域进行缓冲的写,或者用不同的机制去标志异常(例如中断)。 产生异常后,那个存储器空间的值是不确定的。 
     

    3.7 CP15寄存器

    MMU由系统控制寄存器的 2、3、4、5、6、8、10号寄存器和 1号寄存器的一些位控制。
     
    3.7.1 寄存器 1 的 MMU 控制位
     

    寄存器 1 的如下这些位用来控制 MMU:
    M(bit[0])  使能 MMU
        0 = 禁止 MMU
        1 = 允许 MMU
        在没有 MMU和保护单元的系统上,这个位应该读出为0,并忽略写。
    A(bit[1])  使能对齐错检查
        0 = 禁止
        1 = 允许
    S(bit[8])  这是系统保护位,见 3-4节。
    R(bit[9])  这是 ROM 保护位,见 3-4节。 
     
    3.7.2 寄存器 2:转换表基地址 
     

     
    读 CP15寄存器 2时,在 bits[31:14]返回当前活动的第一级转换表的物理地址,bits[13:0]不确定。读 CP15 寄存器 2 时,CRm 和操作数 2 被忽略,并应该是 0。
     
    写 CP15寄存器 2时,在 bits[31:14]更新当前活动的第一级转换表的物理地址,bits[13:0]应该写 0或先前读回的值。写 CP15寄存器 2时,CRm和操作数2被忽略,并应该是 0。

    3.7.3 寄存器 3:域访问控制

    读 CP15寄存器 3时,返回域访问控制寄存器的值。CRm和操作数2被忽略,并应该是 0。
    写 CP15寄存器 3时,更新域访问控制寄存器的值。CRm和操作数2被忽略,并应该是 0。

    域访问控制寄存器包含 16 个 2 位的字段,它定义了对应域的访问权限。见 3-5节。
     
    3.7.4 寄存器 4:保留
     
        读写 CP15 寄存器 4 不可预料结果。
     
    3.7.5 寄存器 5:错误状态  FSR

    读 CP15寄存器 5时,返回 FSR 寄存器的值。FSR 包含最近一次数据错的信息。只有低 9位有效,高 23 位不确定。FSR 指出异常发生时的域和试图访问的类型。
     
    bit[8]  返回 0
    bit[7:4]  指出错位发生时访问的域
    bit[3:0]  试图访问的类型,这些位的编码见表 3-5
     
    FSR在数据错时更新。由具体实现确定取指令异常是否更新 FSR。见 3.6.1 节的“错误地址寄存器(FAR)和错误状态寄存器(FSR)”。CRm和操作数2被忽略,并应该是 0。
     
    写 FSR 将把 FSR设定成写的值。这对于程序调试器非常有用,可以用来恢复FSR的值。高 24 位应该写 0 或上次读到的值。CRm 和操作数 2被忽略,并应该是 0。
     
    3.7.6 寄存器 6:错误地址  FAR

    读 CP15寄存器 6返回 FAR 的值。FAR 保存着错误产生时访问的虚拟地址。在数据错时更新 FAR。由具体实现确定取指令异常是否更新FSR。见3.6.1节的“错误地址寄存器(FAR)和错误状态寄存器(FSR)”。CRm和操作数2被忽略,并应该是 0。
     
    写 FSR 将把 FAR设定成写的值。这对于程序调试器非常有用,可以用来恢复FAR的值。高 24 位应该写 0 或上次读到的值。CRm 和操作数 2被忽略,并应该是 0。
     
    ---注------

    如果使用了第六章描述的快速上下文切换扩展(FCSE),那么:   当存储器错更新 FAR 时,写入 FAR 的是修改的虚拟地址。   当用 MRC指令读或用 MCR指令写FAR时,它的值被当作数据对待,所以没有
    由 FCSE 产生的地址修改。
    ------------ 
     3.7.7 寄存器 8:TLB 功能
     
    当 CP15的寄存器 8 用来控制 TLB 时是只读寄存器。表 3-6显示了定义的 TLB功能和在 MCR 指令中用的 CRm和第二个朝操作数<opcode2>的值。使用没有在表中的CRm和 opcode2 的组合将导致不可预料的结果。
     
    如果下面的任何操作被用在单一 TLB的实现中,则在单一TLB中实现相同的功能:   无效的指令 TLB  (Invalidate instruction TLB)   无效的指令单一入口  (Invalidate instruction single entry)   无效的整个数据 TLB  (Invalidate entire data TLB)   无效的数据单一入口  (Invalidate data single entry)
     
    否则,如果执行一个与特定实现不相关的功能,会导致不确定的结果。
     
    试图用 MRC指令读 CP15 寄存器 8 的结果不确定。
     
    当只有很少量的存储器被重新映射时,无效的单一入口操作能被用来在一些实现中改善性能。对每个被重新映射的存储器区域(节、小页或大页),无效的单一入口需要在存储器区域的虚拟地址上执行。性能的改善来源于不用重新装载与没有被重新映射的存储器区域相关的 TLB 入口。

    ---注------
    无效的单一入口操作的性能改善并不被保证。具体实现可以是单一请求入口无效,
    直到使整个 TLB无效。
    -----------
     
    ---小心------
    当存储器被重新映射时必须使与旧的映射相关的TLB入口无效。如果不这样,可能会进入两个TLB入口覆盖虚拟地址范围的状态。在最好的情况下访问这样的覆盖虚拟地址范围会有不可预料的结果;在某些实现中甚至会物理损坏 MMU。
     
    强烈建议在重新映射存储器时要加倍小心使 TLB 适当地失效。
    ------------

    ---注------
    如果使用了第六章描述的快速上下文切换扩展(FCSE),那么表3-6中的一些功能传递给 CP15 的虚拟地址被当作数据。这意味着对它们来说没有由FCSE产生的地址修改。
    ------------
     
    3.7.8 寄存器 10:TLB 锁定
     
    转换表遍历的执行需要一定的时间,特别当访问慢速的主存储器时。在实时中断处理程序中,当 TLB 不包含中断处理程序的转换和/或要访问的数据时,中断延迟回大量加长。
     
    TLB锁定是一些 ARM存储器系统的特性,它允许把特定的转换表遍历的结果装载到TLB中。这种方式不会被后来的转换表遍历的结果覆盖。由 CP15寄存器10设定。
     
    设 W=LOG2(TLB 入口数),如果需要的话取整(round-up),则 CP15寄存器 10的格式为:

    如果具体的实现有分开的指令和数据 TLB,那么有2个不同的寄存器,由访问寄存器 10 的 MCR或 MRC 指令中的 opcode2 字段选择:
    opcode2 == 0  选择数据 TLB锁定寄存器
    opcode2 == 1  选择指令 TLB锁定寄存器

    如果具体的实现只有唯一的 TLB,那么只有 1 个寄存器,opcode2字段应该为 0。
     
    访问寄存器 10 的 MCR 或 MRC 指令中的 CRm总应该为 0。
     
    写寄存器 10有如下结果:
    victim字段表示下次 TLB失败(miss)时,转换表遍历的结果替代哪个TLB入口。
    Base 字段包含 TLB 替换的策略,只使用从(base)到(TLB入口-1)的 TLB 入口, victim 应该在这个区间。
    转换表遍历的结果在写到 TLB入口时,若 P==1则它被保护起来,不能被寄存器8的使整个 TLB 失效操作影响;若 P==0则会被那些操作给失效掉。
     
    ---注------
    如果 TLB 的入口不是 2 的 N 次方,那么写到大于或等于 TLB入口数的TLB入口的base 或 victim 的值将不确定。
    -----------
     
    读寄存器 10将返回它的值。

    TLB 锁定过程
     
    通常锁定 N个 TLB 入口的过程如下:
     
    1. 禁止中断等,来保证当这个过程执行时不会产生异常
     
    2. 如果一个指令 TLB 或唯一 TLB 被锁定,用 base==N、index==N和P==0写到适当版本的寄存器 10。如果可能,把另指令预取很难理解的分枝预测功能关掉。
     
    3. 使要被锁定的整个 TLB 失效。
     
    4. 如果是指令 TLB 锁定,要确保剩下的锁定过程所要预取的指令相关的 TLB入口都被装载。(要注意锁定是从哪里开始的,通常可能一个TLB入口包含所有这些。这时 TLB被失效后的第一条指令能完成这个功能)
     
       是数据 TLB 锁定,要确保剩下的锁定过程所要访问数据的相关的 TLB入口都被装载。这包含被代码用到的嵌入文字(inline literals)(通常最好避免在锁定过程中使用嵌入文字,并把所有的数据放在由一个 TLB 入口所包含的区域,然后从那里加载一个数据)

       一个唯一 TLB 被锁定,执行以上所有的过程。
     
    5. i 从 0 到 N-1循环
        a. 用 base==i、index==i 和 P==1写到寄存器 10。
        b. 强迫被锁定到 TLB入口i处的转换表遍历结果的存储器区域发生转换
        表遍历:
        * 如果是数据 TLB 或唯一 TLB被锁定,从那个区域加载一个数据
        * 如果是指令 TLB 被锁定,用B5-15页所描述的指令预取高速缓冲寄
        存器 7 来在那个区域产生指令预取。
     
    6. 用 base==N、index==N和 P==0写到寄存器 10。
     
    ---注------
        如果你使用 FCSE,要注意第 5b 步,因为:   如果是数据 TLB 锁定或唯一 TLB 锁定,加载数据指令的地址是会被FCSE修改的。  

       如果是指令 TLB 锁定,用在寄存器 7 的地址被当作数据,所以不会被 FCSE修改。
     
        为了减少混淆,建议锁定过程应该是:   从禁止 FCSE开始(设置 PID 为 0)   把适当的 PID值 OR 到使用的虚拟地址的高 7 位来自己产生修改的虚拟地址。
    -----------

    TLB 解锁过程
     
    用上面的过程解锁被锁定的 TLB 部分:
     
    1. 用寄存器 8 的操作使每个被锁定的单一入口失效
    2. 用 base==0、index==0和 P==0写到寄存器 10。
     
    ---注------
    第一步是为了保证 P==1 的入口在 TLB中不在被剩下。如故它们被剩在TLB中,后续的 TLB 锁定过程中使整个 TLB 失效(第三步)将不会有预期的结果。

    一、MMU的产生

          许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以K为单位进行计算,相应的,当时的程序规模也不大,所以内存容量虽然小,但还是可以容纳当时的程序。但随着图形界面的兴起还用用户需求的不断增大,应用程序的规模也随之膨胀起来,终于一个难题出现在程序员的面前,那就是应用程序太大以至于内存容纳不下该程序,通常解决的办法是把程序分割成许多称为覆盖块(overlay)的片段。覆盖块0首先运行,结束时他将调用另一个覆盖块。虽然覆盖块的交换是由OS完成的,但是必须先由程序员把程序先进行分割,这是一个费时费力的工作,而且相当枯燥。人们必须找到更好的办法从根本上解决这个问题。不久人们找到了一个办法,这就是虚拟存储器(virtual memory).虚拟存储器的基本思想是程序,数据,堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上比如对一个16MB的程序和一个内存只有4MB的机器,OS通过选择,可以决定各个时刻将哪4M的内容保留在内存中,并在需要时在内存和磁盘间交换程序片段,这样就可以把这个16M的程序运行在一个只具有4M内存机器上了。而这个16M的程序在运行前不必由程序员进行分割。

    任何时候,计算机上都存在一个程序能够产生的地址集合,我们称之为地址范围。这个范围的大小由CPU的位数决定,例如一个32位的CPU,它的地址范围是0~0xFFFFFFFF (4G)而对于一个64位的CPU,它的地址范围为0~0xFFFFFFFFFFFFFFFF (64T),这个范围就是我们的程序能够产生的地址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集,这里举一个最简单的例子直观地说明这两者,对于一台内存为256MB的32bit x86主机来说,它的虚拟地址空间范围是0~0xFFFFFFFF(4G),而物理地址空间范围是0x000000000~0x0FFFFFFF(256MB).

    在没有使用虚拟存储器的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储器被读写。而在使用了虚拟存储器的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到内存管理单元——MMU(主角终于出现了)。他由一个或一组芯片组成,一般存在与协处理器中,其功能是把虚拟地址映射为物理地址。

     

    (1)cpu看到的是VA

    (2)caches和MMU使用的是MVA,

    (3)实际物理设设备使用的是PA。

    二、MMU工作过程

          大多数使用虚拟存储器的系统都使用一种称为分页(paging)。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页框(frame).页和页框的大小必须相同。接下来配合图片我以一个例子说明页与页框之间在MMU的调度下是如何进行映射的:

     

                             没图,自己脑补

     

    在这个例子中我们有一台可以生成16位地址的机器,它的虚拟地址范围从0x0000~0xFFFF(64K),而这台机器只有32K的物理地址,因此他可以运行64K的程序,但该程序不能一次性调入内存运行。这台机器必须有一个达到可以存放64K程序的外部存储器(例如磁盘或是FLASH)以保证程序片段在需要时可以被调用。在这个例子中,页的大小为4K,页框大小与页相同(这点是必须保证的,内存和外围存储器之间的传输总是以页为单位的),对应64K的虚拟地址和32K的物理存储器,他们分别包含了16个页和8个页框。

     我们先根据上图解释一下分页后要用到的几个术语,在上面我们已经接触了页和页框,上图中蓝色部分是物理空间,其中每一格表示一个物理页框。橘黄色部分是虚拟空间,每一格表示一个页,它由两部分组成,分别是Frame Index(页框索引)和位p(present 存在位),Frame Index的意义很明显,它指出本页是往哪个物理页框进行映射的,位p的意义则是指出本页的映射是否有效,如上图,当某个页并没有被映射时(或称映射无效,Frame Index部分为X),该位为0,映射有效则该位为1。

    我们执行下面这些指令(本例子的指令不针对任何特定机型,都是伪指令)
    例1:
        MOVE REG,0 //将0号地址的值传递进寄存器REG.
        虚拟地址0将被送往MMU,MMU看到该虚地址落在页0范围内(页0范围是0到4095),从上图我们看到页0所对应(映射)的页框为2(页框2的地址范围是8192到12287),因此MMU将该虚拟地址转化为物理地址8192,并把地址8192送到地址总线上。内存对MMU的映射一无所知,它只看到一个对地址8192的读请求并执行它。MMU从而把0到4096的虚拟地址映射到8192到12287的物理地址。

     例2:

        MOVE REG,8192
        被转换为
        MOVE REG,24576 
        因为虚拟地址8192在页2中,而页2被映射到页框6(物理地址从24576到28671)

      例3:

        MOVE REG,20500
        被转换为
        MOVE REG,12308
        虚拟地址20500在虚页5(虚拟地址范围是20480到24575)距开头20个字节处,虚页5映射到页框3(页框3的地址范围是 12288到16383),于是被映射到物理地址12288+20=12308。

     例4:

    MOV REG,32780

          虚拟地址32780落在页8的范围内,从上图总我们看到页8没有被有效的进行映射(该页被打上X),这是又会发生什么?MMU注意到这个页没有被映射,于是通知CPU发生一个缺页故障(page fault).这种情况下操作系统必须处理这个页故障,它必须从8个物理页框中找到1个当前很少被使用的页框并把该页框的内容写入外围存储器(这个动作被称为page copy),随后把需要引用的页(例4中是页8)映射到刚才释放的页框中(这个动作称为修改映射关系),然后从新执行产生故障的指令(MOV REG,32780)。假设操作系统决定释放页框1,那么它将把虚页8装入物理地址的4-8K,并做两处修改:首先把标记虚页1未被映射(原来虚页1是被影射到页框1的),以使以后任何对虚拟地址4K到8K的访问都引起页故障而使操作系统做出适当的动作(这个动作正是我们现在在讨论的),其次他把虚页8对应的页框号由X变为1,因此重新执行MOV REG,32780时,MMU将把32780映射为4108。

      

    我们已经知道,大多数使用虚拟存储器的系统都使用一种称为分页(paging)的技术,就象我们刚才所举的例子,虚拟地址空间被分成大小相同的一组页,每个页有一个用来标示它的页号(这个页号一般是它在该组中的索引,这点和C/C++中的数组相似)。在上面的例子中0~4K的页号为0,4~8K的页号为1,8~12K的页号为2,以此类推。而虚拟地址(注意:是一个确定的地址,不是一个空间)被MMU分为2个部分,第一部分是页号索引(page Index),第二部分则是相对该页首地址的偏移量(offset). 。我们还是以刚才那个16位机器结合下图进行一个实例说明,该实例中,虚拟地址8196被送进MMU,MMU把它映射成物理地址。16位的CPU总共能产生的地址范围是0~64K,按每页4K的大小计算,该空间必须被分成16个页。而我们的虚拟地址第一部分所能够表达的范围也必须等于16(这样才能索引到该页组中的每一个页),也就是说这个部分至少需要4个bit。一个页的大小是4K(4096),也就是说偏移部分必须使用12个bit来表示(2^12=4096,这样才能访问到一个页中的所有地址),8196的二进制码如下图所示:

      

    该地址的页号索引为0010(二进制码),既索引的页为页2,第二部分为000000000100(二进制),偏移量为4。页2中的页框号为6(页2映射在页框6,见上图),我们看到页框6的物理地址是24~28K。于是MMU计算出虚拟地址8196应该被映射成物理地址24580(页框首地址+偏移量=24576+4=24580)。同样的,若我们对虚拟地址1026进行读取,1026的二进制码为0000010000000010,page index="0000"=0,offset=010000000010=1026。页号为0,该页映射的页框号为2,页框2的物理地址范围是8192~12287,故MMU将虚拟地址1026映射为物理地址9218(页框首地址+偏移量=8192+1026=9218)。以上就是MMU的工作过程。

  • 相关阅读:
    基于NodeJS的全栈式开发
    Android 反编译apk 详解
    AngularJS 中文资料+工具+库+Demo 大搜集
    Mongodb集群搭建的三种方式
    Ubuntu下解决bash 没有那个文件或目录的方法
    ubuntu12.04 安装配置jdk1.7
    CentOS怎样查看系统信息
    Ubuntu 安装 Redis
    Redis快速入门
    js去掉双引号
  • 原文地址:https://www.cnblogs.com/still-smile/p/12155294.html
Copyright © 2020-2023  润新知