您的位置:首页 > 运维架构 > Linux

linux启动 之 bootloader参数传递

2011-12-02 00:50 519 查看
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的解析函数和对各种命令行的解析函数是放在相应的段里的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: