dtb展开成device_node

article/2025/10/24 6:43:33

dtb展开成device_node


文章目录

  • dtb展开成device_node
  • 设备树是如何传递给内核的?
  • 设备树相关结构体
  • 举例
  • of操作函数
    • 与查找节点有关的 OF 函数
      • 1、of_find_node_by_name 函数
      • 2、of_find_node_by_type 函数
      • 3、of_find_compatible_node 函数
      • 4、of_find_matching_node_and_match 函数
      • 5、of_find_node_by_path 函数
    • 查找父/子节点的 OF 函数
      • 1、of_get_parent 函数
      • 2、of_get_next_child 函数
    • 提取属性值的 OF 函数
      • 1、of_find_property 函数
      • 2、of_property_count_elems_of_size 函数
      • 3、of_property_read_u32_index 函数
      • 4、 of_property_read_u8_array 函数
      • 5、of_property_read_u8 函数 of_property_read_u16 函数 of_property_read_u32 函数 of_property_read_u64 函数
      • 6、 of_property_read_string 函数
      • 7、of_n_addr_cells 函数
      • 8、of_n_size_cells 函数
    • 其他常用的 OF 函数
      • 1、of_device_is_compatible 函数
      • 2、of_get_address 函数
      • 3、of_translate_address 函数
      • 4、 of_address_to_resource 函数
      • 5、of_iomap 函数
  • IMX6ULL内核是如何展开设备树为内核可以识别的device_node
    • setup_arch(&command_line);
      • boot_command_line
      • setup_machine_fdt
      • early_fixmap_init
      • unflatten_device_tree


设备树是如何传递给内核的?

在这里插入图片描述

设备树相关结构体

device_node结构体
device_node结构体定义在include/linux/of.h头文件当中。

struct device_node {const char *name; // 节点名称const char *type; // 节点类型phandle phandle; // 节点句柄const char *full_name; // 节点全名struct fwnode_handle fwnode;struct    property *properties; // 属性struct    property *deadprops;    /* removed properties */struct    device_node *parent; // 父节点struct    device_node *child; // 子节点struct    device_node *sibling; // 兄弟节点struct    kobject kobj; // 内核对象unsigned long _flags; // 节点标志void    *data; // 数据
#if defined(CONFIG_SPARC)const char *path_component_name; // 路径组件名称unsigned int unique_id; // 唯一IDstruct of_irq_controller *irq_trans; // 中断控制器
#endif
};

property结构体
property结构体定义在include/linux/of.h头文件当中。

struct property {char    *name; // 属性名称int    length; // 属性长度void    *value; // 属性值struct property *next; // 下一个属性unsigned long _flags; // 属性标志unsigned int unique_id; // 属性唯一IDstruct bin_attribute attr; // 二进制属性
};

举例

下面设备树内置展开后如下

