DTS设备树驱动加载过程解析

本文详细解析了Linux内核从start_kernel开始,如何通过setup_arch、setup_machine_fdt等一系列函数逐步加载并解析DTS设备树的过程。涉及到的关键步骤包括早期初始化扫描、根节点和内存节点的处理,以及设备树的展开和节点属性的解析,为理解设备驱动加载提供了清晰的路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

1start_kernel

start_kernel是kernel起始的地方,由bootloader拉起来。具体的调用在汇编里面,截取部分代码如下:

__mmap_switched:
	// Clear BSS
	adr_l	x0, __bss_start
	mov	x1, xzr
	adr_l	x2, __bss_stop
	sub	x2, x2, x0
	bl	__pi_memset

	adr_l	sp, initial_sp, x4
	mov	x4, sp
	and	x4, x4, #~(THREAD_SIZE - 1)
	msr	sp_el0, x4			// Save thread_info
	//将x21寄存器保存的fdt的地址赋值给__fdt_pointer
	str_l	x21, __fdt_pointer, x5		// Save FDT pointer
	str_l	x24, memstart_addr, x6		// Save PHYS_OFFSET
	mov	x29, #0
#ifdef CONFIG_KASAN
	bl	kasan_early_init
#endif
	//跳转到start_kernel函数地址
	b	start_kernel
ENDPROC(__mmap_switched)

2.setup_arch

void __init setup_arch(char **cmdline_p)
{
   
	pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());

	...

	*cmdline_p = boot_command_line;

	early_fixmap_init();
	early_ioremap_init();

	//这里的__fdt_pointer已经在上面汇编.S文件里面赋值了
	setup_machine_fdt(__fdt_pointer);

	...
	if (acpi_disabled) {
   
		unflatten_device_tree();
		psci_dt_init();
	} else {
   
		psci_acpi_init();
	}
	...

3.setup_machine_fdt

static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
   
	//获取fdt结构体的头部地址
	void *dt_virt = fixmap_remap_fdt(dt_phys);

	if (!dt_virt || !early_init_dt_scan(dt_virt)) {
   
		pr_crit("\n"
			"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
			"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
			"\nPlease check your bootloader.",
			&dt_phys, dt_virt);

		while (true)
			cpu_relax();
	}

	dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
}

上面这个函数大体作用如下:
1.获取dts的头部地址
2.调用fdt函数进行下一步的扫描

4.early_init_dt_scan

bool __init early_init_dt_scan(void *params)
{
   
	bool status;
	//检查这个地址是否是合法地址,就是其魔法数据是否一致。检测通过后将这个地址赋值给全局变量,后面扫描node都是通过这个地址来的
	status = early_init_dt_verify(params);
	if (!status)
		return false;
	//进行早期扫描
	early_init_dt_scan_nodes();
	return true;
}

5.early_init_dt_scan_nodes

void __init early_init_dt_scan_nodes(void)
{
   
	/* 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);
}

通过上面的代码可以看出,这个函数里面明显有回调。接下来看其实现

int __init of_scan_flat_dt(int (*it)(unsigned long node,
				     const char *uname, int depth,
				     void *data),
			   void *data)
{
   
	//这个initial_boot_params就是检查通过后赋给的头地址
	const void *blob = initial_boot_params;
	const char *pathp;
	int offset, rc = 0, depth = -1;
		//扫描所有节点
        for (offset = fdt_next_node(blob, -1, &depth);
             offset >= 0 && depth >= 0 && !rc;
             offset = fdt_next_node(blob, offset, &depth)) {
   

		pathp = fdt_get_name(blob, offset, NULL);
		if (*pathp == '/')
			pathp = kbasename(pathp);
		//调用回调函数处理扫描后得到的数据
		rc = it(offset, pathp, depth
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bruk_spp

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

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

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

打赏作者

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

抵扣说明:

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

余额充值