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的解析函数和对各种命令行的解析函数是放在相应的段里的
一、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的解析函数和对各种命令行的解析函数是放在相应的段里的