u-boot向uClinux的参数传递

本文的开发环境为:u-boot-1.1.6,  uClinux-2.6.x,  bf561 DSP
1   u-boot调用内核的方法
我们知道,利用u-boot可以加载uClinux。它可以将压缩后的uClinux映象在SDRAM中解压并调用,从而将系统控制权交给uClinux。那么它是如何做到这一点的呢?下面的代码来自u-boot/lib_blackfin/bf533_linux.c,从中可以得到答案。
extern image_header_t header;
void do_bootm_linux(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[],
                    ulong addr, ulong * len_ptr, int verify)
{
       int (*appl)(char *cmdline);
       char *cmdline;
 
#ifdef SHARED_RESOURCES
       swap_to(FLASH);
#endif
 
       appl = (int (*)(char *))ntohl(header.ih_ep);
       printf("Starting Kernel at = %x/n", appl);
       cmdline = make_command_line();
       if (icache_status()) {
              flush_instruction_cache();
              icache_disable();
       }
       if (dcache_status()) {
              flush_data_cache();
              dcache_disable();
       }
       (*appl)(cmdline);
}
全局变量header中存放了压缩的uClinux映象文件的信息(参见《 u-boot引导uclinux》一文),ip_ep这个成员存放的就是uClinux解压后的入口点的地址,所以在此直接将它赋给appl作为函数指针。而cmdline这个变量存放的就是要传递给uClinux的参数,它是一个字符串。
关键的调用是由(*appl)(cmdline);来完成的,我们看看它的汇编代码:
R0 = [FP – 4];
P1 = [FP – 8];
CALL(P1);
也就是说,编译器是将要传递的字符串参数指针放在R0中的。其实函数调用时,参数使用的寄存器应该是由编译器决定的。幸运的是VDSP和GCC都是使用的R0!
2   uClinux对接收参数的处理
uClinux的入口点在head.s中,它的第一行语句就是:
       /* R0: argument of command line string, passed from uboot, save it */
       R7 = R0;
这样就把指针保存到了R7中,再往下看有一行语句:
/* pass the uboot arguments to the global value command line */
       R0 = R7;
       call.x _cmdline_init;
cmdline_init是用C写的一个函数,它的实现为:
void cmdline_init(unsigned long r0)
{
       if (r0)
              strncpy(command_line, (char *)r0, COMMAND_LINE_SIZE);
}
这样就把在u-boot存储空间中的启动参数复制到了uClinux管理的存储空间中。而u-boot中的存储空间就可以归uClinux使用了。
3   uClinux将参数传递给相应的模块
在uClinux中,每个模块都可以要求接收从u-boot中传递过来的参数,那么它们是怎么实现的呢?
在init/main.c中有一个函数:
 
/* Check for early params. */
static int __init do_early_param(char *param, char *val)
{
       struct obs_kernel_param *p;
 
       for (p = __setup_start; p < __setup_end; p++) {
              if (p->early && strcmp(param, p->str) == 0) {
                     if (p->setup_func(val) != 0)
                            printk(KERN_WARNING
                                   "Malformed early option '%s'/n", param);
              }
       }
       /* We accept everything at this stage. */
       return 0;
}
它的作用就是将每个参数分发给相应的模块。从代码中可以看出,它将比较传入的参数名称和各个模块需要的参数名称,如果名称一致,它就调用这个模块提供的回调函数。
那么__setup_start和__setup_end是个什么东西呢?
在uClinux的链接文件中有这么几行:
                     ___setup_start = .;
                     INPUT_SECTIONS($LIBRARIES_SML3_CM(.init.setup))
                     ___setup_end = .;
意思就是说__setup_start和__settup_end之间存放的是.init.setup这个段中的内容。如果一个模块需要接收传入参数的话,它只要将一个struct obs_kernel_param结构体放在.init.setup段中就行了。
仔细看各模块的代码可以发现类似这样的语句:
/* init/do_mounts.c */
static char __initdata saved_root_name[64];
static int __init root_dev_setup(char *line)
{
       strlcpy(saved_root_name, line, sizeof(saved_root_name));
       return 1;
}
__setup("root=", root_dev_setup);
关键就在__setup这个宏中:
///*
// * Only for really core code. See moduleparam.h for the normal way.
// *
// * Force the alignment so the compiler doesn't space elements of the
// * obs_kernel_param "array" too far apart in .init.setup.
// */
#define __setup_param(str, unique_id, fn, early)                 /
       static char __setup_str_##unique_id[] __initdata = str;       /
       static struct obs_kernel_param __setup_##unique_id   /
              __attribute_used__                      /
              __attribute__((__section__(".init.setup")))    /
              __attribute__((aligned((sizeof(long))))) /
              = { __setup_str_##unique_id, fn, early }
//
#define __setup(str, fn)                               /
       __setup_param(str, fn, fn, 0)
这样,通过__attribute__的控制就可以保证将参数放在.init.setup这个段中了。如果我们想知道内核支持的参数,只要查找c文件中的__setup宏就行了。
 

你可能感兴趣的:(u-boot向uClinux的参数传递)