2019-07-29 17:53:10 ztp123456 阅读数 335

1、内核启动参数修改mem
mem为分配给linux的可管理内存大小
setenv bootargs 'mem=42M

2、修改MPP加载时的osmem大小
osmem为配置linux内核内存大小。

./load3518ev300 -i -sensor imx307_2l -osmem 42M -board demo


具体配置在load3518ev300中,程序会

    #DDR start:0x40000000, kernel start:0x40000000,  OS(32M); MMZ start:0x42000000
    mem_total=64                      # 64M, total mem
    mem_start=0x40000000              # phy mem start
    os_mem_size=32                    # 32M, os mem
    mmz_start=0x41D00000;             # mmz start addr
    mmz_size=32M;                     # 32M, mmz size

2012-03-28 00:05:35 kevin1078 阅读数 5659

在内核中, kmalloc能够分配的最大连续内存为2的(MAX_ORDER-1)次方个page(参见alloc_pages函数,     "if (unlikely(order >= MAX_ORDER))        return NULL;"), page的大小一般是4K bytes, MAX_ORDER缺省定义为11, 所以如果不修改内核, kmalloc能够分配的最大连续内存一般是4M bytes.

内核中获取4M以上大内存的方法有三种:

1.修改MAX_ORDER, 重新编译内核

2.内核启动选型传递"mem="参数, 如"mem=80M", 预留部分内存; 然后通过request_mem_region和ioremap_nocache将预留的内存映射到模块中. 需要修改内核启动参数, 无需重新编译内核. 但这种方法不支持x86架构, 只支持ARM, PowerPC等非x86架构.

3.在start_kernelmem_init函数之前调用alloc_boot_mem函数预分配大块内存, 需要重新编译内核.


在不重新编译内核的前提下, x86架构下内核中只能获取到最大4M的连续内存, 或者使用vmalloc获取4M以上的非连续内存. 而且, 无论是kmalloc还是vmalloc, 分配的内存越大, 失败的可能性越大; 系统启动后分配内存的时间越早(此时空闲内存越多, 分部也越规律), 成功的可能性越大.


2015-11-10 21:06:51 li_boxue 阅读数 1731

背景

在C6455上有一片外挂DDR2,型号是MICRON的MT47H128M16RT-25EIT:C,容量为256MB,16位数据位宽,设计接口时钟250MHz,接口速率500MHz,地址范围是0xE0000000~0xEFFFFFFF。

问题描述

现在需要将一段初始化的数据定义到DDR中,因此我们将链接文件写成这样:

MEMORY
{
    IRAM (RWX) : org = 0x800000, len = 0x1fbc00
    DDR2 (RWX) : org = 0xe0000000, len = 0x10000000
}
SECTIONS
{
    .slowdata: load > IRAM, run > DDR2
}

在代码中变量定义为

#pragma DATA_SECTION(foo, ".slowdata");
char data[1024*1024] = {
//一些无规律的初值
...
};

链接出错,提示

linker.cmd”, line 148: error #10099-D: program will not fit into available memory. placement with alignment fails for section “.cinit” size 0xfddc3 .

问题分析

既然是链接问题,那么就要分析编译链接的细节。

编译、链接生成的目标文件包含若干个段,其中有些属于未初始化的段,有些为初始化段。未初始化段只保留RAM空间,不占有目标文件空间。初始化段既占用RAM空间也存储在目标文件里。我们关心的初始化全局变量被C/C++编译器生成一个表,存储在.cinit段中。
所有C/C++程序都要与一个bootstrap例程(boot.obj)相链接来执行初始化,该目标文件中定义了_c_int00符号作为整个代码的入口地址。这个目标文件中还包含了用于初始化运行时环境的代码和数据,如设置系统栈、配置寄存器、处理.cinit初始化表、禁止中断,调用_main等。

若链接时设置了运行时初始化,则初始化过程如图所示。加载器先将.cinit段加载到内存中,再在运行时由启动例程拷贝到运行地址。当指定了某些链接选项时,C6000编译器生成的.init段还会以RLE等格式压缩以节省空间,在运行时先解压缩再拷贝。
运行时初始化

若链接时设置了加载时初始化,则初始化过程如图所示。加载器不把.cinit段加载到内存中,而是直接解析并拷贝到运行地址。
加载时初始化

在我们的工程中,链接选项配置为默认值–rom_model即运行时初始化,从map文件可以看出其中有一个.cinit段,长度近1MB,即需要占用RAM中的1MB空间。我们的RAM总共2MB大小,加上其他的代码和数据,剩余空间明显不足以容纳下这个段。

解决方案

