内核小碎碎-第九集 设备树的前世今生 从dts到dtb 到启动参数传递 再到内核解析dtb 最终的使用

内核启动点,位于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, &reg);
			size = dt_mem_next_cell(dt_root_size_cells, &reg);
		  	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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值