DeviceTree(1) - 内核传递与属性处理


文章目录
DeviceTree(1) - 内核传递与属性处理
DeviceTree(2) - platform_device
DeviceTree(3) - 中断
DeviceTree(4) - 设备树中的中断

1 head.s处理

 Kernel4.14 head.s文件分析(arch/arm/kernel/head.S arch/arm/kernel/head-common.S)
总结:
将bootloader传递过来的r1,赋值给了C变量__machine_arch_type
将bootloader传递过来的r2,赋值给了C变量__atags_pointer

a. __lookup_processor_type

 根据r9中存储的机器ID,在__proc_info_list中找到CPU,完成初始化

	mrc	p15, 0, r9, c0, c0		@ get processor id
	bl	__lookup_processor_type		@ r5=procinfo r9=cpuid
	movs	r10, r5				@ invalid processor (r5=0)?
 THUMB( it	eq )		@ force fixup-able long branch encoding
	beq	__error_p			@ yes, error 'p'

__lookup_processor_type_data:
	.long	.
	.long	__proc_info_begin  // C语言变量首地址
	.long	__proc_info_end
	.size	__lookup_processor_type_data, . - __lookup_processor_type_data

b. __vet_atags

	/*
	 * r1 = machine no, r2 = atags or dtb,
	 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
	 */
	 // dtb文件存在头部,根据r2所指内存是否符合dtb中fdt_header的magic
	bl	__vet_atags // 判断atags或dtb是否有效
#ifdef CONFIG_SMP_ON_UP
	bl	__fixup_smp
#endif

c. __create_page_tables

  • 创建页表项,建立虚拟地址与物理地址映射关系

d. __enable_mmu

  • 启动MMU

e. __mmap_switched

  • 从物理地址切换到虚拟地址
/*
 * The following fragment of code is executed with the MMU on in MMU mode,
 * and uses absolute addresses; this is not position independent.
 *
 *  r0  = cp#15 control register
 *  r1  = machine ID
 *  r2  = atags/dtb pointer // r2中始终存在atags/dtb首地址
 *  r9  = processor ID
 */
	__INIT
__mmap_switched:
	adr	r3, __mmap_switched_data // r3 = __mmap_switched_data

	ldmia	r3!, {r4, r5, r6, r7}
	cmp	r4, r5				@ Copy data segment if needed
1:	cmpne	r5, r6
	ldrne	fp, [r4], #4
	strne	fp, [r5], #4
	bne	1b

	mov	fp, #0				@ Clear BSS (and zero fp)