找到了问题原因,就有了解决方法,即把链接选项配置为–ram_model,加载时初始化。这样链接后发现map文件中的.cinit段消失,链接成功。

然后用仿真器加载并运行,结果出现问题——数据并未初始化为指定值。
解决这一问题又要涉及到芯片启动过程,下次再分析。

2014-11-29 22:00:13 zhangxiao93 阅读数 846

GBSHM驱动调试笔记

2014/11/29

 

 

 

一、DDR内存分配

       OMAP3730核心板上的DDR物理内存地址范围从0x80000000到0x85400000,根据framework/include/physaddr.h文件中的定义,从0x85000000到0x85100000的范围内存用作gbshm共享内存存放属主参数。详细定义如下图:


physaddr.h

 

         整个内存的第一块,由操作系统使用,这部分在uboot启动时设定,其他区域不由操作系统直接管理,之后的物理空间用作共享内存使用,它们通过mmap进行管理,应用程序映射到每个进程3G虚拟空间。uboot设定方式如下:

setenv bootargs 'console ttyS0,115200n8 vram 4M omapfb.vram 0:4M omapfb.mode=dvi: omapfb.debug y omapdss.def_disp lcd070mem 100M noinitrd ubi.mtd 4 root ubi0:rootfs rootfstype ubifs'

 

 

 

 

 

 

 

 

 


二、mmap简介

函数原型如下:

void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);

 

返回说明:

成功执行时,mmap()返回被映射区的指针,失败时,mmap()返回MAP_FAILED[其值为(void *)-1]。每次成功调用mmap()后需要调用munmap()。

参数:

start:映射区的开始地址。

 

length:映射区的长度。

 

prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起

PROT_EXEC                                  //页内容可以被执行

PROT_READ                                 //页内容可以被读取

PROT_WRITE                               //页可以被写入

PROT_NONE                                //页不可访问

 

flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体

MAP_FIXED                                   //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。

MAP_SHARED                                      //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。

MAP_PRIVATE                                      //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。

MAP_DENYWRITE                      //这个标志被忽略。

MAP_EXECUTABLE                    //同上

MAP_NORESERVE                      //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。

MAP_LOCKED                                       //锁定映射区的页面,从而防止页面被交换出内存。

MAP_GROWSDOWN                 //用于堆栈,告诉内核VM系统,映射区可以向下扩展。

MAP_ANONYMOUS                   //匿名映射,映射区不与任何文件关联。

MAP_ANON                                 //MAP_ANONYMOUS的别称,不再被使用。

MAP_FILE                                     //兼容标志,被忽略。

MAP_32BIT                                  //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。

MAP_POPULATE                          //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。

MAP_NONBLOCK                       //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

 

fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。

 

offset:被映射对象内容的起点。

 

三、GBSHM驱动

GBSHM作为一个共享设备,是被linux作为字符驱动进行加载的。其主要的实现是mmap()操作函数的编写:



共享内存驱动将指定的物理地址空间通过mmap函数映射到用户空间,具体实现如下:

linux文件系统对mmap函数调用进行处理后,传递给字符设备。字符设备通过实现结构体struct file_operation中定义了设备的mmap函数来对设备物理内存进行映射。

mmap函数的第二个参数struct  vm_area_struct是linux内核中,管理虚拟内存的基本单元,它描述的是一段连续的、具有相同访问属性的虚拟内存空间,该虚拟内存空间的大小为物理内存页面的整数倍。

其中第二个参数vma中包含了映射相关的信息:

l  vma->vm_start 是Linux系统分配好的虚拟地址。很多时候和应用程序中填写的start一致。

l  vma->vm_end 是映射页面结束的地址。

l  vma->vm_pgoff是用来指明基于页面的偏移。注意:该偏移是基于页框的,而且是物理地址。如果我们希望把mmap映射到一个实际的IO地址中去,就需要改写本参数。

l  vma->vm_page_prot 页面的保护属性。

 

在mmap函数中,驱动可以调用页面映射函数 remap_pfn_range()来完成地址映射,remap_pfn_range函数的声明如下:

intremap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsignedlong pfn, unsigned long size, pgprot_t prot);

其中各个参数的含义如下:

l  vma 表示页被映射到的虚拟内存区。

l  virt_addr是重新映射应当开始的用户虚拟地址,这个函数为虚拟地址范围从 virt_addr 到 virt_addr_size这块空间建立页表。

l  pfn表示页帧号,对应虚拟地址应当被映射到的物理地址。这个页帧号简单地将物理地址右移 PAGE_SHIFT 位。大部分使用中,VMA 结构的 vm_paoff 成员正好包含你需要的值,这个函数影响的物理地址范围从(pfn<<PAGE_SHIFT) 到(pfn<<PAGE_SHIFT)+size。

