Chinaunix首页 | 论坛 | 博客
  • 博客访问: 260052
  • 博文数量: 86
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 640
  • 用 户 组: 普通用户
  • 注册时间: 2018-10-15 14:13
个人简介

搭建一个和linux开发者知识共享和学习的平台

文章分类

全部博文(86)

文章存档

2023年(24)

2022年(27)

2019年(8)

2018年(27)

分类: LINUX

2023-02-10 14:29:09

初始化流程
初始化流程如下:


start_kernel
|--- setup_arch(&command_line)
|--- setup_machine_fdt
|--- early_init_dt_scan_nodes
|--- early_init_dt_scan_chosen  扫描 /chosen node, 并保存参数到boot_command_line
|--- early_init_dt_scan_root 获取根节点{size,address}-cells信息,保存到dt_root_size_cells和dt_root_addr_cells
|--- early_init_dt_scan_memory  扫描memory node,保存相关信息到meminfo中
|--- unflatten_device_tree() 生成 node
|--- rest_init
|--- kernel_thread(kernel_init, NULL, CLONE_FS); -> do_fork -> kernel_init
|--- kernel_init_freeable
|--- do_basic_setup
|--- driver_init
|--- of_core_init 主要是创建 kset(devicetree)
略...
|--- do_initcalls
|--- arch_initcall
|--- of_platform_default_populate_init  根据 node 生成 struct platform_device


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
早期初始化
调用路径:


start_kernel
|--- setup_arch(&command_line)
|--- setup_machine_fdt
|--- early_init_dt_scan_nodes
|--- early_init_dt_scan_chosen  扫描 /chosen node, 并保存参数到boot_command_line
|--- early_init_dt_scan_root 获取根节点{size,address}-cells信息,保存到dt_root_size_cells和dt_root_addr_cells
|--- early_init_dt_scan_memory  扫描memory node,保存相关信息到meminfo中
1
2
3
4
5
6
7
of_scan_flat_dt是遍历整个从DTB,为每个node调用传入的函数


void __init early_init_dt_scan_nodes(void)
{
    /* 扫描 /chosen node, 并保存参数到boot_command_line */
    of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);


    /* 获取根节点{size,address}-cells信息,保存到dt_root_size_cells和dt_root_addr_cells */
    of_scan_flat_dt(early_init_dt_scan_root, NULL);


    /* 扫描memory node,保存相关信息到meminfo中 */
    of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
1
2
3
4
5
6
7
8
9
10
11
early_init_dt_scan_chosen
具体代码就不贴了,就贴重点的:


/* Retrieve command line unless forcing */
if (read_dt_cmdline)
    p = of_get_flat_dt_prop(node, "bootargs", &l);


if (p != NULL && l > 0) {
    if (concat_cmdline) {
        int cmdline_len;
        int copy_len;
        strlcat(cmdline, " ", COMMAND_LINE_SIZE); /* 追加一个 " " */
        cmdline_len = strlen(cmdline);
        copy_len = COMMAND_LINE_SIZE - cmdline_len - 1;
        copy_len = min((int)l, copy_len);
        strncpy(cmdline + cmdline_len, p, copy_len); /* 追加到原来的cmdline */
        cmdline[cmdline_len + copy_len] = '\0';
    } else {
        strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE)); /* 直接覆盖原来的cmdline */
    }
}




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
early_init_dt_scan_root
获取根节点{size,address}-cells信息,保存到dt_root_size_cells和dt_root_addr_cells


没啥好说的


early_init_dt_scan_memory
/* 只扫描 “memory” 节点 */
if (strcmp(type, "memory") != 0)
return 0;


reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
    reg = of_get_flat_dt_prop(node, "reg", &l);
if (reg == NULL)
     return 0;


