在很久之前的版本,uboot还需要传machine id和参数区地址给内核,但现在只传递设备树(fdt)的地址给内核了,这些参数全在fdt中做了。uboot将设备树bin文件和kernel加载到内存,然后将fdt地址传给kernel,跳到kernel地址执行。fdt为平坦设备树,即数作为一个一个节点顺序存在bin文件中,不再保持树的形状。
我们来看看设备树bin文件dtb的文件结构
在这里插入图片描述
分头部参数区,内存保留映射区,设备树结构区,和字符串区。当然我们只需要关注设备树的节点存储区,字符串区保存的是属性的名称字符串,因为好多属性都是共有的,可以复用。
头部参数区:

struct boot_param_header {
        u32     magic;----------------用于标dtb文件头,等于OF_DT_HEADER=“0xd00dfeed”
        u32     totalsize;------------dtb文件大小
        u32     off_dt_struct;--------DT structure偏移
        u32     off_dt_strings;-------DT strings偏移
        u32     off_mem_rsvmap;-------memory reserve map偏移
        u32     version;--------------版本号
        u32     last_comp_version;----兼容最早版本号

        /* version 2 fields below */
        u32     boot_cpuid_phys;------physical CPU id

        /* version 3 fields below */
        u32     size_dt_strings;------size of the strings block

        /* version 17 fields below */
        u32 size_dt_struct;-----------size of the DT structure block
};

来看看节点的结构
在这里插入图片描述
节点其实开头结尾和属性开头结尾的magic word

#define FDT_MAGIC   0xd00dfeed  /* 4: version, 4: total size */
#define FDT_TAGSIZE sizeof(uint32_t)

#define FDT_BEGIN_NODE  0x1     /* Start node: full name */
#define FDT_END_NODE    0x2     /* End node */
#define FDT_PROP    0x3     /* Property: name off,
                       size, content */
#define FDT_NOP     0x4     /* nop */
#define FDT_END     0x9

#define FDT_V1_SIZE (7*sizeof(uint32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
#define FDT_V16_SIZE    FDT_V3_SIZE
#define FDT_V17_SIZE    (FDT_V16_SIZE + sizeof(uint32_t))

好了,了解了dtb的文件结构,我们来实战下编译dtb文件并解析。
dts文件如下:


/dts-v1/;
/ {
	#address-cells = <1>;
	#size-cells = <1>;
	memory {
		#address-cells = <1>;
		#size-cells = <1>;
		cma { reg = <0x07000000 0x01000000 0x40000000 0x10000000>; };
	};
};

注意开头的/dts-v1/;声明,不然编不过
使用dtc工具将其编译:

dtc -O dtb -b 0 -o demo.dtb demo.dts

我们使用内核中libfdt库进行解析 。
既然是设备树,我们可以以目录树的方式来解析,如以上节点可以通过/memory/cma来访问,上代码,读取和修改reg属性的值。

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <arpa/inet.h>

#include "libfdt.h"

#define DTB_FILE_NAME "demo.dtb"

unsigned long get_file_size(const char *path)  
{  
    unsigned long filesize = -1;      
    struct stat statbuff;  
    if(stat(path, &statbuff) < 0){  
        return filesize;  
    }else{  
        filesize = statbuff.st_size;  
    }  
    return filesize;  
}  


int main()
{
	int ret = 0;
	int dtb_size = -1;
	char* dtb_buf = NULL; 
	int fd = open(DTB_FILE_NAME, O_RDONLY);
	if (fd < 0)
	{
		printf("open %s failed!!!\n", DTB_FILE_NAME);
		return -1;
	}

	dtb_size = get_file_size(DTB_FILE_NAME);
	if (dtb_size < 0)
	{
		printf("dtb size %d not good!!!\n", dtb_size);
		goto EXIT;
	}
	printf("dtb file: %s  size:%d\n", DTB_FILE_NAME, dtb_size);

	dtb_buf = malloc(dtb_size);
	if (NULL == dtb_buf)
	{
		printf("malloc dtb buf (%d)failed!!!\n", dtb_size);
		goto EXIT;
	}
	memset(dtb_buf, 0, dtb_size);

	ret = read(fd, dtb_buf, dtb_size);
	if (ret != dtb_size)
	{
		printf("read dtb failed (%d)!!!\n", ret);
		goto EXIT;
	}
	ret = fdt_check_header(dtb_buf);
	if (ret != 0)
	{
		printf("check dtb header failed (%d)!!!\n", ret);
		goto EXIT;
	}
	else
	{
		printf("check dtb header success!!!\n");
	}
	int nodeoffset = fdt_path_offset(dtb_buf, "/memory/cma");
	if (nodeoffset < 0)
	{
		printf("failed to offset for /memory/cma = %d \n", nodeoffset);
		goto EXIT;
	}

	int len;
	const void *nodep = fdt_getprop(dtb_buf, nodeoffset, "reg", &len);
	if (len == 0 || nodep == NULL)
	{
		printf("failed to access /memory/cma reg.\n");
		goto EXIT;
	}

	unsigned int *p_data = (unsigned int *)nodep;
	printf("DDR0: start: %#x, len:%#x DDR1: start: %#x, len:%#x\n", 
			ntohl(p_data[0]), ntohl(p_data[1]), ntohl(p_data[2]), ntohl(p_data[3]));

	unsigned int val[4] = {0};
	val[0] =htonl(0x600000);
	val[1] = htonl(0x100000);
	val[2] = htonl(0x800000);
	val[3] = htonl(0x200000);

	ret = fdt_setprop(dtb_buf, nodeoffset, "reg", val, 16);
	if (ret != 0)
	{
		printf("set reg failed!!!\n");
		goto EXIT;
	}

	nodep = fdt_getprop(dtb_buf, nodeoffset, "reg", &len);
	if (len == 0 || nodep == NULL)
	{
		printf("failed to access /memory/cma reg.\n");
		goto EXIT;
	}

	p_data = (unsigned int *)nodep;
	printf("AFFTER DDR0: start: %#x, len:%#x DDR1: start: %#x, len:%#x\n", 
			ntohl(p_data[0]), ntohl(p_data[1]), ntohl(p_data[2]), ntohl(p_data[3]));

	
EXIT:
	if (!dtb_buf)
	{
		free(dtb_buf);
	}
	if (fd >= 0)
	{
		close(fd);
	}
	return 0;
}

执行结果:
在这里插入图片描述
详细工程见我的github:
里面包含makefile,及内核代码makefile修改,生成libfdt库。

https://blog.csdn.net/cc289123557/article/details/51782449

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