首页 > 其他分享 >kernel如何根据dtb文件生成device tree

kernel如何根据dtb文件生成device tree

时间:2023-10-07 20:55:08浏览次数:43  
标签:node kernel struct dtb tree offset device property name

kernel如何根据dtb文件生成device tree

device tree

dtb文件中的内容会被内核组成了device tree,整个tree上由两个数据结构组成:struct device_node和struct property。

struct device_node {
	const char *name;
	phandle phandle;
	const char *full_name;
	struct fwnode_handle fwnode;

	struct	property *properties;
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent;
	struct	device_node *child;
	struct	device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
	struct	kobject kobj;
#endif
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	unsigned int unique_id;
	struct of_irq_controller *irq_trans;
#endif
};
struct property {
	char	*name;
	int	length;
	void	*value;
	struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
	unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
	unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
	struct bin_attribute attr;
#endif
};

以platform driver注册时为例,也需要找到同一bus下所有的device,依次对比device所对应的device_node中的property信息(一般对比compatible,type以及name信息,参照__of_device_is_compatible函数)。

dtb文件内容

那么dtb文件是如何被组织的呢?其实,主要由三部分组成:struct fdt_header、struct fdt_node_header以及struct fdt_property。

struct fdt_header {
	fdt32_t magic;			 /* magic word FDT_MAGIC */
	fdt32_t totalsize;		 /* total size of DT block */
	fdt32_t off_dt_struct;		 /* offset to structure */
	fdt32_t off_dt_strings;		 /* offset to strings */
	fdt32_t off_mem_rsvmap;		 /* offset to memory reserve map */
	fdt32_t version;		 /* format version */
	fdt32_t last_comp_version;	 /* last compatible version */

	/* version 2 fields below */
	fdt32_t boot_cpuid_phys;	 /* Which physical CPU id we're
					    booting on */
	/* version 3 fields below */
	fdt32_t size_dt_strings;	 /* size of the strings block */

	/* version 17 fields below */
	fdt32_t size_dt_struct;		 /* size of the structure block */
};
struct fdt_node_header {
	fdt32_t tag;
	char name[];
};
struct fdt_property {
	fdt32_t tag;
	fdt32_t len;
	fdt32_t nameoff;
	char data[];
};

整个dtb文件简单可以分为3大区域(不考虑reserve):header区域、struct区域以及strings区域。

header区域即dtb开头的区域,存放struct fdt_header的数据。

struct区域存放node以及property的节点信息。

strings区域存放property的name信息。

(struct区域因为即存在node,也存在property,因此需要一些tag来区分)。

// node的开始tag
#define FDT_BEGIN_NODE	0x1		/* Start node: full name */
// node的结束tag
#define FDT_END_NODE	0x2		/* End node */

// property的开始tag,因为有len,所以不需要结束tag
#define FDT_PROP	0x3		/* Property: name off,
					   size, content */
#define FDT_NOP		0x4		/* nop */
#define FDT_END		0x9

访问dtb文件的基础API

tag通常情况下为node或property的开头,或者在读完node或者property之后(需要对齐)的位置上。

  1. fdt_next_tag:返回当前offset的tag值,并修改nextoffset为下一个tag所在位置:
uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);

2, fdt_next_node:返回下一个node所在位置(如果没有下一个node,则*depth为-1)

int fdt_next_node(const void *fdt, int offset, int *depth)
  1. nextprop_:常用于查找node的下一个property,如果下一个tag不是property,返回负值。
static int nextprop_(const void *fdt, int offset)

dtb → device tree

以riscv架构为例,dtb文件的解析发生在setup_arch中:

void __init setup_arch(char **cmdline_p)
{
	parse_dtb();
	setup_initial_init_mm(_stext, _etext, _edata, _end);

	*cmdline_p = boot_command_line;

	early_ioremap_setup();
	sbi_init();
	jump_label_init();
	parse_early_param();

	efi_init();
	paging_init();

	/* Parse the ACPI tables for possible boot-time configuration */
	acpi_boot_table_init();

#if IS_ENABLED(CONFIG_BUILTIN_DTB)
	unflatten_and_copy_device_tree();
#else
	unflatten_device_tree();
#endif
	misc_mem_init();

	init_resources();

#ifdef CONFIG_KASAN
	kasan_init();
#endif

#ifdef CONFIG_SMP
	setup_smp();
#endif

	if (!acpi_disabled)
		acpi_init_rintc_map();

	riscv_init_cbo_blocksizes();
	riscv_fill_hwcap();
	init_rt_signal_env();
	apply_boot_alternatives();
	if (IS_ENABLED(CONFIG_RISCV_ISA_ZICBOM) &&
	    riscv_isa_extension_available(NULL, ZICBOM))
		riscv_noncoherent_supported();
}