endp = reg + (l / sizeof(__be32));
pr_debug("memory scan node %s, reg size %d,\n", uname, l);
/* 扫描属性,为每个调用 early_init_dt_add_memory_arch 添加到memblock中 */
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
     u64 base, size;
    base = dt_mem_next_cell(dt_root_addr_cells, ®);
     size = dt_mem_next_cell(dt_root_size_cells, ®);
    if (size == 0)
        continue;
    pr_debug(" - %llx ,  %llx\n", (unsigned long long)base,
         (unsigned long long)size);
    early_init_dt_add_memory_arch(base, size);
}
return 0;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
early_init_dt_add_memory_arch里面就是做了一堆检查,然后调用 memblock_add(base, size); 将内存添加到系统中


生成node
struct device_node 用来抽象设备树中的一个节点,定义如下:


struct device_node {
    const char *name;
    const char *type;
    phandle phandle;
    const char *full_name; /* 从"/"开始的绝对路径name */
    struct fwnode_handle fwnode;


    struct  property *properties;
    struct  property *deadprops;    /* 如果需要删除某些属性,kernel并非真的删除,而是挂入到deadprops的列表 */
    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;
    struct of_irq_controller *irq_trans;
#endif
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unflatten_device_tree
|--- __unflatten_device_tree(initial_boot_params, NULL, &of_root, alloc, false)
|--- unflatten_dt_nodes(blob, NULL, dad, NULL) {BANNED}中国第一轮扫描,获取整体大小
|--- unflatten_dt_nodes(blob, mem, dad, mynodes) 第二轮扫描,进行实际的展开
|--- of_alias_scan
1
2
3
4
5
具体生成过程,略。


生成pdev
入口函数为:of_platform_default_populate_init,功能是根据 node 生成 struct platform_device


其主要调用路径为:


start_ketnel -> doinitcall -> arch_initcall
of_platform_default_populate_init (drivers/of/platform.c)
|--- of_find_node_by_path("/reserved-memory")
|--- of_find_compatible_node(... "ramoops")  # 优先处理 compatible="ramoops"的 "/reserved-memory"子节点,我们假设没有
|--- of_platform_device_create(node,NULL,NULL);
|--- of_platform_default_populate(NULL, NULL, NULL);
|--- of_platform_populate(root, of_default_bus_match_table, NULL, NULL);
|--- of_platform_bus_create(child, of_default_bus_match_table, NULL, NULL, true); # 创建自己的dev并遍历子节点
|--- for_each_child_of_node(root, child) # 遍历子节点
|--- of_platform_device_create_pdata(bus, NULL, NULL, NULL)
|--- for_each_child_of_node(bus, child) # 遍历子节点
|--- of_platform_bus_create(child, match, NULL, &dev->dev, true);
|--- of_node_put(child); # 添加引用计数
|--- kobject_put(&child->kobj);
            |--- of_node_set_flag(bus, OF_POPULATED_BUS)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
所创建的节点结构体为:


 struct platform_device {
    const char  *name;
    int      id;
    bool        id_auto;
    struct device   dev;
    u32     num_resources;
    struct resource *resource;


    const struct platform_device_id *id_entry;
    char *driver_override; /* Driver name to force a match */


    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;


    /* arch specific additions */
    struct pdev_archdata    archdata;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
根据上面的流程,我们可知 of_platform_bus_create 是{BANNED}最佳重要的函数。


它会创建自己的节点并递归处理子节点。


of_platform_bus_create函数:


static int of_platform_bus_create(struct device_node *bus,
                    const struct of_device_id *matches,
                    const struct of_dev_auxdata *lookup,
                    struct device *parent, bool strict)
{
    const struct of_dev_auxdata *auxdata;
    struct device_node *child;
    struct platform_device *dev;
    const char *bus_id = NULL;
    void *platform_data = NULL;
    int rc = 0;


    /* 如果 node没有 compatible 属性就退出 */
    if (strict && (!of_get_property(bus, "compatible", NULL))) {
        pr_debug("%s() - skipping %s, no compatible prop\n",
             __func__, bus->full_name);
        return 0;
    }


