设为首页 收藏本站
查看: 551|回复: 0

[经验分享] linux下内存大小、起始地址的解析与修改

[复制链接]

尚未签到

发表于 2015-12-10 08:57:03 | 显示全部楼层 |阅读模式
在实际的工作中,由于产品型号的不同,经常需要调整linux所管理的内存的大小,而内核在启动阶段,会两次去解析从uboot传递过来的关于内存的信息,具体如下:


DSC0000.jpg

一、解析从uboot传递过来的tag(在parse_tags中处理)

在uboot的do_bootm_linux()函数中,会创建一系列需要传递给内核的tag,所有的tag以链表形式链接到指定的物理内存中。setup_start_tag用来建立起始的tag,而起始的物理地址由bd->bi_boot_params指定,



  • static void setup_start_tag (bd_t *bd)

  • {
  •     params = (struct tag *) bd->bi_boot_params;

  •     params->hdr.tag = ATAG_CORE;
  •     params->hdr.size = tag_size (tag_core);

  •     params->u.core.flags = 0;
  •     params->u.core.pagesize = 0;
  •     params->u.core.rootdev = 0;

  •     params = tag_next (params);
  • }
bi_boot_params是在board_init中初始化的,此地址是与内核协商一致的用来存放tag的基址。

intboard_init (void)

{

…………

    gd->bd->bi_boot_params =CFG_BOOT_PARAMS;

…………

}

而内存的tag是在setup_memory_tags()函数中创建的,其hdr.tag指定了tag的类型为ATAG_MEM



  • static int __init parse_tag_mem32(const struct tag *tag)

  • {
  •     if (meminfo.nr_banks >= NR_BANKS) {
  •         printk(KERN_WARNING
  •          "Ignoring memory bank 0x%08x size %dKB\n",
  •             tag->u.mem.start, tag->u.mem.size / 1024);
  •         return -EINVAL;
  •     }
  •     arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  •     return 0;
  • }

  • __tagtable(ATAG_MEM, parse_tag_mem32);

  

在内核中,会通过__tagtable 宏来建立起相关的struct tagtable 的数据结构,并放入".taglist.init" 段中,
#define__tag __used __attribute__((__section__(".taglist.init")))

#define__tagtable(tag, fn) \

static structtagtable __tagtable_##fn __tag = { tag, fn }




  • static int __init parse_tag_mem32(const struct tag *tag)

  • {
  •     return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  • }

  • __tagtable(ATAG_MEM, parse_tag_mem32);
而在start_kernel()->setup_arch()->parse_tags()函数中会根据从指定的物理内存中解析出来的tag的类型(即在uboot中写入的hdr.tag)去解析不同的tag。

在内核中此物理内存地址是在MACHINE_START中定义的,其中的boot_params与uboot中的bi_boot_params数据段指向相同的物理内存地址。因此是在uboot中写入tag,在内核中此地址解析tag。



  • MACHINE_START(hi3520v100, "hi3520v100")

  •     .phys_io    = IO_SPACE_PHYS_START,
  •     .io_pg_offst    = (IO_ADDRESS(IO_SPACE_PHYS_START) >> 18) & 0xfffc,
  •     .boot_params    = PHYS_OFFSET + 0x100,
  •     .map_io        = hisilicon_map_io,
  •     .init_irq    = hisilicon_init_irq,
  •     .timer        = &hisilicon_timer,
  •     .init_machine    = hisilicon_init_machine,
  • MACHINE_END


  • struct tagtable {

  •     __u32 tag;
  •     int (*parse)(const struct tag *);
  • };
在parse_tags()中,会根据读出来的tag的类型,即hdr.tag与从".taglist.init"段中的struct tagtable中的tag字段比较,如果相等,便执行struct tagtable中的parse()函数,对内存的tag来讲,其类型是ATAG_MEM,解析函数是parse_tag_mem32();



  • static int __init parse_tag_mem32(const struct tag *tag)

  • {
  •     if (meminfo.nr_banks >= NR_BANKS) {
  •         printk(KERN_WARNING
  •          "Ignoring memory bank 0x%08x size %dKB\n",
  •             tag->u.mem.start, tag->u.mem.size / 1024);
  •         return -EINVAL;
  •     }
  •     arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  •     return 0;
  • }

  • __tagtable(ATAG_MEM, parse_tag_mem32);
