设备树(dtb数据)匹配struct machine_desc结构体

Posted 正在起飞的蜗牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设备树(dtb数据)匹配struct machine_desc结构体相关的知识,希望对你有一定的参考价值。

1、函数调用关系

start_kernel
	setup_arch
		setup_machine_fdt	//解析dtb数据,得到匹配的struct machine_desc结构体,这是用来描述板级配置的
			early_init_dt_verify	//校验dtb数据
			of_flat_dt_match_machine	//匹配最符合的struct machine_desc结构体
				get_next_compat(其实就是arch_get_next_mach)	//读取编译进内核的struct machine_desc结构体的dt_compat属性
				of_flat_dt_match	//将struct machine_desc结构体的dt_compat属性和根节点的"compatible"属性进行匹配,匹配度越好返回的分数越小

2、struct machine_desc结构体匹配逻辑

(1)struct machine_desc结构体描述板级资源的,每块板子都有自己对应的struct machine_desc结构体,一般都是定义在mach-xxx文件中;
(2)在编译内核时,内核一般会把同架构下的mach-xxx目录都编译进去,定义的struct machine_desc结构体会被链接到一起,在链接脚本中会有标号来表示起始和结束的地址;
(3)内核启动过程中会根据起始、结束地址去遍历struct machine_desc结构体,将每个结构体的dt_compat变量和设备树根节点的compatible进行匹配,匹配度最高的struct machine_desc结构体将用于后续内核启动;
(4)如果都匹配不上会有一个默认的struct machine_desc结构体;

3、DT_MACHINE_START宏 和 MACHINE_START宏

#define MACHINE_START(_type,_name)			\\
static const struct machine_desc __mach_desc_##_type	\\
 __used							\\
 __attribute__((__section__(".arch.info.init"))) = 	\\
	.nr		= MACH_TYPE_##_type,		\\
	.name		= _name,

#define 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,			\\
;

(1)两个宏都是定义一个struct machine_desc变量,区别在于初始化的值不同;
(2)MACHINE_START宏是之前采用的方式,在定义时会初始化nr值,nr是机器码,用于和uboot启动内核时传递的机器码进行匹配;
(3)DT_MACHINE_START宏是设备树采用的方式,nr值在设备树中是没有使用的,在定义时nr值统一被初始化成~0。设备树是将根节点的compatible和struct machine_desc的dt_compat变量进行匹配。

4、新旧版本内核中machine_desc结构体的差异

(1)这里新旧版本的划分以设备树技术为标准,没有支持设备树的内核为旧版本,支持设备树的内核为新版本;
(2)旧版本内核中用MACHINE_START宏定义machine_desc结构体;新版本内核用DT_MACHINE_START宏定义machine_desc结构体;
(3)旧版本内核匹配machine_desc结构体是根据nr变量(机器码),和uboot启动内核时传递的机器码进行匹配;
(4)新版本内核匹配machine_desc结构体时依靠dt_compat变量,本质上是字符串,和dts文件根节点的compatible属性进行匹配;

5、arch_get_next_mach()函数

5.1、内核链接脚本

OUTPUT_ARCH(arm)
ENTRY(stext)
jiffies = jiffies_64;
SECTIONS

	······
	
	.init.arch.info : 
	__arch_info_begin = .;
	*(.arch.info.init)
	__arch_info_end = .;
	

    ······

(1)".arch.info.init"属性是用DT_MACHINE_START宏 和 MACHINE_START宏定义struct machine_desc结构体时赋予的,struct machine_desc结构体会被链接在一起;
(2)__arch_info_begin:表示内核中struct machine_desc结构体的起始地址;
(3)__arch_info_end:表示内核中struct machine_desc结构体的结束地址;
总结:所有struct machine_desc结构体会被链接在一起,类似于数组,__arch_info_begin相当于数组起始地址,__arch_info_end相当于数组的结束地址;

5.2、arch_get_next_mach()函数源码

static const void * __init arch_get_next_mach(const char *const **match)

	//struct machine_desc的起始地址
	static const struct machine_desc *mdesc = __arch_info_begin;
	
	const struct machine_desc *m = mdesc;

	//判断是否编译到末尾
	if (m >= __arch_info_end)
		return NULL;

	mdesc++;
	*match = m->dt_compat;	//dt_compat变量是保存的字符串,用于和根节点的compatible属性进行匹配;
	return m;

(1)函数会遍历内核中所有的struct machine_desc结构体,返回对应的struct machine_desc结构体指针和dt_compat变量的值;
(2)dt_compat变量的值拿去和根节点的conpatible属性进行匹配;

6、of_flat_dt_match_machine()函数

const void * __init of_flat_dt_match_machine(const void *default_match,
		const void * (*get_next_compat)(const char * const**))

	const void *data = NULL;
	
	//向将最匹配的machine_desc结构体设置成默认结构体
	const void *best_data = default_match;
	
	const char *const *compat;
	unsigned long dt_root;
	unsigned int best_score = ~1, score = 0;

	//获取dtb数据的根节点
	dt_root = of_get_flat_dt_root();
	
	//循环条件是遍历完内核中的machine_desc结构体
	//其中get_next_compat函数指针就是arch_get_next_mach()函数
	while ((data = get_next_compat(&compat))) 
		//将读取到的machine_desc结构体和根节点的compatible属性进行匹配,匹配度越高返回的score越小
		score = of_flat_dt_match(dt_root, compat);
		if (score > 0 && score < best_score) 
			best_data = data;
			best_score = score;
		
	

	//如果best_data是NULL则代表当前没有可用的machine_desc结构体,打印根节点的compatible属性
	if (!best_data) 
		const char *prop;
		int size;

		pr_err("\\n unrecognized device tree list:\\n[ ");

		prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
		if (prop) 
			while (size > 0) 
				printk("'%s' ", prop);
				size -= strlen(prop) + 1;
				prop += strlen(prop) + 1;
			
		
		printk("]\\n\\n");
		return NULL;
	

	//打印根节点的model属性值
	pr_info("Machine model: %s\\n", of_flat_dt_get_machine_name());

	return best_data;

(1)调用of_flat_dt_match_machine()函数会传入一个默认的machine_desc结构体,如何设备树和内核已有的machine_desc结构体没有匹配上的,则使用默认的machine_desc结构体;
(2)best_score变量是记录匹配度的,初始值是"best_score = ~1",只有匹配分数小于best_score 才会替换最匹配的machine_desc结构体,我猜测一点都不匹配soce应该是0xFFFFFFFF;

以上是关于设备树(dtb数据)匹配struct machine_desc结构体的主要内容,如果未能解决你的问题,请参考以下文章

从设备树(dtb格式数据)中解析出bootargs

Linux设备树使用

第01节_从源头分析_内核head.S对dtb的简单处理

如何编译高通kernal设备树

如何针对开发板配置设备树

Linux 设备树的解释 - DTB文件格式