Device Tree (三) - dtb -> device_node

基于arm平台,Linux 5.10

1,设备树的执行入口setup_arch

linux最底层的初始化部分在HEAD.s中,这是汇编代码,我们暂且不作过多讨论。

在head.s完成部分初始化之后,就开始调用C语言函数,而被调用的第一个C语言函数就是start_kernel:

asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
    ......
    setup_arch(&command_line);
    ......
}

而对于设备树的处理,基本上就在setup_arch()这个函数中。

可以看到,在start_kernel()中调用了setup_arch(&command_line);

void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
    ......

    //根据传入的设备树dtb首地址完成一些初始化操作
    setup_machine_fdt(__fdt_pointer);

    //保证设备树dtb本身存在于内存中而不被覆盖
    arm64_memblock_init();

    //对设备树的具体解析
    unflatten_device_tree();

    ......
}

这三个被调用的函数就是主要的设备树处理函数:

* setup_machine_fdt():根据传入的设备树dtb的首地址完成一些初始化操作。

*arm64_memblock_init():主要是内存相关函数,为设备树保留相应的内存空间,保证设备树dtb本身存在于内存中而不被覆盖。用户可以在设备树中设置保留内存,这一部分同时作了保留指定内存的工作。

* unflatten_device_tree():对设备树具体的解析,事实上在这个函数中所做的工作就是将设备树各节点转换成相应的struct device_node结构体。

2,dtb -> device_node转换过程

kernel V5.10:
start_kernel(void) /* D:\work\source_code\msm-kernel\msm_kernel\init\main.c */
----setup_arch(&command_line); /* D:\work\source_code\msm-kernel\msm_kernel\arch\arm64\kernel\setup.c */
--------setup_machine_fdt(__fdt_pointer); /* D:\work\source_code\msm-kernel\msm_kernel\arch\arm64\kernel\setup.c */
------------early_init_dt_scan(dt_virt)
----------------early_init_dt_scan_nodes();
--------------------of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
--------------------of_scan_flat_dt(early_init_dt_scan_root, NULL);
--------------------of_scan_flat_dt(early_init_dt_scan_memory, NULL);


--------arm64_memblock_init();
------------early_init_fdt_scan_reserved_mem();
----------------early_init_dt_reserve_memory_arch(base, size, false);
--------------------memblock_reserve(base, size);


--------unflatten_device_tree(); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\of\fdt.c */
------------__unflatten_device_tree(initial_boot_params, NULL, &of_root, early_init_dt_alloc_memory_arch, false);
----------------unflatten_dt_nodes(blob, NULL, dad, NULL);
--------------------populate_node(blob, offset, &mem, nps[depth], &nps[depth+1], dryrun))
------------------------np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, __alignof__(struct device_node));
------------------------populate_properties(blob, offset, mem, np, pathp, dryrun);
----------------------------fdt_getprop_by_offset(blob, cur, &pname, &sz);


--------of_alias_scan(early_init_dt_alloc_memory_arch);
--------unittest_unflatten_overlay_base();

3,Device Tree文件(DTB)结构描述的结构体

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 */
    fdt32_t last_comp_version;     /* last compatible version */

    /* version 2 fields below */
    fdt32_t boot_cpuid_phys;     /* Which physical CPU id we're
                        booting on */
    /* version 3 fields below */
    fdt32_t size_dt_strings;     /* size of the strings block */

    /* version 17 fields below */
    fdt32_t size_dt_struct;         /* size of the structure block */
};


struct fdt_reserve_entry {
    fdt64_t address;
    fdt64_t size;
};


struct fdt_node_header {
    fdt32_t tag;
    char name[0];
};


struct fdt_property {
    fdt32_t tag;
    fdt32_t len;
    fdt32_t nameoff;
    char data[0];
};

4,struct device_node/struct property关键结构体

4.1 struct device_node

Device Tree中的每一个node节点经过kernel处理都会生成一个struct device_node的结构体,

struct device_node最终一般会被挂接到具体的struct device结构体。struct device_node结构体描述如下:

struct device_node {
    const char *name;              /* node的名称,取最后一次“/”和“@”之间子串 */
    const char *type;              /* device_type的属性名称,没有为<NULL> */
    phandle phandle;               /* phandle属性值 */
    const char *full_name;        /* 指向该结构体结束的位置,存放node的路径全名,例如:/chosen */
    struct fwnode_handle fwnode;
    struct    property *properties;  /* 指向该节点下的第一个属性,其他属性与该属性链表相接 */
    struct    property *deadprops;   /* removed properties */
    struct    device_node *parent;   /* 父节点 */
    struct    device_node *child;    /* 子节点 */
    struct    device_node *sibling;  /* 姊妹节点,与自己同等级的node */
    struct    kobject kobj;            /* sysfs文件系统目录体现 */
    unsigned long _flags;          /* 当前node状态标志位,见/include/linux/of.h line124-127 */
    void    *data;
};
/* flag descriptions (need to be visible even when !CONFIG_OF) */
#define OF_DYNAMIC        1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED       2 /* node has been detached from the device tree*/
#define OF_POPULATED      3 /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */

