baron · 1 天前

linux kernel中的cmdline的详细介绍

cmdline
1、向linux kernel添加cmdline的四种方式
(1)、 在dts中的bootargs中添加
(2)、在BoardConfig中添加
(3)、在uboot中添加
(4)、在android的Makefile中添加
2、在uboot中,将cmdline统一放置在FDT中
3、在kernel中,从FDT中解析处cmdline并使用
(1)、跳转linux kernel之前-准备cmdline
(2)、kernel启动-解析cmdline

1、向linux kernel添加cmdline的四种方式

在linux启动时候,串口log中会打印cmdline

[ 0.000000] c0 0 (swapper) Kernel command line: earlycon androidboot.selinux=permissive uart_dma keep_dbgclk_on clk_ignore_unused initrd=0xd0000000,38711808 rw crash_page=0x8f040000 initrd=/recoveryrc boot_reason=0x2000 ota_status=0x1001

在linux启动完成后,通过 cat /proc/cmdline也是可以看到cmdline. 那么cmdline是如何添加的呢?

(1)、 在dts中的bootargs中添加

/ {
model = "yyyyyyy";
compatible = "yyyyyyy", "xxxxxxxx";

chosen {

/*
 * initrd parameters not set in dts file since the ramdisk.img size
 * need to check in uboot, and the initrd load address and size will
 * set in uboot stage.
 */
bootargs = "earlycon androidboot.selinux=permissive uart_dma keep_dbgclk_on clk_ignore_unused";
stdout-path = "serial0:115200";

};
......
}

(2)、在BoardConfig中添加

vim device/xxx/xxx_evb/BoardConfigCommon.mk

BOARD_KERNEL_CMDLINE += androidboot.selinux=enforcing androidboot.hardware=xxxx_phone androidboot.dtbo_idx=0

(3)、在uboot中添加

vim u-boot/common/cmd_bootm.c

append_bootargs("recovery=1");

sprintf(dm_buf,"init=/init skip_initramfs rootwait root=/dev/dm-0 dm=\"system none ro,0 1 android-verity /dev/mmcblk0p%d\"",ret);
append_bootargs((const char *)dm_buf);

2、在uboot中,将cmdline统一放置在FDT中

以后再写,哈哈哈哈

3、在kernel中,从FDT中解析处cmdline并使用

(1)、跳转linux kernel之前-准备cmdline

在跳转linux kernel之前(如uboot中),将cmdline数据放到了FDT中,然后将FDT的地址写入到了X0中。然后再跳转linux kernel.

别问我怎么知道的,请看kernel-4.14/Documentation/arm64/booting.txt

Before jumping into the kernel, the following conditions must be met:

  • Quiesce all DMA capable devices so that memory does not get
    corrupted by bogus network packets or disk data. This will save
    you many hours of debug.
  • Primary CPU general-purpose register settings
    x0 = physical address of device tree blob (dtb) in system RAM.
    x1 = 0 (reserved for future use)
    x2 = 0 (reserved for future use)
    x3 = 0 (reserved for future use)

(2)、kernel启动-解析cmdline

linux kernel从stext开始启动,整个流程大概就是读取X0(FDT地址)保存到X21中,又将X21保存到__fdt_pointer全局变量中

然后再将__fdt_pointer解析处cmdline数据到boot_command_line全局变量中

/*
 * The following callee saved general purpose registers are used on the
 * primary lowlevel boot path:
 *
 * Register  Scope           Purpose
 * x21    stext() .. start_kernel() FDT pointer passed at boot in x0
 * x23    stext() .. start_kernel() physical misalignment/KASLR offset
 * x28    __create_page_tables()   callee preserved temp register
 * x19/x20  __primary_switch()     callee preserved temp registers
 */

ENTRY(stext)

bl    preserve_boot_args
bl    el2_setup            // Drop to EL1, w0=cpu_boot_mode
adrp    x23, __PHYS_OFFSET
and    x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0
bl    set_cpu_boot_mode_flag
bl    __create_page_tables
/*
 * The following calls CPU setup code, see arch/arm64/mm/proc.S for
 * details.
 * On return, the CPU will be ready for the MMU to be turned on and
 * the TCR will have been set.
 */
