Linux kernel 解析 cmdline
参考其他博客(结尾处标明),结合自己近期的工作内容,记录此文档。
- 问题: uboot启动成功后将cmdline传给kernel,进入kernel启动阶段,那么kernel如何解析cmdline呢?
uboot 传递cmdline
boot_prep_linux(arch/arm/lib/bootm.c) 中
static void boot_prep_linux(bootm_headers_t *images)
{
char *commandline = env_get("bootargs");
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
#ifdef CONFIG_OF_LIBFDT
debug("using: FDT\n");
if (image_setup_linux(images)) {
printf("FDT creation failed! hanging...");
hang();
}
#endif
} else if (BOOTM_ENABLE_TAGS) {
debug("using: ATAGS\n");
setup_start_tag(gd->bd);
if (BOOTM_ENABLE_SERIAL_TAG)
setup_serial_tag(¶ms);
if (BOOTM_ENABLE_CMDLINE_TAG)
setup_commandline_tag(gd->bd, commandline);
if (BOOTM_ENABLE_REVISION_TAG)
setup_revision_tag(¶ms);
if (BOOTM_ENABLE_MEMORY_TAGS)
setup_memory_tags(gd->bd);
if (BOOTM_ENABLE_INITRD_TAG) {
/*
* In boot_ramdisk_high(), it may relocate ramdisk to
* a specified location. And set images->initrd_start &
* images->initrd_end to relocated ramdisk's start/end
* addresses. So use them instead of images->rd_start &
* images->rd_end when possible.
*/
if (images->initrd_start && images->initrd_end) {
setup_initrd_tag(gd->bd, images->initrd_start,
images->initrd_end);
} else if (images->rd_start && images->rd_end) {
setup_initrd_tag(gd->bd, images->rd_start,
images->rd_end);
}
}
setup_board_tags(¶ms);
setup_end_tag(gd->bd);
} else {
printf("FDT and ATAGS support not compiled in - hanging\n");
hang();
}
board_prep_linux(images);
}
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *)bd->bi_boot_params;
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);
}
static void setup_commandline_tag(bd_t *bd, char *commandline)
{
char *p;
if (!commandline)
return;
/* eat leading white space */
for (p = commandline; *p == ' '; p++);
/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if (*p == '\0')
return;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size =
(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
}
params是一个全局静态变量用来存储要传给kernel的参数。
setup_start_tag初始化第一个tag,是tag_core类型的tag。最后调用tag_next跳到第一个tag末尾,为下一个tag做准备。
setup_commandline_tag设置第二个tag的hdr.tag为ATAG_CMDLINE,然后拷贝cmdline到tags的cmdline结构体中,跳到下一个tag。
setup_end_tag设置最后tag为ATAG_NONE,标志tag结束。
kernel 解析cmdline
参考链接3: link
Linux kernel 中可使用宏 __setup() 处理内核启动参数的解析。
调用流程
init/main.c
start_kernelparse_args(“Booting kernel”,
static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, NULL, &unknown_bootoption);
/* Args looks like “foo=bar,bar2 baz=fuz wiz”. */parse_one unknown_bootoption obsolete_checksetup setup_func // 即obs_kernel_param定义的setup_func()
源代码
struct obs_kernel_param {
const char *str; //参数
int (*setup_func)(char *); //设置参数值的函数
int early;
};
/*
* Only for really core code. See moduleparam.h for the normal way.
*
* Force the alignment so the compiler doesn't space elements of the
* obs_kernel_param "array" too far apart in .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 }
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
static bool __init obsolete_checksetup(char *line)
{
const struct obs_kernel_param *p;
bool had_early_param = false;
p = __setup_start;
do {
int n = strlen(p->str);
if (parameqn(line, p->str, n)) {
if (p->early) {
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '\0' || line[n] == '=')
had_early_param = true;
} else if (!p->setup_func) {
pr_warn("Parameter %s is obsolete, ignored\n",
p->str);
return true;
} else if (p->setup_func(line + n))
return true;
}
p++;
} while (p < __setup_end);
return had_early_param;
}
实例
static int __init serial_is_dis_setup(char *str)
{
if (strcmp(str, "0")){
pr_err("%s[%d]: will disable serial \n", __FUNCTION__, __LINE__);
isUartRxDis_nsb = 1;
isUartTxDis_nsb = 1;
}
return 1;
}
__setup("serial_is_dis=", serial_is_dis_setup);
//#define BOOT_CONSOLE_DISABLE_ARGS "earlycon=serial,0xf4329188 serial_is_dis=1 console=ttyS0,115200"
宏展开
static const char __setup_str_serial_is_dis_setup[] __initconst \
__aligned(1) = "serial_is_dis="; \
static struct obs_kernel_param __setup_str_serial_is_dis_setup \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_serial_is_dis_setup, serial_is_dis_setup, 0 } ## __setup_str_serial_is_dis_setup 在上面定义