MMU

一、MMU学习

  MMU其实就是一个页表。将虚拟地址通过查表的方式,对应到物理地址去他由一个或一组芯片组成,一般存在与协处理器中。

  1.将虚拟地址转化为物理地址

  2.访问权限管理

1.1得出mmu功能

  这个图就说明了MMU的作用:

  有三个任务在运行,运行的地址都是0x400000,如果不进行处理,肯定是不行的。所以加入了MMU,MMU其实就是一个页表。将虚拟地址通过查表的方式,对应到物理地址去。虽然三个任务的运行地址都是0x400000,但是这个地址是虚拟地址,在页表中,将每个任务的虚拟地址对应到不同的物理地址,这样,就实现了各个任务虽然是同一个虚拟地址,但是在不同的物理地址中运行。

1.2映射方式(ARM920T_TRM1_S    57页)

  映射方式:映射方式有两种,段映射和页映射。段映射只用到一级页表,页映射用到一级页表和二级页表。

  映射粒度:段映射的映射粒度有两种,1M section和16M supersection;页映射的映射粒度也有两种,4K small page和64K large page。

1.3页表

  MMU要自动进行虚拟地址到物理地址的转化,首先要找到一级页表,而一级页表的基地址(TTB:translation table base)则是保存在CP15的C2寄存器中。因此,当程序员创建好相应的页表后,需要将页表基地址写入该寄存器。

MMU有一级转换和二级转换,其实就是查几次表。对于一级转换,就是只查一次页表,就确定了物理地址。这个在S3C6410中,方式是段映射。对于二级转换,就要查两次页表或者三次,才能确定物理地址,这个在S3C6410中。有两种方式,一种细页方式,一种粗页方式。这两种的区别就是每一页对应的大小不一样而已。

  通过虚拟地址的[31:20],找到对应的转换描述符,这个转换描述符其实就是一个存在ram中的32位的一个数据。通过判断这个描述符的最后两位,判断是什么转换。

1.3.1一级页表描述符

  一级页表描述符后两位如果是10则表示为段转换。

  如果是段转换,就按照段转换的格式去转化物理地址

  如果是页转换,就在去找第二级的页表,然后再按照二级页表的格式去转化物理地址。

  B:表示是否使能write buffer(使用buffer可以提高cpu运行效率-à当CPU访问内存数据时但是内存没准备此时cpu可以将数据暂时存放于buffer)

  C: 表示是否开启cache(cahce用来保存cpu可能要经常访问内存区域的数据,以便于快速访问)

 XN:

  Domain:用来说明该段属于哪一个域,ARM11共有16个域(由CP15的C3来设置),每个域可以设定不同的权限。将段分配到某个域,就使这段的权限和设定的域权限一样。   (手册arm11 P195)

  P:表示段区间有ECC,ARM11不支持,所以这一位为0.

  AP:访问权限,这个配合域,说明该段地址的访问权限。(手册arm11 P329)

  APX: 提供额外的访问权限。

 S:表示区域是否共享。0不共享,1共享。这个手册上说,是为了多核使用的。目前用不到,设置为0.

   nG:

  Section base address:这个就是段的物理地址的高12位。

1.4段转换

  ARM11是32位的,对于段转换,每一个段代表物理的1MB地址,一级页表总共有4096个转换符,所以够表示的空间是4G。页表的首地址就是TTB,这个地址是很重要的,因为有了这个地址才能知道页表是存在什么地方的,才能通过偏移找到对应的描述符。所以,是有一个寄存器来保存这个地址的,这个寄存器就在CP15的c2寄存器。

  通过虚拟地址的高12位偏移,找到页表中的对应描述符,判断描述符的最后两位,确定是什么转换。10的话就表示是段转换。

  判断是段转换后,将描述符的高12位取出,这个就是物理的基地址,在和虚拟地址的后20位拼接,就得到对应的物理地址。

  对于段转换,每一个段代表物理的1MB地址。为什么是1MB了。因为总共是有4096个描述符,而ARM11是32位的,所以能够表示的空间是4G。