4.2 struct property

kernel会根据Device Tree的结构解析出kernel能够使用的struct property结构体。

kernel根据Device Tree中所有的属性解析出数据填充struct property结构体。struct property 结构体描述如下:

struct property {
    char    *name;
    int    length;
    void    *value;
    struct property *next; //kernel根据Device Tree的文件结构信息转换成struct property结构体,并将同一个node节点下面的所有属性通过property.next指针进行链接,形成一个单链表
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
    unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
    struct bin_attribute attr;
#endif
};

5,setup_machine_fdt

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: device tree blob的虚拟地址
    initial_boot_params = params;
    //计算扁平化设备树的crc32校验码
    of_fdt_crc32 = crc32_be(~0, initial_boot_params,
                fdt_totalsize(initial_boot_params));
    return true;
}

扫描设备树中的各个子节点:

void __init early_init_dt_scan_nodes(void)
{
    int rc = 0;

    /* Retrieve various information from the /chosen node */
    rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
    if (!rc)
        pr_warn("No chosen node found, continuing without\n");

    /* 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);

扫描展开之前的flattened  device tree,对于找到的每一个node调用it函数,data作为it函数的一个参数。

5.1 early_init_dt_scan_chosen

dts example :

chosen {
    bootargs = "console=ttySAC0,115200n8 root=/dev/mmcblk0p1 rw rootwait ignore_loglevel earlyprintk";
};

解析过程:

    /* Retrieve various information from the /chosen node */
    rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
                     int depth, void *data)
{
    int l = 0;
    const char *p = NULL;
    const void *rng_seed;
    char *cmdline = data;

    //扫描chosen节点,打印节点的名字和节点在设备树中的深度
    pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);

    //chosen节点的深度为1,cmdline指针变量指向bootcmd信息,地址不能为空
    if (depth != 1 || !cmdline ||
        (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
        return 0;

    //从扁平设备树中获取initrd的位置
    early_init_dt_check_for_initrd(node);

    ......

    /* Retrieve command line unless forcing */
    //从bootargs属性中读取cmdline
    if (read_dt_cmdline)
        p = of_get_flat_dt_prop(node, "bootargs", &l);

    ......

    pr_debug("Command line is: %s\n", (char *)data);
}

解析出来的command line 存储在全局变量boot_command_line中,作为bootloader启动过程中向kernel的传参。

/* Untouched command line saved by arch-specific code. */
char __initdata boot_command_line[COMMAND_LINE_SIZE];

5.2 early_init_dt_scan_root

    /* Initialize {size,address}-cells info */
    of_scan_flat_dt(early_init_dt_scan_root, NULL);
/**
* early_init_dt_scan_root - fetch the top level address and size cells
*/
int __init early_init_dt_scan_root(unsigned long node, const char *uname,
                   int depth, void *data)
{
    const __be32 *prop;

    //扫描根节点的#address-cells 和 #size-cell属性,根节点深度为0
    if (depth != 0)
        return 0;

    dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
    dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;

    //读取根节点的"#size-cells" property
    prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
    if (prop)
        //使用的设备树dtb文件是以大端序方式存储的,转换为cpu字节序,如果CPU字节序也为大端模式直接返回
        dt_root_size_cells = be32_to_cpup(prop);
    pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);

    //读取根节点的"#address-cells" property
    prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
    if (prop)
        dt_root_addr_cells = be32_to_cpup(prop);
    pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);

    /* break now */
    return 1;
}

解析出来的根节点的#address-cells 和 #size-cells 保存在全局变量中,用来描述地址的属性,即地址的起始位置和所占内存大小。

/* Everything below here references initial_boot_params directly. */
int __initdata dt_root_addr_cells;
int __initdata dt_root_size_cells;

5.3 early_init_dt_scan_memory

dts example:

memory
memory { device_type = "memory"; reg = <0 0 0 0>; };

解析过程:

    /* Setup memory, calling early_init_dt_add_memory_arch */
    of_scan_flat_dt(early_init_dt_scan_memory, NULL);