/ {model = "this is my devicethree!";#address-cells = <1>;#size-cells = <1>;chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";};cpu: cpu@1 {device_type = "cpu";compatible = "arm,cortex-a35","arm,armv8";reg = <0x0 0x1>;};

在这里插入图片描述

of操作函数

与查找节点有关的 OF 函数

1、of_find_node_by_name 函数

struct device_node *of_find_node_by_name(struct device_node         *from,
const char                        *name);

函数参数和返回值含义如下:
of_find_node_by_name 函数通过节点名字查找指定的节点,函数原型如下:
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
name:要查找的节点名字。
返回值:找到的节点,如果为NULL 表示查找失败。

2、of_find_node_by_type 函数

of_find_node_by_type 函数通过 device_type 属性查找指定的节点,函数原型如下:

struct device_node *of_find_node_by_type(struct device_node *from, const char *type)

函数参数和返回值含义如下:

from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,也就是 device_type 属性值。
返回值:找到的节点,如果为NULL 表示查找失败。

3、of_find_compatible_node 函数

of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点,
函数原型如下:

struct device_node *of_find_compatible_node(struct device_node      *from,
const char                      *type,
const char                      *compatible)

函数参数和返回值含义如下:
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为NULL,表示
忽略掉 device_type 属性。
compatible:要查找的节点所对应的 compatible 属性列表。
返回值:找到的节点,如果为NULL 表示查找失败

4、of_find_matching_node_and_match 函数

of_find_matching_node_and_match 函数通过 of_device_id 匹配表来查找指定的节点,函数原
型如下:

struct device_node *of_find_matching_node_and_match(struct device_node             *from,
const struct of_device_id    *matches, const struct of_device_id    **match)

函数参数和返回值含义如下:
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
matches:of_device_id 匹配表,也就是在此匹配表里面查找节点。
match:找到的匹配的 of_device_id。
返回值:找到的节点,如果为NULL 表示查找失败

5、of_find_node_by_path 函数

of_find_node_by_path 函数通过路径来查找指定的节点,函数原型如下:

inline struct device_node *of_find_node_by_path(const char *path)

函数参数和返回值含义如下:
path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。
返回值:找到的节点,如果为NULL 表示查找失败

查找父/子节点的 OF 函数

1、of_get_parent 函数

of_get_parent 函数用于获取指定节点的父节点(如果有父节点的话),函数原型如下:

struct device_node *of_get_parent(const struct device_node *node)

函数参数和返回值含义如下:
node:父节点。
返回值:找到的父节点。

2、of_get_next_child 函数

of_get_next_child 函数用迭代的方式查找子节点,函数原型如下:

struct device_node *of_get_next_child(const struct device_node         *node,
struct device_node                    *prev)

函数参数和返回值含义如下:
node:要查找的父节点的节点。
prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为
NULL,表示从第一个子节点开始。
返回值:找到的下一个子节点。

提取属性值的 OF 函数

1、of_find_property 函数

of_find_property 函数用于查找指定的属性,函数原型如下:

property *of_find_property(const struct device_node   *np,
const char                            *name,
int                                        *lenp)

函数参数和返回值含义如下:
np:设备节点。
name: 属性名字。
lenp:属性值的字节数
返回值:找到的属性。

2、of_property_count_elems_of_size 函数

of_property_count_elems_of_size 函数用于获取属性中元素的数量,比如 reg 属性值是一个
数组,那么使用此函数可以获取到这个数组的大小,此函数原型如下:

int of_property_count_elems_of_size(const struct device_node   *np,
const char                            *propname,
int elem_size)

函数参数和返回值含义如下:
np:设备节点。
proname: 需要统计元素数量的属性名字。
elem_size:元素长度。
返回值:得到的属性元素数量。

3、of_property_read_u32_index 函数

of_property_read_u32_index 函数用于从属性中获取指定标号的 u32 类型数据值(无符号 32
位),比如某个属性有多个 u32 类型的值,那么就可以使用此函数来获取指定标号的数据值,此
函数原型如下:

int of_property_read_u32_index(const struct device_node   *np,
const char                            *propname,
u32                                      index,
u32                                      *out_value)

函数参数和返回值含义如下:
np:设备节点。
proname: 要读取的属性名字。
index:要读取的值标号。
out_value:读取到的值
返回值:0 读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有
要读取的数据,-EOVERFLOW 表示属性值列表太小。

4、 of_property_read_u8_array 函数

of_property_read_u16_array 函数
of_property_read_u32_array 函数
of_property_read_u64_array 函数
这 4 个函数分别是读取属性中 u8、u16、u32 和 u64 类型的数组数据,比如大多数的 reg 属
性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据。这四个函数的原型
如下:

int of_property_read_u8_array(const struct device_node      *np,
const char                              *propname,
u8                                          *out_values,
size_t                                     sz)
int of_property_read_u16_array(const struct device_node    *np,
const char                            *propname,
u16                                      *out_values,
size_t                                   sz)
int of_property_read_u32_array(const struct device_node    *np,
const char                            *propname,
u32                                      *out_values,
size_t                                   sz)
int of_property_read_u64_array(const struct device_node    *np,
const char                            *propname,
u64                                      *out_values,
size_t                                   sz)

函数参数和返回值含义如下:
np:设备节点。
proname: 要读取的属性名字。
out_value:读取到的数组值,分别为 u8、u16、u32 和 u64。
sz:要读取的数组元素数量。
返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没
有要读取的数据,-EOVERFLOW 表示属性值列表太小。

5、of_property_read_u8 函数 of_property_read_u16 函数 of_property_read_u32 函数 of_property_read_u64 函数

有些属性只有一个整形值,这四个函数就是用于读取这种只有一个整形值的属性,分别用
于读取 u8、u16、u32 和u64 类型属性值,函数原型如下:

int of_property_read_u8(const struct device_node        *np,
const char                              *propname,
u8                                          *out_value)
int of_property_read_u16(const struct device_node      *np,
const char                              *propname,
u16                                        *out_value)
int of_property_read_u32(const struct device_node      *np,
const char                              *propname,
u32                                        *out_value)
int of_property_read_u64(const struct device_node      *np,
const char                              *propname,
u64                                        *out_value)

函数参数和返回值含义如下:
np:设备节点。
proname: 要读取的属性名字。
out_value:读取到的数组值。
返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没
有要读取的数据,-EOVERFLOW 表示属性值列表太小。

6、 of_property_read_string 函数

of_property_read_string 函数用于读取属性中字符串值,函数原型如下:

int of_property_read_string(struct device_node    *np,
const char                    *propname,
const char                    **out_string)

函数参数和返回值含义如下:
np:设备节点。
proname: 要读取的属性名字。
out_string:读取到的字符串值。
返回值:0,读取成功,负值,读取失败。

7、of_n_addr_cells 函数

of_n_addr_cells 函数用于获取#address-cells 属性值,函数原型如下:

int of_n_addr_cells(struct device_node *np)

函数参数和返回值含义如下:
np:设备节点。
返回值:获取到的#address-cells 属性值。

8、of_n_size_cells 函数

of_size_cells 函数用于获取#size-cells 属性值,函数原型如下:

int of_n_size_cells(struct device_node *np)

函数参数和返回值含义如下:
np:设备节点。
返回值:获取到的#size-cells 属性值。

其他常用的 OF 函数

1、of_device_is_compatible 函数

of_device_is_compatible 函数用于查看节点的 compatible 属性是否有包含 compat 指定的字
符串,也就是检查设备节点的兼容性,函数原型如下:

int of_device_is_compatible(const struct device_node          *device,
const char                                    *compat)

函数参数和返回值含义如下:
device:设备节点。
compat:要查看的字符串。
返回值:0,节点的 compatible 属性中不包含 compat 指定的字符串;正数,节点的 compatible
属性中包含compat 指定的字符串。

2、of_get_address 函数

of_get_address 函数用于获取地址相关属性,主要是“reg”或者“assigned-addresses”属性
值,函数原型如下:

const     be32 *of_get_address(struct device_node        *dev,
int                                  index,
u64                                *size,
unsigned int                  *flags)

函数参数和返回值含义如下:
dev:设备节点。
index:要读取的地址标号。
size:地址长度。
flags:参数,比如 IORESOURCE_IO、IORESOURCE_MEM 等
返回值:读取到的地址数据首地址,为NULL 的话表示读取失败。

3、of_translate_address 函数

of_translate_address 函数负责将从设备树读取到的地址转换为物理地址,函数原型如下:

u64 of_translate_address(struct device_node         *dev,
const     be32                   *in_addr)

函数参数和返回值含义如下:
dev:设备节点。
in_addr:要转换的地址。
返回值:得到的物理地址,如果为OF_BAD_ADDR 的话表示转换失败。

4、 of_address_to_resource 函数

函数看名字像是从设备树里面提取资源值,但是本质上就是将 reg 属性值,然后将其转换为 resource 结构体类型,函数原型如下所示

int of_address_to_resource(struct device_node     *dev,
int                                index,
struct resource             *r)

函数参数和返回值含义如下:
dev:设备节点。
index:地址资源标号。
r:得到的 resource 类型的资源值。
返回值:0,成功;负值,失败。

5、of_iomap 函数

of_iomap 函数用于直接内存映射,以前我们会通过 ioremap 函数来完成物理地址到虚拟地
址的映射,采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟地址,
不需要使用 ioremap 函数了。当然了,你也可以使用 ioremap 函数来完成物理地址到虚拟地址
的内存映射,只是在采用设备树以后,大部分的驱动都使用 of_iomap 函数了。of_iomap 函数本
质上也是将reg 属性中地址信息转换为虚拟地址,如果 reg 属性有多段的话,可以通过 index 参
数指定要完成内存映射的是哪一段,of_iomap 函数原型如下:

void     iomem *of_iomap(struct device_node       *np,
int                                  index)

函数参数和返回值含义如下:
np:设备节点。
index:reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话index 就设置为 0。
返回值:经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。

IMX6ULL内核是如何展开设备树为内核可以识别的device_node

打开内核源码init/main.c,找到start_kernel函数

asmlinkage __visible void __init start_kernel(void)
{char *command_line;char *after_dashes;/** Need to run as early as possible, to initialize the* lockdep hash:*/
/** 初始化锁定机制* 设置初始任务的栈结束标志* 设置处理器ID* 初始化调试对象* 设置初始栈金丝雀* 初始化cgroup* 禁用中断* 启动CPU* 初始化页地址* 打印内核版本信息* 设置内核命令行* 设置CPU掩码* 设置每个CPU的区域* 准备启动CPU* 构建所有可用的空闲页列表* 初始化页分配器*/
lockdep_init();
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();/** Set up the the initial canary ASAP:*/
boot_init_stack_canary();cgroup_init_early();local_irq_disable();
early_boot_irqs_disabled = true;/** Interrupts are still disabled. Do necessary setups, then* enable them*/
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
mm_init_cpumask(&init_mm);
setup_command_line(command_line);
setup_nr_cpu_ids();
setup_per_cpu_areas();
smp_prepare_boot_cpu();    /* arch-specific boot-cpu hooks */build_all_zonelists(NULL, NULL);
page_alloc_init();

在这里插入图片描述

为什么要从start_kernel函数入手?
start_kernel函数是内核启动阶段的入囗,类似于main函数。
在start_kernel函数里面有非常多的子函数,这些子函数都是完成Linux内核初始化的函数,这里我们只关心和dtb展开有关的函数。

setup_arch(&command_line);

/arch/arm64/kernel/setup.c
start_kernel
setup_arch

void __init setup_arch(char **cmdline_p)
{setup_processor();setup_machine_fdt(__fdt_pointer);init_mm.start_code = (unsigned long) _text;init_mm.end_code   = (unsigned long) _etext;init_mm.end_data   = (unsigned long) _edata;init_mm.brk       = (unsigned long) _end;*cmdline_p = boot_command_line;early_fixmap_init();early_ioremap_init();parse_early_param();/**  Unmask asynchronous aborts after bringing up possible earlycon.* (Report possible System Errors once we can report this occurred)*/local_async_enable();efi_init();arm64_memblock_init();/* Parse the ACPI tables for possible boot-time configuration */acpi_boot_table_init();paging_init();request_standard_resources();early_ioremap_reset();if (acpi_disabled) {unflatten_device_tree();psci_dt_init();cpu_read_bootcpu_ops();
#ifdef CONFIG_SMPof_smp_init_cpus();
#endif} else {psci_acpi_init();acpi_init_cpus();}#ifdef CONFIG_SMPsmp_build_mpidr_hash();
#endif#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)conswitchp = &dummy_con;
#endif
#endifif (boot_args[1] || boot_args[2] || boot_args[3]) {pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n""\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n""This indicates a broken bootloader or old kernel\n",boot_args[1], boot_args[2], boot_args[3]);}
}

boot_command_line

*cmdline_p = boot_command_line;
char __initdata boot_command_line[COMMAND_LINE_SIZE];
#define COMMAND_LINE_SIZE 1024

记录了uboot传递给内核的boot_command_line,大小是4096.。如果uboot传递给boot_command_line的大小超过4096,就要修改这个数组的大小。

setup_machine_fdt

  setup_machine_fdt(__fdt_pointer);

参数__fdt_pointer是dtb位于内存的地址
打开arch/arm64/head.S,找到下列内容

/** Preserve the arguments passed by the bootloader in x0 .. x3*/
preserve_boot_args:mov    x21, x0                // x21=FDTadr_l    x0, boot_args            // record the contents ofstp    x21, x1, [x0]            // x0 .. x3 at kernel entrystp    x2, x3, [x0, #16]dmb    sy                // needed before dc ivac with// MMU offadd    x1, x0, #0x20            // 4 x 8 bytesb    __inval_cache_range        // tail call
ENDPROC(preserve_boot_args)

在这里插入图片描述

可以看到dtb位于内存的地址是x0传递过来的,x0里面存放的地址是规定的。

1:    cmp    x6, x7b.hs    2fstr    xzr, [x6], #8            // Clear BSSb    1b
2:adr_l    sp, initial_sp, x4str_l    x21, __fdt_pointer, x5        // Save FDT pointerstr_l    x24, memstart_addr, x6        // Save PHYS_OFFSETmov    x29, #0b    start_kernel
ENDPROC(__mmap_switched)

在这里插入图片描述

str_l x21, __fdt_pointer, x5将设备位于内存的地址保存到__fdt_pointer。
setup_machine_fdt函数
此时mmu已经开启,需要将dtb位于内存物理地址映射成虚拟地址
start_kernel
setup_arch
setup_machine_fdt

static void __init setup_machine_fdt(phys_addr_t dt_phys)
{if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) {early_print("\n""Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n""The dtb must be 8-byte aligned and passed in the first 512MB of memory\n""\nPlease check your bootloader.\n",dt_phys, phys_to_virt(dt_phys));while (true)cpu_relax();}dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
}

phys_to_virt(dt_phys)将dtb位于内存物理地址映射成虚拟地址
of_flat_dt_get_machine_name获取model和compatible属性的值

early_init_dt_scan函数中调用early_init_dt_scan(phys_to_virt(dt_phys)),用于扫描设备树(Device Tree)
start_kernel
setup_arch
setup_machine_fdt
early_init_dt_scan

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();return true;}

early_init_dt_scan调用early_init_dt_verify(params);检查设备树头部是否满足要求。将内存中dtb的虚拟地址保存到了initial_boot_params
start_kernel
setup_arch
setup_machine_fdt
early_init_dt_scan
early_init_dt_verify

bool __init early_init_dt_verify(void *params)
{if (!params)return false;/* check device tree validity */if (fdt_check_header(params))return false;/* Setup flat device-tree pointer */initial_boot_params = params;of_fdt_crc32 = crc32_be(~0, initial_boot_params,fdt_totalsize(initial_boot_params));return true;
}

start_kernel
setup_arch
setup_machine_fdt
early_init_dt_scan
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);
}

