linux启动 之 bootloader参数传递

bootloader 结束时要传三个值分别给r0 r1 r2三个寄存器。
一、cpuid
根据我个人的理解,r0寄存器可能应该是存储cpuid。cpuid的作用是什么呢? include/asm/procinfo.h中有个结构体:
struct proc_info_list {
    unsigned int        cpu_val;
    unsigned int        cpu_mask;
    unsigned long        __cpu_mm_mmu_flags;    /* used by head.S */
    unsigned long        __cpu_io_mmu_flags;    /* used by head.S */
    unsigned long        __cpu_flush;        /* used by head.S */
    const char        *arch_name;
    const char        *elf_name;
    unsigned int        elf_hwcap;
    const char        *cpu_name;
    struct processor    *proc;
    struct cpu_tlb_fns    *tlb;
    struct cpu_user_fns    *user;
    struct cpu_cache_fns    *cache;
};
这个结构体保存了一些标志、名字、和core的操作函数,启动时会用到。
arch/arm/mm/proc-*.S文件针对arm的不同的core会填充一个结构体,编译链接时会将其放在(arch/arm/kernel/vmlinux.lds.S)
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
的段里。
start_kernel->setup_arch->setup_processor->list = lookup_processor_type(read_cpuid_id());
会根据read_cpuid_id()返回的cpuid号找到对应core的这个struct proc_info_list结构体。
read_cpuid_id->read_cpuid
#define read_cpuid(reg)             /                                                                 
  ({                /
    unsigned int __val;         /
    asm("mrc  p15, 0, %0, c0, c0, " __stringify(reg)  /
        : "=r" (__val)          /
        :             /
        : "cc");            /
    __val;              /
  })
这个是arm的协处理器的操作语句,大概意思是把从协处理器p15的c0中读出cpuid给__val,返回__val。
据此看来,kernel可以自己找出cpuid,进而找出它的struct proc_info_list结构体,所以不需要bootloader做什么,所以r0寄存器普遍保存的都是0。
二、machine_arch_type
这个值是kernel执行make XXXX_defconfig 后生成的,该文件在arch/arm/configs/下。生成他的是一个脚本和配置文件,在arch/arm/tools/下,用脚本gen-mach-types,通过mach-types生成
include/asm-arm/mach-types.h文件。
arch/arm/tools/mach-types文件中的行是关于你新板型的信息eg:
mx51_swan      MACH_MX51_SWAN  MX51_SWAN   2989
编译时根据该行会生成4个变量: machine_is_xxx  CONFIG_xxxx   MACH_TYPE_xxx   number
会在./include/asm-arm/mach-types.h中生成machine_is_mx51_swan() 和MACH_TYPE_MX51_SWAN宏,MACH_TYPE_MX51_SWAN的值是number
#ifdef CONFIG_MACH_MX50_RDP
  # ifdef machine_arch_type
  #  undef machine_arch_type
  #  define machine_arch_type __machine_arch_type
  # else                                                                                                
  #  define machine_arch_type MACH_TYPE_MX50_RDP
  # endif
  # define machine_is_mx50_rdp()  (machine_arch_type == MACH_TYPE_MX50_RDP)
  #else
  # define machine_is_mx50_rdp()  (0)
  #endif
如果bootloader传机器编号给kernel,__machine_arch_type就会成为这个值(猜测)。machine_arch_type最终会成为MACH_TYPE_MX50_RDP这个值(number)。而这个号
有什么用呢?和cpuid的用法一样,arch/arm/mach-XXX/board-XXX.c文件中MACHINE_START定义了一些struct machine_desc的结构体(arch/arm/include/asm/mach/arch.h)
struct machine_desc {
    /*
(>   * Note! The first four elements are used
     * by assembler code in head.S, head-common.S
     */
    unsigned int    nr;   /* architecture number  */
    unsigned int    phys_io;  /* start of physical io */
    unsigned int    io_pg_offst;  /* byte offset for io 
               * page tabe entry  */
}>
    const char    *name;    /* architecture name  */
    unsigned long   boot_params;  /* tagged list    */
  
    unsigned int    video_start;  /* start of video RAM */
    unsigned int    video_end;  /* end of video RAM */
  
    unsigned int    reserve_lp0 :1; /* never has lp0  */
    unsigned int    reserve_lp1 :1; /* never has lp1  */
    unsigned int    reserve_lp2 :1; /* never has lp2  */
    unsigned int    soft_reboot :1; /* soft reboot    */
    void      (*fixup)(struct machine_desc *,
             struct tag *, char **,
             struct meminfo *);
    void      (*map_io)(void);/* IO mapping function  */
    void      (*init_irq)(void);
    struct sys_timer  *timer;   /* system tick timer  */
    void      (*init_machine)(void);
  };