/**
* early_init_dt_scan_memory - Look for and parse memory nodes
*/
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                     int depth, void *data)
{
    const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
    const __be32 *reg, *endp;
    int l;
    bool hotpluggable;

    //device_type需要是memory
    /* We are scanning "memory" nodes only */
    if (type == NULL || strcmp(type, "memory") != 0)
        return 0;

    endp = reg + (l / sizeof(__be32));
    hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);

    pr_debug("memory scan node %s, reg size %d,\n", uname, l);

    //根据address-cells 和 size-cells解析memory reg
    while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
        u64 base, size;

        base = dt_mem_next_cell(dt_root_addr_cells, &reg);
        size = dt_mem_next_cell(dt_root_size_cells, &reg);

        if (size == 0)
            continue;
        pr_debug(" - %llx ,  %llx\n", (unsigned long long)base,
            (unsigned long long)size);

        //memblock_add(base, size)
        early_init_dt_add_memory_arch(base, size);

        if (!hotpluggable)
            continue;
        
        //memblock_reserve(base, size)
        if (early_init_dt_mark_hotplug_memory_arch(base, size))
            pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",
                base, base + size);
    }

    return 0;
}

扫描具有device_type = “memory”属性的/memory或者/memory@0节点下面的reg属性值,并把相关信息保存在meminfo中,全局变量meminfo保存了系统内存相关的信息。

所有设备树都需要一个memory设备节点,它描述了系统的物理内存布局。如果系统有多个内存块,可以创建多个memory节点,或者可以在单个memory节点的reg属性中指定这些地址范围和内存空间大小。

6,arm64_memblock_init

dts example:

memreserve
/memreserve/ 0x0000a800 0x000f5800;


reserved-memory
reserved_memory: reserved-memory {              
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;

    hyp_mem: hyp_region@80000000 {
        no-map;
        reg = <0x0 0x80000000 0x0 0x600000>;
    };

    xbl_dtlog_mem: xbl_dtlog_region@80600000 {
        no-map;
        reg = <0x0 0x80600000 0x0 0x40000>;
    };

    xbl_ramdump_mem: xbl_ramdump_region@80640000 {
        no-map;
        reg = <0x0 0x80640000 0x0 0x1c0000>;
    };

    aop_image_mem: aop_image_region@80800000 {
        no-map;
        reg = <0x0 0x80800000 0x0 0x60000>;
    };
};

解析过程:

/**
* early_init_fdt_scan_reserved_mem() - create reserved memory regions
*
* This function grabs memory from early allocator for device exclusive use
* defined in device tree structures. It should be called by arch specific code
* once the early allocator (i.e. memblock) has been fully activated.
*/
void __init early_init_fdt_scan_reserved_mem(void)
{
    int n;
    u64 base, size;

    if (!initial_boot_params)
        return;

    /* Process header /memreserve/ fields */
    // 解析/memreserve/,告诉内核哪一些内存空间需要被保留而不应该被系统覆盖使用,因为在内核启动时常常需要动态申请大量的内存空间,只有提前进行注册,用户需要使用的内存才不会被系统征用而造成数据覆盖
    for (n = 0; ; n++) {
        fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
        if (!size)
            break;
        early_init_dt_reserve_memory_arch(base, size, false);
    }
    
    // 解析"reserved-memory"节点,分配保留空间
    of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
    fdt_init_reserved_mem();
}

有时我们需要在 Linux 内核中预留一部分内存空间用作特殊用途(给安全模块使用,给其它处理器使用,或是给特定的驱动程序使用等),在 Device Tree 中有提供两种方法对预留内存进行配置:memreserve 和 reserved-memory。

1) memreserve

memreserve 的使用方法比较简单,如下所示,会将从地址 0x40000000 开始共 1MB 的内存空间预留出来:

/memreserve/ 0x40000000 0x00100000;

使用 memreserve 预留出来的内存一般无法再被 Linux 系统使用(当然,也可以通过特殊方法让代码固定访问该地址,但这种并非标准用法,在此不展开描述)。

2) reserved-memory

reserved-memory 框架提供了更多样的使用方法,并且与内核的DMA API 和 CMA框架紧密联系。

