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

4
我们可以像这样查看内核命令行:

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用于启动内核的。实际上,这个命令行是如何传递给内核程序的?我猜想可能是将命令行作为字符串加载到内存中,并且处理器的一个寄存器(例如arm64处理器的x1寄存器)被设置为该字符串地址?我特别关注arm64情况。

4个回答

3

有时我也会遇到同样的问题,所以现在是深入研究的时候了。

虽然 x86 使用 特殊协议来引导Linux,但在 arm64 的情况下,采用了不同的方案。与内核通信时,引导加载程序只将一个地址-已加载的平面设备树 (FDT)放入x0寄存器中。

这里是来自Linux 和设备树,运行时配置的摘录:

In most cases, a DT will be the sole method of communicating data from firmware to the kernel, so also gets used to pass in runtime and configuration data like the kernel parameters string and the location of an initrd image.

Most of this data is contained in the /chosen node, and when booting Linux it will look something like this:

chosen {
        bootargs = "console=ttyS0,115200 loglevel=8";
        initrd-start = <0xc8000000>;
        initrd-end = <0xc8200000>;
};

这里有一个DT的例子

接下来,在内核早期引导期间,OF/FDT模块解析传递的设备树填充boot_command_line变量


2
  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-Boot的bootargs变量,可以在menuconfig CONFIG_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优先于设备树。因此,要使用设备树,您应该删除bootargs

env delete bootargs

设备树节点示例:

chosen {
    bootargs = "console=ttyS0,115200 loglevel=8 init=/sbin/init";

1

具体细节取决于引导加载程序和特定内核的head.S汇编语言启动模块。

在Arm64上,该启动代码位于文件arch/arm64/kernel/head.S中。

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

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

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


谢谢,这对我很有帮助。在哪里查看呢? - Chan Kim

1
在内核源代码中的 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: %s\n", 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;
[...]

关于 grub,详见这里

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接