内核中对uboot传参tags的校验
Posted 代二毛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核中对uboot传参tags的校验相关的知识,希望对你有一定的参考价值。
前言
此篇博客介绍的是内核如何解析uboot传递的tag参数。
uboot给内核传tag参数
参考博客:《uboot以tag方式给内核传参》。
* (taglist.init)段
······
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
·······
上面是摘抄自内核的链接脚本,__tagtable_begin和__tagtable_end表示taglist.init段在链接地址的起始和结束,在内核处理tag传参时需要用到。。
struct tagtable结构体
struct tagtable
__u32 tag;//代表不同类型的tag参数
int (*parse)(const struct tag *);//与tag类型绑定的解析函数
;
struct tagtable结构体由一个整形数tag和函数指针parse构成,其中tag代表不同类型的tag参数,函数指针就是该类型tag参数的处理函数。此结构体就是对每种类型的tag参数绑定一个处理函数。
__tagtable宏
#define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \\
static struct tagtable __tagtable_##fn __tag = tag, fn
static int __init parse_tag_cmdline(const struct tag *tag)
strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
return 0;
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
//将宏展开:
static struct tagtable __tagtable_parse_tag_cmdline __used __attribute__((__section__(".taglist.init"))) = \\
ATAG_CMDLINE, parse_tag_cmdline
_tagtable宏的作用:
1.定义一个静态局部变量,类型是struct tagtable,名字是__tagtable##fn,并赋值;
2.对定义的结构体赋予".taglist.init"的段属性,链接的时候用__tagtable宏定义的结构体会被链接到一起,类型与一个结构体数组。
重点:在内核中对每种类型的tag参数都要用__tagtable宏去绑定一个解析函数。
struct tag结构体
struct tag_header
u32 size; //结构体的大小
u32 tag; //结构体的类型
;
struct tag
struct tag_header hdr;
union //此枚举体包含了uboot传给内核参数的所有类型
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;
struct tag_mtdpart mtdpart_info;
u;
;
struct tag结构体定义在arch/arm/include/asm/setup.h中。参考博客:《uboot以tag方式给内核传参》。
内核汇编阶段对tag参数的校验
__vet_atags:
tst r2, #0x3 @ 检查tag参数所在起始地址是否4字节对齐
bne 1f @如果地址没有4字节对齐则跳转到标号1处,校验失败
ldr r5, [r2, #0] @r2中保存的是第一个tag参数的地址,此句相当于r5=r2->hdr.size
cmp r5, #ATAG_CORE_SIZE @比较r5和ATAG_CORE_SIZE是否相等,
cmpne r5, #ATAG_CORE_SIZE_EMPTY @如果上一句不相等,则比较r5和ATAG_CORE_SIZE_EMPTY是否相等
bne 1f @上一句代码的结果不相等则跳转到标号1处,校验失败
ldr r5, [r2, #4] @此句相当于r5=r2->hdr.tag
ldr r6, =ATAG_CORE @将ATAG_CORE赋值给r6
cmp r5, r6 @判断r5是否等于ATAG_CORE
bne 1f @上一句代码的结果不相等则跳转到标号1处,校验失败
mov pc, lr @校验成功,函数返回
1: mov r2, #0
mov pc, lr
ENDPROC(__vet_atags)
1.ATAG_CORE_SIZE:ATAG_CORE类型tag结构体的大小;
2.ATAG_CORE_SIZE_EMPTY:第一个tag类型为空,代表uboot没有给内核传tag;
3.ATAG_CORE:这是tag的类型,tag参数的第一个必须是此类型;
4.整个校验的思路:查看uboot传的第一个tag结构体是不是ATAG_CORE类型;
内核C语言阶段对tag参数的校验
调用的函数层次关系
//init/main.c
void __init start_kernel(void)
void __init setup_arch(char **cmdline_p)
setup_processor();
mdesc = setup_machine(machine_arch_type);
tags = phys_to_virt(mdesc->boot_params);
parse_tags(tags)
parse_tag(t)
t->parse(tag)
(1)start_kernel()函数是内核启动C语言阶段的第一个函数,功能是各模块的初始化;
(2)setup_arch()函数是对uboot传的tag参数进行解析,其中就包括boot_command_line;
(3)setup_processor()函数是校验CPU的id是否支持;
(4)setup_machine()函数返回描述当前开发板的结构体地址;
(5)phys_to_virt()函数将物理地址转化成虚拟地址;
setup_arch函数
void __init setup_arch(char **cmdline_p)
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
setup_processor();//处理CPUID
mdesc = setup_machine(machine_arch_type);//将描述开发板信息的结构体指针赋值给mdesc变量
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);//将物理地址转换成虚拟地址
printk("@@@@@@@ atags_pointer not null\\n");
else if (mdesc->boot_params)
tags = phys_to_virt(mdesc->boot_params);//将物理地址转换成虚拟地址,mdesc->boot_params就是开发板描述信息里对tag参数所在内存地址
printk("@@@@@@@ boot params not null\\n");
if (tags->hdr.tag == ATAG_CORE) //校验第一个tag参数是不是ATAG_CORE类型
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);
squash_mem_tags()函数:首先判断是否处理过内存相关信息,如果nr_banks 不为0则代表以及处理过内存相关信息,调用squash_mem_tags()函数,将tag中ATAG_MEM类型的改为ATAG_NONE类型。
squash_mem_tags函数
void __init squash_mem_tags(struct tag *tag)
for (; tag->hdr.size; tag = tag_next(tag))
if (tag->hdr.tag == ATAG_MEM)
tag->hdr.tag = ATAG_NONE;
遍历tag参数,将ATAG_MEM类型改为ATAG_NONE类型;
parse_tags函数
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);
遍历tag结构体,每个结构体都用parse_tag()函数去解析;
备注:只有最后一个结构体的size为零。
parse_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++)
if (tag->hdr.tag == t->tag)
t->parse(tag);
break;
return t < &__tagtable_end;
(1)__tagtable_begin和 __tagtable_end是在链接脚本里指定的,表示.taglist.init段的起始和结束地址;
(2)for循环就是去遍历struct tagtable数组,查找与传进来的tag类型匹配的struct tagtable结构体;
(3)如果匹配到struct tagtable结构体,就用绑定的解析函数去解析tag参数;
以上是关于内核中对uboot传参tags的校验的主要内容,如果未能解决你的问题,请参考以下文章