推荐先阅读一下内核自带的文档 Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt,里面有其详细的语法说明和注意事项(例如 reserved-memory 节点中的 #address-cells 和 #size-cells 的值需要与根节点的保持一致)。

7,unflatten_device_tree

Device Tree的解析首先从unflatten_device_tree()开始,代码列出如下:

/**
* unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
*/
void __init unflatten_device_tree(void)
{
    //参数initial_boot_params指向Device Tree在内存中的首地址,of_root在经过该函数处理之后,会指向根节点(of_root节点的树结构)
    //early_init_dt_alloc_memory_arch是一个函数指针,为struct device_node和struct property结构体分配内存的回调函数(callback)
    __unflatten_device_tree(initial_boot_params, NULL, &of_root,
                early_init_dt_alloc_memory_arch, false);

    /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
    of_alias_scan(early_init_dt_alloc_memory_arch);

    unittest_unflatten_overlay_base();
}
/**
* __unflatten_device_tree - create tree of device_nodes from flat blob
*
* unflattens a device-tree, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used.
* @blob: The blob to expand
* @dad: Parent device node
* @mynodes: The device_node tree created by the call
* @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree
* @detached: if true set OF_DETACHED on @mynodes
*
* Returns NULL on failure or the memory chunk containing the unflattened
* device tree on success.
*/
void *__unflatten_device_tree(const void *blob,
                  struct device_node *dad,
                  struct device_node **mynodes,
                  void *(*dt_alloc)(u64 size, u64 align),
                  bool detached)
{
    int size;
    void *mem;

    pr_debug(" -> unflatten_device_tree()\n");

    if (!blob) {
        pr_debug("No device tree pointer\n");
        return NULL;
    }

    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));
    
    //检查dtb header是否有效
    if (fdt_check_header(blob)) {
        pr_err("Invalid device tree blob header\n");
        return NULL;
    }

    /* First pass, scan for size */
    //第一次是为了得到Device Tree转换成struct device_node和struct property结构体需要分配的内存大小
    size = unflatten_dt_nodes(blob, NULL, dad, NULL);
    if (size < 0)
        return NULL;

    size = ALIGN(size, 4);
    pr_debug("  size is %d, allocating...\n", size);

    /* Allocate memory for the expanded device tree */
    mem = dt_alloc(size + 4, __alignof__(struct device_node));
    if (!mem)
        return NULL;

    memset(mem, 0, size);

    *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);

    pr_debug("  unflattening %p...\n", mem);

    /* Second pass, do actual unflattening */
    //第二次调用才是具体填充每一个struct device_node和struct property结构体
    unflatten_dt_nodes(blob, mem, dad, mynodes);
    if (be32_to_cpup(mem + size) != 0xdeadbeef)
        pr_warn("End of tree marker overwritten: %08x\n",
            be32_to_cpup(mem + size));

    if (detached && mynodes) {
        of_node_set_flag(*mynodes, OF_DETACHED);
        pr_debug("unflattened tree is detached\n");
    }

    pr_debug(" <- unflatten_device_tree()\n");
    return mem;
}

展开设备树节点unflatten_dt_nodes:

/**
* unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
* @blob: The parent device tree blob
* @mem: Memory chunk to use for allocating device nodes and properties
* @dad: Parent struct device_node
* @nodepp: The device_node tree created by the call
*
* It returns the size of unflattened device tree or error code
*/
static int unflatten_dt_nodes(const void *blob,
                  void *mem,
                  struct device_node *dad,
                  struct device_node **nodepp)
{
    struct device_node *root;
    int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH    64
    struct device_node *nps[FDT_MAX_DEPTH];
    void *base = mem;
    //计算展开的设备树占用内存空间大小的时候dryrun为1
    bool dryrun = !base;

    if (nodepp)
        *nodepp = NULL;

    /*
     * We're unflattening device sub-tree if @dad is valid. There are
     * possibly multiple nodes in the first level of depth. We need
     * set @depth to 1 to make fdt_next_node() happy as it bails
     * immediately when negative @depth is found. Otherwise, the device
     * nodes except the first one won't be unflattened successfully.
     */
    if (dad)
        depth = initial_depth = 1;

    root = dad;
    nps[depth] = dad;

    //遍历dtb,解析每一个device_node和property
    for (offset = 0;
         offset >= 0 && depth >= initial_depth;
         offset = fdt_next_node(blob, offset, &depth)) {
        //子节点的深度不能超过最大深度64
        if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH - 1))
            continue;

        if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
            !of_fdt_device_is_available(blob, offset))
            continue;

        //填充节点
        if (!populate_node(blob, offset, &mem, nps[depth],
                   &nps[depth+1], dryrun))
            return mem - base;

        //根节点 of_root
        if (!dryrun && nodepp && !*nodepp)
            *nodepp = nps[depth+1];
        if (!dryrun && !root)
            root = nps[depth+1];
    }

    if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
        pr_err("Error %d processing FDT\n", offset);
        return -EINVAL;
    }

    /*
     * Reverse the child list. Some drivers assumes node order matches .dts
     * node order
     */
    if (!dryrun)
        reverse_nodes(root);

    return mem - base;
}

