小张学linux内核: 七. 设备树的使用
在很久之前的版本,uboot还需要传machine id和参数区地址给内核,但现在只传递设备树(fdt)的地址给内核了,这些参数全在fdt中做了。uboot将设备树bin文件和kernel加载到内存,然后将fdt地址传给kernel,跳到kernel地址执行。fdt为平坦设备树,即数作为一个一个节点顺序存在bin文件中,不再保持树的形状。我们来看看设备树bin文件dtb的文件结构分头部参数区,内存
在很久之前的版本,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
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)