学习uboot前奏之hardware-mmu[s3c2440]

接着上一节的SDRAM,这一节我们来学习下MMU。

内存管理单元(Memory Management Unit)简称MMU,主要负责虚拟地址到物理地址的映射和权限检查。本文主要讲物理地址映射过程

S3C2440的MMU提供如下特性:

  • 4种映射长度:段(1MB)、大页(64kB)、小页(4kB)、极小页(1kB)
  • 可无效整个TLB
  • 可以单独无效某一个TLB条目
    等特性

在讲具体的地址映射过程过程之前,有必要说明三种地址的概念:

  • VA(Virtual address)虚拟地址,这个地址是CPU启动MMU后CPU看到的地址
  • MVA(Modified Virtual address)变化后虚拟地址,这个地址是提供给MMU和CACHE使用的
  • PA(Physical Address)物理地址,对应最终发生读写的地址

MVA是除CPU核外的其他部分看见的虚拟地址,VA与MVA之间的变换关系如下:

如果VA<32M,需要使用进程标识PID(通过读CP15的C13获得)来装换为MVA,当然这个由硬件自动完成
if (VA < 32M) then
    MVA = VA | (PID << 25)
else
    MVA = VA

虚拟地址到物理地址的转换


一般情况下有两种方式:用一个确定的数学公式进行转换或者用表格的形式,这类表格就被称为页表,页表由一个个条目(描述符)组成,每一个条目存储了一段虚拟地址对应的物理地址以及访问权限,或者下一级页表的地址。

在ARM CPU使用第二种方法,在S3C2440中,最多会用到两级页表。以段(section)的方式映射只要用到一级页表,以页的方式映射的话则需要用到两级页表,下面大致描述下转换过程:

  1. 根据给定的虚拟地址找到一级页表中的条目
  2. 如果此条目是段描述符,则返回物理地址,转换结束
  3. 否则如果此条目是二级页表描述符,继续利用虚拟地址在此二级页表中寻找下一个条目
  4. 如果这第二个条目是页描述符,则返回物理地址,转换结束


上图描述的是段映射方式地址转换的过程,TTB base代表一级页表的地址,它存储在协处理器CP15的寄存器C2(页表基址寄存器),一级页表的地址必须是16K对应的。
32位CPU的虚拟地址空间为4GB,一级页表中使用了4096个描述符,每个描述符对应1MB的虚拟地址。描述符中要么存储了它对应的物理地址,要么存储了下一级页表的页表基地址。

本文主要讲述段映射方式,当以段的方式进行映射,虚拟地址MVA到物理地址PA的转换过程如下:

  1. 页表基址寄存器[31:14]和MVA[31:20]组成一个低两位位0的32位地址,MMU利用这个地址找到段描述符
  2. 取出段描述符的位[31:20]—段基址,它和MVA[19:0]组成一个32位的物理地址—这就是MVA对应的PA

这里写图片描述

下面简单描述下MMU的权限检查功能:指一块内存是否允许读,是否允许写。具体由CP15寄存器C3(域访问寄存器)、描述符的域、CP15寄存器C1的R/S/A位以及描述符的AP位联合起作用。
CP15寄存器C1中A位表示是否需要对地址进行对齐检查

内存的访问权限检查可以概括位一下两点:

  1. “域”决定是否需要对某块内存进行权限检查
  2. “AP”绝对如何对某块内存做权限检查
    CP15寄存器C3中共32位,每两位分为一个域,所以共16个域
    domain
    每个域的两位有四种可能的值分别代表:
    这里写图片描述

AP和R/S的组合决定如果对地址进行权限检查
这里写图片描述

段描述符的格式如下:
这里写片描述
下面将以具体的代码来讲解下MMU是如何实现地址映射的,首先分析下链接脚本:

SECTIONS { 
  firtst    0x00000000 : { head.o init.o }
  second    0xB0004000 : AT(2048) { leds.o }
} 
 可以看出head.o init.o 加载地址和运行的虚拟地址都是0,leds的加载地址是2048处,运行地  
 址为0xB0004000,所以必须将虚拟地址OxB0004000映射到相应的物理地址才能正常运行。
Head.s的片段如下
_start:
    ldr sp, =4096                       @ 设置栈指针,以下都是C函数,调用前需要设好栈
    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    bl  memsetup                        @ 设置存储控制器以使用SDRAM
    bl  copy_2th_to_sdram               @ 将第二部分代码复制到SDRAM
    bl  create_page_table               @ 设置页表
    bl  mmu_init                        @ 启动MMU
    ldr sp, =0xB4000000                 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)
    ldr pc, =0xB0004000                 @ 跳到SDRAM中继续执行第二部分代码
其中
/*
 * 将第二部分代码复制到SDRAM
 */
void copy_2th_to_sdram(void)
{
    unsigned int *pdwSrc  = (unsigned int *)2048;
    unsigned int *pdwDest = (unsigned int *)0x30004000;

    while (pdwSrc < (unsigned int *)4096)/*拷贝4K字节*/
    {
        *pdwDest = *pdwSrc;
        pdwDest++;
        pdwSrc++;
    }
}
在MMU开启之前程序运行在实际的物理地址,copy_2th_to_sdram将物理地址偏移2048处开始的4k  
字节拷贝到0x30004000,我们知道s3c2440 sdram物理地址是从0x30000000开始的。  

从0x30000000开始到0x3000400016K字节,用于段映射的页表,每条段描述符长度为4个字节代  
表物理1M空间,16K字节共4096条记录,刚好对应32位处理器的4G地址虚拟地址空间。

下面重点说下,创建段页表描述符条目的过程

/*
 * 设置页表
 */
void create_page_table(void)
{

/* 
 * 用于段描述符的一些宏定义
 */ 
#define MMU_FULL_ACCESS     (3 << 10)   /* 访问权限 */
#define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 */
#define MMU_SPECIAL         (1 << 4)    /* 必须是1 */
#define MMU_CACHEABLE       (1 << 3)    /* cacheable */
#define MMU_BUFFERABLE      (1 << 2)    /* bufferable */
#define MMU_SECTION         (2)         /* 表示这是段描述符 */
#define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
                             MMU_SECTION)
#define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
                             MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
#define MMU_SECTION_SIZE    0x00100000

    unsigned long virtuladdr, physicaladdr;
    unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;

    /*
     * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
     * 为了在开启MMU后仍能运行第一部分的程序,
     * 将0~1M的虚拟地址映射到同样的物理地址
     */
    virtuladdr = 0;
    physicaladdr = 0;
    *(mmu_tlb_base + (virtuladdr >> 20)) = \
    (physicaladdr & 0xFFF00000) |  MMU_SECDESC_WB;

    /*
     * 0x56000000是GPIO寄存器的起始物理地址,
     * GPBCON和GPBDAT这两个寄存器的物理地址0x56000050、0x56000054,
     * 为了在第二部分程序中能以地址0xA0000050、0xA0000054来操作GPFCON、GPFDAT,
     * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
     */
    virtuladdr = 0xA0000000;
    physicaladdr = 0x56000000;
    *(mmu_tlb_base + (virtuladdr >> 20)) = \
    (physicaladdr & 0xFFF00000) | MMU_SECDESC;

    /*
     * SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
     * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
     * 总共64M,涉及64个段描述符
     */
    virtuladdr = 0xB0000000;
    physicaladdr = 0x30000000;
    while (virtuladdr < 0xB4000000)
    {
        *(mmu_tlb_base + (virtuladdr >> 20)) = \
        (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
        virtuladdr += 0x100000;
        physicaladdr += 0x100000;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值