cmdline(二):uboot cmdline怎么传?&&cmdline kernel怎么用?

简介: cmdline(二):uboot cmdline怎么传?&&cmdline kernel怎么用?


1、uboot以tag方式给内核传参–怎么传?

我们知道cmdline是由bootloader传给kernel的。

怎么传输的?

uboot是通过在启动内核时,向内核传递tag参数,其中就包括cmdline

1、tag方式传参

  • (1)struct tag,tag是一个数据结构,在uboot和linux kernel中都有定义tag数据机构,而且定义是一样的。
  • (2)tag_header和tag_xxx。tag_header中有这个tag的size和类型编码,kernel拿到一个tag后先分析tag_header得到tag的类型和大小,然后将tag中剩余部分当作一个tag_xxx来处理。
  • (3)tag_start与tag_end。kernel接收到的传参是若干个tag构成的,这些tag由ATAG_CORE类型的tag起始,到ATAG_NONE类型的tag结束。
  • (4)tag传参的方式是由linux kernel发明的,kernel定义了这种向自身传参的方式,uboot只是实现了这种传参方式从而可以支持给kernel传参。(接口的依赖倒置)

2、内核如何接收tag参数

启动内核的代码:theKernel (0, machid, bd->bi_boot_params);

其中bd->bi_boot_params就是所有tag结构体所在的首地址,这个地址是保存在全局变量gd->bd中的,

在uboot启动的前期会指定内存地址用于存放tag结构体,然后在启动内核的时候传给内核,

内核拿到地址就会从该地址去遍历tag结构体,内核会判断tag的类型,如果是ATAG_CORE类型的tag则是起始的tag

如果是ATAG_NONE则是最后一个tag结构体,不用再往后遍历。

3、tag结构体

struct tag_header {
    u32 size; //结构体的大小
    u32 tag;  //结构体的类型
  };
struct tag {
       struct tag_header hdr;
    union {   //此枚举体包含了uboot传给内核参数的所有类型
        struct tag_core         core;
        struct tag_mem32        mem;
        struct tag_videotext    videotext;
        struct tag_ramdisk      ramdisk;
        struct tag_initrd       initrd;
        struct tag_serialnr     serialnr;
        struct tag_revision     revision;
        struct tag_videolfb     videolfb;
        struct tag_cmdline      cmdline;
        /*
        * Acorn specific
        */
        struct tag_acorn        acorn;
        /*
         * DC21285 specific
         */
        struct tag_memclk       memclk;
        struct tag_mtdpart      mtdpart_info;
    } u;
};

4、构建tag结构体

/* The list must start with an ATAG_CORE node */
  #define ATAG_CORE 0x54410001
  /* The list ends with an ATAG_NONE node. */
  #define ATAG_NONE 0x00000000
  #define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
  #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
  static struct tag *params;
  static void setup_start_tag (bd_t *bd)
  {
    params = (struct tag *) bd->bi_boot_params;//bd->bi_boot_params是专门用于保存tag结构体的内存首地址
    params->hdr.tag = ATAG_CORE;  //ATAG_CORE类型是tag结构体的开始
    params->hdr.size = tag_size (tag_core); //结构体的大小
    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;
    params = tag_next (params); //将指针偏移params->hdr.size个字节,让params指向下一个可用的内存地址
  }
  `````````中间省略掉其他类型tag结构体的构建
  static void setup_end_tag (bd_t *bd)
  {
    params->hdr.tag = ATAG_NONE;
    params->hdr.size = 0;
  }

当你想实现相应的参数在uboot传输到kernel,比如uboot在设备树解析后,把里面的参数传输到kernel。那么你要在uboot和kernel都相应的添加,你参考已经存在的tag结构体添加解析,uboot中传递,内核中解析。(但是推荐设备树传递,比较方便。)

因此参数的传递其实也是通过地址的方式:uboot把内核复制到SDRAM之后,需要跳转到内核的入口函数执行。在跳转之前,还要给内核传递启动参数。传递方式是uboot把启动参数按一定的格式放在指定的地址(位于SDRAM),启动内核之后,内核再去这个地址上读取启动参数。

在uboot中,将cmdline统一放置在FDT中。FDT,flatted device tree,扁平设备树。熟悉linux的人对这个概念应该不陌生。

简单理解为将部分设备信息结构存放到device tree文件中。

2、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)

(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);
}
目录
相关文章
|
1月前
|
存储 编译器 Linux
完全理解ARM启动流程:Uboot-Kernel
完全理解ARM启动流程:Uboot-Kernel
82 0
|
1月前
|
Linux 开发工具 Android开发
cmdline(一):cmdline是什么?&&cmdline怎么添加?
cmdline(一):cmdline是什么?&&cmdline怎么添加?
123 0
|
1月前
|
存储 安全 Linux
UBoot怎么跳转到Kernel:uboot与linux的交界
UBoot怎么跳转到Kernel:uboot与linux的交界
59 0
|
1月前
|
编译器
内核编译bpf
内核编译bpf
34 0
|
Linux 编译器
Uboot + Kernel
Uboot + Kernel
159 0
|
存储 Java Unix
Linux下的lds链接脚本基础
Linux下的lds链接脚本基础
|
存储 Linux C语言
Linux下的lds链接脚本详解【转】
转自:http://www.cnblogs.com/li-hao/p/4107964.html 转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.
1515 0
|
Linux
linux kernel 为什么需要initrd
今天看了一本书, linux内核技术手册, 很多东西豁然开朗, 里面有一些东西写点总结给大家看一下 其实我还挺喜欢看手册之类的书, 因为看完之后, 可以对某个工具的所有功能有个大概的了解, 比如Makefile手册, vim手册, gcc手册. 虽然所有的用法不会都去尝试一遍, 但是知道了有这个东西, 哪天用得时候就会想起来, 不然的话, 需要解决一个问题的时候, 都不知道有这个东西的存在,
2089 0

热门文章

最新文章