引导加载程序如何将内核命令行传递给内核



我们可以看到内核命令行如下:

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变量。

  1. 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
  1. 如果您不想使用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"

  1. 第三种方法是通过编译的设备树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中。

当调用启动代码时,它假设寄存器x0x3是引导参数,并将它们保存在boot_params阵列中。稍后,C代码验证只有第一个参数是非零的。其他三个必须为零;显然,它们在历史上曾被使用过。

因此,实际上只有一个参数。它是什么?x0寄存器指向FDT(扁平设备树(。此设备树blob还包含命令行。

请参阅drivers/of/fdt.c中的代码,了解如何从DT blob中检索并保留命令行(通过查找名为"bootargs"的节点(。

最新更新