他主要是用来定义具体板子信息,最主要的(个人认为)unsigned int    nr;(机器号) void      (*init_machine)(void);(板子的初始化函数)。
每个不同板子会有自己的struct machine_desc结构体对象,均被放在
__arch_info_begin = .;
 *(.arch.info.init)
 __arch_info_end = .;
段里。
start_kernel->setup_arch->mdesc = setup_machine(machine_arch_type)->list = lookup_machine_type(nr);(arch/arm/kernel/head-common.S)->__lookup_machine_type
会根据上面生成的machine_arch_type机器号在__arch_info_begin段里找,只要machine_arch_type和struct machine_desc里的nr相等的就找到了,返回该结构体的首地址。
那么struct machine_desc里的nr又是什么时候被赋值的呢?请看arch/arm/include/asm/mach/arch.h文件
#define MACHINE_START(_type,_name)      /
  static const struct machine_desc __mach_desc_##_type  /
   __used             /
   __attribute__((__section__(".arch.info.init"))) = {  /
8>  .nr   = MACH_TYPE_##_type,    /
    .name   = _name,
2>
  #define MACHINE_END       /
  };
正是在定义处为nr赋了上面生成的MACH_TYPE_xxx的值。
由此看出,好像bootloader不传机器码也没什么问题。
三、内核参数链表
r2中存储的是内核参数链表,这个值可不是可有可无的,应为他里面有各种资源信息和kernel启动参数。
BootLoader 可以通过两种方法传递参数给内核, 一种是旧的参数结构方式(parameter_struct),主要是2.6 之前的内核使用的方式。另外一种就是现在的2.6 内核在用的参数链表 (tagged list) 方式。这些参数主要包括,系统的根设备标志,页面大小,内存的起始地址和大小,RAMDISK 的起始地址和大小,压缩的RAMDISK 根文件系统的起始地址和大小,内核命令参数等[3][4][5]。

内核参数链表的格式和说明可以从内核源代码目录树中的 include/asm-arm/setup.h[2]中找到,参数链表必须以ATAG_CORE 开始,以ATAG_NONE 结束。这里的ATAG_CORE,ATAG_NONE 是各个参数的标记,本身是一个32 位值,例如:ATAG_CORE=0x54410001。

其它的参数标记还包括: ATAG_MEM32 , ATAG_INITRD , ATAG_RAMDISK ,ATAG_COMDLINE 等。每个参数标记就代表一个参数结构体,由各个参数结构体构成了参数链表。参数结构体的定义如下:

struct tag
{
struct tag_header hdr;
union {
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;
struct tag_acorn acorn;
struct tag_memclk memclk;
} u;
};

参数结构体包括两个部分,一个是 tag_header 结构体,一个是u 联合体。

tag_header 结构体的定义如下:
struct tag_header
{
u32 size;
u32 tag;
};

其中 size:表示整个tag 结构体的大小(用字的个数来表示,而不是字节的个数),等于tag_header 的大小加上u 联合体的大小,例如,参数结构体ATAG_CORE 的
size=(sizeof(tag->tag_header)+sizeof(tag->u.core))>>2,一般通过函数tag_size(struct * tag_xxx)来获得每个参数结构体的size。其中tag:表示整个tag 结构体的标记,如:ATAG_CORE等。

联合体u 包括了所有可选择的内核参数类型,包括:tag_core, tag_mem32,tag_ramdisk等。参数结构体之间的遍历是通过函数tag_next(struct * tag)来实现的。本系统参数链表包括的结构体有: ATAG_CORE , ATAG_MEM, ATAG_RAMDISK, ATAG_INITRD32 ,ATAG_CMDLINE,ATAG_END。在整个参数链表中除了参数结构体ATAG_CORE 和ATAG_END 的位置固定以外,其他参数结构体的顺序是任意的。本BootLoader 所传递的参数链表如下:第一个内核参数结构体,标记为ATAG_CORE,参数类型为tag_core。每个参数类型的定义请参考源代码文件。

tag_array 初始化为指向参数链表的第一个结构体的指针。

tag_array->hdr.tag=ATAG_CORE;
tag_array->hdr.size=tag_size(tag_core);
tag_array->u.core.flags=1;
tag_array->u.core.pagesize=4096;
tag_array->u.core.rootdev=0x00100000;
tag_array=tag_next(tag_array);
tag_array->hdr.tag=ATAG_MEM;
tag_array->hdr.size=tag_size(tag_mem32);
tag_array->u.mem.size=0x04000000;
tag_array->u.mem.start=0x20000000;
tag_array=tag_next(tag_array);
……
tag_array->hdr.tag=ATAG_NONE;
tag_array->hdr.size=0;
tag_array=tag_next(tag_array);

最后将内核参数链表复制到内核默认的物理地址0x20000100 处。这样参数链表就建好了。
这个链表没有像前两个一样放在那个段里,只是将该链表的首地址放在了struct machine_desc的unsigned long   boot_params;中。

顺便提一句,对各种tag的解析函数和对各种命令行的解析函数是放在相应的段里的
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值