    /* 如果已经创建过,则直接返回 */
    if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
        pr_debug("%s() - skipping %s, already populated\n",
            __func__, bus->full_name);
        return 0;
    }
    /* 检查是不是需要特殊处理的节点, lookup参数传进来为NULL 忽略即可 */
    auxdata = of_dev_lookup(lookup, bus);
    if (auxdata) {
        bus_id = auxdata->name;
        platform_data = auxdata->platform_data;
    }
/* 特殊处理,略... */
    if (of_device_is_compatible(bus, "arm,primecell")) {
        /*
         * Don't return an error here to keep compatibility with older
         * device tree files.
         */
        of_amba_device_create(bus, bus_id, platform_data, parent);
        return 0;
    }


    /* 进行具体的 struct platform_device 节点创建 */
    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
    if (!dev || !of_match_node(matches, bus))
        return 0;


    /* 递归处理子节点 */
    for_each_child_of_node(bus, child) {
        pr_debug("   create child: %s\n", child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
            of_node_put(child);
            break;
  }
    }
    /* 标记该节点已经处理 */
    of_node_set_flag(bus, OF_POPULATED_BUS);
 
    return rc;


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
函数概要:


of_node_check_flag(bus, OF_POPULATED_BUS):检查是否已经处理过
of_dev_lookup(lookup, bus):检查是否需要特殊处理
bus_id = auxdata->name;
platform_data = auxdata->platform_data;
of_platform_device_create_pdata(bus, bus_id, platform_data, parent):进行实际的节点从创建
for_each_child_of_node(bus, child):遍历子节点
of_platform_bus_create(child, matches, lookup, &dev->dev, strict):递归处理子节点
of_node_set_flag(bus, OF_POPULATED_BUS):标记该节点已经处理
of_platform_device_create_pdata函数指向具体节点的创建。


代码如下:


/* 
 * 创建一个 struct platform_device 节点
 */
static struct platform_device *of_platform_device_create_pdata(
                      struct device_node *np,
                      const char *bus_id,
                      void *platform_data,
                      struct device *parent)
{
    struct platform_device *dev;


    /* 判断 status 状态 */
    if (!of_device_is_available(np) ||
        of_node_test_and_set_flag(np, OF_POPULATED))
        return NULL;


    /* 分配并初始化*/
    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        goto err_clear_flag;


    dev->dev.bus = &platform_bus_type;
    dev->dev.platform_data = platform_data; /* 这个 platform_data 应该为NULL*/
    /* 
    * 扫描node的 dma-ranges 属性,并初始化 dev->coherent_dma_mask,dma_mask
    * 如果节点有 iommus 属性
    * dev->archdata.mapping = sunxi_mapping (arm_setup_iommu_dma_ops 和具体架构有关)
    * dev->archdata.dma_ops = coherent ? &iommu_coherent_ops : &iommu_ops
    * 否则:
    * dev->archdata.dma_ops = coherent ? &arm_coherent_dma_ops : &arm_dma_ops;
    */
    of_dma_configure(&dev->dev, dev->dev.of_node);
    /* 没配置 CONFIG_GENERIC_MSI_IRQ_DOMAIN,此函数为空 */
    of_msi_configure(&dev->dev, dev->dev.of_node);


    if (of_device_add(dev) != 0) {
        of_dma_deconfigure(&dev->dev);
        platform_device_put(dev);
        goto err_clear_flag;
    }


    return dev;


err_clear_flag:
    of_node_clear_flag(np, OF_POPULATED);
    return NULL;
}


 struct platform_device *of_device_alloc(struct device_node *np,
                  const char *bus_id,
                  struct device *parent)
{
    struct platform_device *dev;
    int rc, i, num_reg = 0, num_irq;
    struct resource *res, temp_res;


/* 
* 分配并初始化一个 platform_device
* 会初始化以下成员:
* pdev.name
* pdev.id
* pdev.dev(device_initialize(&pdev.dev))
* pdev.dev.release = platform_device_release;
* 调用 arch_setup_pdev_archdata(&pdev); 该函数在ARM上为空
*/
    dev = platform_device_alloc("", -1);
    if (!dev)
        return NULL;


