【嵌入式Linux】设备树参考资源 | 编译运行 | 加载解析

本文详细介绍了设备树(Devicetree)的资源、编译过程,包括参考官网、指导文档和示例源码。在内核中,通过`makedtbs`命令编译设备树,并展示了如何在U-BOOT中设置启动参数加载设备树。内核启动时,会检查设备树的兼容性并进行非扁平化处理,确保设备树正确加载并初始化。

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

一、参考资源

  • 官网
//https://www.devicetree.org/  
//可下载pdf手册  devicetree-specification-v0.4-rc1.pdf
//https://github.com/devicetree-org/devicetree-specification/tree/main/source  可网页翻译
  • 指导文档
//内核文件中
Documentation/devicetree/bindings 指导如果编写设备树的协议文档
  • 示例源码
//厂商BSP中提供的设备树	~/bsp/Linux_for_Tegra/source/public/hardware/nvidia
//内核中发布的设备树  ~/kernel-4.9/arch/arm64/boot/dts/nvidia

二、编译运行

官方内核中发布的

编译

$ cd linux-4.9.307	
$ make dtbs /* 编译生成设备树dtb   因为前面导入Nvidia的配置了,相关CONFIG已经选好,自动找到设备树目录编译
  DTC     arch/arm64/boot/dts/nvidia/tegra210-p2371-0000.dtb
  DTC     arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dtb
  DTC     arch/arm64/boot/dts/nvidia/tegra210-p2571.dtb
  DTC     arch/arm64/boot/dts/nvidia/tegra210-smaug.dtb
  */
{//arch/arm64/boot/dts/nvidia/Makefile
dtb-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-norrin.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-0000.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-2180.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2571.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-smaug.dtb  //指定编译的设备树名

always      := $(dtb-y)    
clean-files := *.dtb	
}

tegra210-smaug.dts  //描述开发板
tegra210.dtsi  //描述cpu//内核里发布的设备树,和厂商BSP给的有差异,以BSP里为准 (原因可能是厂商未同步发布到新版内核中)

运行

# setenv bootargs initrd=0xa0000000,0x800000 root=/dev/ram0 rw  rootwait rootfstype=ext2 console=ttyS0,115200 init=/linuxrc ip=192.168.9.9 loglevel=8
# setenv yhai_bootcmd  ext4load mmc 1:1 0x84000000 /home/yhbd/Image \; ext4load mmc 1:1 83100000	  /home/yhbd/tegra210-smaug.dtb \; ext4load mmc 1:1  0xa0000000 /home/yhbd/ramdisk.img.gz \; booti 0x84000000 - 83100000
# mw  83100000  0  50     //注意:必须把设备树拷贝到的内存地址 清零,避免用之前残留的(否则会发现,用不存在或有问题的dtb文件也能启动内核)      
# run  yhai_bootcmd  //运行(注:如发现和期望不一致,可手动的ext4load 下载每个文件看大小,甚至用md命令看二进制的值)
# cat /sys/firmware/devicetree/base/compatible  //查看实际加载的设备树的名字

三、加载解析

U-BOOT与内核匹配检查

# bd  /*查看板子信息结构体	
arch_number = 0x0000000000000000  机器码(板子id) -> 在uboot和内核之间进行比对适配
   内核启动后start kernel 报检测id错误Error: unrecognized/unsupported machine ID,就是不匹配导致。 
boot_params = 0x0000000080000100  启动参数bootargs的存放地址
fdt_blob    = 0x00000000fc5fa380
*/	
# booti 0x84000000 - 83100000  //0x84000000为内核地址  -表示忽略ramdisk地址  83100000为设备树的地址
 
arm32 //启动有fdt addr时,会 dtb中的 compatible 属性与内核 DT_MACHINE_START 列表匹配,有则匹配能启动
      //没有fdt addr时,则检测 机器ID 与 MACHINE_START(老版本)
arm64 //取消匹配检查 

内核的设备树安装

arch/arm64/kernel/setup.c