与dtb相关的是parse_dtb()函数以及unflatten_device_tree()(这里分析CONFIG_BUILTIN_DTB没有ENABLE的情况)。

parse_dtb

static void __init parse_dtb(void)
{
	/* Early scan of device tree from init memory */
	if (early_init_dt_scan(dtb_early_va)) {
		const char *name = of_flat_dt_get_machine_name();

		if (name) {
			pr_info("Machine model: %s\n", name);
			dump_stack_set_arch_desc("%s (DT)", name);
		}
	} else {
		pr_err("No DTB passed to the kernel\n");
	}

#ifdef CONFIG_CMDLINE_FORCE
	strscpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
	pr_info("Forcing kernel command line to: %s\n", boot_command_line);
#endif
}

这里重点是两个函数:early_init_dt_scan以及of_flat_dt_get_machine_name。

early_init_dt_scan函数做一些早期的检查,check_sum(校验)→{size, address}-cells→系统启动参数→将memory节点加入memblock中。

of_flat_get_machine_name去查找了根节点的model属性(调用了前面提到nextprop_接口,实际常用方式为nextprop_的上层封装函数fdt_first_property_offset以及fdt_next_property_offset)。

unflatten_device_tree

void __init unflatten_device_tree(void)
{
	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false);

	/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
	of_alias_scan(early_init_dt_alloc_memory_arch);

	unittest_unflatten_overlay_base();
}

核心函数是__unflatten_device_tree,这个函数用于构建device tree。

此时:initial_boot_params为dtb首地址,of_root要生成的设备树根节点,early_init_dt_alloc_memory_arch封装了memblock_alloc(为memblock的内存分配函数)。

__unflatten_device_tree:

void *__unflatten_device_tree(const void *blob,
			      struct device_node *dad,
			      struct device_node **mynodes,
			      void *(*dt_alloc)(u64 size, u64 align),
			      bool detached)
{
	int size;
	void *mem;
	int ret;

	if (mynodes)
		*mynodes = NULL;

	pr_debug(" -> unflatten_device_tree()\n");

	if (!blob) {
		pr_debug("No device tree pointer\n");
		return NULL;
	}

	pr_debug("Unflattening device tree:\n");
	pr_debug("magic: %08x\n", fdt_magic(blob));
	pr_debug("size: %08x\n", fdt_totalsize(blob));
	pr_debug("version: %08x\n", fdt_version(blob));

	if (fdt_check_header(blob)) {
		pr_err("Invalid device tree blob header\n");
		return NULL;
	}

	/* First pass, scan for size */
	size = unflatten_dt_nodes(blob, NULL, dad, NULL);
	if (size <= 0)
		return NULL;

	size = ALIGN(size, 4);
	pr_debug("  size is %d, allocating...\n", size);

	/* Allocate memory for the expanded device tree */
	mem = dt_alloc(size + 4, __alignof__(struct device_node));
	if (!mem)
		return NULL;

	memset(mem, 0, size);

	*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);

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

	/* Second pass, do actual unflattening */
	ret = unflatten_dt_nodes(blob, mem, dad, mynodes);

	if (be32_to_cpup(mem + size) != 0xdeadbeef)
		pr_warn("End of tree marker overwritten: %08x\n",
			be32_to_cpup(mem + size));

	if (ret <= 0)
		return NULL;

	if (detached && mynodes && *mynodes) {
		of_node_set_flag(*mynodes, OF_DETACHED);
		pr_debug("unflattened tree is detached\n");
	}

	pr_debug(" <- unflatten_device_tree()\n");
	return mem;
}

依据pr_debug以及注释可以看到这个函数的流程很简单,核心部分在于两次unflatten_dt_nodes的调用。第一次调用计算出了需要分配的内存大小(使用局部变量base=mem,第一次调用增加mem,之后返回mem - base),分配完内存后,第二次调用填充数据(此时参数带了申请的mem)。

unflatten_dt_nodes:

static int unflatten_dt_nodes(const void *blob,
			      void *mem,
			      struct device_node *dad,
			      struct device_node **nodepp)
{
	struct device_node *root;
	int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH	64
	struct device_node *nps[FDT_MAX_DEPTH];

	// 如果mem存在,则dryrun为false
	// 若mem不存在,则dryrun为true
	void *base = mem;
	bool dryrun = !base;
	int ret;

	if (nodepp)
		*nodepp = NULL;

