Chan Kim
Chan Kim

Reputation: 5929

how does the bootloader pass the kernel command line to the kernel?

We can see the kernel command line like this :

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

This command line seems to be what grub has passed to the kernel for booting. How is this command line actually passed to the kernel program? I guess maybe the command line is loaded in memory as a string and a register of the processor (like x1, if arm64 processor) is set to that string address? I'm interested in arm64 case especially.

Upvotes: 5

Views: 4086

Answers (4)

Dražen Grašovec
Dražen Grašovec

Reputation: 802

  1. U-Boot enviroment variable bootargs is used to pass kernel arguments to kernel via command line. So you have to define this enviroment variable in U-Boot manually or with U-Boot script.

    setenv bootargs <your argumets>
    

This is how command line arguments looks in 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

Or you can check command line arguments with

cat /proc/cmdline
  1. If you dont want to use U-Boot bootargs variable, you can define kernel command line string in menuconfig CONFIG_CMDLINE, and enable the CONFIG_CMDLINE_FORCE to override 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. Third method is to pass it via compiled device-tree blob. But U-Boot has precedence over device-tree. So to use device-tree you should delete bootargs

env delete bootargs

Device tree node example:

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

Upvotes: 3

SergA
SergA

Reputation: 1174

The same question sometimes popped up to me. So this is a moment to deep dive.

While x86 uses special protocol for booting Linux, in the case of arm64 a different scheme is used. For communication with kernel, boot loader puts only single address - loaded flatten device tree (FDT) into the x0 register.

Here is excerpt from Linux and the Device Tree, Runtime configuration:

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>;
};

Here is another example of DT.

Next, during kernel early boot, the OF/FDT module parses the passed device tree and fill in boot_command_line variable.

Upvotes: 4

Kaz
Kaz

Reputation: 58578

The details vary based on the bootloader and he head.S assembly language startup module of the specific kernel.

On Arm64, that startup code is in the file arch/arm64/kernel/head.S.

When the startup code is invoked, it assumes that the registers x0 through x3 are boot parameters and saves them in the boot_params array. Later, C code validates that only the first parameter is nonzero. The other three must be zero; evidently, they were historically used.

Thus, there is only one parameter, effectively. What is it? The x0 register points to the FDT (flattened device tree). This device tree blob contains the command line also.

See the code in drivers/of/fdt.c how the command line is retrieved from he DT blob (by looking for a node called "bootargs"), and retained.

Upvotes: 1

Rachid K.
Rachid K.

Reputation: 5211

In the kernel source code init/main.c, the kernel entry point is 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);
[...]

In the preceding lines, there is a call to setup_arch() to get the command line in an architecture dependent way.

For ARM64, setup_arch() is defined in 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;
[...]

Concerning grub, it is detailed here.

Upvotes: 1

Related Questions