l  size是重新映射的区的大小,以字节为单位。

l  prot是给新 VMA 要求的"protection",驱动可使用vma->vm_page_prot

中找到的值。

 

四、libgbshm.a的实现

1.        对外接口

在libgbshm.h中定义LIBGBSHM_OBJ结构体,并声明以下四个接口函数:

GBSHM_INIT();

//创建并返回LIBGBSHM_OBJ的一个指针句柄。

GBSHM_DESTROY(LIBGBSHM_OBJ*phandle);

//该函数销毁初始化的函数句柄

GBSHM_PARA_GET(LIBGBSHM_OBJ*ptrhandle_gbshm, int cmd, void * ptr, int length);

//该函数获取共享内存的内容

GBSHM_PARA_SET(LIBGBSHM_OBJ*ptrhandle_gbshm, int cmd, void * ptr, int length);

//该函数设置共享内存的内容

2.        内部逻辑

上述4个接口函数的内部逻辑于libgbshm.c中实现。具体实现略。

 

五、测试程序编写

 

1.        在framework/driver/gbshm目录改写Makefile.am

添加sbin_PROGRAMES:


添加源文件和链接库:

 

2.        编写gbshm_test_read.cgbshm_test_write.c文件

gbshm_test_write.c


gbshm_test_read.c


六、问题

libgbshm.c中的gbshm_para_set()函数锁无法实现——bus error。                                                                                                                    

2014-09-01 00:34:43 edwardlulinux 阅读数 3767


很久没有写博客了,由于之前的写关于OMAP3530文章还没有整理。再加上一直在找工作,找到工作后又投入到另外的平台去工作。始终在忙忙碌碌,但是对于代码确实渐渐疏远。

在做项目的时候要使用DDR3分配内存,不经意间使用要和MMU以及TLB打交道。因此特地写下这篇文章以备后用。(工作就是在和遗忘作斗争)!

 

Linux在启动之初会建立临时页表,但是在start_kerne函数中setup_arch又会建立正真的页表和页目录。那么两套方案是如何过渡的?假如在MMU开启的时候把之前的临时页表给覆盖了或者修改了,会不会影响后续的启动过程?带着这些问题分析一下。



首先来看一下基于ARM的页表管理和MMU的行为分析:


Arm上的linux(正式)页表采用的是一级粗页表结合二级小页表实现4G空间的访问。如上图说明。


一级表 (1024 Entrys


二级表 (1024 Entrys


虚拟地址后12Offset寻址空间是4096B 4k的空间


Arm上的linux(临时)页表采用的是段式页表,每一个entry可以映射1M的空间,结合后面的20bits位(寻址空间正好是1M


一级表 (4096 Entrys


虚拟地址的后20offset寻址空间是1M


接着来看一下linux如何建立页表的过程。


Head.S中有一段使用汇编编写的初始化代码。Mmu.c中有一段使用c语言写的建立页表的代码。C语言的代码很经典,可能汇编更经典。这里不多分析了。可以百度文章很多分析。


关键问题在于一个变量swap_pgdir


1..macro    pgtbl, rd  


2.  


3.       ldr   \rd, =(KERNEL_RAM_PADDR - 0x4000)  


4.  


5..endm  


KERNEL_RAM_PADDR = 0 x XXXXXXXX这是内存的物理地址,那么页表的建立也在这物理地址相关的区域内。


临时页表使用的是段式映射,也称之为平坦映射。那么4G的空间划分为1M为单位的访问单元,需要4096Entrys。应为Arm采用32位的数据线,因此每一个Entry占用一个32位的区段,也就是4B


正式页表建立的过程分为二级映射也寻找index的过程。每次把线性地址划分为两段,每一段都作为索引根据TLB BASE的便宜寻找下一级的索引项。最后结合虚拟地址的最后偏移(10 bit)作为依据在4K的空间内寻址。



问题来了,这两种映射会不会应为后一种映射的建立把之前的映射破坏掉,导致linux一个复杂的寻址系统无法正常工作呢?答案肯定不会。


图示比文字描述来的直接,还是直接上两张图说明问题:


由上图可知:临时页表建立的空间和正式页表建立的空间分别部署于不同的空间,因此不会出现覆盖或者修改等现象。同时一二级页表项目录中的内容页值得研究。最后两位同时表现出来的控制逻辑,让MMU翻译地址的过程中有章可循。结合MMU中的AP位规定了访问空间的属性,是否可以访问拒绝访问等。


    最后希望图示可以帮助读者理解映射的意图。文中难免有些地方会引起歧义或者不足之初,希望linux大侠指正点评。

                                                                                             谢谢



没有更多推荐了,返回首页