【转载】Uboot与Linux之间的参数传递

U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb等。而Linux kernel也会读取和处理这些参数。两者之间通过struct tag来传递参数。

U-boot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;Linux kernel通过这个地址,用parse_tags分析出传递过来的参数。

本文主要以U-boot传递RAM和Linux kernel读取RAM参数为例进行说明。

1、u-boot给kernel传RAM参数

./common/cmd_bootm.c文件中(指Uboot的根目录),bootm命令对应的do_bootm函数,当分析uImage中信息发现OS是Linux时,调用./lib_arm/bootm.c文件中的do_bootm_linux函数来启动Linux kernel。

在do_bootm_linux函数中:

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],                                   ulong addr, ulong *len_ptr, int verify)
{
  ......
  #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
      defined (CONFIG_CMDLINE_TAG) ||       defined (CONFIG_INITRD_TAG) ||       defined (CONFIG_SERIAL_TAG) ||       defined (CONFIG_REVISION_TAG) ||       defined (CONFIG_LCD) ||       defined (CONFIG_VFD)
  setup_start_tag (bd);      //初始化tag结构体开始

#ifdef CONFIG_SERIAL_TAG
  setup_serial_tag (&params);
#endif

#ifdef CONFIG_REVISION_TAG
  setup_revision_tag (&params);
#endif

#ifdef CONFIG_SETUP_MEMORY_TAGS
  setup_memory_tags (bd);      //设置RAM参数
#endif

#ifdef CONFIG_CMDLINE_TAG
  setup_commandline_tag (bd, commandline);
#endif

#ifdef CONFIG_INITRD_TAG
  if (initrd_start && initrd_end)
    setup_initrd_tag (bd, initrd_start, initrd_end);
#endif

#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
   setup_videolfb_tag ((gd_t *) gd);
#endif
  setup_end_tag (bd);              //初始化tag结构体结束
#endif

  ......
  ......

  theKernel (0, machid, bd->bi_boot_params);
//传给Kernel的参数= (struct tag *)型的bd->bi_boot_params
//bd->bi_boot_params在board_init 函数中初始化,如对于at91rm9200,初始化在at91rm9200dk.c的board_init中进行:bd->bi_boot_params=PHYS_SDRAM + 0x100;
//这个地址也是所有taglist的首地址,见下面的setup_start_tag函数
}

对于setup_start_tag和setup_memory_tags函数说明如下。

函数setup_start_tag也在此文件中定义,如下:

static void setup_start_tag (bd_t *bd)
{
  params = (struct tag *) bd->bi_boot_params;
  //初始化(struct tag *)型的全局变量params为bd->bi_boot_params的地址,之后的setup tags相关函数如下面的setup_memory_tags就把其它tag的数据放在此地址的偏移地址上。
  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);
}

RAM相关参数在bootm.c中的函数setup_memory_tags中初始化:

static void setup_memory_tags (bd_t *bd)
{
  int i;
  for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
    params->hdr.tag = ATAG_MEM;
    params->hdr.size = tag_size (tag_mem32);
    params->u.mem.start = bd->bi_dram[i].start;
    params->u.mem.size = bd->bi_dram[i].size;
    params = tag_next (params);
  }      //初始化内存相关tag
}

2、Kernel读取U-boot传递的相关参数

对于Linux Kernel,ARM平台启动时,先执行arch/arm/kernel/head.S,此文件会调用arch/arm/kernel/head- common.S和arch/arm/mm/proc-arm920.S中的函数,并最后调用start_kernel:

......

b     start_kernel

......

init/main.c中的start_kernel函数中会调用setup_arch函数来处理各种平台相关的动作,包括了u-boot传递过来参数的分析和保存:

start_kernel()
{
  ......
  setup_arch(&command_line);
  ......
}

其中,setup_arch函数在arch/arm/kernel/setup.c文件中实现,如下:

