U-boot如何向kernel传递参数 + kernel如何读取参数

本文转自:http://zqwt.012.blog.163.com/blog/static/12044684201332535457347/

 

UBOOT版本:UBOOT2010.06

硬件环境:ARM

U-boot 会给 linux Kernel 传递很多参数,例如:串口、RAM、commandline(bootargs)等。而 linux kernel 也会读取和处理这些参数。它们两者之间通过ATAG方式来传递参数。

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

这里以 U-boot 传递 RAM 参数和Linux kernel 读取 RAM 参数为例进行介绍。

1、u-boot 向kernel 传递RAM 参数

./common/cmd_bootm.c 文件调用 ./uboot/arch/arm/lib/bootm.c 文件中的 do_nand_boot 函数来启动 Linux kernel。

    在do_nand_boot 函数中:

int do_nand_boot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

       DECLARE_GLOBAL_DATA_PTR;

       int ret;

       bd_t *bd = gd->bd;

       ulong addr, data, len, initrd_start, initrd_end;

     void (*theKernel)(int zero, int arch, uint params);

int strlen;

char *commandline = getenv ("bootargs");

      setup_start_tag (bd);    // 初始化第一个kernel tag结构体

      setup_serial_tag (&params);

      setup_revision_tag (&params);

      setup_memory_tags (bd);

theKernel (0,

machid,

bd->bi_boot_params  // 传给 Kernel 的参数= (struct tag *) 型的 bd->bi_boot_params

);

}

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 相关参数在函数 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 传递的ATAG参数

对于 Linux Kernel,ARM 平台启动时,先执行 arch/arm/kernel/head.S ,此文件会调用arch/arm/kernel/head-common.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

……

}

对于处理 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 函数。

总结一下

简单的讲,uboot利用函数指针及传参规范,它将

l   R0: 0x0

l   R1: 机器号

l   R2: 参数地址

三个参数传递给内核。

其中,R2寄存器传递的是一个指针,这个指针指向一个TAG区域。

UBOOT和Linux内核之间正是通过这个扩展了的TAG区域来进行复杂参数的传递,如 command line,文件系统信息等等,用户也可以扩展这个TAG来进行更多参数的传递。TAG区域的首地址,正是R2的值。

TAG区域由许多个TAG数组构成,每一个TAG传递一种特定类型的参数。各种TAG的定义可以参考:

arch/arm/include/asm/setup.h。

下面是一个TAG区的例子:

0x30000100 00000005 54410001 00000000 00000000

0x30000110 00000000 0000000F 54410009 746F6F72

0x30000120 65642F3D 61722F76 7220306D 6F632077

0x30000130 6C6F736E 74743D65 2C305379 30303639

0x30000140 696E6920 6C2F3D74 78756E69 EA006372

0x30000150 00000004 54420005 30300040 00200000

0x30000160 00000000 00000000

我们可以看到一共有三个TAG:

l   第一个TAG的长度是5个字,类型是ATAG_CORE(54410001),有三个元素,均为全零.TAG区必须以这个TAG开头.

l   第二个TAG的长度是F个字,类型是ATAG_CMDLINE(54410009),这是一个字符串,是向内核传递的kernel command line

l   第三个TAG的长度是4个字,类型是ATAG_INITRD2(54410005),有两个元素,第一个是start:30300040(30300000+40), 

第二个是size:200000(2M)

l   如果说还有第四个TAG,那就是末尾的两个全零,这是TAG结束的标志。

这些TAG是在函数do_bootm_linux函数中建立起来的。具体建立哪些TAG,由相应的控制宏决定。具体可以参考相应代码。例子中第一个TAG是起始TAG,如果环境变量中有bootargs,则建立第二个TAG,如果bootm有两个参数(引导文件系统),则会读取文件系统头部的必要信息,建立第三个TAG.

内核启动后,将根据R2寄存器的值找到这些TAG,并根据TAG类型,调用相应的函数进行处理,从而获取内核运行的必要信息。

附录:

1、关于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 结构体的指针

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
更新说明: 2017-02-04(yaya) Ls command: Empty Folder returns false. 2016-12-08(yaya) 修正lz4、vhd不显示解压缩进度指示。增加lzma解压缩进度指示。 2016-11-09(不点) 0x8205 bit 5 = 1: 使checkkey闲置循环停止指令。 2016-04-13(yaya) 支持动画菜单 setmenu --graphic-entry=类型=菜单行数=菜单列数=图形宽(像素)=图形高(像素)=菜单行间距(像素) 菜单项0的路径文件名 类型: 位0:高亮指定颜色 位1:高亮颜色翻转 位2:高亮显示线框 位7:背景透明(最好使用黑色背景) 文件名: *n.??? 格式 n=00-99 高亮颜色由 color HIGHLIGHT=0xrrggbb 指定。 字符可以使用任意字型、字高、颜色,可以辅以图标。 2016-03-25(yaya) 菜单字符可以使用不同字型。 例如:"七" 使用不同字型,将 .hex 文件中的 unicode 码 “4e03” 修改为 “0080”, 将菜单中的 "七" 修改为 “\X0080”。 2016-03-23(yaya) 增强 echo 函数功能。 例如:echo -e \x18 显示 UTF-8 字符 0x18。 echo -e \X2191 显示 unicode 字符 0x2191。 2016-03-15(yaya) 1.增加动画控制热键 F2:播放/停止。 2.增加动画控制位 0x835b,位0:0/1=停止/播放。 3.增加精简字库模式:--simp=起始0,终止0,...,起始3,终止3 中文可以使用 --simp= ,内置字库应当包含 DotSize=[font_h],['simp'] 例如:font --font-high=24 --simp= /24_24.hex DotSize=24,simp 不使用热键: 可以加载 32*32 unifont 全字库 使用热键: 可以加载 24*24 unifont 全字库 使用精简字库: 可以加载 46*46 汉字全字库 使用精简字库及热键:可以加载 40*40 汉字全字库 4.不再支持 bin 格式字库。 2016-03-03(yaya) 1.增加图像背景色设置方法。 splashimage --fill-color=[0xrrggbb] 作用之一,作为小图像的背景。 作用之二,直接作为菜单的背景(即不加载图像背景)。此时只设置字体的前景色即可。 2.增加动画菜单。 splashimage --animated=[type]=[delay]=[last_num]=[x]=[y] START_FILE 类型[type]:bit 0-3: 播放次数 bit 4: 永远重复 bit 7: 透明背景 type=00:禁止播放 播放n次:序列图像各显示n次,时间独占。可作为启动前导、序幕。 永远重复:序列图像无限循环,时间与菜单共享。可作为菜单里的动画。 背景透明:即抠像。要求4角像素为背景色。 背景色最好为白色或黑色,这样可以去除一些灰色杂波。若是彩色背景,则应当非常干净。 提醒:请以16进制方式输入。否则易错。 延迟[delay]:序列图像之间的延迟。单位是滴答,即1/18.2秒。 序列数[last_num]:序列图像总数(2位数,从1开始计数)。 偏移[x]、[y]:图像偏移,单位像素。 起始图像文件 START_FILE 命名规则:*n.??? n: 1-9 或 01-99 或 001-999。 3.增加固定图像的背景色可以透明。 splashimage [--offset=[type]=[x]=[y]] FILE 类型[type]:bit 7: 透明背景 2016-02-14(yaya) setmenu 函数增加菜单项目背景短/满参数(默认短) 2016-01-19(yaya) splashimage 函数增加图像起始偏移(默认0) 2015-08-20(yaya) 1.支持非

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值