ok6410,mmu,内存管理
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,内存管理的更多相关文章
- MMU内存管理单元
arm-linux学习-(MMU内存管理单元) 什么是MMU MMU(Memory Management Unit)主要用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及 ...
- [转帖]MMU内存管理单元
MMU内存管理单元 https://www.cnblogs.com/alantu2018/p/9002309.html 之前对这一块一直不理解 最近学习了点 CPU time slice 以及 con ...
- MMU内存管理单元相关知识点总结
1.MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件 ...
- 02-JZ2440裸机学习之MMU内存管理单元【转】
本文转载自:https://blog.csdn.net/fengyuwuzu0519/article/details/66479248 1.MMU定义: MMU是Memory Management U ...
- linux内核(四)内存管理单元MMU
1,基本概念 一个程序运行时没必要全部都同时装入内存,只需要把当前需要运行的部分装入内存即可,这样就使得一个大程序可以在较小的内存中运行,也使得内存中可以同时装入更多的程序并发执行,从用户角度看,该系 ...
- linux内存管理初始化
内存管理子系统是linux内核最核心最重要的一部分,内核的其他部分都需要在内存管理子系统的基础上运行.而对其初始化是了解整个内存管理子系统的基础.对相关数据结构的初始化是从全局启动例程start_ke ...
- 启动期间的内存管理之初始化过程概述----Linux内存管理(九)
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检 ...
- arm-linux内存管理学习笔记(1)-内存页表的硬件原理
linux kernel集中了世界顶尖程序猿们的编程智慧,犹记操作系统课上老师讲操作系统的四大功能:进程调度 内存管理 设备驱动 网络.从事嵌入式软件开发工作,对设备驱动和网络接触的比較多. 而进程调 ...
- 我理解的Linux内存管理
众所周知,内存管理是Linux内核中最基础,也是相当重要的部分.理解相关原理,不管是对内存的理解,还是对大家写用户态代码都很有帮助.很多书上.很多文章都写了相关内容,但个人总觉得内容太复杂,不是太容易 ...
随机推荐
- Wince常见操作
1.获取本地代码启动路径 //在代码启动路径下存在 Files 文件夹 Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName(). ...
- liunux mysql MySQL表名不区分大小写的设置方法
原来Linux下的MySQL默认是区分表名大小写的,通过如下设置,可以让MySQL不区分表名大小写:1.用root登录,修改 /etc/my.cnf:2.在[mysqld]节点下,加入一行: lowe ...
- C++ 中超类化和子类化常用API
在windows平台上,使用C++实现子类化和超类化常用的API并不多,由于这些API函数的详解和使用方法,网上一大把.本文仅作为笔记,简单的记录一下. 子类化:SetWindowLong,GetWi ...
- JS中数组Array的用法示例介绍 (转)
new Array() new Array(len) new Array([item0,[item1,[item2,...]]] 使用数组对象的方法: var objArray=new Array() ...
- C++与C#对比学习:类初始化
类和柏拉图的理念世界 我们知道面向对象编程中到处是一个个的类,但类只是个概念性的东西,不是个实体,不占内存,你没实例化之前也不能用它.只有把类实例化成一个对象后,它才是一个真正存在的实体.占有内存,能 ...
- js判断是否为ie6以外的浏览器,若是,则调用相应脚本
if(navigator.userAgent.indexOf("MSIE 6.0") < 0) { //相应JavaScript脚本}
- 2.1顺序容器-vector
vector 1) * :使用vector必须包含vector头文件.可变长的动态数组,支持随机访问,所有STL算法都可以对vector进行操作. ** :随机根据下标访问某个元素的时间是一个常数 ...
- mysqli的增强功能
批量执行sql语句 批量执行dml语句 基本语法 $sqls="sql1.sql2.sql3...." mysqli::multi_query($sqls) 案例: $mysqli ...
- Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL
服务端: 最终项目结构: 这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据. Step 1:创建CustomData类 package com.ldb.android.e ...
- Android Tab -- 使用Fragment、FragmentManager来实现
原文地址:http://blog.csdn.net/crazy1235/article/details/42678877 效果: 代码:https://github.com/ldb-github/La ...