ARM :
一. 启动条件
通常从系统上电执行的boot loader的代码, 而要从boot loader跳转到linux kernel的第一条指令处执行需要一些特定的条件。
这里讨论下进入到linux kernel时必须具备的一些条件,这一般是boot loader在跳转到kernel之前要完成的:
1. CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;
2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址就是物理地址;
3. 数据cache(Data cache)必须是关闭的
4. 指令cache(Instruction cache)可以是打开的,也可以是关闭的,这个没有强制要求;
5. CPU 通用寄存器0 (r0)必须是 0;
6. CPU 通用寄存器1 (r1)必须是 ARM Linux machine type (关于machine type, 我们后面会有讲解)
7. CPU 通用寄存器2 (r2) 必须是 kernel parameter list 的物理地址(parameter list 是由boot loader传递给kernel,用来描述设备信息属性的列表)。
二. starting kernel
KERNEL_RAM_ADDR arch/arm/kernel/head.S 0xc0008000 kernel在RAM中的虚拟地址
PAGE_OFFSET include/asm-arm/memeory.h 0xc0000000 内核空间的起始虚拟地址
TEXT_OFFSET arch/arm/Makefile 0x00008000 内核在RAM中起始位置相对于RAM起始地址的偏移
在 arch/arm/kernel/head-common.S 中:
00026: /*
00027: * The following fragment of code is executed with the MMU on in MMU mode,
00028: * and uses absolute addresses; this is not position independent.
00029: *
00030: * r0 = cp#15 control register
00031: * r1 = machine ID
00032: * r9 = processor ID
00033: */
00034: .type __mmap_switched, %function
00035: __mmap_switched:
00036: adr r3, __switch_data + 4
00037:
00038: ldmia r3!, {r4, r5, r6, r7}
00039: cmp r4, r5 @ Copy data segment if needed
00040: 1: cmpne r5, r6
00041: ldrne fp, [r4], #4
00042: strne fp, [r5], #4
00043: bne 1b
00044:
00045: mov fp, #0 @ Clear BSS (and zero fp)
00046: 1: cmp r6, r7
00047: strcc fp, [r6],#4
00048: bcc 1b
00049:
00050: ldmia r3, {r4, r5, r6, sp}
00051: str r9, [r4] @ Save processor ID
00052: str r1, [r5] @ Save machine type
00053: bic r4, r0, #CR_A @ Clear 'A' bit
00054: stmia r6, {r0, r4} @ Save control register values
00055: b start_kernel
asmlinkage void __init start_kernel(void)->
void __init setup_arch(char **cmdline_p)->
mdesc = setup_machine_fdt(__atags_pointer); //通过atags_pointer获取(devtree = phys_to_virt(dt_phys))
void __init unflatten_device_tree(void);
void __init unflatten_device_tree(void)
{
__unflatten_device_tree(initial_boot_params, &allnodes,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
}
这些工作完成解析DTS文件。保存到全局链表allnodes中。
随后,当系统启动到board文件时,会调用.init_machine,高通平台对应的是msm_init()。接着调用of_platform_populate(….)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。注意:不是dtsi文件中所有的节点都会被注册,在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。
void __init msm8610_init(void)
{
struct of_dev_auxdata *adata = msm8610_auxdata_lookup;
if (socinfo_init() < 0)
pr_err("%s: socinfo_init() failed\n", __func__);
msm8610_init_gpiomux();
board_dt_populate(adata);
msm8610_add_drivers();
}
在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_module()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。