在内核中,物理内存的起始地址和大小存放在一个struct meminfo meminfo的全局变量中,



  • struct meminfo {

  •     int nr_banks;
  •     struct membank bank[NR_BANKS];
  • };


  • struct membank {

  •     unsigned long start;
  •     unsigned long size;
  •     int node;
  • };
nr_banks表示内核总共管理了多少个bank。

structmembank记录了内核中各个bank的信息,start表示起始地址,size表示此bank的大小,node表示此bank属于哪个内存结点。

Linux内核可以管理多个不连续的物理内存,每段连续的物理内存的大小和起始地址存在一个struct membank结构体中,有多少段物理内存,就有多少个bank。

parse_tag_mem32解析在uboot中建立的关于内存的tag,把其中的物理内存地址和大小填充到bank中。


二、解析从uboot传递过来的boot_command_line(在parse_cmdline函数中解析)。

boot_command_line命令行是在uboot的fix_bootargs()函数里建立的。即在uboot中看到的bootargs的环境变量



  • static void fix_bootargs(char *cmdline)

  • {
  • .……
  •     /* fix "mem=" params */
  •     p = strstr(cmdline,"mem=");
  •     if(!p) {
  •         sprintf(args," mem=%dM",gd->bd->bi_dram[0].size/0x200000);
  •         strcat(cmdline,args);
  •     }
  • ……………
  • }

在内核中是通过early_mem()来解析boot_command_line中有关内存大小的参数行的。



  • static void __init early_mem(char **p)

  • {
  •     static int usermem __initdata = 0;
  •     unsigned long size, start;

  •     /*

  •      * If the user specifies memory size, we
  •      * blow away any automatically generated
  •      * size.
  •      */
  •     if (usermem == 0) {
  •         usermem = 1;
  •         meminfo.nr_banks = 0;
  •     }

  •     start = PHYS_OFFSET;
  •     size = memparse(*p, p);
  •     if (**p == '@')
  •         start = memparse(*p + 1, p);

  •     arm_add_memory(start, size);
  • }
  • __early_param("mem=", early_mem);
该函数解析从uboot传递进来的boot_command_line命令行参数中以“mem=”开头的命令行,如果boot_command_line中有以“mem=”开头的命令行,就调用该函数解析“mem=”之后的关于内存的信息,

把内存的大小写到对应的bank中去,内存的基地址在此处是一个默认值。如果有两段不连续的物理内存,可以在boot_command_line中设置如下内容即可:

mem=72M@0xe2000000 mem=128M@0xe8000000

在此处,定义了static int usermem __initdata = 0,从而设置meminfo.nr_banks= 0,这样把前面解析uboot的tag所赋值的bank内容又重写了,所以相当于前面解析tag的操作没有生效,起作用的还是此处的解析boot_command_line的操作。



三、以上是内核启动过程中所做的两次解析内存参数的操作,在实际应用中需要修改linux内存大小时可以采取相应的方法:

1、 修改uboot中内存相关的tags或者bootargs的命令行参数。这种做法虽然可以修改linux管理的内存的大小,但是由于要修改uboot,这样会对产品生产中增加困难,而且bootloader在原则上是要尽量少做改动,防止由于修改bootloader造成板子无法启动等问题,所以此方法不推荐使用。

2、 通过在解析boot_command_line之前修改其中的”mem=”之后的相关内容来修改linux所管理的内存大小,这样可以做到不同产品间的兼容性,而且后续的产品升级等方面也比较简单容易操作。


在海思平台上实现了这种做法。

void__init hikio_fix_meminfo(char *cmdline,struct meminfo *mi)

{

……………….

    strcpy(p,p+8);

    strcat(cmdline," mem=72M");

    mi->bank[0].size = 72*0x100000;

}

然后在解析cmdline之前执行此函数。
hikio_fix_meminfo(from,&meminfo);/*wangqian fix for cost-down boards*/

memcpy(boot_command_line, from,COMMAND_LINE_SIZE);

boot_command_line[COMMAND_LINE_SIZE-1] ='\0';

parse_cmdline(cmdline_p, from);

这种方法可以很方便的根据不同的产品型号修改内存的大小,而且只需要修改内核部分,不用去对uboot进行改动,所以是最方便快捷的方式。


  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-149008-1-1.html 上篇帖子: Linux 时间同步问题 下篇帖子: Linux下C如何调用动态库
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表