early_init_dt_scan
early_init_dt_verify(params);
initial_boot_params //将内存中dtb的虚拟地址保存到了initial_boot_params
early_init_dt_scan_nodes

early_fixmap_init

start_kernel
setup_arch
early_fixmap_init //保存dtb位于内存的地址,并设置为只读,不可修改

void __init early_fixmap_init(void)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
unsigned long addr = FIXADDR_START;
pgd = pgd_offset_k(addr);
pgd_populate(&init_mm, pgd, bm_pud);
pud = pud_offset(pgd, addr);
pud_populate(&init_mm, pud, bm_pmd);
pmd = pmd_offset(pud, addr);
pmd_populate_kernel(&init_mm, pmd, bm_pte);/** The boot-ioremap range spans multiple pmds, for which* we are not preparted:*/
BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)!= (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));if ((pmd != fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)))|| pmd != fixmap_pmd(fix_to_virt(FIX_BTMAP_END))) {WARN_ON(1);pr_warn("pmd %p != %p, %p\n",pmd, fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)),fixmap_pmd(fix_to_virt(FIX_BTMAP_END)));pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",fix_to_virt(FIX_BTMAP_BEGIN));pr_warn("fix_to_virt(FIX_BTMAP_END):   %08lx\n",fix_to_virt(FIX_BTMAP_END));pr_warn("FIX_BTMAP_END:       %d\n", FIX_BTMAP_END);pr_warn("FIX_BTMAP_BEGIN:     %d\n", FIX_BTMAP_BEGIN);
}}