    /* 统计io资源:主要是检索 'reg'和 'reg-names' 属性 */
    while (of_address_to_resource(np, num_reg, &temp_res) == 0)
        num_reg++;
    /* 统计中断资源: 主要是检索 ‘interrupts’ 属性 */
    num_irq = of_irq_count(np);


    /* 
     * 填充上述资源到 pdev->resource
     * reg属性指定的地址 res的 flags = IORESOURCE_MEM
     * interrupts属性指定的地址 res 的flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
     */
    if (num_irq || num_reg) {
        res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
        if (!res) {
            platform_device_put(dev);
            return NULL;
        }


        dev->num_resources = num_reg + num_irq;
        dev->resource = res;
        for (i = 0; i < num_reg; i++, res++) {
            rc = of_address_to_resource(np, i, res);
            WARN_ON(rc);
        }
        if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
            pr_debug("not all legacy IRQ resources mapped for %s\n",
                 np->name);
    }
/* 
* 初始化 dev 的一些属性
*/
    dev->dev.of_node = of_node_get(np);
    dev->dev.fwnode = &np->fwnode;
    dev->dev.parent = parent ? : &platform_bus;


    if (bus_id)
        dev_set_name(&dev->dev, "%s", bus_id);
    else
        of_device_make_bus_id(&dev->dev);


    return dev;
}


void of_device_make_bus_id(struct device *dev)
{
    struct device_node *node = dev->of_node;
    const __be32 *reg;
    u64 addr;
    const char __maybe_unused *name;


    /* Construct the name, using parent nodes if necessary to ensure uniqueness */
    while (node->parent) {
        /*
         * If the address can be translated, then that is as much
         * uniqueness as we need. Make it the first component and return
         */
        reg = of_get_property(node, "reg", NULL);
        if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
            /* 
             * 一般情况都是走这里
             * 所以可看看出 Name = 'reg_addr.node_name.dev_name'
             * 此时 dev_name = ""
             */
            dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
                     (unsigned long long)addr, node->name,
                     dev_name(dev));
            return;
        }


        /* format arguments only used if dev_name() resolves to NULL */
        dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
                 strrchr(node->full_name, '/') + 1, dev_name(dev));
        node = node->parent;
    }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
小结
小结:


设备树初始化时,会给每个node创建一个结构体struct platform_device,且默认初始化以下成员
dev->dev.bus = &platform_bus_type;
dev_set_name(dev, name),name生成规则:reg地址.node_name
dev->dev.dev.release = platform_device_release
初始化 dev->resource 和 dev->num_resources:
统计MEM资源(检索 reg和 reg-names 属性),可使用 platform_get_resource(pdev, IORESOURCE_MEM, index)获取
统计IRQ资源(检索 interrupts 属性),可使用 platform_get_irq(pdev, index)获取
初始化 dev->archdata.dma_ops(比较重要,影响后续的dma_alloc_xxx函数),有以下4种情况(这里以ARM架构为例,其余架构差不多)
有coherent属性(即使在设备树有 dma-coherent属性)&& 有iommu(即设备树中有iommus 属性)-> 赋值为 iommu_coherent_ops
有coherent属性(即使在设备树有 dma-coherent属性)&& 无iommu(即设备树中有iommus 属性)-> 赋值为 iommu_ops
无coherent属性(即使在设备树有 dma-coherent属性)&& 有iommu(即设备树中有iommus 属性)-> 赋值为 arm_coherent_dma_ops
无coherent属性(即使在设备树有 dma-coherent属性)&& 无iommu(即设备树中有iommus 属性)-> 赋值为 arm_dma_ops
————————————————
版权声明:本文为CSDN博主「心情复杂i」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44495904/article/details/127938071
阅读(340) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~