linux驱动之设备树(1)

参考:linux设备驱动程序-设备树(1)-dtb转换成device_node - 牧野星辰 - 博客园

内核启动后如何调用和解析设备树二进制文件dtb的

 在linux-3.4.y/init/main.c中,linux内核启动后调用start_kernel函数; 其中有setup_arch函数,对于arm平台会调用arch/arm/kernel/setup.c文件中的setup_arch();

void __init setup_arch(char **cmdline_p)
{
	struct machine_desc *mdesc;

	setup_processor();

	//根据传入的设备树dtb的首地址完成一些初始化操作;
	//__atags_pointer参数这个全局变量就是r2的寄存器值;是设备树在内存中的起始地址;
	mdesc = setup_machine_fdt(__atags_pointer);
	if (!mdesc)
		mdesc = setup_machine_tags(machine_arch_type);
	machine_desc = mdesc;
	machine_name = mdesc->name;

	if (mdesc->restart_mode)
		reboot_setup(&mdesc->restart_mode);

	init_mm.start_code = (unsigned long) _text;
	init_mm.end_code   = (unsigned long) _etext;
	init_mm.end_data   = (unsigned long) _edata;
	init_mm.brk	   = (unsigned long) _end;

	/* populate cmd_line too for later use, preserving boot_command_line */
	strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
	*cmdline_p = cmd_line;

	parse_early_param();

	sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
	sanity_check_meminfo();

	//内存相关,为设备树保留相应的内存空间,保证设备树dtb本身在内存中而不被覆盖,用户可在设备树中保留内存;
	arm_memblock_init(&meminfo, mdesc);

    ...

	//展开设备树,实际上就是解析dtb,并转换为struct device_node结构体;
	unflatten_device_tree();

    ...
}

1. setup_machine_fdt函数

 函数作用: 如果在r2中向内核传递了一个dtb,那么使用它来选择正确的machine_desc并设置系统。 

struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
	struct boot_param_header *devtree;
	struct machine_desc *mdesc, *mdesc_best = NULL;
	unsigned int score, mdesc_score = ~1;
	unsigned long dt_root;
	const char *model;

	if (!dt_phys)
		return NULL;

	//物理地址转虚拟地址;
	devtree = phys_to_virt(dt_phys);

	/* check device tree validity */
	//验证设备树有效性
	if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
		return NULL;

	/* Search the mdescs for the 'best' compatible value match */
	initial_boot_params = devtree;
	
	//获取根节点
	dt_root = of_get_flat_dt_root();

	//循环读取设备树根目录下的compatible属性;
	for_each_machine_desc(mdesc) {
		//mdesc->dt_compat 为设备树compatible字符数组
		score = of_flat_dt_match(dt_root, mdesc->dt_compat);
		if (score > 0 && score < mdesc_score) {
			mdesc_best = mdesc;
			mdesc_score = score;
		}
	}

	...

    //获取根节点下的model属性
	model = of_get_flat_dt_prop(dt_root, "model", NULL);
	if (!model)
		model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
	if (!model)
		model = "<unknown>";
	pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);

	//扫描设备树中的各节点
	//分别是处理choose节点的处理,root节点中除了子节点外的属性信息,memory节点;
	/* Retrieve various information from the /chosen node */
	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
	/* Initialize {size,address}-cells info */
	of_scan_flat_dt(early_init_dt_scan_root, NULL);
	/* Setup memory, calling early_init_dt_add_memory_arch */
	of_scan_flat_dt(early_init_dt_scan_memory, NULL);

	/* Change machine number to match the mdesc we're using */
	__machine_arch_type = mdesc_best->nr;

	return mdesc_best;
}

其中的of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);语句是将boot_command_line作为回调函数early_init_dt_scan_chosen的参数传入,并拷贝从choose属性读取到的命令行参数到boot_command_line中;

 