unflatten_device_tree

unflatten_device_tree是dtb转换为device的核心函数

void __init unflatten_device_tree(void)
{__unflatten_device_tree(initial_boot_params, &of_root,early_init_dt_alloc_memory_arch);/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */of_alias_scan(early_init_dt_alloc_memory_arch);
}

start_kernel
setup_arch
unflatten_device_tree
__unflatten_device_tree

static void __unflatten_device_tree(void *blob,struct device_node **mynodes,void * (*dt_alloc)(u64 size, u64 align))
{unsigned long size;int start;void *mem;pr_debug(" -> unflatten_device_tree()\n");if (!blob) {pr_debug("No device tree pointer\n");return;}pr_debug("Unflattening device tree:\n");pr_debug("magic: %08x\n", fdt_magic(blob));pr_debug("size: %08x\n", fdt_totalsize(blob));pr_debug("version: %08x\n", fdt_version(blob));if (fdt_check_header(blob)) {pr_err("Invalid device tree blob header\n");return;}/* First pass, scan for size */start = 0;size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);size = ALIGN(size, 4);pr_debug("  size is %lx, allocating...\n", size);/* Allocate memory for the expanded device tree */mem = dt_alloc(size + 4, __alignof__(struct device_node));memset(mem, 0, size);*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);pr_debug("  unflattening %p...\n", mem);/* Second pass, do actual unflattening */start = 0;unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);if (be32_to_cpup(mem + size) != 0xdeadbeef)pr_warning("End of tree marker overwritten: %08x\n",be32_to_cpup(mem + size));pr_debug(" <- unflatten_device_tree()\n");
}
size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);