	/*
	 * We're unflattening device sub-tree if @dad is valid. There are
	 * possibly multiple nodes in the first level of depth. We need
	 * set @depth to 1 to make fdt_next_node() happy as it bails
	 * immediately when negative @depth is found. Otherwise, the device
	 * nodes except the first one won't be unflattened successfully.
	 */
	if (dad)
		depth = initial_depth = 1;

	root = dad;
	nps[depth] = dad;

	// 遍历所有的node
	for (offset = 0;
	     offset >= 0 && depth >= initial_depth;
	     offset = fdt_next_node(blob, offset, &depth)) {
		if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH - 1))
			continue;

		if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
		    !of_fdt_device_is_available(blob, offset))
			continue;

		// 核心函数populate_node
		ret = populate_node(blob, offset, &mem, nps[depth],
				   &nps[depth+1], dryrun);
		if (ret < 0)
			return ret;

		if (!dryrun && nodepp && !*nodepp)
			*nodepp = nps[depth+1];
		if (!dryrun && !root)
			root = nps[depth+1];
	}

	if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
		pr_err("Error %d processing FDT\n", offset);
		return -EINVAL;
	}

	/*
	 * Reverse the child list. Some drivers assumes node order matches .dts
	 * node order
	 */
	if (!dryrun)
		reverse_nodes(root);

	return mem - base;
}

在unflatten_dt_nodes函数中,遍历所有的node,在node的遍历过程中,调用populate_node函数。

static int populate_node(const void *blob,
			  int offset,
			  void **mem,
			  struct device_node *dad,
			  struct device_node **pnp,
			  bool dryrun)
{
	struct device_node *np;
	const char *pathp;
	int len;

	pathp = fdt_get_name(blob, offset, &len);
	if (!pathp) {
		*pnp = NULL;
		return len;
	}

	len++;

	np = unflatten_dt_alloc(mem, sizeof(struct device_node) + len,
				__alignof__(struct device_node));
	if (!dryrun) {
		char *fn;
		of_node_init(np);
		np->full_name = fn = ((char *)np) + sizeof(*np);

		memcpy(fn, pathp, len);

		if (dad != NULL) {
			np->parent = dad;
			np->sibling = dad->child;
			dad->child = np;
		}
	}

	populate_properties(blob, offset, mem, np, pathp, dryrun);
	if (!dryrun) {
		np->name = of_get_property(np, "name", NULL);
		if (!np->name)
			np->name = "<NULL>";
	}

	*pnp = np;
	return 0;
}

在使用unflatten_dt_alloc函数分配完device_node的内存之后,调用populate_properties函数分配device_node管理的所有property。

static void populate_properties(const void *blob,
				int offset,
				void **mem,
				struct device_node *np,
				const char *nodename,
				bool dryrun)
{
	struct property *pp, **pprev = NULL;
	int cur;
	bool has_name = false;

	pprev = &np->properties;
	for (cur = fdt_first_property_offset(blob, offset);
	     cur >= 0;
	     cur = fdt_next_property_offset(blob, cur)) {
		const __be32 *val;
		const char *pname;
		u32 sz;

		val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
		if (!val) {
			pr_warn("Cannot locate property at 0x%x\n", cur);
			continue;
		}

		if (!pname) {
			pr_warn("Cannot find property name at 0x%x\n", cur);
			continue;
		}

		if (!strcmp(pname, "name"))
			has_name = true;

		pp = unflatten_dt_alloc(mem, sizeof(struct property),
					__alignof__(struct property));
		if (dryrun)
			continue;

		/* We accept flattened tree phandles either in
		 * ePAPR-style "phandle" properties, or the
		 * legacy "linux,phandle" properties.  If both
		 * appear and have different values, things
		 * will get weird. Don't do that.
		 */
		if (!strcmp(pname, "phandle") ||
		    !strcmp(pname, "linux,phandle")) {
			if (!np->phandle)
				np->phandle = be32_to_cpup(val);
		}

		/* And we process the "ibm,phandle" property
		 * used in pSeries dynamic device tree
		 * stuff
		 */
		if (!strcmp(pname, "ibm,phandle"))
			np->phandle = be32_to_cpup(val);

		pp->name   = (char *)pname;
		pp->length = sz;
		pp->value  = (__be32 *)val;
		*pprev     = pp;
		pprev      = &pp->next;
	}

	/* With version 0x10 we may not have the name property,
	 * recreate it here from the unit name if absent
	 */
	if (!has_name) {
		const char *p = nodename, *ps = p, *pa = NULL;
		int len;

		while (*p) {
			if ((*p) == '@')
				pa = p;
			else if ((*p) == '/')
				ps = p + 1;
			p++;
		}

		if (pa < ps)
			pa = p;
		len = (pa - ps) + 1;
		pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
					__alignof__(struct property));
		if (!dryrun) {
			pp->name   = "name";
			pp->length = len;
			pp->value  = pp + 1;
			*pprev     = pp;
			memcpy(pp->value, ps, len - 1);
			((char *)pp->value)[len - 1] = 0;
			pr_debug("fixed up name for %s -> %s\n",
				 nodename, (char *)pp->value);
		}
	}
}