//arch/arm64/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
    pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());
    setup_machine_fdt(__fdt_pointer); //设备树安装(__fdt_pointer 是 DTB位于内存中的物理地址)
    
    unflatten_device_tree();//非扁平化,也就是把dtb构造成树的结构(避免每次都扫描dtb,慢)
}

static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
    const char *name;
    void *dt_virt = fixmap_remap_fdt(dt_phys); /*因MMU已打开,要转为内核中能访问的虚地址
    
    void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
     {
	void *dt_virt;
	int size;

	dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
	if (!dt_virt)
	   return NULL;

	memblock_reserve(dt_phys, size); 把这片内存给保留起来 -> 后续的内存分配中就不会使用DTB占用的内存
	return dt_virt;
    }

        */
    if (!dt_virt || !early_init_dt_scan(dt_virt)) { //dtb扫描,完成早期初始化
        pr_crit("\n"     //blob: 是一种二进制数据格式
            "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); 为下面方式添加了机器名的输出
    name = of_flat_dt_get_machine_name(); /*获取设备的机器名(对应设备树里的model 和 compatible)
	 const char * __init of_flat_dt_get_machine_name(void)
	 {
		const char *name;
		unsigned long dt_root = of_get_flat_dt_root();

		name = of_get_flat_dt_prop(dt_root, "model", NULL);
		if (!name)
		   name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
		return name;
	}
      */
    if (!name){
       pr_info("of_flat_dt_get_machine_name fail\n"); 
        return;
    }
    pr_info("Machine model: %s\n", name); //输出设备名:如  NVIDIA Jetson Nano Developer Kit(对应设备树里的model) -> 看到该信息,才说明设备树加载成功
    dump_stack_set_arch_desc("%s (DT)", name); 
    
}

内核对设备树扫描和检测

drivers/of/fdt.c

//drivers/of/fdt.c
bool __init early_init_dt_scan(void *params)
{
    bool status;

    status = early_init_dt_verify(params); //设备树合法性验证
    if (!status)
        return false;

    early_init_dt_scan_nodes(); /* 设备树的节点扫描 完成早期初始化
	void __init early_init_dt_scan_nodes(void)
	{
	   //扫描 /chosen 节点信息 (把bootargs属性值拷贝到boot_command_line)
	   of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

	   //扫描根节点的属性信息 {size,address}-cells  
	   of_scan_flat_dt(early_init_dt_scan_root, NULL);

	   //扫描 memory节点,完成内存配置初始化  
	   of_scan_flat_dt(early_init_dt_scan_memory, NULL);
	}       
     */
    return true;
}

bool __init early_init_dt_verify(void *params)
{
    if (!params)
        return false;

    /* check device tree validity */
    if (fdt_check_header(params)) /*检查dtb的头信息
        int fdt_check_header(const void *fdt)
	{
	   if (fdt_magic(fdt) == FDT_MAGIC) { //检查dtb的幻数
		//define FDT_MAGIC   0xd00dfeed  必须和前面dtb 开始二级制的值一致(00000000: d00d feed)

	    if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
		return -FDT_ERR_BADVERSION;
	    if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
		return -FDT_ERR_BADVERSION;
	    } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {

		if (fdt_size_dt_struct(fdt) == 0)
		   return -FDT_ERR_BADSTATE;
	        } else {
		return -FDT_ERR_BADMAGIC;
	        }   

		 return 0;
	    }
         */
        return false;

    /* Setup flat device-tree pointer */
    initial_boot_params = params;  //把DTB基地址放入到initial_boot_params
    of_fdt_crc32 = crc32_be(~0, initial_boot_params, //计算crc32校验码
                fdt_totalsize(initial_boot_params));
    return true;
}

查看DTB二进制

$ vim -b arch/arm64/boot/dts/nvidia/tegra210-smaug.dtb
:%!xxd	 //转换为十六进制显示,如
%!xxd -r //返回原显示

//对应结构体
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 */
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值