小论嵌入设备开发中可能用到内存映射方法
康华 :主要从事 Linux 操作系统内核、虚拟机、Linux 技术标准、计算机安全、软件测试等领域的研究与开发工作,曾就职 MII-HP 软件实验室 、瞬联软件公司/MOTOROLA,现就职于Lenovo研究院 。其所合写的Linux专栏见http://www.csdn.net/subject/linux/。 如果需要可以联系通过 kanghua151@msn.com (MSN)联系我.
本文将以我前期开发的项目为蓝本,就开发中遇到的有关内存映射的问题进行一些总结。希望能帮助新接触Linux内存映射的朋友梳理一下思路。
首先我大致描述一下我们所设计的板卡特性。我们使用的板卡之一是MPC8560,它是一个POWERPC系列的双核(E500 和 CPM)板卡,我们用它来做通讯系统。该板卡的物理地址空间布局如下所示:
Processor Memory Map of MPC8560
0000_0000 7FFF_FFFF <2G System Memory (onboard DRAM)
8000_0000 A000_0000 512M PCI Memory Space
A000_0000 A100_0000 16M PCI I/O Space
E000_0000 E400_0000 64M Solider FLASH
E800 0000 E800 1000 4k Framer register
E800 1000 E810 0000 512K CPLD register
E880 0000 E900 0000 512K Socket
FDF0 0000 FE00 0000 1M Mpc8560 Register
FFF0 0000 FFFF FFFF 1M Book Bank
从上图可以看到,系统物理物理范围不得大于2G,默认空间从0000_0000开始;系统的PCI内存空间大小为512M,默认空间从8000_0000到A000_0000;系统另外还有Framer register,CPLD register和Mpc8560 Register三块寄存器空间(其它空间我们这里不用考虑)。 实际上我们板卡的物理内存为512M。
我们的应用对系统有如下要求(为了能更集中说明问题,所列需求和实际系统配置有所出入):
1.
系统需要有保留208M的PCI内存空间用于导入其它板卡的内存映射(因为要导入13块其它板卡的各16M内存,也就是它需要能访问到其它13个板卡)。
2.
系统本身需要留出16M的连续内存用于导出到其它系统中。
3.
为了和已有系统兼容,新系统需要留出虚拟地址C8000000-C8800000存储配置信息,我们这里称为CDB(configure database)。CBD需要能被应用进程访问。
4.
系统要能操作各种寄存器。
好了,在这些束缚下我们该如何布置我们的内存映射呢?首先我们知道Linux系统中默认情况下内核空间占据高1G(POWERPC中有时占据高2G)的逻辑空间,用户空间占据剩下的逻辑空间(0000_00000到C000_0000 或D000_0000)。而上面第2点需要却要求我们必须预留出空间C8000000-C8800000给用户程序访问,因此最直接有效的方法是,我们需要将内核空间上移动到D0000000处(POWERPC体系映射采用段寄存器,系统共分16个段,每段映射256M,因此映射边界只能依10000000跨度划分)。
然而新问题又出现了。当内核空间缩小到512M后,我们发现要想满足第1个条件,既留出208M的空间,以及剩下的几个小块寄存器地址空间,那么就无法将512M的物理内存全部映射到内核空间中。怎么办了? 解决办法就是将一半物理内存(256M)作为高端内存(而传统上系统只把高于896M以上的内存作为高端内存的),避免启动时便静态映射于内核逻辑空间。这是因为高端内存只能被动态分配且现场映射,不会启动时不经分配就直接映射到内核空间。不过这并不是说我们系统一半的内存不可用,因为系统在需要时仍然可以指明在高端内存分配可用内存,然后建立内存映射,从而使用它(如:
_ _get_free_pages(GFP_HIGHMEM,X)
,
kmap( )
)。
注释:修改内核地址空间和高端内存空间需要修改
TASK_SIZE, KERNEL_START,
和
LOWMEM_SIZE
等值。
另外,我们为了满足第2个条件,即将16M连续物理内存空间保留下来导出。若要分配连续物理内存空间尽可以使用
kmalloc来分配,不过现在的内核还无法分配大到16M的连续空间,因此我们需要采用预留内存的方法——留出一段内存不让Linux内核看到(也就是说内核不管理这段区间,不过可要搞清楚,这段内存和高端内存不同,它并未处于高端内存),具体做法是在启动时
通过启动参数“
mem=240M”来通知内核其只管理240M的物理内存。然后在使用前再将这部分内存映射到内核空间(如,ioremap (0xF000000 /* 240M */, 0x1000000 /* 16M */);
最后为了能在内核空间访问Framer register、CPLD register和Mpc8560 Register寄存器 我们必须也将其映射到内核空间,方法很简单就是用下面语句:
request_mem_region(CPLD_ADDRESS, CPLD_SIZE, "CPLD");
CPLD_MAPPED_ADDR = (UBYTE *)ioremap(CPLD_ADDRESS,CPLD_SIZE);
记住上面所的要求和解释,我们下面给出系统的物理内存布局和虚拟地址空间布局。
MPC8560
物理内存布局
Kernel (include drivers) 00000000~000480000 <4M
粗略估计值
Initrd Image 00480000~00780000 <3M 粗略估计值
-------------------------------------------- 0x00780000
可给应用程序动态分配的物理内存
――――――――――――――――――――――
Pci buffer 0F000000-10000000 16M
-------------------------------------------- 0x10000000 256M
以上为高端内存
只能现场分配且映射才可被使用
-------------------------------------------- 0x20000000 512M
Linux系统运行后,其虚拟地址空间布局如下:
-------------------------------------------------- 0x00000000
Application
Space
Libraries map
Space
---------------------------------------------------0xC8000000
CDB Map Space
0xC8000000-0xC8800000 8M
Stack
0xCE000000~0xCFFFFFFF 16M
---------------------------------------------------0xD0000000
Kernel Space Begin
Physical memory
maps(Kernel logical space) 256M
---------------------------------------------------0xE0000000
Kernel virtual space
256M
---------------------------------------------------0xFFFFFFFF
注意,上图中的内核虚拟空间(非对应连续物理地址的内核空间,详解请看内核驱动开发一书)存在打散的部分,这些部分中会容纳我们的208M
的PCI
内存空间
,16M
的导出地址空间,以及各种寄存器空间
等等。但具体的映射地址则由内核在运行时确定。
上面的例子初读可能并非显而易见,不明白的地方请多参考资料下载中给出的内核教材,并结合内核源代码进行分析。搞清各种内存映射关系是开发嵌入系统必不可少的功课,希望本文能帮助大家学习。