设备树(三)——设备树配置信息处理

本文详细介绍了Linux内核启动过程中,如何从DTB(Device Tree Blob)中解析设备配置信息,并将其转换为设备树(device tree)的过程。包括bootargs的获取、地址和大小单元的确定、内存设置、DTB区域的保留、以及设备树的展开和属性填充等关键步骤。

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

 

1.函数调用过程:
start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        mdesc = setup_machine_fdt(__atags_pointer);  // arch/arm/kernel/devtree.c
                    early_init_dt_scan_nodes();      // drivers/of/ftd.c
                        /* Retrieve various information from the /chosen node */取出choosen属性的bootargs
                        of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

跳转到early_init_dt_scan_chosen,判断这个节点是否为chosen节点,如果不是则返回0,继续下一个节点

                        /* Initialize {size,address}-cells info */   处理#adress_cells  size_cells的值
                        of_scan_flat_dt(early_init_dt_scan_root, NULL);

                        /* Setup memory, calling early_init_dt_add_memory_arch *//设置内存,从根节点的memory节点取出reg所指定的base值和size值
                        of_scan_flat_dt(early_init_dt_scan_memory, NULL);

a. /chosen节点中bootargs属性的值, 存入全局变量: boot_command_line
b. 确定根节点的这2个属性的值: #address-cells, #size-cells
   存入全局变量: dt_root_addr_cells, dt_root_size_cells
c. 解析/memory中的reg属性, 提取出"base, size", 最终调用memblock_add(base, size);

 

2._dtb转换为device_node(unflatten)

函数调用过程:
start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        arm_memblock_init(mdesc);   // arch/arm/kernel/setup.c
            early_init_fdt_reserve_self();
                    /* Reserve the dtb region */
                    // 把DTB所占区域保留下来, 即调用: memblock_reserve
                    early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
                                      fdt_totalsize(initial_boot_params),
                                      0);           
            early_init_fdt_scan_reserved_mem();  // 根据dtb中的memreserve信息, 调用memblock_reserve
            
        unflatten_device_tree();    // arch/arm/kernel/setup.c
            __unflatten_device_tree(initial_boot_params, NULL, &of_root,
                        early_init_dt_alloc_memory_arch, false);            // drivers/of/fdt.c
                
                /* First pass, scan for size */
                size = unflatten_dt_nodes(blob, NULL, dad, NULL);
                
                /* Allocate memory for the expanded device tree */
                mem = dt_alloc(size + 4, __alignof__(struct device_node));
                
                /* Second pass, do actual unflattening */
                unflatten_dt_nodes(blob, mem, dad, mynodes);
                    populate_node
                        np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
                                    __alignof__(struct device_node));
                        
                        np->full_name = fn = ((char *)np) + sizeof(*np);
                        
                        populate_properties
                                pp = unflatten_dt_alloc(mem, sizeof(struct property),
                                            __alignof__(struct property));
                            
                                pp->name   = (char *)pname;
                                pp->length = sz;
                                pp->value  = (__be32 *)val;

a. 在DTB文件中, 
   每一个节点都以TAG(FDT_BEGIN_NODE, 0x00000001)开始, 节点内部可以嵌套其他节点,
   每一个属性都以TAG(FDT_PROP, 0x00000003)开始

b. 每一个节点都转换为一个device_node结构体:
        struct device_node {
            const char *name;  // 来自节点中的name属性, 如果没有该属性, 则设为"NULL"
            const char *type;  // 来自节点中的device_type属性, 如果没有该属性, 则设为"NULL"
            phandle phandle;
            const char *full_name;  // 节点的名字, node-name[@unit-address]
            struct fwnode_handle fwnode;

            struct  property *properties;  // 节点的属性
            struct  property *deadprops;    /* removed properties */
            struct  device_node *parent;   // 节点的父亲
            struct  device_node *child;    // 节点的孩子(子节点)
            struct  device_node *sibling;  // 节点的兄弟(同级节点)
        #if defined(CONFIG_OF_KOBJ)
            struct  kobject kobj;
        #endif
            unsigned long _flags;
            void    *data;
        #if defined(CONFIG_SPARC)
            const char *path_component_name;
            unsigned int unique_id;
            struct of_irq_controller *irq_trans;
        #endif
        };

c. device_node结构体中有properties, 用来表示该节点的属性
   每一个属性对应一个property结构体:
        struct property {

           bool deleted;
            char    *name;    // 属性名字, 指向dtb文件中的字符串
           
struct data val;;   // 属性值, 指向dtb文件中value所在位置, 数据仍以big endian存储
            struct property *next;
           
 struct label *labels;

        };1

static char *label[10] = {
    "DMA",
    "IRQ-0",
    "IRQ-1",
    "OCERR",
    "PABRT",
    "RIPRR",
    "PPERR",
    "FTRGT",
    "RISCI",
    "RACK"
};

例如:



   
d. 这些device_node构成一棵树, 根节点为: of_root

### MTK 设备树配置方法 对于MTK设备而言,设备树(Device Tree, DT)用于描述硬件特性并提供给内核以便其能够适当地初始化和驱动这些硬件组件。在MTK平台上构建和修改设备树通常涉及以下几个方面[^1]: #### 编写设备树源文件(DTS) 设备树源文件采用`.dts`扩展名编写,这是一种人类可读的ASCII文本格式。该文件定义了节点和属性来表示硬件资源及其关系。 ```c /dts-v1/; /plugin/; / { compatible = "mediatek,<soc_name>"; cpus { /* CPU 节点 */ ... }; memory@0 { /* 内存节点 */ device_type = "memory"; reg = <...>; }; }; ``` 上述代码片段展示了如何创建一个基本框架下的DTS文件结构,其中包含了CPU以及内存的信息声明。 #### 修改现有设备树 当需要调整特定外设参数或添加新功能支持时,则需编辑现有的.dtsi文件——这是预编译好的通用部分,或者是项目特有的.dts文件。例如,在处理显示控制器设置时可能会涉及到如下操作: ```c &disp_sys { status = "okay"; lcdif: lcd-controller@<address> { pinctrl-names = "default"; pinctrl-0 = <&lcd_pins_a>; // 定义引脚控制 port { disp_out: endpoint { remote-endpoint = <&dsi_in>; }; }; }; } ``` 此段落说明了怎样激活显示器子系统并将它连接到其他模块上,通过指定状态为“okay”,意味着启用这个组件;而端口(port)内的endpoint则用来建立与其他实体之间的链接。 #### 构建与加载自定义DTB 完成所有必要的更改之后,利用工具链中的dtc命令可以将.DTS转换成二进制形式(.dtb),随后将其放置于适当位置供引导程序加载至RAM中执行解析工作。具体过程可能依赖具体的开发环境有所不同,但一般遵循以下模式: ```bash $ dtc -I dts -O dtb -o my_device_tree.dtb my_device_tree.dts ``` 这条指令会把名为`my_device_tree.dts`的源码转化为对应的设备树Blob(`.dtb`)文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值