我们可以看到内核命令行如下:
ckim@chan-ubuntu:~/$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.15.0-122-generic root=UUID=99c66a0a-39c1-451c-9f72-ad1576aafb41 ro quiet splash
这个命令行似乎就是grub传递给内核进行引导的命令行。这个命令行实际上是如何传递给内核程序的?我想也许命令行是作为字符串加载到内存中的,并且处理器的寄存器(比如x1,如果是arm64处理器(被设置为该字符串地址?我对arm64病例特别感兴趣。
我有时会想到同样的问题。所以现在是深入研究的时候了。
x86使用特殊协议来启动Linux,而在arm64的情况下,则使用不同的方案。对于与内核的通信,引导加载程序只将单个地址加载的扁平设备树(FDT(放入x0
寄存器。
以下是Linux和设备树的摘录,运行时配置:
在大多数情况下,DT将是从固件到内核,因此也用于在运行时传递配置数据,如内核参数字符串和位置initrd图像的。
大部分数据包含在/selected节点中,并且在启动时Linux看起来像这样:
chosen { bootargs = "console=ttyS0,115200 loglevel=8"; initrd-start = <0xc8000000>; initrd-end = <0xc8200000>; };
这里是DT的另一个例子。
接下来,在内核早期引导期间,OF/FFDT模块解析传递的设备树并填充boot_command_line
变量。
-
U-Boot环境变量bootargs用于传递内核参数通过命令行传递到内核。所以你必须定义U-Boot中的环境变量手动或使用U-Boot脚本。
setenv bootargs <your argumets>
这就是命令行参数在dmesg:中的样子
Kernel command line: console=ttyS0,115200n8 init=/sbin/init root=/dev/ram rw earlyprintk uio_pdrv_genirq.of_id=generic-uio xenon-mtd-concat.total_flash_size=0x8000000
或者您可以使用检查命令行参数
cat /proc/cmdline
- 如果您不想使用U-Bootbootargs变量,您可以定义menuconfigCONFIG_CMDLINE中的内核命令行字符串,以及启用CONFIG_CMDLINE_FORCE以覆盖U-Boot
CONFIG_CMDLINE_FORCE=y
CONFIG_CMDLINE="console=ttyS0,115200n8 init=/sbin/init root=/dev/ram rw earlyprintk uio_pdrv_genirq.of_id=generic-uio"
- 第三种方法是通过编译的设备树blob传递它。但是U-Boot优先于设备树。因此,要使用设备树,您应该删除引导参数
env delete bootargs
设备树节点示例:
chosen {
bootargs = "console=ttyS0,115200 loglevel=8 init=/sbin/init";
在内核源代码init/main.c中,内核入口点是start_kernel((:
[...]
/* Untouched command line saved by arch-specific code. */
char __initdata boot_command_line[COMMAND_LINE_SIZE];
[...]
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
char *after_dashes;
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them.
*/
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
early_security_init();
setup_arch(&command_line);
[...]
pr_notice("Kernel command line: %sn", boot_command_line);
[...]
在前面的几行中,有一个对setup_arch((的调用,以依赖于体系结构的方式获取命令行。
对于ARM64,setup_arch((在arch/ARM64/kernel/setup.c:中定义
void __init setup_arch(char **cmdline_p)
{
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = (unsigned long) _end;
*cmdline_p = boot_command_line;
[...]
关于食物,这里有详细说明。
详细信息因引导加载程序和特定内核的head.S
汇编语言启动模块而异。
在Arm64上,启动代码位于文件arch/arm64/kernel/head.S
中。
当调用启动代码时,它假设寄存器x0
到x3
是引导参数,并将它们保存在boot_params
阵列中。稍后,C代码验证只有第一个参数是非零的。其他三个必须为零;显然,它们在历史上曾被使用过。
因此,实际上只有一个参数。它是什么?x0
寄存器指向FDT(扁平设备树(。此设备树blob还包含命令行。
请参阅drivers/of/fdt.c
中的代码,了解如何从DT blob中检索并保留命令行(通过查找名为"bootargs"
的节点(。