这里主要是使用fdt_first_property_offset函数和fdt_next_property_offset函数对node的所有properties进行遍历并进行内容填充,注意如果没有name这个properties,则会手动创建。

标签:node,kernel,struct,dtb,tree,offset,device,property,name
From: https://www.cnblogs.com/codetrap/p/17747472.html

相关文章

  • 递归解析Json,实现生成可视化Tree+快速获取JsonPath
    内部平台的一个小功能点的实现过程,分享给大家:递归解析Json,可以实现生成可视化Tree+快速获取JsonPath。步骤:1.利用JsonPath读取根,获取JsonObject2.递归层次遍历JsonObjec,保存结点信息3.利用zTree展示结点为可视化树,点击对应树的结点即可获取对应结点的JsonPath1.利用JsonPath......
  • 界面组件DevExpress WinForms v23.1 - TreeList、UI模板全新升级
    DevExpressWinForms拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任!DevExpressWinForm 控件已正式发布v23.1版本,此版......
  • c# 用SapNwRfc调用sap内置bom函数用TreeView把bom展示出来
    一个需求,winform根据料号,查询sap 的bom,然后用控件调用sap内置bom函数,根据料号查询bom用TreeView把bom展示出来树形控件TreeView展示出来,TreeView的好处是父级子级直观明了。sap关于bom 的tcode 主要是cs11,cs12,cs13。cs12可以显示多级bom,查询出来是这样的: 这种表现方式,不是......
  • 基于现有dtb文件修改内容
     当有修改dtb需要,又不想经过linux编译生成dtb时,可以通过dtc工具进行dtb->dts,dts->dtb的转换。dtc工具可以进行dtb和dts之间的转换,dts是可编辑文本。Ubuntu下安装dtc工具:sudoapt-getinstalldevice-tree-compiler1dtc将dtb反编译为dtsdtc-Idtb-Odtsxxx.d......
  • 【bitset】【线段树】CF633G Yash And Trees 题解
    CF633G简单题。先看到子树加和子树质数个数和,果断转换为dfs序进行处理。既然有区间求和,考虑线段树。若对于每一个节点维护一个\(cnt\)数组,用二进制数\(x\)来表示,即当\(cnt_i=1\)时第\(i\)位为\(1\)。设当前节点为\(u\),左右子节点分别为\(ls\)和\(rs\)。发现......
  • chisel安装和使用+联合体union的tagged属性+sv读取文件和显示+sv获取系统时间+vcs编译
    chisel安装和使用sbt:scalabuildtool,是scala的默认构建工具,配置文件是build.sbt。mill:一个新的java/scala构建工具,运行较快,与sbt可以共存,配置文件是build.sc。chisel的安装可以参考这篇文章。安装过程务必联网,而没有联网情况下的安装,按照其它的说明,如移动缓存文件等,并无法正常......
  • 前端最新支持四级及以下结构仿企查查、天眼查关联投资机构 股权结构 tree树形结构 控
    ​随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单独开发,单独维护,而且他们之间可以随意的进行组合。大大提升开发效......
  • 文章《Semantic Kernel -- LangChain 的替代品?》的错误和疑问 探讨
    微信公众号文章SemanticKernel——LangChain的替代品?[1],它使用的示例代码是Python,他却发了这么一个疑问:支持的语言对比(因为SemanticKernel是用C#开发的,所以它对C#比较支持)如上所示。不清楚SemanticKernel为什么要用C#来开发,C#相比Python和JavaScript来说使用......
  • 洛谷 Power Tree 题解
    题目链接PowerTree分析将叶子节点按dfs序重标号后,每次控制操作可以转化为将子树内叶子节点所在区间加(或减)一个数不难可以想到将叶子区间进行差分每次对\(l\)到\(r\)的操作可以转化为将\(l\)上的数转移到\(r+1\)上每次操作后差分数组的和不变将所有点权变为\(0\)......
  • HDU 5834 Magic boy Bi Luo with his excited tree
    题意:给出一棵\(n\)个节点的树,树上每一个节点都有一个权值\(v\),每条边都有一个代价\(w\),从一个点出发,经过一个点可以得到等同于其点权的收益,经过一个点可以得到等同于其点权的收益,经过一条边可以得到等同于其权值的代价,点权只会获得一次,但是代价会花费多次。对于每个点,询问从这个......