• linux驱动之设备树(1)


    参考:linux设备驱动程序-设备树(1)-dtb转换成device_node - 牧野星辰 - 博客园

    内核启动后如何调用和解析设备树二进制文件dtb的

     在linux-3.4.y/init/main.c中,linux内核启动后调用start_kernel函数; 其中有setup_arch函数,对于arm平台会调用arch/arm/kernel/setup.c文件中的setup_arch();

    1. void __init setup_arch(char **cmdline_p)
    2. {
    3. struct machine_desc *mdesc;
    4. setup_processor();
    5. //根据传入的设备树dtb的首地址完成一些初始化操作;
    6. //__atags_pointer参数这个全局变量就是r2的寄存器值;是设备树在内存中的起始地址;
    7. mdesc = setup_machine_fdt(__atags_pointer);
    8. if (!mdesc)
    9. mdesc = setup_machine_tags(machine_arch_type);
    10. machine_desc = mdesc;
    11. machine_name = mdesc->name;
    12. if (mdesc->restart_mode)
    13. reboot_setup(&mdesc->restart_mode);
    14. init_mm.start_code = (unsigned long) _text;
    15. init_mm.end_code = (unsigned long) _etext;
    16. init_mm.end_data = (unsigned long) _edata;
    17. init_mm.brk = (unsigned long) _end;
    18. /* populate cmd_line too for later use, preserving boot_command_line */
    19. strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
    20. *cmdline_p = cmd_line;
    21. parse_early_param();
    22. sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
    23. sanity_check_meminfo();
    24. //内存相关,为设备树保留相应的内存空间,保证设备树dtb本身在内存中而不被覆盖,用户可在设备树中保留内存;
    25. arm_memblock_init(&meminfo, mdesc);
    26. ...
    27. //展开设备树,实际上就是解析dtb,并转换为struct device_node结构体;
    28. unflatten_device_tree();
    29. ...
    30. }

    1. setup_machine_fdt函数

     函数作用: 如果在r2中向内核传递了一个dtb,那么使用它来选择正确的machine_desc并设置系统。 

    1. struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
    2. {
    3. struct boot_param_header *devtree;
    4. struct machine_desc *mdesc, *mdesc_best = NULL;
    5. unsigned int score, mdesc_score = ~1;
    6. unsigned long dt_root;
    7. const char *model;
    8. if (!dt_phys)
    9. return NULL;
    10. //物理地址转虚拟地址;
    11. devtree = phys_to_virt(dt_phys);
    12. /* check device tree validity */
    13. //验证设备树有效性
    14. if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
    15. return NULL;
    16. /* Search the mdescs for the 'best' compatible value match */
    17. initial_boot_params = devtree;
    18. //获取根节点
    19. dt_root = of_get_flat_dt_root();
    20. //循环读取设备树根目录下的compatible属性;
    21. for_each_machine_desc(mdesc) {
    22. //mdesc->dt_compat 为设备树compatible字符数组
    23. score = of_flat_dt_match(dt_root, mdesc->dt_compat);
    24. if (score > 0 && score < mdesc_score) {
    25. mdesc_best = mdesc;
    26. mdesc_score = score;
    27. }
    28. }
    29. ...
    30. //获取根节点下的model属性
    31. model = of_get_flat_dt_prop(dt_root, "model", NULL);
    32. if (!model)
    33. model = of_get_flat_dt_prop(dt_root, "compatible", NULL);
    34. if (!model)
    35. model = "";
    36. pr_info("Machine: %s, model: %s\n", mdesc_best->name, model);
    37. //扫描设备树中的各节点
    38. //分别是处理choose节点的处理,root节点中除了子节点外的属性信息,memory节点;
    39. /* Retrieve various information from the /chosen node */
    40. of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
    41. /* Initialize {size,address}-cells info */
    42. of_scan_flat_dt(early_init_dt_scan_root, NULL);
    43. /* Setup memory, calling early_init_dt_add_memory_arch */
    44. of_scan_flat_dt(early_init_dt_scan_memory, NULL);
    45. /* Change machine number to match the mdesc we're using */
    46. __machine_arch_type = mdesc_best->nr;
    47. return mdesc_best;
    48. }

    其中的of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);语句是将boot_command_line作为回调函数early_init_dt_scan_chosen的参数传入,并拷贝从choose属性读取到的命令行参数到boot_command_line中;

     

    of_scan_flat_dt(early_init_dt_scan_root, NULL);语句是查找#size-cells和#address-cells属性,并赋值全局dt_root_size_cellsdt_root_addr_cells ;

    of_scan_flat_dt(early_init_dt_scan_memory, NULL);语句主要获取memory相关信息计算后,调用early_init_dt_add_memory_arch(base, size);分配空间;

    2. arm_memblock_init函数

    1. void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
    2. {
    3. int i;
    4. for (i = 0; i < mi->nr_banks; i++)
    5. memblock_add(mi->bank[i].start, mi->bank[i].size);
    6. /* Register the kernel text, kernel data and initrd with memblock. */
    7. memblock_reserve(__pa(_stext), _end - _stext);
    8. ...
    9. if (phys_initrd_size) {
    10. memblock_reserve(phys_initrd_start, phys_initrd_size);
    11. /* Now convert initrd to virtual addresses */
    12. initrd_start = __phys_to_virt(phys_initrd_start);
    13. initrd_end = initrd_start + phys_initrd_size;
    14. }
    15. arm_mm_memblock_reserve();
    16. arm_dt_memblock_reserve();
    17. arm_memblock_steal_permitted = false;
    18. memblock_allow_resize();
    19. memblock_dump_all();
    20. }

    主要是为设备树分配保留内存空间;

    3. unflatten_device_tree函数 

    1. void __init unflatten_device_tree(void)
    2. {
    3. __unflatten_device_tree(initial_boot_params, &allnodes,
    4. early_init_dt_alloc_memory_arch);
    5. /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
    6. of_alias_scan(early_init_dt_alloc_memory_arch);
    7. }

     

    __unflatten_device_tree函数中,扫描得出设备树转换成device node需要的空间,然后系统申请内存空间,第二次就进行真正的解析工作;代码如下:

    1. static void __unflatten_device_tree(struct boot_param_header *blob,
    2. struct device_node **mynodes,
    3. void * (*dt_alloc)(u64 size, u64 align))
    4. {
    5. unsigned long start, mem, size;
    6. struct device_node **allnextp = mynodes;
    7. ...
    8. /* 获取大小 */
    9. start = ((unsigned long)blob) +
    10. be32_to_cpu(blob->off_dt_struct);
    11. size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
    12. size = (size | 3) + 1;
    13. pr_debug(" size is %lx, allocating...\n", size);
    14. /* Allocate memory for the expanded device tree */
    15. //分配空间
    16. mem = (unsigned long)
    17. dt_alloc(size + 4, __alignof__(struct device_node));
    18. ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
    19. pr_debug(" unflattening %lx...\n", mem);
    20. /* Second pass, do actual unflattening */
    21. //实际解析工作
    22. start = ((unsigned long)blob) +
    23. be32_to_cpu(blob->off_dt_struct);
    24. unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
    25. ...
    26. }

    参数allnodes就是struct device_node指针,将其传入进入后,通过解析设备树对其赋值填充;

    of_alias_scan函数中:

    1. void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
    2. {
    3. struct property *pp;
    4. //获取根下的chosen节点属性
    5. of_chosen = of_find_node_by_path("/chosen");
    6. if (of_chosen == NULL)
    7. of_chosen = of_find_node_by_path("/chosen@0");
    8. //获取aliases节点
    9. of_aliases = of_find_node_by_path("/aliases");
    10. if (!of_aliases)
    11. return;
    12. //遍历所有aliases节点
    13. for_each_property_of_node(of_aliases, pp) {
    14. const char *start = pp->name;
    15. const char *end = start + strlen(start);
    16. struct device_node *np;
    17. struct alias_prop *ap;
    18. int id, len;
    19. /* Skip those we do not want to proceed */
    20. //过滤不想处理的
    21. if (!strcmp(pp->name, "name") ||
    22. !strcmp(pp->name, "phandle") ||
    23. !strcmp(pp->name, "linux,phandle"))
    24. continue;
    25. np = of_find_node_by_path(pp->value);
    26. if (!np)
    27. continue;
    28. /* walk the alias backwards to extract the id and work out
    29. * the 'stem' string */
    30. while (isdigit(*(end-1)) && end > start)
    31. end--;
    32. len = end - start;
    33. if (kstrtoint(end, 10, &id) < 0)
    34. continue;
    35. /* Allocate an alias_prop with enough space for the stem */
    36. //分配空间
    37. ap = dt_alloc(sizeof(*ap) + len + 1, 4);
    38. if (!ap)
    39. continue;
    40. ap->alias = start;
    41. //将所有aliases内容添加到链表
    42. of_alias_add(ap, np, id, start, len);
    43. }
    44. }

     

  • 相关阅读:
    宽表为什么横行?
    SciencePub学术 | Elsevier出版社SCIE&EI征稿中
    数据的使用、表关系的创建、Django框架的请求生命周期流程图
    做自媒体的素材都是在哪里找的呢?
    字符函数和字符串函数(C语言)
    Spring Security详细学习第二篇(授权,异常处理,跨域)
    环境分析检测小剂量移液用耐受硝酸盐酸PFA材质吸管特氟龙移液枪枪头
    华硕主板升级更新BIOS版本
    服务器开发系列(二)——Jetson Xavier NX
    彻底理解并解决服务器出现大量TIME_WAIT
  • 原文地址:https://blog.csdn.net/qq_39048131/article/details/126574307