【linux】U-BOOT与linux kernel通信: struct tag
欢迎转载,转载时需保留作者信息。
博客园地址: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规定了,以其中atag格式举例,详情查阅Documentation/arm/Setup
格式结构体为struct tag: 定义在 Setup.h (src\arch\arm\include\uapi\asm):147
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;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
u-boot会按照上述格式,在内存中划分一块atag参数区域,对该区域进行赋值。此处不对u-boot做讨论,后面在专门写文章。当赋值完成后,将cpu初始化成 MMU = off, D-cache = off, I-cache = dont care, r0 = 0, r1 = machine nr, r2 = atags or dtb pointer,跳转到linux代码起始处。
Linux atag参数解析流程
Linux代码执行后,在第一阶段(start_kernel函数之前),会验证该区域正确性(bl __vet_atags)。
进入第二阶段后(start_kernel函数之后):会开始真正解析atag参数,并赋值给相应的变量。
Init.h (src\include\linux) :中定义了大部分的 section,比较重要的有
#define __init __section(.init.text) __cold notrace
#define __initdata __section(.init.data)
#define __initconst __constsection(.init.rodata)
#define __exitdata __section(.exit.data)
#define __exit_call __used __section(.exitcall.exit)
流程:start_kernel →setup_arch→setup_machine_tags→parse_tags→parse_tag
在setup_arch中调用:
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
__atags_pointer定义为: unsigned int __atags_pointer __initdata;
__atags_pointer初始化: 在 head-common.S (src\arch\arm\kernel)中:
__mmap_switched:
adr r3, __mmap_switched_data
ldmia r3!, {r4, r5, r6, r7}
……………………
ARM( ldmia r3, {r4, r5, r6, r7, sp})
…………………….
str r2, [r6] @ Save atags pointer
……………………
.type __mmap_switched_data, %object
__mmap_switched_data:
.long __data_loc @ r4
.long _sdata @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long __atags_pointer @ r6
在函数 setup_machine_tags中有:
if (__atags_pointer)
tags = phys_to_virt(__atags_pointer);
else if (mdesc->atag_offset)
tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);
得到tags = phys_to_virt(__atags_pointer); 见下图:
→parse_tags(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);
}
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;
}
上述__tagtable_begin, __tagtable_end在src\arch\arm\kernel\vmlinux.lds
中定义:
.init.tagtable : {
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
}
Setup.h (src\arch\arm\include\asm)可以看到.taglist.init段定义:
#define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static const struct tagtable __tagtable_##fn __tag = { tag, fn }
atags_parse.c (src\arch\arm\kernel) 中:有
__tagtable(ATAG_CORE, parse_tag_core);
__tagtable(ATAG_SERIAL, parse_tag_serialnr);等。
综上:parse_tags是依次处理每个struct tag, 对每个struct tag,扫描__tagtable_begin到__tagtable_end之间的struct tagtable,一旦对象的tag相等,则调用struct tagtable的parse, 具体为函数parse_tag_core, parse_tag_serialnr等函数。
u-boot命令行bootargs处理
http://blog.csdn.net/xiayulewa/article/details/45191679中有说过, u-boot可以通过
setenv bootargs root=/dev/ram0 initrd=0x32000000,0x200000 rootfstype=ext2 console=ttySAC0,57600 init=/linuxrc ip=192.168.1.3
给linux传递参数,详细的过程不说,经过上述讨论,知道u-boot和linux是以atag方式通信的,实际阅读u-boot源代码的确如此,上述红色部分会以ATAG_CMDLINE的方式传递。
在Atags_parse.c (src\arch\arm\kernel) 中有:
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
parse_tag_cmdline设置了default_command_line数组。
而在setup_machine_tags函数中,
char *from = default_command_line;
.............
/* parse_early_param needs a boot_command_line */
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
又将default_command_line拷贝到boot_command_line数组
boot_command_line数组处理流程为:
start_kernel→parse_early_param(boot_command_line已经被setup_machine_tags函数赋值)
→parse_early_options→parse_args→parse_one→handle_unknown(param, val, doing)即do_early_param函数
/* Check for early params. */
static int __init do_early_param(char *param, char *val, const char *unused)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && parameq(param, p->str)) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0)
pr_warn("Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
在src\arch\arm\kernel\vmlinux.lds中有:
__setup_start = .; *(.init.setup) __setup_end = .;
而在Init.h (src\include\linux) 中,有.init.setup段的定义:
#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_##unique_id[] __initconst \
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
以串口处理为例:Printk.c (src\kernel)中有
__setup("console=", console_setup);
console=xxx串口名字为何必须为ttySAC
前面已经讨论到__setup("console=", console_setup);了,当内核参数中有console=xxx时,便会执行
console_setup→__add_preferred_console
__add_preferred_console函数中有:
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
if (strcmp(console_cmdline[i].name, name) == 0 &&
console_cmdline[i].index == idx) {
if (!brl_options)
selected_console = i;
return 0;
}
.........
c = &console_cmdline[i];
strlcpy(c->name, name, sizeof(c->name));
c->options = options;
此时console_cmdline[i].name还未初始化,为空字符串,循环结束时i = 0。最后将console=xxx中的xxx赋值给console_cmdline[0].name。
接下来,start_kernel→console_init
void __init console_init(void)
{
initcall_t *call;
/* Setup the default TTY line discipline. */
tty_ldisc_begin();
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
call = __con_initcall_start;
while (call < __con_initcall_end) {
(*call)();
call++;
}
}
在src\arch\arm\kernel\vmlinux.lds中有:
__con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
在Init.h (src\include\linux)中,有.con_initcall.init段定义。
#define console_initcall(fn) \
static initcall_t __initcall_##fn \
__used __section(.con_initcall.init) = fn
在Samsung.c (src\drivers\tty\serial)中有:
console_initcall(s3c24xx_serial_console_init);
故综上:有start_kernel→console_init→s3c24xx_serial_console_init→register_console(&s3c24xx_serial_console);
其中有代码:
/*
* See if this console matches one we selected on
* the command line.
*/
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
i++) {
if (strcmp(console_cmdline[i].name, newcon->name) != 0)
continue;
……………..
由此可见,只有s3c24xx_serial_console.name和console_cmdline[i].name的名字一样,console才能被正常初始化。
而
static struct console s3c24xx_serial_console = {
.name = S3C24XX_SERIAL_NAME,
……….
#define S3C24XX_SERIAL_NAME "ttySAC"
综上:console=xxx串口名字必须为ttySAC,使用方式为下面红色部分标注的:
setenv bootargs root=/dev/ram0 initrd=0x32000000,0x200000 rootfstype=ext2 console=ttySAC0,57600 init=/linuxrc ip=192.168.1.3
【linux】U-BOOT与linux kernel通信: struct tag的更多相关文章
- Linux 线程与进程,以及通信
http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...
- Linux 内核态与用户态通信 netlink
参考资料: https://blog.csdn.net/zqixiao_09/article/details/77131283 https://www.cnblogs.com/lopnor/p/615 ...
- linux用户态和内核态通信之netlink机制【转】
本文转载自:http://blog.csdn.net/zcabcd123/article/details/8272360 这是一篇学习笔记,主要是对<Linux 系统内核空间与用户空间通信的实现 ...
- Linux下c开发 之 线程通信(转)
Linux下c开发 之 线程通信(转) 1.Linux“线程” 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种“多进程单线程”的操作系统.Linu ...
- Linux中 /boot 目录介绍 【转载】
Linux中 /boot 目录介绍 转自:点击打开链接 一./boot/目录中的文件和目录 Linux系统在本地启动时,目录/boot/非常重要,其中的文件和目录有: (1)系统Kernel的配置文件 ...
- linux下 /boot 分区空间不足及其衍生问题
linux的/boot引导分区有时会提示空间不足的问题,虽不影响系统的正常运行,但是人类天生对于未知的恐惧感总是影响心情的.而且在按安装其他软件的过程在中可能会出现以下问题: gzip: stdout ...
- Linux学习-Boot Loader: Grub2
boot loader 的两个 stage 在 BIOS 读完信息后,接下来就是会到第一个开机装置 的 MBR 去读取 boot loader 了.这个 boot loader 可以具有选单功能.直接 ...
- Linux用户态与内核态通信的几种方式
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Linux 用 ...
- Linux下c开发 之 线程通信
Linux下c开发 之 线程通信 1.Linux“线程” 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种“多进程单线程”的操作系统.Linux本身 ...
随机推荐
- Kali Rolling在虚拟机安装后的设置
Kali Linux在2016年的第一个发行版——Kali Rolling是Debian的即时更新版,只要Debian中有更新,更新包就会放入Kali Rolling中,供用户下载使用.它为用户提供了 ...
- There is an error in invoking javac. A full JDK (not just JRE) is required
最近调整了磁盘分区,硬盘里什么都没有了,可惜了我很多项目还有数据库资源然后把以前ssh项目重新导入进来的时候出现了一个错误org.apache.jasper.JasperException: PWC6 ...
- gcc/g++ 如何支持c11 / c++11标准编译
如果用命令 g++ -g -Wall main.cpp 编译以下代码 : /* file : main.cpp */ #include <stdio.h> int main() { in ...
- Poj 2499 Binary Tree(贪心)
题目链接:http://poj.org/problem?id=2499 思路分析:结点向左边移动时结点(a, b)变为( a+b, b),向右边移动时( a, b )变为( a, a + b); 为求 ...
- bespoke_百度百科
bespoke_百度百科 bespoke
- Maven创建项目: Failed to execute goal org.apache.maven.plugin( mvn archetype:create)
一.概述: 在使用mvn 命令mvn archetype:create -DgroupId=com.chuanliu.c11 -DartifactId=c11searcher在控制创建maven项目和 ...
- JavaScript自调用匿名函数
Self-Invoking Anonymous Function,即自调用匿名函数.顾名思义,该函数没有名称,不同的是,该函数定义后立即被调用.该函数的作用是在应用中初始化或做一次性工作. 普通匿名函 ...
- 如何A掉未来程序改
话说有这样一道神题:[集训队互测2015]未来程序·改. 大意是要求写一个简单的C++解释器!这里去掉了C++的许多特性,连简单的break和continue都没有了! 话说NOI被屠了之后,一时心血 ...
- Hdu 1079 Calendar Game
Problem地址:http://acm.hdu.edu.cn/showproblem.php?pid=1079 一道博弈题.刚开始想用判断P点和N点的方法来打表,但无奈不知是哪里出错,总是WA.于是 ...
- mongodb副本集自动切换修复节点解决方案
副本集部署 1.启动mongod 在每台运行mongod服务的机器上增加配置文件/etc/mongodb-rs.conf,内容为: [root@MongodbF-A etc]# vi /etc/mon ...