内核启动点,位于arch/arm/kernel/head.S
.arm
__HEAD
ENTRY(stext)
接着head.S
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
bl __vet_atags
__vet_atags:在arch/arm/head-common.S中
用于检查r2保存的dtb指针合法性
接着head.S
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
__mmap_switched 定义在arch/arm/head-common.S
在其中
str r9, [r0] @ Save processor ID
str r7, [r1] @ Save machine type
str r8, [r2] @ Save atags pointer
cmp r3, #0
strne r10, [r3] @ Save control register values
mov lr, #0
b start_kernel
start_kernel定义在init/main.c中
在其中,调用关系
setup_arch(&command_line);
setup_arch定义在arch/arm/kernel/ 下面分析设备树相关的一些结构体和函数。
const struct machine_desc *mdesc;
其定义
struct machine_desc {
unsigned int nr; /* 架构号 */
const char *name; /* 架构名 */
unsigned long atag_offset; /* tagged list (relative) */
const char *const *dt_compat; /* compatible值数组 */
......
}
一般全局的machine_desc 赋值在:
```c
static const char * const v2m_dt_match[] __initconst = {
"arm,vexpress",
NULL,
};
DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
.dt_compat = v2m_dt_match,
.l2c_aux_val = 0x00400000,
.l2c_aux_mask = 0xfe0fffff,
.smp = smp_ops(vexpress_smp_dt_ops),
.smp_init = smp_init_ops(vexpress_smp_init_ops),
MACHINE_END
这里
#define DT_MACHINE_START(_name, _namestr)
static const struct machine_desc _mach_desc##_name
__used
attribute((section(".arch.info.init"))) = {
.nr = ~0,
.name = _namestr,
但这里,machine_desc *mdesc是setup_arch函数的局部变量
mdesc = setup_machine_fdt(__atags_pointer);
分析setup_machine_fdt,其根据传入的dtb blob首地址填充machine_desc。
->early_init_dt_scan
-->early_init_dt_verify
--->fdt_check_header 校验dtb合法性
--->/* Setup flat device-tree pointer */
initial_boot_params = __atags_pointer;
-->early_init_dt_scan_nodes
--->of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
---->const void *blob = initial_boot_params;就是__atags_pointer。
---->fdt_next_node
----->fdt_next_tag 找到下一个标签,见下图,每个节点从FDT_BEGIN_NODE开始,到FDT_END_NODE结束
----->fdt_next_node返回depth 和 nextoffset 用于查找下一个节点
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); 处理chosen节点
如://子节点
chosen {
bootargs = "console=ttySAC0,115200n8 root=/dev/mmcblk0p1 rw rootwait ignore_loglevel earlyprintk"; //属性 值
};
---->p = of_get_flat_dt_prop(node, "bootargs", &l); 获取bootargs参数值
of_scan_flat_dt(early_init_dt_scan_root, NULL);
---->dt_root_size_cells root节点下的#size-cells值
---->dt_root_addr_cells root节点下的#address-cells值
of_scan_flat_dt(early_init_dt_scan_memory, NULL);扫描memory节点,建立memory映射
其中,
base = dt_mem_next_cell(dt_root_addr_cells, ®);
size = dt_mem_next_cell(dt_root_size_cells, ®);
early_init_dt_add_memory_arch(base, size);见图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200416125533443.png)
memory@60000000 {
device_type = "memory";
reg = <0x60000000 0x40000000>;
};
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200416084316144.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ppbmdzaGFveW91,size_16,color_FFFFFF,t_70)
->of_flat_dt_match_machine
-->arch_get_next_mach
--->machine_desc *mdesc = __arch_info_begin;
__arch_info_begin为.:即当前位置,也即 *(.arch.info.init)的起始 。见下:
```c
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
上文machine_desc 定义时属性,__attribute__((__section__(".arch.info.init"))) = { \ 定义在这个段
--->*match = m->dt_compat; //mps2_compat数组 "arm,vexpress",
--->score = of_flat_dt_match(dt_root, compat); //compat为 "arm,vexpress",
内核源码arch\arm\mach-vexpress\v2m.c中定义的DT_MACHINE_START那部分和设备树的compatible【下面为设备树的根节点的部分内容】去匹配
--->pr_info("Machine model: %s\n", of_flat_dt_get_machine_name()); 打印model值"V2P-CA9"
/ {
model = "V2P-CA9";
arm,hbi = <0x191>;
arm,vexpress,site = <0xf>;
compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";
interrupt-parent = <&gic>;
#address-cells = <1>;
#size-cells = <1>;
......
在setup_arch函数中接着向下
->machine_desc = mdesc; 由设备树根节点compatible匹配到的内核中通过DT_MACHINE_START定义的machine_desc,见上文,该结构体元素值如下。
.nr = ~0,
.name = _namestr, //"ARM-Versatile Express"
.dt_compat = v2m_dt_match,
.l2c_aux_val = 0x00400000,
.l2c_aux_mask = 0xfe0fffff,
.smp = smp_ops(vexpress_smp_dt_ops),
.smp_init = smp_init_ops(vexpress_smp_init_ops),
->unflatten_device_tree();
–>__unflatten_device_tree(initial_boot_params, NULL, &of_root, early_init_dt_alloc_memory_arch, false);
—>size = unflatten_dt_nodes(blob, NULL, dad, NULL);
其中,blob为initial_boot_params,dad为NULL。第二个参数void *mem为null。此函数中void base = mem=null=0; bool dryrun = !base=true.
–>if (!populate_node(blob, offset, &mem, nps[depth],
&nps[depth+1], dryrun))
return mem - base;
遍历所有节点,mem=0=null开始累加device_node、节点名、property结构体等大小,减去base=0得到总的需求空间size。
–>mem = dt_alloc(size + 4, alignof(struct device_node)); 分配展开设备树需要的内存空间
–>/ Second pass, do actual unflattening */
unflatten_dt_nodes(blob, mem, dad, mynodes);
注意此处的0xdeadbeef内存填写检查小技巧
unflatten_dt_nodes(initial_boot_params, 上面dt_alloc分配的地址, null, &of_root);
–>kernel根据Device Tree的文件结构信息转换成struct property结构体,并将同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表。
struct property {
char *name; /* property full name */
int length; /* property value length */
void *value; /* property value */
struct property *next; /* next property under the same node */
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr; /* 属性文件,与sysfs文件系统挂接 */
};
Device Tree中的每一个node节点经过kernel处理都会生成一个struct device_node的结构体,struct device_node最终一般会被挂接到具体的struct device结构体。
struct device_node {
const char *name; /* node的名称,取最后一次“/”和“@”之间子串 */
const char *type; /* device_type的属性名称,没有为<NULL> */
phandle phandle; /* phandle属性值 */
const char *full_name; /* 指向该结构体结束的位置,存放node的路径全名,例如:/chosen */
struct fwnode_handle fwnode;
struct property *properties; /* 指向该节点下的第一个属性,其他属性与该属性链表相接 */
struct property *deadprops; /* removed properties */
struct device_node *parent; /* 父节点 */
struct device_node *child; /* 子节点 */
struct device_node *sibling; /* 姊妹节点,与自己同等级的node */
struct kobject kobj; /* sysfs文件系统目录体现 */
unsigned long _flags; /* 当前node状态标志位,见/include/linux/of.h line124-127 */
void *data;
};
/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED 2 /* node has been detached from the device tree*/
#define OF_POPULATED 3 /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */
参数initial_boot_params指向Device Tree在内存中的首地址,of_root在经过该函数处理之后,会指向根节点,early_init_dt_alloc_memory_arch是一个函数指针,为struct device_node和struct property结构体分配内存的回调函数(callback)。unflatten_dt_nodes函数具体是填充每一个struct device_node和struct property结构体。
最终的使用
platform_device与device_node