Chinaunix首页 | 论坛 | 博客
  • 博客访问: 62137
  • 博文数量: 19
  • 博客积分: 195
  • 博客等级: 入伍新兵
  • 技术积分: 150
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-15 23:19
文章分类
文章存档

2012年(19)

分类:

2012-10-16 12:27:00

Uboot和kernel之间有时候需要参数传递,比如:uboot中通过i2c读取产品id,如果kernel中也需要根据产品id区分型号,那么就会产生一个问题:uboot中可以通过给订好的i2c接口读取,但kernel中没有做好的接口,尤其是驱动程序中通过i2c读取信息,这就要自己在kernel中实现i2c的接口,这样可能会与i2c驱动冲突,引起很多不确定的问题。所以通过uboot把产品型号这个参数传到kernel中,就解决了这个问题。本文以uboot向kernel传递产品id这个参数来示例。

u-boot会给Linux Kernel传递很多参数,而Linux kernel也会读取和处理这些参数。两者之间通过struct tag来传递参数。U-boot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;Linux kernel通过这个地址,用parse_tags分析出传递过来的参数。

1、u-boot

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

在do_bootm_linux函数中

以下是简化后的函数

int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)

{

bd_t *bd = gd->bd;

char *s;

int machid = bd->bi_arch_number;//获取产品型号

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

char *commandline = getenv ("bootargs");//获取命令行参数

theKernel = (void (*)(int, int, uint))images->ep;//获取kernel入口点地址

s = getenv ("machid");

setup_start_tag (bd);//设置tag起始标志

setup_serial_tag (¶ms);//设置串口参数

setup_revision_tag (¶ms);设置版本参数

//user add

setup_product_tag(¶ms);设置产品id参数,这个参数时用户自家添加的

//user end

setup_memory_tags (bd);

setup_commandline_tag (bd, commandline);

setup_videolfb_tag ((gd_t *) gd);

setup_end_tag (bd);//设置tags结束标志

theKernel (0, machid, bd->bi_boot_params);

/* does not return */

return 1;

}

对上面这个函数有如下解释:

首先,uboot把要传递的参数放到内存中的一个指定的地址,kernel需要从这个地址中取出参数,在board_init()函数中,这个参数 bd->bi_boot_params被赋值为:

#define LINUX_BOOT_PARAM_ADDR 0x80000100

gd->bd->bi_boot_params = LINUX_BOOT_PARAM_ADDR;

最终theKernel (0, machid, bd->bi_boot_params);通过第三个参数传递给kernel,因为theKernel被赋值为kernel入口点,所以这个函数就是启动kernel并且传递三个参数

static struct tag *setup_start_tag(struct tag *params)

{

params->hdr.tag = ATAG_CORE;//此标志表示这是tag的开始

params->hdr.size = tag_size(tag_core);//占用空间大小

params->u.core.flags = 0;

params->u.core.pagesize = 4096;

params->u.core.rootdev = 0;

return tag_next(params);

}

#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)) //指向下一个参数的开始地址

其次,添加自己的参数:

void setup_product_tag(struct tag **in_params)

{

params->hdr.tag = ATAG_G_PRODUCT;//参数类型的标志

params->hdr.size = tag_size (tag_g_product);//占用空间大小

params->u.productid.product_id = g_product;//设置产品id, g_product中存着从i2c读出的产品型号

params = tag_next (params);//指向下一个参数地址,这样就把自己的参数存到了tag列表中了,即实现了uboot的参数传递

}

定义自己的参数类型和数据结构,自己添加的数据类型和结构需要在kernel中也对应添加

#define ATAG_G_PRODUCT 0x5441000c

struct tag_g_product {

u32 product_id;

};

struct tag {

struct tag_header hdr;

union {

struct tag_core core;

struct tag_mem32 mem;

struct tag_videotext videotext;

struct tag_ramdisk ramdisk;

struct tag_initrd initrd;

struct tag_serialnr serialnr;

struct tag_revision revision;

struct tag_videolfb videolfb;

struct tag_cmdline cmdline;

struct tag_acorn acorn;

struct tag_memclk memclk;

//usr add

struct tag_g_product productid;//添加自己的数据类型

//end add

} u;

}

设置结束tag,表明参数已经结束了

static void setup_end_tag (bd_t *bd)

{

params->hdr.tag = ATAG_NONE;//结束标记

params->hdr.size = 0;

}

2、对应的kernel中:

在setup.c中的setup_arch()函数中调用了parse_tags(tags);这个函数,此函数完成解析参数列表中的参数并调用对应的处理函数。

遍历每个tag,然后调用parse_tag()函数

static void __init parse_tags(const struct tag *t)

{

for (; t->hdr.size; t = tag_next(t))

if (!parse_tag(t))

printk(KERN_WARNING

"Ignoring unrecognised tag 0x%08x\n",

t->hdr.tag);

}

static int __init parse_tag(const struct tag *tag)

{

extern struct tagtable __tagtable_begin, __tagtable_end;

struct tagtable *t;

for (t = &__tagtable_begin; t < &__tagtable_end; t++)//遍历tag链表

if (tag->hdr.tag == t->tag) {//判断标志

t->parse(tag);//调用相应的解析函数

break;

}

return t < &__tagtable_end;

}

//把自己的处理函数和数据结构添加到tag列表中

static int __init parse_tag_product_id(const struct tag *tag)//对应的处理函数

{

g_product = tag->u.productid.product_id;//把tags中的产品id赋给kernel中的全局变量

return 0;

}

__tagtable(ATAG_G_PRODUCT, parse_tag_product_id);

#define __tagtable(tag, fn) \

static struct tagtable __tagtable_##fn __tag = { tag, fn } //把自定义的标记和对应的处理函数添加到结构体中

#define __tag __used __attribute__((__section__(".taglist.init")))//把生成的tag编译到taglist.init段中,

__tagtable_begin,定义在arch/arm/vmlinux.lds,vmlinux.lds这是编译器用的文件

__arch_info_begin = .;

*(.arch.info)

__arch_info_end = .;

__tagtable_begin = .;

*(.taglist)

__tagtable_end = .;

*(.data.init)

. = ALIGN(16);

由此可见,编译器链接脚本把tag列表开始位置__tagtable_begin 指向了.taglist开始,就是我们通过__tagtable()宏生成的列表,结束位置__tagtable_end指向了.taglist的下一个段,所以parse_tag()这个函数中就是在遍历这个列表

//usr add 用户自己定义的类型和数据结构,应该与uboot中的定义一致

#define ATAG_G_PRODUCT 0x5441000c

struct tag_g_product {

u32 product_id;

};

struct tag {

struct tag_header hdr;

union {

struct tag_core core;

struct tag_mem32 mem;

struct tag_videotext videotext;

struct tag_ramdisk ramdisk;

struct tag_initrd initrd;

struct tag_serialnr serialnr;

struct tag_revision revision;

struct tag_videolfb videolfb;

struct tag_cmdline cmdline;

struct tag_acorn acorn;

struct tag_memclk memclk;

//usr add

struct tag_g_product productid;

//usr add

} u;

};

阅读(961) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~