以段的方式进行映射时,虚拟地址MVA到物理地址PA的转换过程如下:

  ①页表基址寄存器位[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU利用这个地址找到描述符

  ②取出段描述符的位[31:20](段基址),它和MVA[19:0]组成一个32位的物理地址(这就是MVA对应的PA)

1.5页转换(ARM11  360页)

  二级转换

1.6程序设计

1.6.1建立页表

  使用段映射的方式,那么就要将段映射的描述符写入到相应的内存中去。

  首先将页表放在ddr的第一个位置,也就是0x50000000。所以需要将这个地址写入到TTB中。

  其次,开启MMU后,使用的就是虚拟地址了,所以还要建立程序段的页表,不然一旦开启MMU,CPU去取指令,这个时候发出的就是虚拟地址,但是又没有建立虚拟地址到物理地址的映射表。这样的话,就取不到指令了,程序就崩溃了。所以还需要对代码区建立映射页表。代码区的映射就是将虚拟地址直接转换为物理地址使用。

  将这个地址给映射到0x7F000000区域去。

  unsigned long *ttb = (unsigned long *)0x50000000;//获取基地址

  unsigned long vaddr, paddr;

  vaddr = 0xA0000000;

  paddr = 0x7f000000;

  *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC;

  //获得一级描述符的地址     并且赋值

  首先申明一个ttb指针,这个指针就指向一级表的基地址,也就是ddr的首地址,0x50000000。然后再申明两个变量,一个表示虚拟地址,一个表示物理地址。

  根据前面的一级段描述符格式,往对应的内存单元写入数据即可。段描述符的高12位是物理地址的高12位,所以首先有一个物理地址和0xFFF00000与的操作,将物理地址的高12位写入到段描述符。

  这里使用*(ttb + (vaddr >> 20))来对对应的页描述符进行赋值。在一级页表中,是通过虚拟地址的高12位来决定偏移是多少。高12位也就是位移20位,所以这里就是加上虚拟地址右移20位的值,也就是取出高12位的值。注意,这里是指针操作,假设这个时候虚拟地址的高12位值是5,那么写的地址不是0x50000005,而是0x50000014。因为指针的数据类型是long型,4个字节,所以加1,地址就会加4,加5,地址就会加5*4=20。

  后面位的配置:

  设置为访问权限是11,也就是不进行访问权限的检查。将域设置为0。CACHE和write BUFFER都给关掉。因为这只是对一个寄存器的操作,没有必要使用CACHE和write BUFFER。

  这样,就建立好了GPIO的页表。访问虚拟地址0xc7000000,也就是访问物理地址0x7f000000。整个段空间是1Mb。

1.6.2建立代码区的页表

  这个时候,代码已经是在内存中运行的了。所以,直接将内存的所有地址空间进行映射,空间是64MB大小。

  

  使用的宏

  

  其实就是多一个开启CACHE和WRITE BUFFER。

  内存映射是映射一片区域,从0x50000000地址开始到0x54000000这64M的虚拟地址给映射到0x50000000地址开始到0x54000000这64M物理地址上去。这样,即使使用虚拟地址去取ddr中数据,也是能够取到的。

  因为一个段是映射的1MB空间,所以总共需要64个段,十六进制也就是0x40。所以在while里面判断是虚拟地址要小于0x54000000。再者,每一个段表示1MB空间,所以虚拟地址和物理地址都要加1MB。也就是0x100000。

  这样,就将页表给建立好了。如果还需要使用其他地址区域,就都得为这些区域建立映射表。

1.6.3开启mmu

  TTB是很重要的,保存的是一级页表的首地址。首先就应该先要设置这个TTB。往CP15的C2寄存器里面写入这个值就好了,因为这个寄存器就是保存的TTB。

  然后是设置域的权限,将域的权限设置为11,不进行访问权限检查。设置CP15的c3寄存器。

  最后打开MMU。设置CP15的c1寄存器组的c0。是对CP15进行操作,用C语言是实现不了的,使用嵌入汇编。

  

  就设置好了MMU,并打开了MMU。之后使用的地址都是虚拟地址了。而且该虚拟地址必须是要在页表中建立的,不然的话,就会产生异常。

ok6410,mmu,内存管理的更多相关文章

  1. MMU内存管理单元

    arm-linux学习-(MMU内存管理单元) 什么是MMU MMU(Memory Management Unit)主要用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及 ...

  2. [转帖]MMU内存管理单元

    MMU内存管理单元 https://www.cnblogs.com/alantu2018/p/9002309.html 之前对这一块一直不理解 最近学习了点 CPU time slice 以及 con ...

  3. MMU内存管理单元相关知识点总结

    1.MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件 ...

  4. 02-JZ2440裸机学习之MMU内存管理单元【转】

    本文转载自:https://blog.csdn.net/fengyuwuzu0519/article/details/66479248 1.MMU定义: MMU是Memory Management U ...

  5. linux内核(四)内存管理单元MMU

    1,基本概念 一个程序运行时没必要全部都同时装入内存,只需要把当前需要运行的部分装入内存即可,这样就使得一个大程序可以在较小的内存中运行,也使得内存中可以同时装入更多的程序并发执行,从用户角度看,该系 ...

  6. linux内存管理初始化

    内存管理子系统是linux内核最核心最重要的一部分,内核的其他部分都需要在内存管理子系统的基础上运行.而对其初始化是了解整个内存管理子系统的基础.对相关数据结构的初始化是从全局启动例程start_ke ...

  7. 启动期间的内存管理之初始化过程概述----Linux内存管理(九)

    在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检 ...

  8. arm-linux内存管理学习笔记(1)-内存页表的硬件原理

    linux kernel集中了世界顶尖程序猿们的编程智慧,犹记操作系统课上老师讲操作系统的四大功能:进程调度 内存管理 设备驱动 网络.从事嵌入式软件开发工作,对设备驱动和网络接触的比較多. 而进程调 ...

  9. 我理解的Linux内存管理

    众所周知,内存管理是Linux内核中最基础,也是相当重要的部分.理解相关原理,不管是对内存的理解,还是对大家写用户态代码都很有帮助.很多书上.很多文章都写了相关内容,但个人总觉得内容太复杂,不是太容易 ...

随机推荐

  1. Wince常见操作

    1.获取本地代码启动路径 //在代码启动路径下存在 Files 文件夹 Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName(). ...

  2. liunux mysql MySQL表名不区分大小写的设置方法

    原来Linux下的MySQL默认是区分表名大小写的,通过如下设置,可以让MySQL不区分表名大小写:1.用root登录,修改 /etc/my.cnf:2.在[mysqld]节点下,加入一行: lowe ...

  3. C++ 中超类化和子类化常用API

    在windows平台上,使用C++实现子类化和超类化常用的API并不多,由于这些API函数的详解和使用方法,网上一大把.本文仅作为笔记,简单的记录一下. 子类化:SetWindowLong,GetWi ...

  4. JS中数组Array的用法示例介绍 (转)

    new Array() new Array(len) new Array([item0,[item1,[item2,...]]] 使用数组对象的方法: var objArray=new Array() ...

  5. C++与C#对比学习:类初始化

    类和柏拉图的理念世界 我们知道面向对象编程中到处是一个个的类,但类只是个概念性的东西,不是个实体,不占内存,你没实例化之前也不能用它.只有把类实例化成一个对象后,它才是一个真正存在的实体.占有内存,能 ...

  6. js判断是否为ie6以外的浏览器,若是,则调用相应脚本

    if(navigator.userAgent.indexOf("MSIE 6.0") < 0) { //相应JavaScript脚本}

  7. 2.1顺序容器-vector

    vector 1) *   :使用vector必须包含vector头文件.可变长的动态数组,支持随机访问,所有STL算法都可以对vector进行操作. ** :随机根据下标访问某个元素的时间是一个常数 ...

  8. mysqli的增强功能

    批量执行sql语句 批量执行dml语句 基本语法 $sqls="sql1.sql2.sql3...." mysqli::multi_query($sqls) 案例: $mysqli ...

  9. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL

    服务端: 最终项目结构: 这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据. Step 1:创建CustomData类 package com.ldb.android.e ...

  10. Android Tab -- 使用Fragment、FragmentManager来实现

    原文地址:http://blog.csdn.net/crazy1235/article/details/42678877 效果: 代码:https://github.com/ldb-github/La ...