小论嵌入设备开发中可能用到内存映射方法

                                                                                                                                         ―― 康华

      本文将以我前期开发的项目为蓝本,就开发中遇到的有关内存映射的问题进行一些总结。希望能帮助新接触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_0000A000_0000;系统另外还有Framer registerCPLD  registerMpc8560 Register三块寄存器空间(其它空间我们这里不用考虑)。 实际上我们板卡的物理内存为512M

   我们的应用对系统有如下要求(为了能更集中说明问题,所列需求和实际系统配置有所出入):

1.    系统需要有保留208MPCI内存空间用于导入其它板卡的内存映射(因为要导入13块其它板卡的各16M内存,也就是它需要能访问到其它13个板卡)。

2.    系统本身需要留出16M的连续内存用于导出到其它系统中。

3.    为了和已有系统兼容,新系统需要留出虚拟地址C8000000-C8800000存储配置信息,我们这里称为CDB(configure database)CBD需要能被应用进程访问。

4.    系统要能操作各种寄存器。

好了,在这些束缚下我们该如何布置我们的内存映射呢?首先我们知道Linux系统中默认情况下内核空间占据高1G(POWERPC中有时占据高2G)的逻辑空间,用户空间占据剩下的逻辑空间(0000_00000C000_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 registerCPLD  registerMpc8560 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

注意,上图中的内核虚拟空间(非对应连续物理地址的内核空间,详解请看内核驱动开发一书)存在打散的部分,这些部分中会容纳我们的208MPCI内存空间 16M的导出地址空间,以及各种寄存器空间等等。但具体的映射地址则由内核在运行时确定。

 

上面的例子初读可能并非显而易见,不明白的地方请多参考资料下载中给出的内核教材,并结合内核源代码进行分析。搞清各种内存映射关系是开发嵌入系统必不可少的功课,希望本文能帮助大家学习。