概念
启动参数是用来配置和调整系统设置的重要工具
参数来源
- 引导加载程序
- 代码嵌入
参数形式
- 设备树/Tag
- 命令行
命令行设置
代码嵌入
以arm 版本为例,其configs目录下的目标板的配置文件中存在如下类似内容:
....................
CONFIG_CMDLINE="noinitrd root=/dev/mtdblock2 rootfstype=jffs2 fbcon=rotate:1"
......................
GRUB设置
- 修改fgrub.cfg 的内容
- 使用grub2-mkconfig命令生效配置
设备树设置
代码嵌入
在头文件vmlinux.lds.h中可以发现如下定义:
#define KERNEL_DTB() \
STRUCT_ALIGN(); \
__dtb_start = .; \
KEEP(*(.dtb.init.rodata)) \
__dtb_end = .;
启动参数的获取
arc 架构
....................................
; Uboot - kernel ABI
; r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2
; r1 = magic number (always zero as of now)
; r2 = pointer to uboot provided cmdline or external DTB in mem
; These are handled later in handle_uboot_args()
st r0, [@uboot_tag]
st r1, [@uboot_magic]
st r2, [@uboot_arg]
.................................
由以上代码可知:
- 引导程序知道是否需要传递信息给内核
- 引导程序知道若需要传递信息给内核,信息的类型是怎样的
- 相关内容存储在uboot_arg全局变量中
而后在内核的初始化中将有如下逻辑
void __init setup_arch(char **cmdline_p)
{
handle_uboot_args(); //分析uboot传递的信息
..........................
}
void __init handle_uboot_args(void)
{
//默认分析信息来源
bool use_embedded_dtb = true;
bool append_cmdline = false;
/* check that we know this tag */
if (uboot_tag != UBOOT_TAG_NONE &&
uboot_tag != UBOOT_TAG_CMDLINE &&
uboot_tag != UBOOT_TAG_DTB) {
pr_warn(IGNORE_ARGS "invalid uboot tag: '%08x'\n", uboot_tag);
goto ignore_uboot_args;
}
if (uboot_magic != UBOOT_MAGIC_VALUE) {
pr_warn(IGNORE_ARGS "non zero uboot magic\n");
goto ignore_uboot_args;
}
if (uboot_tag != UBOOT_TAG_NONE &&
uboot_arg_invalid((unsigned long)uboot_arg)) {
pr_warn(IGNORE_ARGS "invalid uboot arg: '%px'\n", uboot_arg);
goto ignore_uboot_args;
}
/* see if U-boot passed an external Device Tree blob */
if (uboot_tag == UBOOT_TAG_DTB) { //若传递的是设备树,则开始分析指向设备树的内存
machine_desc = setup_machine_fdt((void *)uboot_arg);
/* external Device Tree blob is invalid - use embedded one */
use_embedded_dtb = !machine_desc; //判断是否是需要使用内嵌的设备树
}
if (uboot_tag == UBOOT_TAG_CMDLINE) //若传递的命令行,则执行字符串扩展
append_cmdline = true;
ignore_uboot_args:
if (use_embedded_dtb) { //使用内嵌的设备树信息
machine_desc = setup_machine_fdt(__dtb_start);
if (!machine_desc)
panic("Embedded DT invalid\n");
}
/*
* NOTE: @boot_command_line is populated by setup_machine_fdt() so this
* append processing can only happen after.
*/
if (append_cmdline) { //使用uboot传递的命令行参数
/* Ensure a whitespace between the 2 cmdlines */
strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
strlcat(boot_command_line, uboot_arg, COMMAND_LINE_SIZE);
}
}
在通过设备树指针寻找合适的machine_desc的过程中会执行scan指向设备树的内存
const struct machine_desc * __init setup_machine_fdt(void *dt)
{
const struct machine_desc *mdesc;
unsigned long dt_root;
if (!early_init_dt_scan(dt))
return NULL;
........................
}
bool __init early_init_dt_scan(void *params)
{
................
early_init_dt_scan_nodes();
return true;
}
void __init early_init_dt_scan_nodes(void)
{
...........................................
/* Retrieve various information from the /chosen node */
rc = early_init_dt_scan_chosen(boot_command_line);
if (rc)
pr_warn("No chosen node found, continuing without\n");
.............................
}
其核心函数如下
int __init early_init_dt_scan_chosen(char *cmdline)
{
int l, node;
const char *p;
const void *rng_seed;
const void *fdt = initial_boot_params;
//寻找合适的启动参数保持节点
node = fdt_path_offset(fdt, "/chosen");
if (node < 0)
node = fdt_path_offset(fdt, "/chosen@0");
if (node < 0)
/* Handle the cmdline config options even if no /chosen node */
goto handle_cmdline;
.....................
/* Retrieve command line */
//获取节点下的启动参数属性,若有效则拷贝
p = of_get_flat_dt_prop(node, "bootargs", &l);
if (p != NULL && l > 0)
strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE));
handle_cmdline:
/*
* CONFIG_CMDLINE is meant to be a default in case nothing else
* managed to set the command line, unless CONFIG_CMDLINE_FORCE
* is set in which case we override whatever was found earlier.
*/
//依据编译配置使用内置的命令行参数
#ifdef CONFIG_CMDLINE
#if defined(CONFIG_CMDLINE_EXTEND)
strlcat(cmdline, " ", COMMAND_LINE_SIZE);
strlcat(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)
strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#else
/* No arguments from boot loader, use kernel's cmdl*/
if (!((char *)cmdline)[0])
strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#endif
#endif /* CONFIG_CMDLINE */
pr_debug("Command line is: %s\n", (char *)cmdline);
return 0;
}
arm架构
arm架构中有一种tag的结构也可以传递启动参数,其逻辑如下:
void __init setup_arch(char **cmdline_p)
{
const struct machine_desc *mdesc = NULL;
void *atags_vaddr = NULL;
if (__atags_pointer)
atags_vaddr = FDT_VIRT_BASE(__atags_pointer);
setup_processor();
if (atags_vaddr) {
mdesc = setup_machine_fdt(atags_vaddr);
if (mdesc)
memblock_reserve(__atags_pointer,
fdt_totalsize(atags_vaddr));
}
if (!mdesc) //当通过设备树的方式获取machine_desc失败时使用tag的方式解析
mdesc = setup_machine_tags(atags_vaddr, __machine_arch_type);
......................
}