void __init setup_arch(char **cmdline_p)
{
  struct tag *tags = (struct tag *)&init_tags;
  struct machine_desc *mdesc;
  char *from = default_command_line;

  setup_processor();

  mdesc = setup_machine(machine_arch_type);
  machine_name = mdesc->name;

  if (mdesc->soft_reboot)
    reboot_setup("s");

  if (__atags_pointer)
//指向各种tag起始位置的指针,定义如下:
//unsigned int __atags_pointer __initdata;
//此指针指向__initdata段,各种tag的信息保存在这个段中。
    tags = phys_to_virt(__atags_pointer);
  else if (mdesc->boot_params)
    tags = phys_to_virt(mdesc->boot_params);

  if (tags->hdr.tag != ATAG_CORE)
    convert_to_tag_list(tags);

  if (tags->hdr.tag != ATAG_CORE)
    tags = (struct tag *)&init_tags;

  if (mdesc->fixup)
    mdesc->fixup(mdesc, tags, &from, &meminfo);

  if (tags->hdr.tag == ATAG_CORE) {
    if (meminfo.nr_banks != 0)
      squash_mem_tags(tags);

      save_atags(tags);
      parse_tags(tags);

//处理各种tags,其中包括了RAM参数的处理。
//这个函数处理如下tags:
__tagtable(ATAG_MEM, parse_tag_mem32);
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
__tagtable(ATAG_SERIAL, parse_tag_serialnr);
__tagtable(ATAG_REVISION, parse_tag_revision);
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
  }

  init_mm.start_code = (unsigned long) &_text;
  init_mm.end_code   = (unsigned long) &_etext;
  init_mm.end_data   = (unsigned long) &_edata;
  init_mm.brk       = (unsigned long) &_end;

  memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
  boot_command_line[COMMAND_LINE_SIZE-1] = ‘\0‘;
  parse_cmdline(cmdline_p, from); //处理编译内核时指定的cmdline或u-boot传递的cmdline
  paging_init(&meminfo, mdesc);
  request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP
  smp_init_cpus();
#endif
  cpu_init();

  init_arch_irq = mdesc->init_irq;
  system_timer = mdesc->timer;
  init_machine = mdesc->init_machine;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
  conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
  conswitchp = &dummy_con;
#endif
#endif

  early_trap_init();
}

对于处理RAM的tag,调用了parse_tag_mem32函数:

static int __init parse_tag_mem32(const struct tag *tag)
{
  ......
  arm_add_memory(tag->u.mem.start, tag->u.mem.size);
  ......
}

_tagtable(ATAG_MEM, parse_tag_mem32);

上述的arm_add_memory函数定义如下:

static void __init arm_add_memory(unsigned long start, unsigned long size)
{
  struct membank *bank;

  size -= start & ~PAGE_MASK;

  bank = &meminfo.bank[meminfo.nr_banks++];
  bank->start = PAGE_ALIGN(start);
  bank->size = size & PAGE_MASK;
  bank->node = PHYS_TO_NID(start);
}

如上可见,parse_tag_mem32函数调用arm_add_memory函数把RAM的start和size等参数保存到了meminfo结构的meminfo结构体中。最后,在setup_arch中执行下面语句:

paging_init(&meminfo, mdesc);

对没有MMU的平台上调用arch/arm/mm/nommu.c中的paging_init,否则调用arch/arm/mm/mmu.c中的paging_init函数。这里暂不分析mmu.c中的paging_init函数。

3、关于U-boot中的bd和gd

U-boot中有一个用来保存很多有用信息的全局结构体--gd_t(global data缩写),其中包括了bd变量,可以说gd_t结构体包括了u-boot中所有重要全局变量。最后传递给内核的参数,都是从gd和bd中来的,如上述的setup_memory_tags函数的作用就是用bd中的值来初始化RAM相应的tag。

对于ARM平台这个结构体的定义大致如下:

include/asm-arm/global_data.h

typedef struct global_data {

  bd_t        *bd;
  unsigned long flags;
  unsigned long baudrate;
  unsigned long have_console; /* serial_init() was called */
  unsigned long reloc_off;       /* Relocation Offset */
  unsigned long env_addr;       /* Address of Environment struct */
  unsigned long env_valid;       /* Checksum of Environment valid? */
  unsigned long fb_base; /* base address of frame buffer */
  void        **jt;        /* jump table */

} gd_t;

在U-boot中使用gd结构之前要用先用宏DECLARE_GLOBAL_DATA_PTR来声明。这个宏的定义如下:

include/asm-arm/global_data.h

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

从这个宏的定义可以看出,gd是一个保存在ARM的r8寄存器中的gd_t结构体的指针。

说明:本文的版本为U-boot-1.3.4、Linux-2.6.28,平台是ARM。

【转载】Uboot与Linux之间的参数传递

时间: 2024-07-31 23:09:12