of_scan_flat_dt(early_init_dt_scan_root, NULL);语句是查找#size-cells和#address-cells属性,并赋值全局dt_root_size_cellsdt_root_addr_cells ;

of_scan_flat_dt(early_init_dt_scan_memory, NULL);语句主要获取memory相关信息计算后,调用early_init_dt_add_memory_arch(base, size);分配空间;

2. arm_memblock_init函数

void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
{
	int i;

	for (i = 0; i < mi->nr_banks; i++)
		memblock_add(mi->bank[i].start, mi->bank[i].size);

	/* Register the kernel text, kernel data and initrd with memblock. */
	memblock_reserve(__pa(_stext), _end - _stext);

    ...

	if (phys_initrd_size) {
		memblock_reserve(phys_initrd_start, phys_initrd_size);

		/* Now convert initrd to virtual addresses */
		initrd_start = __phys_to_virt(phys_initrd_start);
		initrd_end = initrd_start + phys_initrd_size;
	}

	arm_mm_memblock_reserve();
	arm_dt_memblock_reserve();

	arm_memblock_steal_permitted = false;
	memblock_allow_resize();
	memblock_dump_all();
}

主要是为设备树分配保留内存空间;

3. unflatten_device_tree函数 

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);
}

 

__unflatten_device_tree函数中,扫描得出设备树转换成device node需要的空间,然后系统申请内存空间,第二次就进行真正的解析工作;代码如下:

static void __unflatten_device_tree(struct boot_param_header *blob,
			     struct device_node **mynodes,
			     void * (*dt_alloc)(u64 size, u64 align))
{
	unsigned long start, mem, size;
	struct device_node **allnextp = mynodes;

	...

	/* 获取大小 */
	start = ((unsigned long)blob) +
		be32_to_cpu(blob->off_dt_struct);
	size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
	size = (size | 3) + 1;

	pr_debug("  size is %lx, allocating...\n", size);

	/* Allocate memory for the expanded device tree */
    //分配空间
	mem = (unsigned long)
		dt_alloc(size + 4, __alignof__(struct device_node));

	((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);

	pr_debug("  unflattening %lx...\n", mem);

	/* Second pass, do actual unflattening */
    //实际解析工作
	start = ((unsigned long)blob) +
		be32_to_cpu(blob->off_dt_struct);
	unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);

    ...
}

参数allnodes就是struct device_node指针,将其传入进入后,通过解析设备树对其赋值填充;

of_alias_scan函数中:

void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
	struct property *pp;

    //获取根下的chosen节点属性
	of_chosen = of_find_node_by_path("/chosen");
	if (of_chosen == NULL)
		of_chosen = of_find_node_by_path("/chosen@0");

    //获取aliases节点
	of_aliases = of_find_node_by_path("/aliases");
	if (!of_aliases)
		return;

    //遍历所有aliases节点
	for_each_property_of_node(of_aliases, pp) {
		const char *start = pp->name;
		const char *end = start + strlen(start);
		struct device_node *np;
		struct alias_prop *ap;
		int id, len;

		/* Skip those we do not want to proceed */
        //过滤不想处理的
		if (!strcmp(pp->name, "name") ||
		    !strcmp(pp->name, "phandle") ||
		    !strcmp(pp->name, "linux,phandle"))
			continue;

		np = of_find_node_by_path(pp->value);
		if (!np)
			continue;

		/* walk the alias backwards to extract the id and work out
		 * the 'stem' string */
		while (isdigit(*(end-1)) && end > start)
			end--;
		len = end - start;

		if (kstrtoint(end, 10, &id) < 0)
			continue;

		/* Allocate an alias_prop with enough space for the stem */
        //分配空间
		ap = dt_alloc(sizeof(*ap) + len + 1, 4);
		if (!ap)
			continue;
		ap->alias = start;

        //将所有aliases内容添加到链表
		of_alias_add(ap, np, id, start, len); 
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天未及海宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值