1:	cmp	r6, r7
	strcc	fp, [r6],#4
	bcc	1b

 ARM(	ldmia	r3, {r4, r5, r6, r7, sp}) // 将r3连续的5个地址单元分别赋给r4, r5, r6, r7, sp
 THUMB(	ldmia	r3, {r4, r5, r6, r7}	)
 THUMB(	ldr	sp, [r3, #16]		)
	str	r9, [r4]			@ Save processor ID
	str	r1, [r5]			@ Save machine type
	str	r2, [r6]			@ Save atags pointer  // 将r2地址中的值存储到r6所指地址中
	cmp	r7, #0
	strne	r0, [r7]			@ Save control register values
	mov	lr, #0
	b	start_kernel
ENDPROC(__mmap_switched)

// 上面的意思时r2中存储的atags或dtb中值会被存储到__mmap_switched_data地址的内存中

__mmap_switched_data:
	.long	__data_loc			@ r4
	.long	_sdata				@ r5
	.long	__bss_start			@ r6
	.long	_end				@ r7
	.long	processor_id			@ r4 // C语言变量,在汇编中出现,代表变量地址
	.long	__machine_arch_type		@ r5
	.long	__atags_pointer			@ r6 // 将atags或dtb写到变量__atags_pointer中
#ifdef CONFIG_CPU_CP15
	.long	cr_alignment			@ r7
#else
	.long	0				@ r7
#endif
	.long	init_thread_union + THREAD_START_SP @ sp
	.size	__mmap_switched_data, . - __mmap_switched_data

	__FINIT
	.text

 

f. init/main.c

   asmlinkage __visible void __init start_kernel(void)

2 平台信息处理

2.1 dts文件声明支持何种machine_desc

/ {
	model = "STMicroelectronics STM32F429i-DISCO board";
	compatible = "st,stm32f429i-disco", "st,stm32f429";// 该属性序列存在优先级选择,多个字符串时,优劣程度从第一个到最后一个排列

2.2 machine_desc支持哪些单板

static const char *const stm32_compat[] __initconst = {
	"st,stm32f429",
	"st,stm32f469",
	"st,stm32f746",
	"st,stm32h743",
	NULL
};

DT_MACHINE_START(STM32DT, "STM32 (Device Tree Support)")
	.dt_compat = stm32_compat, // 表明支持哪些单板
	.restart = armv7m_restart,
MACHINE_END

2.3 有多个machine_desc跟dts吻合,怎么选择

 使用dts中compatible与machine_desc中dt_compat比较
 规则:吻合的compatible取值位置越低越好

内核处理dtb流程:主要

start_kernel // init/main.c
	setup_arch(&command_line); // arch/arm/kernel/setup.c
		const struct machine_desc *mdesc; // 定义machine_desc结构
	    mdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.c
		    early_init_dt_verify(phys_to_virt(dt_phys)) // phys_to_virt物理地址转虚拟地址
			    // 设备树指针params = phys_to_virt(dt_phys) = phys_to_virt(__atags_pointer)
				// __atags_pointer就是在汇编中r2 = atags or dtb
			    initial_boot_params = params; // drivers/of/fdt.c
			of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
				{
					// 循环取每一个compatible,计算得分
					while ((data = get_next_compat(&compat))) {
					score = of_flat_dt_match(dt_root, compat);
							/*of_flat_dt_match
								int of_fdt_match(const void *blob, unsigned long node,
									const char *const *compat)
								{
									unsigned int tmp, score = 0;
								
									if (!compat)
										return 0;
								
									while (*compat) {
										tmp = of_fdt_is_compatible(blob, node, *compat);
										if (tmp && (score == 0 || (tmp < score)))
											score = tmp;
										compat++;
									}
								
									return score;
								}
							*/
						if (score > 0 && score < best_score) {
							best_data = data;
							best_score = score;
						}
						}
				}
		mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type); // 机器ID的匹配
		machine_desc = mdesc;
	    machine_name = mdesc->name;
		unflatten_device_tree();

<三>设备树节点中配置信息处理

start_kernel // init/main.c
	setup_arch(&command_line); // arch/arm/kernel/setup.c
		early_init_dt_scan_nodes(); // arch/arm/kernel/devtree.c
			{	// drivers/of/fdt.c
				/* 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);
			}

 处理chosen中bootargs参数

int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
				     int depth, void *data)
{
	int l = 0;
	const char *p = NULL;
	char *cmdline = data;

	pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

	if (depth != 1 || !cmdline ||
	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
		return 0;

	early_init_dt_check_for_initrd(node);

	/* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */
	if (overwrite_incoming_cmdline || !cmdline[0])
		strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE);

	/* Retrieve command line unless forcing */
	if (read_dt_cmdline)
		p = of_get_flat_dt_prop(node, "bootargs", &l);

	if (p != NULL && l > 0) {
		if (concat_cmdline) {
			int cmdline_len;
			int copy_len;
			strlcat(cmdline, " ", COMMAND_LINE_SIZE);
			cmdline_len = strlen(cmdline);
			copy_len = COMMAND_LINE_SIZE - cmdline_len - 1;
			copy_len = min((int)l, copy_len);
			strncpy(cmdline + cmdline_len, p, copy_len);
			cmdline[cmdline_len + copy_len] = '\0';
		} else {
			strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE));
		}
	}

	pr_debug("Command line is: %s\n", (char*)data);

	/* break now */
	return 1;
}

处理memory中指明表示参数格式的信息
{
 #size-cells:表值大小用多少个32位标识
 #address-cells:表地址用多少个32位标识
}

int __init early_init_dt_scan_root(unsigned long node, const char *uname,
				   int depth, void *data)
{
	const __be32 *prop;

	if (depth != 0)
		return 0;

	dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
	dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;

	prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
	if (prop)
		dt_root_size_cells = be32_to_cpup(prop); // 大端到CPU模式转换
	pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);

	prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
	if (prop)
		dt_root_addr_cells = be32_to_cpup(prop);
	pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);

	/* break now */
	return 1;
}

处理memory中属性信息
{
 根据early_init_dt_scan_root中拿到的标识进行偏移运算
}

int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
				     int depth, void *data)
{
	const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
	const __be32 *reg, *endp;
	int l;
	bool hotpluggable;

	/* We are scanning "memory" nodes only */
	if (type == NULL) {
		/*
		 * The longtrail doesn't have a device_type on the
		 * /memory node, so look for the node called /memory@0.
		 */
		if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
			return 0;
	} else if (strcmp(type, "memory") != 0)
		return 0;

	reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
	if (reg == NULL)
		reg = of_get_flat_dt_prop(node, "reg", &l);
	if (reg == NULL)
		return 0;

	endp = reg + (l / sizeof(__be32));
	hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);

	pr_debug("memory scan node %s, reg size %d,\n", uname, l);

	while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
		u64 base, size;

		base = dt_mem_next_cell(dt_root_addr_cells, &reg);
		size = dt_mem_next_cell(dt_root_size_cells, &reg);

		if (size == 0)
			continue;
		pr_debug(" - %llx ,  %llx\n", (unsigned long long)base,
		    (unsigned long long)size);

		early_init_dt_add_memory_arch(base, size); // memblock_add(base, size);

		if (!hotpluggable)
			continue;

		if (early_init_dt_mark_hotplug_memory_arch(base, size))
			pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",
				base, base + size);
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值