【转载】Uboot与Linux之间的参数传递的相关文章

如何实现uboot和linux之间的参数传递

参考http://cgxcn.blog.163.com/blog/static/132312422009101133251202/ 参考:http://blog.chinaunix.net/uid-14833587-id-76499.html 参考:http://lixuefeng26.blog.sohu.com/204383842.html 原理就是:uboot将要传递的参数,保存到一个指定的物理位置:然后linux从该物理位置获取数据即可 1.先看一下uboot自带的参数传递过程: A. 首

转载windows与linux之间文件的传输方式总结

原文地址:http://www.cnblogs.com/wxjnew/archive/2013/06/05/3118808.html windows与linux之间文件的传输方式总结 当然,windows与linux之间文件的传输的两种方式有很多,这里就仅仅列出工作中遇到的,作为笔记: 方法一:安装SSH Secure Shell Client客户端 安装即可登录直接拖拉到linux,如果从linux下导出文件则需要如此操作 文件自动回到windows窗口的当前目录: 方法2:通过windows

【linux】U-BOOT与linux kernel通信: struct tag

  欢迎转载,转载时需保留作者信息. 邮箱:[email protected] 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http://blog.csdn.net/xiayulewa     u-boot与linux通信格式       如上图,开机时执行u-boot, u-boot引导完后,就是交给linux系统了,但是linux需要一些基本信息,如内存大小,启动方式等,这就涉及到u-boot和linux通信. 而通信格式由linux

uboot 和linux 下flash 写入速度差异分析

韩大卫@吉林师范大学 2015.1.27. 转载请表明出处 ********************************************* uboot 和linux 下flash 的写入速度不一样, 主要原因是两者使用的延迟最小延时时间不一样. linux比较大. 原因如下: flash 芯片手册中有两个重要的时间参数: 第一个是一般的块写入超时时间, 记为time-ty, 第二个是最大的块写入超时时间, 记为time-max. 假设有两个CFI nor flash 芯片, A 的t

linux之间文件传输(之scp)

linux的scp命令 linux 的 scp 命令 可以 在 linux 之间复制 文件 和 目录: ==================scp 命令==================scp 可以在 2个 linux 主机间复制文件: 命令基本格式: scp [可选参数] file_source file_target ======从 本地 复制到 远程======* 复制文件:        * 命令格式:                scp local_file [email prot

【转载 更新】Linux工具之AWK 2.基础知识

1. awk简介 awk是一种编程语言,用于在linux/unix下对文本和数据进行处理.数据可以来自标准输入.一个或多个文件,或其它命令的输出.它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具.它在命令行中使用,但更多是作为脚本来使用.awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作.如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指

主程序与子程序之间的参数传递的四种方法

主程序调用子程序是,往往需要向子程序传递一些参数,同样,子程序运行的时候也需要把一些结果参数传回给主程序,主程序与子程序之间的这种信息传递称为参数传递,我们把主程序传给子存续的参数称为子程序的入口参数,把由子程序传给主程序的参数称为子程序的出口参数 有多种传递参数的方法,寄存器传递法,约定内存单元传递法,堆栈传递法和CALL后续传递法 4.2.1利用寄存器传递参数 利用寄存器传递参数就是把参数放在约定的寄存器中,这种方法的优点是实行啊你个简单和调用方便,但是由于寄存器的个数有限,并且寄存器往往还

转载如何实现portlet之间的传递参数

Liferay 6开发学习(三十):跨页面Portlet之间的调用与数据传递 2014年10月09日 Liferay 评论 2 条 阅读 4,209 views 次 Portlet之间的通信方法有多种,比如PortletSession,Public Render Parameter,Portlet event等.但是这些方法使用起来都很复杂,像Public Render Parameter和event等,在跨页上就力所不及. 在很多时候我们要想实现Portlet之间的参数传递和数据共享,我最常用

【转载更新】Linux工具之AWK 4.实例

======基 础 篇====== 1.无pattern的action实例 a.awk ‘{print NR $1 $NF}’data.txt   打印行号,第一列和最后一列,中间无分隔符 b.awk ‘{print $1,$NF}’ data.txt     打印第一列和最后一列,并且中间有分隔符 c.awk‘{print$0,$NF+10}’data.txt  打印整行,并打印 最后一行加上10的结果 2.有pattern的action实例 a.awk ‘/[0-9]/’ data.txt