bl    __cpu_setup            // initialise processor
b    __primary_switch

ENDPROC(stext)

这里调用了:

preserve_boot_args

__primary_switch

在preserve_boot_args将X0(fdt地址)暂时先保存到了X21中

preserve_boot_args:

mov    x21, x0                // x21=FDT

adr_l    x0, boot_args            // record the contents of
stp    x21, x1, [x0]            // x0 .. x3 at kernel entry
stp    x2, x3, [x0, #16]

dmb    sy                // needed before dc ivac with
                    // MMU off

mov    x1, #0x20            // 4 x 8 bytes
b    __inval_dcache_area        // tail call

ENDPROC(preserve_boot_args)

__primary_switch调用了__primary_switched
__primary_switch:

ifdef CONFIG_RANDOMIZE_BASE

mov    x19, x0                // preserve new SCTLR_EL1 value
mrs    x20, sctlr_el1            // preserve old SCTLR_EL1 value

endif

bl    __enable_mmu

ifdef CONFIG_RELOCATABLE

bl    __relocate_kernel

ifdef CONFIG_RANDOMIZE_BASE

ldr    x8, =__primary_switched
adrp    x0, __PHYS_OFFSET
blr    x8



__primary_switched将X21(fdt地址)保存到了__fdt_pointer全局变量中

__primary_switched:

adrp    x4, init_thread_union
add    sp, x4, #THREAD_SIZE
adr_l    x5, init_task
msr    sp_el0, x5            // Save thread_info

adr_l    x8, vectors            // load VBAR_EL1 with virtual
msr    vbar_el1, x8            // vector table address
isb

stp    xzr, x30, [sp, #-16]!
mov    x29, sp

str_l    x21, __fdt_pointer, x5        // Save FDT pointer

ldr_l    x4, kimage_vaddr        // Save the offset between
sub    x4, x4, x0            // the kernel virtual and
str_l    x4, kimage_voffset, x5        // physical mappings

// Clear BSS
adr_l    x0, __bss_start
mov    x1, xzr
adr_l    x2, __bss_stop
sub    x2, x2, x0
bl    __pi_memset
dsb    ishst                // Make zero page visible to PTW

在setup_arch()的时候,调用setup_machine_fdt将fdt解析到了boot_command_line全局变量中

void __init setup_arch(char **cmdline_p)
{

pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());

......

*cmdline_p = boot_command_line;

......

setup_machine_fdt(__fdt_pointer);

......
}

setup_machine_fdt()—>early_init_dt_scan()—>early_init_dt_scan_nodes()

在中,将fdt解析到了boot_command_line中

of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line)

static void __init setup_machine_fdt(phys_addr_t dt_phys)
{

void *dt_virt = fixmap_remap_fdt(dt_phys);
const char *name;

if (!dt_virt || !early_init_dt_scan(dt_virt)) {
    pr_crit("\n"
        "Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
        "The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
        "\nPlease check your bootloader.",
        &dt_phys, dt_virt);

    while (true)
        cpu_relax();
}

name = of_flat_dt_get_machine_name();
if (!name)
    return;
/* backward-compatibility for third-party applications */
machine_desc_set(name);

pr_info("Machine model: %s\n", name);
dump_stack_set_arch_desc("%s (DT)", name);

}

bool __init early_init_dt_scan(void *params)
{

 bool status;

 status = early_init_dt_verify(params);
 if (!status)
     return false;

 early_init_dt_scan_nodes();
 return true;

}

void __init early_init_dt_scan_nodes(void)
{

 /* Retrieve various information from the /chosen node */
 of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

 /* Initialize {size,address}-cells info */
 of_scan_flat_dt(early_init_dt_scan_root, NULL);

 /* Setup memory, calling early_init_dt_add_memory_arch */
 of_scan_flat_dt(early_init_dt_scan_memory, NULL);

}

在start_kernel()打印了cmdline.

asmlinkage __visible void __init start_kernel(void)

{

pr_notice(“Kernel command line: %s\n”, boot_command_line);

}

推荐阅读
关注数
9491
文章数
256
vx: coding_the_world
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息