Kernel怎么解析这个传过来的参数呢?
(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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
arm64即时采用uefi也依然会通过fdt传递内核参数:
/sys/firmware/fdt
/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x29d (669)
// off_dt_struct: 0x38
// off_dt_strings: 0x1c8
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 17
// boot_cpuid_phys: 0x0
// size_dt_strings: 0xd5
// size_dt_struct: 0x190/ {
#size-cells = <0x00000002>;
#address-cells = <0x00000002>;
chosen {
linux,uefi-secure-boot = <0x00000002>;
linux,uefi-mmap-desc-ver = <0x00000001>;
linux,uefi-mmap-desc-size = <0x00000030>;
linux,uefi-mmap-size = <0x00000660>;
linux,uefi-mmap-start = <0x00000000 0xd5906018>;
linux,uefi-system-table = <0x00000000 0xebd00018>;
bootargs = "BOOT_IMAGE=/vmlinuz-5.4.18-110-generic root=UUID=f37c69cb-25df-419d-894e-f0835987b1c8 ro quiet splash loglevel=0 resume=UUID=716ad9b4-4ee1-4c2a-960d-b83e16a551b5 security=kysec";
linux,initrd-end = <0x00000000 0xd3e87611>;
linux,initrd-start = <0x00000000 0xcf00f000>;
};
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
uefi将内核当成efi文件执行入口:
efi_pe_entry:
{
cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); //获取cmdline 启动参数
efi_info("Booting Linux Kernel...\n");
si = setup_graphics();
status = handle_kernel_image(&image_addr, &image_size,
&reserve_addr,
&reserve_size,
image); //重新将内核镜像移动位置,地址保存在image_addr
status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr,
initrd_addr, initrd_size,
cmdline_ptr, fdt_addr, fdt_size);
update_fdt((void *)fdt_addr, fdt_size,
(void *)*new_fdt_addr, MAX_FDT_SIZE, cmdline_ptr,
initrd_addr, initrd_size);
fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
strlen(cmdline_ptr) + 1);
//最终更新fdt 供内核使用
efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr)); //重新进入内核arch/arm64/kernel/efi-entry.S 汇编
}
SYM_CODE_START(efi_enter_kernel)
/*
* efi_pe_entry() will have copied the kernel image if necessary and we
* end up here with device tree address in x1 and the kernel entry
* point stored in x0. Save those values in registers which are
* callee preserved.
*/
ldr w2, =primary_entry_offset
add x19, x0, x2 // relocated Image entrypoint
mov x20, x1 // DTB address
...
/* Jump to kernel entry point */
mov x0, x20
mov x1, xzr
mov x2, xzr
mov x3, xzr
br x19 //跳入内核,此时x0寄存器保存fdt的地址
SYM_CODE_END(efi_enter_kernel)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
(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)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
这里调用了:
- 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
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
__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
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
在setup_arch()的时候,调用setup_machine_fdt将fdt解析到了boot_command_line全局变量中
etup_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);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
在start_kernel()打印了cmdline.