uboot在完成所有工作之後使用theKernel()啓動內核
theKernel (0, machid, bd->bi_boot_params);
傳遞三個參數0、machid、TAG的首地址,分別存入r0,r1,r2,之後啓動內核。
內核中獲取TAG首地址則是在arch/arm/kernel/setup.c文件的setup_arch()函數中(內核版本:linux-2.6.22.6)
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;
setup_processor();
mdesc = setup_machine(machine_arch_type); //獲得開發板對應的machine_desc結構體
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if (mdesc->boot_params) //如果定義了bootloader傳入的TAG地址
tags = phys_to_virt(mdesc->boot_params); //得到傳入的TAG虛擬地址
......
}
setup_machine()函數:獲得開發板對應的machine_desc結構體
static struct machine_desc * __init setup_machine(unsigned int nr)
{
struct machine_desc *list;
/*
* locate machine in the list of supported machines.
*/
list = lookup_machine_type(nr);
if (!list) {
printk("Machine configuration botched (nr %d), unable "
"to continue.\n", nr);
while (1);
}
printk("Machine: %s\n", list->name);
return list;
}
lookup_machine_type()定義在arch/arm/kernel/head-common.S中
具體作用就是通過u-boot傳入的機器id在.arch.info.init段中找到匹配的machine_desc結構體。
.long __proc_info_begin
.long __proc_info_end
3: .long .
.long __arch_info_begin
.long __arch_info_end
__lookup_machine_type:
adr r3, 3b @得到3標號的物理地址
ldmia r3, {r4, r5, r6} @r4=r3 r5=__arch_info_begin r6=__arch_info_end
sub r3, r3, r4 @ r3 = r3 - r4 得到虛擬地址與物理地址的差
add r5, r5, r3 @ r5 = __arch_info_begin的物理地址
add r6, r6, r3 @ r6 = __arch_info_end的物理地址
1: ldr r3, [r5, #MACHINFO_TYPE] @ @ r5 =machine_desc r3=machine_desc 中的nr即機器id
teq r3, r1 @ r1爲bootloader傳入的機器id 比較兩者
beq 2f @ 如果相等跳轉到2forward標號
add r5, r5, #SIZEOF_MACHINE_DESC @ 得到下個machine_desc結構體
cmp r5, r6 @比較是否到段的結尾
blo 1b @如果不是跳轉到1back標號
mov r5, #0 @ unknown machine
2: mov pc, lr
那machine_desc結構體是由什麼定義的呢?
內核對應每一種能支持開發板都會使用宏MACHINE_START和MACHINE_END來定義一個
machine_desc結構,它定義了開發板相關的一些屬性及函數,比如機器類型id,起始I/O物理地址、bootloader傳入的參數地址等等。
定義如下:
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END
對於SMDK2440開發板,定義如下:(位於arch/arm/mach-s3c2440/mach-smdk2440.c)
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <[email protected]> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100, //S3C2410_SDRAM_PA = 0x30000000
//所以得到bootloader傳入的TAG首地址爲0x30000100
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
分析到這裏其實不難看出,內核並沒有使用theKernel()傳入的TAG首地址,而是通過machine_desc的boot_params項獲得了TAG首地址。
如果u-boot設置的TAG首地址與內核中boot_params參數不一致時,可能會出現問題。