填充节点:

static bool populate_node(const void *blob,
              int offset,
              void **mem,
              struct device_node *dad,
              struct device_node **pnp,
              bool dryrun)
{
    struct device_node *np;
    const char *pathp;
    unsigned int l, allocl;
    
    //获取节点的名字
    pathp = fdt_get_name(blob, offset, &l);
    if (!pathp) {
        *pnp = NULL;
        return false;
    }

    //device node name以 '\0'为结束符
    allocl = ++l;

    //为device_node 和 节点名字 分配内存空间
    np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
                __alignof__(struct device_node));
    if (!dryrun) {
        char *fn;
        //初始化node,设置node的kobj和fwnode->ops
        of_node_init(np);
        np->full_name = fn = ((char *)np) + sizeof(*np);
        
        //设置node的full_name
        memcpy(fn, pathp, l);

        //设置node的父亲节点和兄弟节点
        if (dad != NULL) {
            np->parent = dad;
            np->sibling = dad->child;
            dad->child = np;
        }
    }
    
    //填充节点的属性
    populate_properties(blob, offset, mem, np, pathp, dryrun);
    if (!dryrun) {
        np->name = of_get_property(np, "name", NULL);
        if (!np->name)
            np->name = "<NULL>";
    }

    *pnp = np;
    return true;
}

填充节点的属性:

static void populate_properties(const void *blob,
                int offset,
                void **mem,
                struct device_node *np,
                const char *nodename,
                bool dryrun)
{
    struct property *pp, **pprev = NULL;
    int cur;
    bool has_name = false;

    pprev = &np->properties;
    //遍历该节点下的所有属性
    for (cur = fdt_first_property_offset(blob, offset);
         cur >= 0;
         cur = fdt_next_property_offset(blob, cur)) {
        const __be32 *val;
        const char *pname;
        u32 sz;
        
        //获取属性的名字和值 key = value
        val = fdt_getprop_by_offset(blob, cur, &pname, &sz);
        if (!val) {
            pr_warn("Cannot locate property at 0x%x\n", cur);
            continue;
        }

        if (!pname) {
            pr_warn("Cannot find property name at 0x%x\n", cur);
            continue;
        }

        //如果有"name" 属性,做个标记
        if (!strcmp(pname, "name"))
            has_name = true;

        //为属性分配内存空间
        pp = unflatten_dt_alloc(mem, sizeof(struct property),
                    __alignof__(struct property));
        if (dryrun)
            continue;

        /* 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.
         */
        //对于"phandle"属性和"linux,phandle"属性,直接填充struct device_node的phandle字段,不放在属性链表中
        if (!strcmp(pname, "phandle") ||
            !strcmp(pname, "linux,phandle")) {
            if (!np->phandle)
                np->phandle = be32_to_cpup(val);
        }

        /* And we process the "ibm,phandle" property
         * used in pSeries dynamic device tree
         * stuff
         */
        if (!strcmp(pname, "ibm,phandle"))
            np->phandle = be32_to_cpup(val);

        //填充property 结构体成员
        pp->name   = (char *)pname;
        pp->length = sz;
        pp->value  = (__be32 *)val;
        //加入属性链表
        *pprev     = pp;
        pprev      = &pp->next;
    }

    /* With version 0x10 we may not have the name property,
     * recreate it here from the unit name if absent
     */
    //为每个node节点添加一个name的属性
    //node的名称,取最后一次“/”和“@”之间子串
    if (!has_name) {
        const char *p = nodename, *ps = p, *pa = NULL;
        int len;

        while (*p) {
            if ((*p) == '@')
                pa = p;
            else if ((*p) == '/')
                ps = p + 1;
            p++;
        }

        if (pa < ps)
            pa = p;
        len = (pa - ps) + 1;
        pp = unflatten_dt_alloc(mem, sizeof(struct property) + len,
                    __alignof__(struct property));
        if (!dryrun) {
            pp->name   = "name";
            pp->length = len;
            pp->value  = pp + 1;
            *pprev     = pp;
            pprev      = &pp->next;
            memcpy(pp->value, ps, len - 1);
            ((char *)pp->value)[len - 1] = 0;
            pr_debug("fixed up name for %s -> %s\n",
                 nodename, (char *)pp->value);
        }
    }

    if (!dryrun)
        *pprev = NULL;
}

此后,内核就可以根据device_node来创建设备。

参考链接:

https://www.cnblogs.com/schips/p/linux_driver_dtb_to_device_node.html

Device Tree(四):文件结构解析

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值