首页 > 代码库 > 如何实现uboot和linux之间的参数传递

如何实现uboot和linux之间的参数传递

参考http://cgxcn.blog.163.com/blog/static/132312422009101133251202/

参考:http://blog.chinaunix.net/uid-14833587-id-76499.html

参考:http://lixuefeng26.blog.sohu.com/204383842.html

原理就是:uboot将要传递的参数,保存到一个指定的物理位置;然后linux从该物理位置获取数据即可

1.先看一下uboot自带的参数传递过程: 

A. 首先说明两个结构体:

boot/u-boot/include/asm-mips

typedef    struct       global_data {

bd_t          *bd;

unsigned long  flags;

unsigned long  baudrate;

unsigned long  have_console; /* serial_init() was called */

unsigned long  ram_size;         /* RAM size */

unsigned long  reloc_off; /* Relocation Offset */

unsigned long  env_addr;         /* Address  of Environment struct */

unsigned long  env_valid;         /* Checksum of Environment valid? */

void           **jt;          /* jump table */

} gd_t

boot/u-boot/include/asm-mips

typedef struct bd_info {

int              bi_baudrate;    /* serial console baudrate */

unsigned long  bi_ip_addr;       /* IP Address */

unsigned char  bi_enetaddr[6];        /* Ethernet adress */

unsigned long  bi_arch_number;     /* unique id for this board */

unsigned long  bi_boot_params;     /* where this board expects params */

unsigned long  bi_memstart;  /* start of DRAM memory */

unsigned long  bi_memsize;    /* size      of DRAM memory in bytes */

unsigned long  bi_flashstart;   /* start of FLASH memory */

unsigned long  bi_flashsize;     /* size  of FLASH memory */

unsigned long  bi_flashoffset; /* reserved area for startup monitor */

} bd_t;

B. do_bootm_linuxboot/u-boot/lib_mips/mips_linux.c)中,有如下处理:

linux_params_init (UNCACHED_SDRAM (gd->bd->bi_boot_params), commandline);  --- 此函数,就是将commandline所指示的启动参数,存放到UNCACHED_SDRAM (gd->bd->bi_boot_params)所指示的物理地址中;同时,利用全局变量linux_argc,linux_argv,linux_env来分别指示这些参数的总数,位置以及环境变量的位置

#if defined(CONFIG_AR7100) || defined(CONFIG_AR7240)

/* Pass the flash size as expected by current Linux kernel for AR7100 */

flash_size_mbytes = gd->bd->bi_flashsize/(1024 * 1024); 

theKernel (linux_argc, linux_argv, linux_env, flash_size_mbytes); /*启动内核,也就是说从ntohl (hdr->ih_ep)这个地址开始运行(这里不是函数调用,而是直接运行此地址上的东西;具体就是kernel_entry),其传递的参数就是linux_argc, linux_argv, linux_env, flash_size_mbytes*/

#else

而在linux/kernels/mips-linux-2.6.31/arch/mips/kernel/head.S中,有:

NESTED(kernel_entry, 16, sp)

LONG_S              a0, fw_arg0               # firmware arguments

LONG_S              a1, fw_arg1[xxx1] 

LONG_S              a2, fw_arg2

LONG_S              a3, fw_arg3

……..

j                  start_kernel

这些,就是将四个参数:linux_argc, linux_argv, linux_env, flash_size_mbytes,分别传递给:fw_arg0,fw_arg1,fw_arg2,fw_arg3了;并且最终跳转到start_kernel函数中

C. start_kernellinux/kernels/init/main.c)中,有:

->setup_arch(&command_line)(linux/kernels/arch/mips/kernel/setup.c)中,分别调用:

prom_init()(linux/kernels/arch/mips/xxx/prom.c)[xxx2] 

arch_mem_init(cmdline_p); (linux/kernels/arch/mips/kernel/setup.c)[xxx3] 

 

2. 那么,如果我们要将uboot下自定义的一些参数,传递给linux内核。该如何做? 这里,笔者由于工作需要,需要将uboot下记录的一个bootpara结构体参数,传递给linux内核。 实现方案如下:

² Cmd_bootm.c中的do_bootm函数,在:

case IH_COMP_LZMA:

           printf ("   Uncompressing %s ... ", name);

           i = lzma_inflate ((unsigned char *)data, len, (unsigned char*)ntohl(hdr->ih_load), &unc_len);

           if (i != LZMA_RESULT_OK) {

                    printf ("LZMA ERROR %d - must RESET board to recover\n", i);

                    SHOW_BOOT_PROGRESS (-6);

                    udelay(100000);

                    do_reset (cmdtp, flag, argc, argv);

           }

/*当解压缩内核成功后,则意味着后面就要调用do_bootm_linux启动内核了;那么这个时候,需要将uboot下记录的boot_para信息,存放到内存的指定位置;

从而传递给linux内核;

所选择的存放位置,需要放在内核解压缩地址的前面(这里留1K空间,存放bootpara参数);这样才能不会影响内核的启动*/          

#ifdef CONFIG_XXX_BOOT             

           else {

                    unsigned int save_position = ntohl(hdr->ih_load) - 1024;[xxx4] 

                    save_bootpara(save_position, &gBootPara);

           }

#endif                 

           break;

其中,

void save_bootpara(unsigned int save_position, T_BOOT_PARA *ptBootPara) {

    memmove((unsigned char *)save_position, ptBootPara, sizeof(T_BOOT_PARA));

    return;

}

² Prom.cprom_init函数中,新增:

#define KERNEL_LOAD_ADDR  0x80002000[xxx5] 

#ifdef CONFIG_XXX_BOOT    

    get_bootpara(KERNEL_LOAD_ADDR-1024[xxx6] , ptBootPara);

#endif 

其中,

int get_bootpara(unsigned int load_position, T_BOOT_PARA *ptBootPara) {

      memmove(ptBootPara, (unsigned char *)load_position, sizeof(T_BOOT_PARA));[xxx7] 

 return 0;

           }


 [xxx1]实际对应linux_argv的值,也就是指向存放参数的地址

 [xxx2]prom_init中,就有使用fw_arg0,fw_arg1,fw_arg2,fw_arg3的处理了,利用这些参数,恢复成全局变量:arcs_cmdline的值

 [xxx3]利用arcs_cmdline,得到command_lineboot_command_line的值

 [xxx4]hdr->ih_load具体是0x80002000,这是由uboot的mkimages在制作内核镜像时指定的。这是一个位于mips的KSEG0地址空间的地址。而KSEG0地址空间的范围是0x8000,0000--0x9FFF,FFFF。因此,所能利用的也就是从0x8000,0000--hdr->ih_load这段范围了。正好,笔者选择的1K是可以放得下的。
这里的利用linux内核的内存解压缩地址的前1024字节的空间,来存放要传递的参数。

 [xxx5]uboot中,将linux内核释放到的内存地址

 [xxx6]存储bootpara参数的地址

 [xxx7]获取存储的bootpara参数内容

如何实现uboot和linux之间的参数传递