第一次扫描,统计设备树需要的内存大小,第二个参数是NULL,说明只统计大小,不做其他的事。

mem = dt_alloc(size + 4, __alignof__(struct device_node));

一次就把需要的空间分配出来
unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
第二次扫描,应为第二个参数不是NULL,所以会执行核心函数中的某几个函数构造device_node树
start_kernel
setup_arch
unflatten_device_tree
__unflatten_device_tree
unflatten_dt_node //解析dtb文件中的各个节点

static void * unflatten_dt_node(void *blob,void *mem,int *poffset,struct device_node *dad,struct device_node **nodepp,unsigned long fpsize,bool dryrun)
{const __be32 *p;struct device_node *np;struct property *pp, **prev_pp = NULL;const char *pathp;unsigned int l, allocl;static int depth = 0;int old_depth;int offset;int has_name = 0;int new_format = 0;pathp = fdt_get_name(blob, *poffset, &l);if (!pathp)return mem;allocl = ++l;/* version 0x10 has a more compact unit name here instead of the full* path. we accumulate the full path size using "fpsize", we'll rebuild* it later. We detect this because the first character of the name is* not '/'.*/if ((*pathp) != '/') {new_format = 1;if (fpsize == 0) {/* root node: special case. fpsize accounts for path* plus terminating zero. root node only has '/', so* fpsize should be 2, but we want to avoid the first* level nodes to have two '/' so we use fpsize 1 here*/fpsize = 1;allocl = 2;l = 1;pathp = "";} else {/* account for '/' and path size minus terminal 0* already in 'l'*/fpsize += l;allocl = fpsize;}}np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));if (!dryrun) {char *fn;of_node_init(np);np->full_name = fn = ((char *)np) + sizeof(*np);if (new_format) {/* rebuild full path for new format */if (dad && dad->parent) {strcpy(fn, dad->full_name);
#ifdef DEBUGif ((strlen(fn) + l + 1) != allocl) {pr_debug("%s: p: %d, l: %d, a: %d\n",pathp, (int)strlen(fn),l, allocl);}
#endiffn += strlen(fn);}*(fn++) = '/';}memcpy(fn, pathp, l);prev_pp = &np->properties;if (dad != NULL) {np->parent = dad;np->sibling = dad->child;dad->child = np;}}/* process properties */for (offset = fdt_first_property_offset(blob, *poffset);(offset >= 0);(offset = fdt_next_property_offset(blob, offset))) {const char *pname;u32 sz;if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {offset = -FDT_ERR_INTERNAL;break;}if (pname == NULL) {pr_info("Can't find property name in list !\n");break;}if (strcmp(pname, "name") == 0)has_name = 1;pp = unflatten_dt_alloc(&mem, sizeof(struct property),__alignof__(struct property));if (!dryrun) {/* We accept flattened tree phandles either in* ePAPR-style "phandle" properties, or the* legacy "linux,phandle" properties.  If both* appear and have different values, things* will get weird.  Don't do that. */if ((strcmp(pname, "phandle") == 0) ||(strcmp(pname, "linux,phandle") == 0)) {if (np->phandle == 0)np->phandle = be32_to_cpup(p);}/* And we process the "ibm,phandle" property* used in pSeries dynamic device tree* stuff */if (strcmp(pname, "ibm,phandle") == 0)np->phandle = be32_to_cpup(p);pp->name = (char *)pname;pp->length = sz;pp->value = (__be32 *)p;*prev_pp = pp;prev_pp = &pp->next;}}/* with version 0x10 we may not have the name property, recreate* it here from the unit name if absent*/if (!has_name) {const char *p1 = pathp, *ps = pathp, *pa = NULL;int sz;while (*p1) {if ((*p1) == '@')pa = p1;if ((*p1) == '/')ps = p1 + 1;p1++;}if (pa < ps)pa = p1;sz = (pa - ps) + 1;pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,__alignof__(struct property));if (!dryrun) {pp->name = "name";pp->length = sz;pp->value = pp + 1;*prev_pp = pp;prev_pp = &pp->next;memcpy(pp->value, ps, sz - 1);((char *)pp->value)[sz - 1] = 0;pr_debug("fixed up name for %s -> %s\n", pathp,(char *)pp->value);}}if (!dryrun) {*prev_pp = NULL;np->name = of_get_property(np, "name", NULL);np->type = of_get_property(np, "device_type", NULL);if (!np->name)np->name = "<NULL>";if (!np->type)np->type = "<NULL>";}old_depth = depth;*poffset = fdt_next_node(blob, *poffset, &depth);if (depth < 0)depth = 0;while (*poffset > 0 && depth > old_depth)mem = unflatten_dt_node(blob, mem, poffset, np, NULL,fpsize, dryrun);if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)pr_err("unflatten: error %d processing FDT\n", *poffset);/** Reverse the child list. Some drivers assumes node order matches .dts* node order*/if (!dryrun && np->child) {struct device_node *child = np->child;np->child = NULL;while (child) {struct device_node *next = child->sibling;child->sibling = np->child;np->child = child;child = next;}}if (nodepp)*nodepp = np;return mem;
}

在这里插入图片描述


http://chatgpt.dhexx.cn/article/PFWIuFAt.shtml

相关文章

设备树之DTS与DTB格式

目录 一、设备树 二、DTS格式 2.1 属性 2.2 节点 2.3 引用其他节点 2.4 小总结 三、DTB格式 3.1 结构 3.2 分析 一、设备树 对于点灯字符设备驱动程序可以有三种写法&#xff0c;首先是传统方法&#xff0c;这种方式直接在程序中写死&#xff0c;其次是利用总线设备驱…

Android boot.img dtb.img 编译过程

最近做RK3588案子,修改dts后,导致boot.img过大,编译出错,整体分析下boot.img过大的原因是因为在打包boot.img过程中,dbt.img过大导致,所以整体分析下boot.img编译过程,尤其是dbt.img的生成过程. boot.img生成过程 在Andorid跟目录下执行, source build/envsetup.sh 然后lunch x…

Linux 设备树(二) dtc dts/dtsi dtb的关系

在学习设备树之前&#xff0c;我们先来了解一下跟设备树相关的三个对象&#xff0c;分别是dtc、dts/dtsi、dtb。 dtc:用来编译设备树的工具 dts:设备树描述文件 dtsi:设备树头文件 dtb:编译后的二进制文件 dtc设备树编译工具 dtc是用来编译设备树的工具&#xff0c;就像gcc可以…

dtb如何转换到platform_device

分2步&#xff0c;第一步是首先转换为device_node&#xff0c;第二步device_node转换为platform_device。 第一步 /*** unflatten_device_tree - create tree of device_nodes from flat blob** unflattens the device-tree passed by the firmware, creating the* tree of st…

U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明

最近&#xff0c;工作重心要从裸机开发转移到嵌入式 Linux 系统开发&#xff0c;在之前的博文 Linux 之八 完整嵌入式 Linux 环境、&#xff08;交叉&#xff09;编译工具链、CPU 体系架构、嵌入式系统构建工具 中详细介绍了嵌入式 Linux 环境&#xff0c;接下来就是实际动手学…

dts、dtb的那些事儿

笔者最近在支持新的案子&#xff0c;过于忙碌&#xff0c;好久没更新了&#xff0c;勿怪。 1、设备树大变革故事 2011年3月17日的ARM Linux邮件列表有封邮件“this whole ARM thing is a fucking pain in the ass”引起了轩然大波&#xff0c;原来是我们的Linux之父Linus Torv…

DBT工具简介

What is DBT 在将数据加载到集中式数据仓库之前&#xff0c;必须对其进行清理、保持一致并根据需要进行组合。换句话说&#xff0c;必须转换数据&#xff0c;这就是我们所谓的 ETL&#xff08;提取、转换、加载&#xff09;和 ELT 中的“T”。 这是挖掘数据价值的关键一步。 而…

4.2uboot对设备树的支持——dtb的修改原理

本节说明在uboot中修改dtb的原理。 在uboot中&#xff0c;有一些命令支持对dtb文件进行修改。 当我们想要修改dtb文件时&#xff0c;可以直接修改dts文件&#xff0c;然后编译dts文件生成新的dtb文件&#xff0c;再将新的dtb文件载入设备。 或者&#xff0c;我们也可以在ubo…

DBT是什么

关于DBT DBT 是一种数据转换工作流&#xff0c;可帮助您完成更多工作&#xff0c;同时产生更高质量的结果。您可以使用 dbt 来模块化和集中分析代码&#xff0c;同时还为数据团队提供软件工程工作流中常见的护栏。在将数据模型安全部署到生产环境之前&#xff0c;通过监控和可见…

2.1设备树的规范(dts和dtb)——DTS格式

本节学习设备树的规范。 使用设备树时&#xff0c;需要编写dts文件&#xff0c;然后使用dtc编译dts文件&#xff0c;生成dtb文件。 所以本节分为两部分&#xff0c;第一部分讲解dts格式&#xff0c;第二部分讲解dtb格式。 首先看一下dts文件的布局。 DTS文件布局&#xff0…

关于EMUELC适配各种机型,DTB如何修改教程

很多爱好者留言&#xff0c;都想问关于EMUELC的dtb适配机型问题&#xff0c;这里我就出一个教程&#xff0c;如何修改dtb&#xff0c;然后去适配自己的机型&#xff0c;然后启动 。这里我只是提供方法&#xff0c;具体的调试是需要原理图进行配置。 首先&#xff0c;不管是aml…

DTBO简介

1、DTBO简介 设备树 (DT) 是用于描述“不可发现”硬件的命名节点和属性构成的一种数据结构。操作系统&#xff08;例如在 Android 中使用的 Linux 内核&#xff09;会使用 DT 来支持 Android 设备使用的各种硬件配置。硬件供应商会提供自己的 DT 源文件&#xff0c;接下来…

【DTB/DTBO 分区介绍】

如果你的 DTB/DTBO 位于专属的分区&#xff08;例如 dtb 和 dtbo 分区&#xff09;中&#xff0c;请使用以下表格结构和头文件格式&#xff1a; 数据结构 dt_table_header 仅适用于 dtb/dtbo 分区&#xff1b;您不能在kernel( image.gz) 末尾处附加此格式。如果您有一个 DTB/D…

2.2设备树的规范(dts和dtb)——DTB格式

本节讲述设备树的dtb格式。 上节讲述了dts格式。回顾上节&#xff0c;在dts文件和dtsi文件中&#xff0c;可以使用C语言的define和include&#xff0c;使用方法和作用也同C语言相同。 编写dts文件后&#xff0c;需要使用dtc工具将dts文件编译成dtb文件。dtc工具可以检查dts文…

「设备树」dtb给内核的两种工作模式

一&#xff0c;传递dtb给内核 对于传统bootloader提供两种工作模式&#xff1a;一是启动加载模式&#xff08;start loading&#xff09;&#xff0c;一是下载模式&#xff08;downloading&#xff09; 工作在启动加载模式时&#xff0c;bootloader会自动执行bootcmd命令&#…

设备树_dtb文件分析

前言&#xff1a;我之前的原计划是没有打算写设备树dtb文件分析&#xff0c;但是情势所迫啊&#xff01;&#xff0c;学习还是要一步一步来的。 在前面的章节提到过.dts文件以文本方式对系统设备树进行描述&#xff0c;经过Device Tree Compiler(dtc)将dts文件转换成二进制文件…

Linux设备树学习2 - DTB文件格式

一. DTB文件简介 DTB文件是由DTS文件通过dtc命令编译生成的二进制文件。DTS文件不能直接被内核解析&#xff0c;需要编译成DTB文件才可以直接被内核识别并解析使用的。 二. DTB文件内容布局 从上图可以看出&#xff0c;DTB由四个部分组成&#xff0c;分别是struct fdt_header&a…

setUserVisibleHint-- fragment真正的onResume和onPause方法

这个情况仅适合与多个fragment之间切换时统计&#xff0c;而非activity和fragment同时交互&#xff0c;因当时项目为首页4个fargment时长统计&#xff0c;因此适合&#xff0c;经下面网友评论指出&#xff0c;特在这里写出此问题&#xff0c;因最近项目较忙&#xff0c;具体情况…

onCreate与onStart区别,onStart与onResume区别

http://www.cnblogs.com/kofi1122/archive/2011/04/10/2011772.html Activity生命周期之我见 关于Activity生命周期的文章很多&#xff0c;而且大部分也说得很详细&#xff0c;所以作为关于这方面的内容我本来不想多说&#xff0c;但是大家可能跟我之前一样&#xff0c;在看这方…

Flutter BaseWidget 实现onResume、onPause()

熟读唐诗三百首&#xff0c;不会作诗也会吟。——孙洙 最近用Flutter开发的项目算是完成了开发到上线第一阶段了。任何一个项目开始了&#xff0c;若想追求的是更好&#xff0c;那么就需要下功夫对项目用户体验和代码效率深入的研究了。作为用户和产品经理、老板、UI、不懂技术…